mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-01-31 20:03:37 +00:00
feat: integrate ClaudeUsageService and update API routes for usage tracking
This commit is contained in:
@@ -45,6 +45,7 @@ import { getTerminalService } from "./services/terminal-service.js";
|
||||
import { SettingsService } from "./services/settings-service.js";
|
||||
import { createSpecRegenerationRoutes } from "./routes/app-spec/index.js";
|
||||
import { createClaudeRoutes } from "./routes/claude/index.js";
|
||||
import { ClaudeUsageService } from "./services/claude-usage-service.js";
|
||||
|
||||
// Load environment variables
|
||||
dotenv.config();
|
||||
@@ -112,6 +113,7 @@ const agentService = new AgentService(DATA_DIR, events);
|
||||
const featureLoader = new FeatureLoader();
|
||||
const autoModeService = new AutoModeService(events);
|
||||
const settingsService = new SettingsService(DATA_DIR);
|
||||
const claudeUsageService = new ClaudeUsageService();
|
||||
|
||||
// Initialize services
|
||||
(async () => {
|
||||
@@ -142,7 +144,7 @@ app.use("/api/workspace", createWorkspaceRoutes());
|
||||
app.use("/api/templates", createTemplatesRoutes());
|
||||
app.use("/api/terminal", createTerminalRoutes());
|
||||
app.use("/api/settings", createSettingsRoutes(settingsService));
|
||||
app.use("/api/claude", createClaudeRoutes());
|
||||
app.use("/api/claude", createClaudeRoutes(claudeUsageService));
|
||||
|
||||
// Create HTTP server
|
||||
const server = createServer(app);
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import { Router, Request, Response } from "express";
|
||||
import { ClaudeUsageService } from "../../services/claude-usage-service.js";
|
||||
|
||||
export function createClaudeRoutes(): Router {
|
||||
export function createClaudeRoutes(service: ClaudeUsageService): Router {
|
||||
const router = Router();
|
||||
const service = new ClaudeUsageService();
|
||||
|
||||
// Get current usage (fetches from Claude CLI)
|
||||
router.get("/usage", async (req: Request, res: Response) => {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useState, useEffect, useMemo } from "react";
|
||||
import { useState, useEffect, useMemo, useCallback } from "react";
|
||||
import {
|
||||
Popover,
|
||||
PopoverContent,
|
||||
@@ -29,7 +29,7 @@ export function ClaudeUsagePopover() {
|
||||
return !claudeUsageLastUpdated || Date.now() - claudeUsageLastUpdated > 2 * 60 * 1000;
|
||||
}, [claudeUsageLastUpdated]);
|
||||
|
||||
const fetchUsage = async (isAutoRefresh = false) => {
|
||||
const fetchUsage = useCallback(async (isAutoRefresh = false) => {
|
||||
if (!isAutoRefresh) setLoading(true);
|
||||
setError(null);
|
||||
try {
|
||||
@@ -38,7 +38,7 @@ export function ClaudeUsagePopover() {
|
||||
throw new Error("Claude API not available");
|
||||
}
|
||||
const data = await api.claude.getUsage();
|
||||
if (data.error) {
|
||||
if ("error" in data) {
|
||||
throw new Error(data.message || data.error);
|
||||
}
|
||||
setClaudeUsage(data);
|
||||
@@ -47,26 +47,20 @@ export function ClaudeUsagePopover() {
|
||||
} finally {
|
||||
if (!isAutoRefresh) setLoading(false);
|
||||
}
|
||||
};
|
||||
}, [setClaudeUsage]);
|
||||
|
||||
// Auto-fetch on mount if data is stale
|
||||
useEffect(() => {
|
||||
if (isStale) {
|
||||
fetchUsage(true);
|
||||
}
|
||||
}, []);
|
||||
}, [isStale, fetchUsage]);
|
||||
|
||||
useEffect(() => {
|
||||
// Initial fetch when opened
|
||||
if (open) {
|
||||
if (!claudeUsage) {
|
||||
if (!claudeUsage || isStale) {
|
||||
fetchUsage();
|
||||
} else {
|
||||
const now = Date.now();
|
||||
const stale = !claudeUsageLastUpdated || now - claudeUsageLastUpdated > 2 * 60 * 1000;
|
||||
if (stale) {
|
||||
fetchUsage(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,7 +75,7 @@ export function ClaudeUsagePopover() {
|
||||
return () => {
|
||||
if (intervalId) clearInterval(intervalId);
|
||||
};
|
||||
}, [open]);
|
||||
}, [open, claudeUsage, isStale, claudeRefreshInterval, fetchUsage]);
|
||||
|
||||
// Derived status color/icon helper
|
||||
const getStatusInfo = (percentage: number) => {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
// Type definitions for Electron IPC API
|
||||
import type { SessionListItem, Message } from "@/types/electron";
|
||||
import type { ClaudeUsageResponse } from "@/store/app-store";
|
||||
import { getJSON, setJSON, removeItem } from "./storage";
|
||||
|
||||
export interface FileEntry {
|
||||
@@ -483,7 +484,7 @@ export interface ElectronAPI {
|
||||
) => Promise<{ success: boolean; error?: string }>;
|
||||
};
|
||||
claude?: {
|
||||
getUsage: () => Promise<ClaudeUsage>;
|
||||
getUsage: () => Promise<ClaudeUsageResponse>;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ import type {
|
||||
SuggestionType,
|
||||
} from "./electron";
|
||||
import type { Message, SessionListItem } from "@/types/electron";
|
||||
import type { Feature } from "@/store/app-store";
|
||||
import type { Feature, ClaudeUsageResponse } from "@/store/app-store";
|
||||
import type {
|
||||
WorktreeAPI,
|
||||
GitAPI,
|
||||
@@ -1019,7 +1019,7 @@ export class HttpApiClient implements ElectronAPI {
|
||||
|
||||
// Claude API
|
||||
claude = {
|
||||
getUsage: (): Promise<any> => this.get("/api/claude/usage"),
|
||||
getUsage: (): Promise<ClaudeUsageResponse> => this.get("/api/claude/usage"),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -515,7 +515,7 @@ export interface AppState {
|
||||
}
|
||||
|
||||
// Claude Usage interface matching the server response
|
||||
export interface ClaudeUsage {
|
||||
export type ClaudeUsage = {
|
||||
sessionTokensUsed: number;
|
||||
sessionLimit: number;
|
||||
sessionPercentage: number;
|
||||
@@ -535,7 +535,15 @@ export interface ClaudeUsage {
|
||||
costUsed: number | null;
|
||||
costLimit: number | null;
|
||||
costCurrency: string | null;
|
||||
}
|
||||
|
||||
lastUpdated: string;
|
||||
userTimezone: string;
|
||||
};
|
||||
|
||||
// Response type for Claude usage API (can be success or error)
|
||||
export type ClaudeUsageResponse =
|
||||
| ClaudeUsage
|
||||
| { error: string; message?: string };
|
||||
|
||||
/**
|
||||
* Check if Claude usage is at its limit (any of: session >= 100%, weekly >= 100%, OR cost >= limit)
|
||||
|
||||
Reference in New Issue
Block a user