mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-02 21:49:42 +02:00
feat(usage): replay last-known plan usage in the SSE init snapshot
The header chip previously only repopulated on reload from per-browser localStorage, so a fresh browser (or cleared storage) stayed blank until a session next rendered telemetry. Store the latest broadcast telemetry process-wide (plan-usage-latest.ts) and include it as `planUsage` in getLightState — the per-connection SSE init snapshot — so handleInit paints the chip immediately on every fresh load / reconnect, authoritative over the localStorage restore. Null until the first telemetry of the process. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,22 @@
|
||||
/**
|
||||
* @fileoverview Process-wide last-known plan-usage telemetry (account-global).
|
||||
*
|
||||
* The status-telemetry route writes the latest broadcast value here; the SSE
|
||||
* init snapshot (`getLightState`) replays it so the header "Plan Usage Limits"
|
||||
* chip shows immediately on a fresh page load / SSE reconnect — before any new
|
||||
* statusline render arrives, and without relying on per-browser localStorage.
|
||||
*
|
||||
* Null until the first telemetry of the process; cleared naturally on restart.
|
||||
*
|
||||
* @module plan-usage-latest
|
||||
*/
|
||||
|
||||
let latest: Record<string, unknown> | null = null;
|
||||
|
||||
export function setLatestPlanUsage(value: Record<string, unknown>): void {
|
||||
latest = value;
|
||||
}
|
||||
|
||||
export function getLatestPlanUsage(): Record<string, unknown> | null {
|
||||
return latest;
|
||||
}
|
||||
@@ -2258,6 +2258,10 @@ class CodemanApp {
|
||||
this._serverCjkOverride = data.inputCjkForm || false;
|
||||
this._updateCjkInputState();
|
||||
|
||||
// Plan-usage chip: server's last-known telemetry, so it shows immediately on
|
||||
// a fresh load / reconnect (authoritative; wins over the localStorage restore).
|
||||
if (data.planUsage) this.updatePlanUsageChip(data.planUsage);
|
||||
|
||||
// Update version displays (header and toolbar)
|
||||
if (data.version) {
|
||||
const versionEl = this.$('versionDisplay');
|
||||
|
||||
@@ -23,6 +23,7 @@ import {
|
||||
type RawStatuslinePayload,
|
||||
} from '../../usage-telemetry.js';
|
||||
import { SessionStatusTelemetry } from '../sse-events.js';
|
||||
import { setLatestPlanUsage } from '../plan-usage-latest.js';
|
||||
import type { SessionPort, EventPort } from '../ports/index.js';
|
||||
|
||||
export function registerStatusTelemetryRoutes(app: FastifyInstance, ctx: SessionPort & EventPort): void {
|
||||
@@ -56,7 +57,9 @@ export function registerStatusTelemetryRoutes(app: FastifyInstance, ctx: Session
|
||||
if (!ctx.sessions.has(id)) lastSig.delete(id);
|
||||
}
|
||||
}
|
||||
ctx.broadcast(SessionStatusTelemetry, { sessionId, ...telemetry });
|
||||
const payload = { sessionId, ...telemetry };
|
||||
setLatestPlanUsage(payload); // replayed in the SSE init snapshot for fresh loads
|
||||
ctx.broadcast(SessionStatusTelemetry, payload);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -124,6 +124,7 @@ import {
|
||||
import type { EventLoopMonitorHandle } from '../utils/index.js';
|
||||
import { MAX_CONCURRENT_SESSIONS, MAX_SSE_CLIENTS } from '../config/map-limits.js';
|
||||
import { SseEvent } from './sse-events.js';
|
||||
import { getLatestPlanUsage } from './plan-usage-latest.js';
|
||||
import type { ScheduledRun } from './ports/index.js';
|
||||
import { registerAuthMiddleware, registerSecurityHeaders, registerHostGuard } from './middleware/auth.js';
|
||||
import { installRouteErrorHandler } from './route-error-handler.js';
|
||||
@@ -1627,6 +1628,7 @@ export class WebServer extends EventEmitter {
|
||||
subagents: subagentWatcher.getRecentSubagents(15), // 15 min to avoid stale agents
|
||||
timestamp: now,
|
||||
inputCjkForm: process.env.INPUT_CJK_FORM?.toUpperCase() === 'ON',
|
||||
planUsage: getLatestPlanUsage(), // last-known plan-usage telemetry, for the header chip on fresh load
|
||||
};
|
||||
|
||||
this.cachedLightState = { data: result, timestamp: now };
|
||||
|
||||
Reference in New Issue
Block a user