Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions apps/server/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ import { AutoModeService } from "./services/auto-mode-service.js";
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();
Expand Down Expand Up @@ -111,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 () => {
Expand Down Expand Up @@ -141,6 +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(claudeUsageService));

// Create HTTP server
const server = createServer(app);
Expand Down
43 changes: 43 additions & 0 deletions apps/server/src/routes/claude/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { Router, Request, Response } from "express";
import { ClaudeUsageService } from "../../services/claude-usage-service.js";

export function createClaudeRoutes(service: ClaudeUsageService): Router {
const router = Router();

// Get current usage (fetches from Claude CLI)
router.get("/usage", async (req: Request, res: Response) => {
try {
// Check if Claude CLI is available first
const isAvailable = await service.isAvailable();
if (!isAvailable) {
res.status(503).json({
error: "Claude CLI not found",
message: "Please install Claude Code CLI and run 'claude login' to authenticate"
});
return;
}

const usage = await service.fetchUsageData();
res.json(usage);
} catch (error) {
const message = error instanceof Error ? error.message : "Unknown error";

if (message.includes("Authentication required") || message.includes("token_expired")) {
res.status(401).json({
error: "Authentication required",
message: "Please run 'claude login' to authenticate"
});
} else if (message.includes("timed out")) {
res.status(504).json({
error: "Command timed out",
message: "The Claude CLI took too long to respond"
});
} else {
console.error("Error fetching usage:", error);
res.status(500).json({ error: message });
}
}
});

return router;
}
35 changes: 35 additions & 0 deletions apps/server/src/routes/claude/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/**
* Claude Usage types for CLI-based usage tracking
*/

export type ClaudeUsage = {
sessionTokensUsed: number;
sessionLimit: number;
sessionPercentage: number;
sessionResetTime: string; // ISO date string
sessionResetText: string; // Raw text like "Resets 10:59am (Asia/Dubai)"

weeklyTokensUsed: number;
weeklyLimit: number;
weeklyPercentage: number;
weeklyResetTime: string; // ISO date string
weeklyResetText: string; // Raw text like "Resets Dec 22 at 7:59pm (Asia/Dubai)"

sonnetWeeklyTokensUsed: number;
sonnetWeeklyPercentage: number;
sonnetResetText: string; // Raw text like "Resets Dec 27 at 9:59am (Asia/Dubai)"

costUsed: number | null;
costLimit: number | null;
costCurrency: string | null;

lastUpdated: string; // ISO date string
userTimezone: string;
};

export type ClaudeStatus = {
indicator: {
color: "green" | "yellow" | "orange" | "red" | "gray";
};
description: string;
};
Loading
Loading