feat(cockpit): quick wins — sync chip, cost budget guard, digest, recent commits (W3)
Four small, high-delight features that turn passive capture into glanceable signals.
- Sync chip on project cards: ahead/behind vs upstream + last-commit time, folded
into the existing concurrent per-repo metadata pass (git rev-list --count
--left-right @{u}...HEAD + git log -1 --format=%ct; no upstream → undefined, no route).
- Cost budget guard: COST_BUDGET_USD env (0 = off); a per-session one-shot latch
(Session.budgetNotified, cost is monotonic so never re-armed) fires a single push
on threshold crossing in manager.handleStatusLine; the already-broadcast telemetry
frame carries the warn (tg-cost-warn styling derived from costUsd>=budget via
/config/ui — no new ServerMessage). web-push title added to sw-push.js.
- "While you were away" digest: GET /digest?since= → {finished, needsInput, stuck,
totalCostUsd, sessions[]} aggregate over manager.list(); FE banner on reconnect.
- Recent commits per project: src/http/git-log.ts (NUL-delimited git log → CommitInfo[]),
GET /projects/log?path= (isValidGitDir), textContent-inert render in project detail.
All git via execFile (no shell) + validated cwd; new routes read-only; commit
messages rendered via textContent. Verified: typecheck + build:web clean, 1904 pass
at --test-timeout=30000 (two default-5s failures are slow-sandbox real-subprocess
timeout flakes — the known ring-buffer test + a new real-git-clone sync test — not
logic regressions).
This commit is contained in:
@@ -265,6 +265,29 @@ export function createSessionManager(
|
||||
if (session === undefined) return;
|
||||
session.telemetry = telemetry;
|
||||
broadcast(session, { type: 'telemetry', telemetry });
|
||||
maybeAlertBudget(session, telemetry);
|
||||
}
|
||||
|
||||
/**
|
||||
* W3 quick-wins (b): fire a one-shot cost-budget alert when a session's cost
|
||||
* first crosses COST_BUDGET_USD. Mirrors the A5 stuck-latch shape but is NEVER
|
||||
* re-armed (cost is monotonic) — so it fires at most once per session. Disabled
|
||||
* when the budget is 0/unset or the frame carries no cost.
|
||||
*
|
||||
* No new ServerMessage variant: the "warning broadcast" is the telemetry frame
|
||||
* already sent above (clients derive the warn from costUsd >= costBudgetUsd via
|
||||
* GET /config/ui). The one distinct new action on crossing is a 'budget' push.
|
||||
*/
|
||||
function maybeAlertBudget(session: Session, telemetry: StatusTelemetry): void {
|
||||
if (cfg.costBudgetUsd <= 0) return; // disabled
|
||||
if (session.budgetNotified) return; // already alerted (latch)
|
||||
const cost = telemetry.costUsd;
|
||||
if (cost === undefined || cost < cfg.costBudgetUsd) return; // no crossing
|
||||
|
||||
session.budgetNotified = true;
|
||||
void notifyService?.notify(session, 'budget').catch((err: unknown) => {
|
||||
console.error('[manager] budget notification failed', err);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -136,6 +136,7 @@ export function createSession(
|
||||
// v0.7 Walk-away Workbench fields (T-spawn-env):
|
||||
timeline: Object.freeze([] as TimelineEvent[]),
|
||||
stuckNotified: false, // A5: re-armed to false by each pty output
|
||||
budgetNotified: false, // W3(b): cost-budget one-shot latch, never re-armed
|
||||
telemetry: null, // B2: updated by manager.handleStatusLine
|
||||
// W2: inject follow-up queue — empty at spawn; replaced wholesale by manager.
|
||||
queue: Object.freeze([] as string[]),
|
||||
|
||||
Reference in New Issue
Block a user