mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-06 07:29:42 +02:00
feat(statusline): delegate to the statusline the exporter shadows
Claude Code ranks a repo's .claude/settings.local.json above ~/.claude/settings.json, so the statusLine Codeman injects for the Plan Usage chip shadows whatever statusline the user configured globally. The inline exporter then printed Codeman's own footer in its place, and running `claude` by hand in a managed repo rendered the bare word `codeman` — the response the server returns for an unknown session id. The exporter becomes a generated shim, following the deepseek-status-shim pattern: a versioned .mjs in the data dir, refreshed on a marker change, written through temp-and-rename so a live render cannot read a half-written file. It forwards the same payload to /api/status-telemetry and, concurrently, resolves the statusline it shadows and prints that. Codeman's footer still appears when there is nothing to shadow, so the exporter keeps its value on a machine with no statusline of its own. The delegate resolves at render time, walking the settings files Claude Code consults from the render directory upward and then the home ones, skipping Codeman's own entry in either form. Late resolution means editing a global statusline needs no reinjection. Ownership now keys on the version-free `codeman-statusline-shim` token, and applyStatusLineConfig still reads the old /api/status-telemetry command as ours, so managed repos upgrade in place instead of being mistaken for hand-authored. A hand-authored statusLine is left alone exactly as before. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> (cherry picked from commit 641795821fdbb171f9f47c4909c6e8945465d05b)
This commit is contained in:
committed by
Codeman maintainer
parent
c4b74415ee
commit
c375268879
@@ -92,7 +92,7 @@ Tests: `test/docker-hosts.test.ts`, `test/docker-exec-options.test.ts`, `test/do
|
||||
|
||||
### Plan-usage chip (statusLine telemetry)
|
||||
|
||||
**Plan-usage chip** (`showPlanUsageLimits`, per-device: desktop default **ON** since 1.9.3, handhelds OFF) renders compact Claude and Codex provider rows. Claude Code (v2.1.80+) pipes a JSON blob to a configured `statusLine.command` on each render; on Pro/Max it carries a `rate_limits` object (`five_hour`/`seven_day` windows only — no Opus weekly field — each `{used_percentage 0-100, resets_at epoch-SECONDS}`). Codeman injects its OWN statusLine exporter (`generateStatusLineCommand()` in `hooks-config.ts`, identified by the `/api/status-telemetry` marker — it only ever adds/updates/removes a statusLine that is _ours_, never a user's hand-authored one) that POSTs the blob to `POST /api/status-telemetry`. That route (auth-exempt like `/api/hook-event` — localhost-only, hook-secret-gated whenever auth is active, COD-91) parses via `usage-telemetry.ts` (pure, unit-tested), broadcasts SSE `session:statusTelemetry` (de-duped per session by `telemetrySignature` since the statusline fires on every assistant message), and returns a compact plain-text footer for the exporter to **print-through**. Main Codex subscription usage comes from the signed-in host CLI's read-only app-server `account/rateLimits/read` request at startup and every 5 minutes; `usage-telemetry.ts` selects only the main `codex` bucket (never model-specific buckets such as Spark), maps whatever 5-hour/7-day windows it supplies, and omits the provider row when unavailable. Credentials stay inside the CLI and no auth material is sent to the browser. `plan-usage-latest.ts` merges both process-wide sources and replays them in the SSE init snapshot (`getLightState`) so `#planUsageChip` renders immediately on page load/reconnect. `planUsageChipEnabled()` remains the single resolver behind the checkbox, chip visibility, and Claude create-time exporter flag. **Distinct from auto-resume** (which reacts to the Claude limit _message_; this proactively shows live percentages). Design: `docs/usage-limits-display-plan.md`. Tests: `test/usage-telemetry.test.ts`, `test/codex-plan-usage.test.ts`, `test/plan-usage-chip.test.ts`, `test/plan-usage-latest.test.ts`.
|
||||
**Plan-usage chip** (`showPlanUsageLimits`, per-device: desktop default **ON** since 1.9.3, handhelds OFF) renders compact Claude and Codex provider rows. Claude Code (v2.1.80+) pipes a JSON blob to a configured `statusLine.command` on each render; on Pro/Max it carries a `rate_limits` object (`five_hour`/`seven_day` windows only — no Opus weekly field — each `{used_percentage 0-100, resets_at epoch-SECONDS}`). Codeman injects its OWN statusLine exporter (`generateStatusLineCommand()` in `hooks-config.ts` — it only ever adds/updates/removes a statusLine that is _ours_, never a user's hand-authored one) that POSTs the blob to `POST /api/status-telemetry`. ⚠️ **Taking that slot SHADOWS the user's own statusline**: a repo's `.claude/settings.local.json` outranks `~/.claude/settings.json`, so the inline exporter replaced whatever the user had configured globally, and a hand-run `claude` in a managed repo rendered the bare word `codeman` (what the route returns for an unknown session id). The exporter is therefore a GENERATED shim — `statusline-shim.ts` writes `dataPath('codeman-statusline-shim.mjs')`, same pattern as `deepseek-status-shim.ts`: versioned marker, refreshed when it changes, temp-and-rename because a live render can be executing the path mid-refresh. It forwards the blob and, concurrently, resolves the statusline it shadows and prints THAT; the route's footer is the fallback for when there is nothing to shadow. ⚠️ The delegate resolves at RENDER time, not injection time (walk the settings files Claude Code consults from the render dir upward, then the home ones, first non-ours wins), so editing a global statusline needs no reinjection. ⚠️ **Ownership keys on the version-free `codeman-statusline-shim` token**, and `isCodemanStatusLine()` ALSO accepts the pre-shim `/api/status-telemetry` command: drop that second form and every repo an older Codeman managed reads as hand-authored, so the upgrade refuses to touch it and the user keeps the shadowing exporter forever. The shim skips both forms when hunting a delegate, which is the same predicate serving as its loop guard. That route (auth-exempt like `/api/hook-event` — localhost-only, hook-secret-gated whenever auth is active, COD-91) parses via `usage-telemetry.ts` (pure, unit-tested), broadcasts SSE `session:statusTelemetry` (de-duped per session by `telemetrySignature` since the statusline fires on every assistant message), and returns a compact plain-text footer for the exporter to **print-through**. Main Codex subscription usage comes from the signed-in host CLI's read-only app-server `account/rateLimits/read` request at startup and every 5 minutes; `usage-telemetry.ts` selects only the main `codex` bucket (never model-specific buckets such as Spark), maps whatever 5-hour/7-day windows it supplies, and omits the provider row when unavailable. Credentials stay inside the CLI and no auth material is sent to the browser. `plan-usage-latest.ts` merges both process-wide sources and replays them in the SSE init snapshot (`getLightState`) so `#planUsageChip` renders immediately on page load/reconnect. `planUsageChipEnabled()` remains the single resolver behind the checkbox, chip visibility, and Claude create-time exporter flag. **Distinct from auto-resume** (which reacts to the Claude limit _message_; this proactively shows live percentages). Design: `docs/usage-limits-display-plan.md`. Tests: `test/usage-telemetry.test.ts`, `test/codex-plan-usage.test.ts`, `test/plan-usage-chip.test.ts`, `test/plan-usage-latest.test.ts`, `test/statusline-shim.test.ts` (runs the generated shim as a real subprocess, since tsc never sees it).
|
||||
|
||||
### Cron jobs
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Plan Usage Limits Display — Design & As-Built
|
||||
|
||||
> **Status: SHIPPED — deployed to prod + pushed to master, not yet released (2026-06-14).** App Settings → Display → **Plan Usage Limits** (`showPlanUsageLimits`). **Default changed in 1.9.3: desktop now defaults ON, handhelds stay OFF, resolved via `planUsageChipEnabled()`.** The per-device notes further down describing it as opt-in/synced record the original 2026-06-14 shape, not current behavior. Commits `c82f6c8` (feature) → `4d9d93d` (end-to-end fixes) → `eae225b` (per-user reconcile) → `95fb5fc` (init-snapshot replay). Full suite green (2869), CI green. No changeset/version bump yet.
|
||||
> **Status: SHIPPED — deployed to prod + pushed to master, not yet released (2026-06-14).** App Settings → Display → **Plan Usage Limits** (`showPlanUsageLimits`). **Default changed in 1.9.3: desktop now defaults ON, handhelds stay OFF, resolved via `planUsageChipEnabled()`.** The per-device notes further down describing it as opt-in/synced record the original 2026-06-14 shape, not current behavior. **The exporter changed shape on 2026-09-11**: it is now a generated shim (`src/statusline-shim.ts`) that prints the statusline it shadows and falls back to the footer below only when there is nothing to shadow, so the passages describing the footer as the exporter's own output record the original shape too. See `docs/architecture-invariants.md#plan-usage-chip-statusline-telemetry`. Commits `c82f6c8` (feature) → `4d9d93d` (end-to-end fixes) → `eae225b` (per-user reconcile) → `95fb5fc` (init-snapshot replay). Full suite green (2869), CI green. No changeset/version bump yet.
|
||||
>
|
||||
> Two surfaces from one `statusLine` callback:
|
||||
> - **Header chip** (top-right) — account-wide **plan limits**: `5h 35% · 7d 38%`, per-window green/yellow/red.
|
||||
|
||||
Reference in New Issue
Block a user