mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-07 07:59:42 +02:00
fix(usage): make plan-usage chip work end-to-end + session-status footer
End-to-end testing on the real install surfaced several issues the unit tests missed: - Injection gate excluded real sessions: gated on workingDir under CASES_DIR, but sessions run in linked cases / real repos. Drop the gate (match updateCaseModel, which writes settings.local.json unconditionally). - statusLine curl failed on HTTPS: prod is loopback HTTPS with a self-signed cert; `curl -s` returns 000. Use `curl -sk` (loopback only). applyStatusLineConfig now also updates an out-of-date ours-command so the fix propagates. - Footer hijacked by limits: the in-terminal statusline now shows CURRENT SESSION status — `Opus 4.8 (1M context) in:562,411 out:1,188 ctx:56%` — while the account-wide plan limits live only in the header chip. - Chip blank after reload: persist last-known to localStorage and restore on load (account-global, slow-moving; 12h freshness guard). - Readability + color: per-window green/yellow/red by usage (<60 / 60–84 / ≥85), bolder labels and values. - Drop the renderIndexHtml strip (client-side reveal only, response-viewer pattern) — fixes server-index-title test fragility to local settings. Footer fields flow through context_window.total_input_tokens/total_output_tokens (schema + parser). Tests updated; verified live (footer, chip, colors, reload persistence) on the real install. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -298,10 +298,13 @@ export function registerSessionRoutes(
|
||||
}
|
||||
|
||||
// Plan-usage statusLine exporter (App Settings → Display → "Plan Usage
|
||||
// Limits"). Claude-only, and only inside Codeman-managed cases (never
|
||||
// arbitrary user repos, which may carry a hand-authored statusLine). Adds or
|
||||
// removes our exporter so toggling the setting off cleans up on next create.
|
||||
if ((body.mode ?? 'claude') === 'claude' && workingDir.startsWith(CASES_DIR + '/')) {
|
||||
// Limits"). Claude-only. Runs for ANY working dir — including linked cases /
|
||||
// real repos, which is where most sessions actually run — mirroring
|
||||
// updateCaseModel above (which likewise writes settings.local.json
|
||||
// unconditionally). Safe against a user's own statusLine: applyStatusLineConfig
|
||||
// only ever removes a statusLine that is OURS (marker-guarded), and adds/removes
|
||||
// ours so toggling the setting off cleans up on the next create.
|
||||
if ((body.mode ?? 'claude') === 'claude') {
|
||||
await applyStatusLineConfig(workingDir, body.statusLineTelemetry === true);
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,8 @@ import { StatusTelemetrySchema } from '../schemas.js';
|
||||
import { parseBody } from '../route-helpers.js';
|
||||
import {
|
||||
parseStatusTelemetry,
|
||||
formatStatusLineText,
|
||||
parseSessionStatus,
|
||||
formatSessionStatusText,
|
||||
telemetrySignature,
|
||||
type RawStatuslinePayload,
|
||||
} from '../../usage-telemetry.js';
|
||||
@@ -40,24 +41,28 @@ export function registerStatusTelemetryRoutes(app: FastifyInstance, ctx: Session
|
||||
return 'codeman';
|
||||
}
|
||||
|
||||
const telemetry = parseStatusTelemetry(data as RawStatuslinePayload | undefined);
|
||||
if (!telemetry) {
|
||||
// No plan-limit data yet (pre-first-response or non-subscriber auth).
|
||||
return 'codeman';
|
||||
}
|
||||
const payload = data as RawStatuslinePayload | undefined;
|
||||
|
||||
const sig = telemetrySignature(telemetry);
|
||||
if (lastSig.get(sessionId) !== sig) {
|
||||
lastSig.set(sessionId, sig);
|
||||
// Bound the map across long multi-session runs: prune dead sessions.
|
||||
if (lastSig.size > 256) {
|
||||
for (const id of [...lastSig.keys()]) {
|
||||
if (!ctx.sessions.has(id)) lastSig.delete(id);
|
||||
// Plan-usage limits (account-wide) → broadcast to the header chip, when
|
||||
// present and changed (the statusline fires on every assistant message).
|
||||
const telemetry = parseStatusTelemetry(payload);
|
||||
if (telemetry) {
|
||||
const sig = telemetrySignature(telemetry);
|
||||
if (lastSig.get(sessionId) !== sig) {
|
||||
lastSig.set(sessionId, sig);
|
||||
// Bound the map across long multi-session runs: prune dead sessions.
|
||||
if (lastSig.size > 256) {
|
||||
for (const id of [...lastSig.keys()]) {
|
||||
if (!ctx.sessions.has(id)) lastSig.delete(id);
|
||||
}
|
||||
}
|
||||
ctx.broadcast(SessionStatusTelemetry, { sessionId, ...telemetry });
|
||||
}
|
||||
ctx.broadcast(SessionStatusTelemetry, { sessionId, ...telemetry });
|
||||
}
|
||||
|
||||
return formatStatusLineText(telemetry);
|
||||
// In-terminal statusline footer → CURRENT SESSION status (model / tokens /
|
||||
// context %), NOT the plan limits. Available from the first render, even
|
||||
// before rate_limits appears.
|
||||
return formatSessionStatusText(parseSessionStatus(payload));
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user