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:
@@ -129,6 +129,36 @@ describe('renderTelemetryGauge', () => {
|
||||
expect(c.querySelector('.tg-cost')).toBeNull()
|
||||
})
|
||||
|
||||
// ── cost budget warn (W3 b) ──
|
||||
|
||||
it('adds tg-cost-warn when costUsd >= costBudgetUsd', () => {
|
||||
const c = makeContainer()
|
||||
renderTelemetryGauge(c, makeTelemetry({ costUsd: 6 }), 30_000, 5)
|
||||
expect(c.querySelector('.tg-cost')?.classList.contains('tg-cost-warn')).toBe(true)
|
||||
})
|
||||
|
||||
it('adds tg-cost-warn exactly at the budget boundary (>=)', () => {
|
||||
const c = makeContainer()
|
||||
renderTelemetryGauge(c, makeTelemetry({ costUsd: 5 }), 30_000, 5)
|
||||
expect(c.querySelector('.tg-cost')?.classList.contains('tg-cost-warn')).toBe(true)
|
||||
})
|
||||
|
||||
it('does NOT add tg-cost-warn when costUsd < budget', () => {
|
||||
const c = makeContainer()
|
||||
renderTelemetryGauge(c, makeTelemetry({ costUsd: 3 }), 30_000, 5)
|
||||
expect(c.querySelector('.tg-cost')?.classList.contains('tg-cost-warn')).toBe(false)
|
||||
})
|
||||
|
||||
it('does NOT add tg-cost-warn when budget is 0 or undefined', () => {
|
||||
const c1 = makeContainer()
|
||||
renderTelemetryGauge(c1, makeTelemetry({ costUsd: 6 }), 30_000, 0)
|
||||
expect(c1.querySelector('.tg-cost')?.classList.contains('tg-cost-warn')).toBe(false)
|
||||
|
||||
const c2 = makeContainer()
|
||||
renderTelemetryGauge(c2, makeTelemetry({ costUsd: 6 }), 30_000)
|
||||
expect(c2.querySelector('.tg-cost')?.classList.contains('tg-cost-warn')).toBe(false)
|
||||
})
|
||||
|
||||
// ── model chip ──
|
||||
|
||||
it('renders model chip when model is present', () => {
|
||||
|
||||
Reference in New Issue
Block a user