Commit graph

738 commits

Author SHA1 Message Date
AgentSeal
8d727d9cc4 feat(overview): full comma-grouped numbers for a precise, spacious look
Render token counts as full thousands-grouped integers (e.g.
3,926,923,819) instead of abbreviated M/B, so the roomy tables read like
a precise statement. Update the overview test to match.
2026-06-20 19:12:26 +02:00
AgentSeal
40042365ea feat(overview): cache in/out tokens table, roomier tables
Add a Tokens breakdown table (Input / Output / Cache in / Cache out /
Total with share %) so cache write/read are shown clearly instead of
crammed into the Totals line, and widen table cell padding to 2 spaces so
columns aren't congested.
2026-06-20 19:06:31 +02:00
Resham Joshi
1dba4e0aa4
feat(web): in-dashboard device discovery, share-from-browser, redesign + hardening (#534)
* feat(web): device discovery + pairing endpoints for the dashboard

Add /api/identity, /api/devices/scan (mDNS browse with confirm code and
paired status), and /api/devices/pair (approve-style pairing via
linkRemote). Backs the Search local devices button so pairing can happen
from the browser instead of the CLI.

* feat(web): two-panel dashboard with eywa-inspired warm theme

Redesign the dashboard into a left sidebar (device switcher + Search
local devices) and a right content panel, restyled to a warm-paper,
forest-green archival theme (serif display numbers, ink text, hairline
borders) in place of the dark midnight theme. The Search local devices
modal discovers nearby devices over mDNS and pairs in one click (approve
on the other device), backed by /api/devices/scan and /api/devices/pair.

* feat(web): use the CodeBurn flame logo in the dashboard header

Replace the placeholder triangle with the flame mark (the repo's
codeburn-symbolic vector), tinted the theme's forest green, and set the
same flame as the favicon.

* feat(web): color the All-devices chart by device, add task-level combined views

In the All devices view the daily chart now stacks by device (one color
per device) instead of by model, and the combined panels add a By task
breakdown plus in/out token detail. Devices that cannot be reached are
hidden entirely rather than shown as error rows.

* feat(web): use the flame image as the dashboard logo and favicon

Replace the inline vector flame with the brand flame PNG
(public/codeburn-flame.png) in the header and as the favicon.

* fix(web): trim flame logo padding and tighten brand spacing

Crop the transparent border off the flame PNG (it was 184px of padding
each side) and reduce the header gap so the mark sits close to the
wordmark.

* feat(web): cost/tokens toggle, cache read/write, full feature panels

Add a Cost/Tokens unit toggle that switches the hero number and the chart
between dollars and tokens. Surface cache write/read token totals as
metric cards. Add per-device panels for subagents, skills, MCP servers,
and a savings / retry-tax / routing-waste summary. The combined view
gains cache totals and the unit toggle too.

* feat(web): use CodeBurn website logos and add community links

Use the website's navbar flame (logo.png) with the Code+Burn wordmark in
the header, and the three-flame app icon (icon.png) as the favicon.
Add Website, Discord, and X links to the sidebar footer.

* fix(web): use the single-flame logo for the favicon too

The three-flame icon was wrong for the tab; use the same single flame as
the navbar everywhere and drop the unused three-flame asset.

* chore(web): set dashboard title to CodeBurn - Local Dashboard

* harden sharing + dashboard for public launch

Security:
- pairing: cap PIN attempts (close window after 5 wrong guesses) so a
  6-digit PIN cannot be brute-forced within the TTL on a 0.0.0.0 listener.
- web dashboard: reject non-loopback Host (defeats DNS rebinding that
  could read unsanitized local usage) and cross-origin requests (CSRF);
  require application/json on the pair endpoint.
- store tokens with 0600 perms (was world-readable), 0700 dir.

Robustness:
- client: per-request timeout so a hung/asleep peer cannot hang a pull;
  pullDevices fetches remotes concurrently and isolates failures.
- dashboard: normalize peer payloads at the boundary and add an error
  boundary so a peer on a different version cannot white-screen the SPA;
  finite-guard fmtNum/compactUsd.

Tests: PIN attempt-cap test added (1269 pass).

* feat(web): share this device from the dashboard, with browser approval

Add a Share this device toggle to the dashboard sidebar. It runs the
secure share server in-process (mTLS + mDNS advertise) so no terminal is
needed, with a Keep sharing always option (persisted; otherwise it stops
after idle). Incoming approve-style pairings are queued and surfaced in
the browser as an Approve/Deny prompt with the matching code, instead of
a terminal prompt. The shared payload is sanitized; start degrades
gracefully if the port is already held by a CLI share.

* fix(sharing): keep a paired device from dropping on a transient pull

- client: 8s -> 15s timeout and a fresh socket per request (agent:false) so
  the pinned-fingerprint check always reads this connection's cert.
- share server: wrap request handling so a getUsage error returns a fast
  500 instead of hanging the caller (which times out and drops the device).
- dashboard: re-pull paired devices every 20s so a brief drop self-heals
  instead of staying gone until you change tabs.

* fix(sharing): re-sanitize remote payloads on receipt

A peer might run an older build that does not strip its own project
names/sessions. Sanitize every remote payload when we receive it too, so
project names never cross into our dashboard regardless of the sender's
version. Aggregate numbers (cost, tokens, models, tools, daily) are kept.

* harden dashboard sharing: version-skew, lifecycle, server errors

- normalize remote daily-history entries so a peer on an older build (daily
  rows missing topModels) can no longer crash the chart / brick the page.
- ShareController: commit always/sharing state only after listen() binds, so
  a port-in-use start no longer reports sharing when it is not.
- attach durable error/tlsClientError handlers to both HTTPS servers so a
  malformed peer connection cannot crash the process.
- reset view/provider selection when the viewed device disappears or lacks
  the selected provider (no more empty/no-selection state on sleep-wake).
- by-device chart: stable per-device color/key so bars do not reshuffle when
  a device drops or returns between polls.

* polish: device identity, approval guard, queue cap, formatting

- give each device a stable unique id (cert fingerprint for remotes,
  'local' for this device); key the sidebar, selection, and by-device
  chart by id so two devices sharing a hostname no longer collide.
- approval prompt: drop a request from the UI the moment it is answered
  so it cannot be double-clicked.
- share controller: cap concurrent pending approvals and allow only one
  per device, so a LAN peer cannot flood the prompt.
- usd(): render negatives as -$5.00, consistent with compactUsd.
2026-06-20 19:02:03 +02:00
Resham Joshi
3ac7f68284
feat(web): show usage by device in the dashboard (#533)
Add /api/devices to the web server: it returns this machine plus every
paired device (via pullDevices), each kept separate. The dashboard gains
a device switcher with an All devices combined view (totals, a per-device
breakdown table, and a merged chart) plus a per-device tab. Remote
payloads arrive sanitized, so their project and session detail is shown
as private rather than listed. With no devices paired, it renders exactly
as before.
2026-06-20 16:33:48 +02:00
Resham Joshi
887374de2c
feat(sharing): securely combine usage across your own devices (#532)
* feat(sharing): pairing, token, and device-identity core

First piece of local device sharing: self-cert fingerprint identity
(trust-on-first-use), a one-time expiring pairing PIN, fingerprint-bound
tokens, and a peer store that authorizes a pull only when both the token
and the TLS peer fingerprint match the same paired device. Pure logic,
fully unit-tested; the TLS share server and host pull build on this.

* feat(sharing): secure mutual-TLS transport + pairing handshake

Add device identity (self-signed cert, persisted; fingerprint = sha256 of
the cert DER), an HTTPS share server (mutual TLS: presents its cert, reads
the client's, and serves /api/usage only when the bearer token AND the
client fingerprint match the same paired peer), a one-time-PIN pairing
endpoint, and a fingerprint-pinning client. Verified end to end on
loopback: PIN pairing, pinned authed pull, and rejection of a wrong PIN,
a token replayed from another device, and a mismatched server
fingerprint. Adds the selfsigned dep (Node cannot generate certs natively).

* feat(sharing): share + devices CLI (pair, pull, combine)

Phase 3 terminal flow: codeburn share runs the secure server on-demand
(stops after 10 min idle; --always to persist, --pair to add a device),
and codeburn devices add <host> --pin <pin> pairs and pins a remote.
codeburn devices pulls this machine plus every paired device, keeps each
separate, and prints a per-device table with a simple summed Combined
row (no server-side merge). Persists identity and peers under the config
dir. Host pair-and-pull flow covered by a loopback integration test.

* feat(sharing): mDNS discovery + approve-style (no-PIN) pairing

Add bonjour-service discovery (advertise/browse over the LAN), a short
confirmation code derived from both cert fingerprints (Bluetooth-style
'do these match?' check), and an approve-style pairing endpoint that
prompts the owner instead of requiring a typed PIN. Loopback-tested:
approved device gets a working token with a matching code on both sides,
declined device is rejected.

* feat(sharing): AirDrop-style discover + approve UX

codeburn share now advertises on the LAN and approves incoming devices
interactively (confirm the matching code, no typed PIN). codeburn devices
add (no args) discovers nearby devices, lets you pick one, shows the
confirmation code, and waits for the owner to approve. Manual
add <host> --pin stays as a fallback for networks that block mDNS.

* feat(sharing): share only aggregates, never project names or paths

Sanitize each device's payload before it leaves the machine: drop
topProjects and topSessions (project names + session detail) and send
only aggregate numbers (cost, tokens, models, tools, activities, daily).
What you are working on stays local; only the totals travel.
2026-06-20 16:24:53 +02:00
Resham Joshi
2d44aeaedb
feat(web): local React dashboard served by codeburn web (#531)
Add a Vite + React 19 + Tailwind v4 + Recharts SPA (dash/) and a 'web'
command that serves the built UI plus a local /api/usage endpoint backed
by the existing menubar aggregation. Midnight theme with CodeBurn's
orange chart ramp: hero cost, a stacked-by-model daily bar chart with a
custom tooltip and hover-dim, metric cards, by-tool and by-activity bar
lists, and top-projects and tools tables. Period and provider filters,
react-query with skeleton loading. Stays 100% local.

build:dash builds the SPA into dist/dash (shipped via package files,
served at runtime); a missing build falls back to a helpful message.
2026-06-20 16:21:25 +02:00
Tiago Santos
75c32e6d65
fix: fix and improve test isolation and collision with environment (#530)
* fix: fix and improve test isolation and collision with environment

* docs: remove unnecessary comment

* test(env-isolation): clear CODEBURN_FORCE_MACOS_MAJOR and pin TZ

Two env vars read in src/ were not isolated: CODEBURN_FORCE_MACOS_MAJOR
(now cleared so it cannot leak between tests) and TZ (now pinned to UTC,
since clearing it falls back to the OS zone and would shift date buckets
versus a clean CI runner).

---------

Co-authored-by: AgentSeal <hello@agentseal.org>
2026-06-20 13:42:10 +02:00
Resham Joshi
60410a2632
chore(dev): silence the tsx module.register deprecation warning (#529)
Some checks are pending
CI / semgrep (push) Waiting to run
Node 26 flags tsx's loader use of module.register() as deprecated, which
prints DEP0205 on every npm run dev. Run dev with
NODE_OPTIONS=--no-deprecation so the output stays clean. Dev-only; the
built dist is unaffected.
2026-06-20 11:46:59 +02:00
Resham Joshi
c55dba2dd2
feat(overview): plain-text monthly usage overview command (#528)
* feat(overview): plain-text monthly usage overview command

Add 'codeburn overview', a copy-pasteable text report. Defaults to the
current month, with --from/--to to filter and --no-color for plain
output. Renders Totals, By tool, Top models, Highest-value days, Top
projects, a daily table, By activity, and Tools, with colored section
headings (stripped under --no-color or when piped).

Reuses the existing session aggregation. Cost gains thousands
separators and tokens roll up to a B unit for readability via
display-only wrappers, without changing the shared formatters. Project
names use the path basename instead of the sanitized full path.

* fix(cli): exit cleanly on EPIPE when the stdout pipe closes early

Piping output to a reader that closes early (| head, quitting less, a
missing command) made stdout writes throw EPIPE and crash with an
unhandled error event. Handle EPIPE on process.stdout and exit 0 so
piping the overview and other commands behaves normally.

* docs(readme): document and highlight the overview command

Add a 'Your month at a glance' section featuring codeburn overview with
examples and sample output, a Commands-table entry, and the provider
flag note.
2026-06-20 11:46:45 +02:00
Resham Joshi
fac7d141ce
fix(dashboard): scope the plan banner to its own provider tab (#524)
Some checks are pending
CI / semgrep (push) Waiting to run
A provider-scoped plan (e.g. SuperGrok, provider grok) was rendered on
every tab including All, where its budget was compared against spend
from providers it does not cover. Show each plan only on the tab whose
active provider matches, so the plan's spend lines up with the costs on
screen.
2026-06-19 19:04:54 +02:00
Resham Joshi
6a26ee8284
feat(opencode): read file-based JSON sessions (OpenCode 1.1+) (#523)
OpenCode 1.1+ stores sessions as file-based JSON under
storage/{session,message,part}/ instead of a SQLite DB, so the
SQLite-only provider discovered zero sessions on current installs and
reported no usage at all.

Add a file-based discovery and parser path, preferred when present and
falling back to the SQLite DB for pre-migration installs. Extract the
shared message-to-call logic (token extraction, tool and bash parsing,
cost) into session-message.ts so the file path, the SQLite path, and
Kilo Code stay identical.
2026-06-19 19:04:51 +02:00
Resham Joshi
8792670897
docs: add Grok Build to the supported-tools wall (#522)
Adds the Grok logo to the README logo wall and bumps the tool count to 30, following the Grok Build provider (#521).
2026-06-19 17:25:10 +02:00
Resham Joshi
ebfb1de0cb
feat(providers): add Grok Build provider (#521)
Adds Grok Build (xAI coding CLI) as an eager provider with caching- and compaction-aware token estimation, real tool/shell extraction, Skills & Agents from spawn_subagent, grok-build pricing, and SuperGrok plan presets. Tested against real sessions; full suite green.
2026-06-19 17:21:41 +02:00
Resham Joshi
81ada9c9b8
docs(readme): add zerostack to the supported-tools wall (#520)
Add the zerostack logo (from gi-dellav/zerostack) to the logo wall and bump the
tool count to 29, now that the zerostack provider has landed (#519).
2026-06-19 15:34:13 +02:00
kevinpauer
7c42534910
Add zerostack provider (#519)
* Add zerostack provider scaffold

Register zerostack (gi-dellav/zerostack) as a core provider reading
plain-text sessions from $XDG_DATA_HOME/zerostack/sessions/. Parser is
modeled on pi.ts; on-disk schema is unverified and must be confirmed
against real sessions before a PR (see docs/providers/zerostack.md).

* Rework zerostack provider against real session schema

Replace the initial scaffold (modeled on pi.ts JSONL) with the verified
format from a real local run and the zerostack source:

- Sessions are one JSON file per session under the platform data dir
  (~/Library/Application Support/zerostack/sessions on macOS,
  $XDG_DATA_HOME/zerostack/sessions on Linux; ZS_DATA_DIR overrides).
- Tokens are cumulative session totals, not per-call, so emit one
  ParsedProviderCall per session from total_input/output_tokens.
- Recompute cost via LiteLLM (calculateCost); OpenRouter ids arrive
  route-prefixed and resolve through canonical names.
- zerostack persists only final assistant text, so tools/bash are empty.

Verified against a real session (DeepSeek v4 Pro via OpenRouter):
34.1K in / 961 out / $0.016, matching the recorded total_cost. Adds a
fixture-based test and rewrites the provider doc to match.
2026-06-19 14:44:45 +02:00
Resham Joshi
8967311f05
Update README.md 2026-06-19 13:00:34 +02:00
Resham Joshi
a41a3b89c6
docs: finish README overhaul (#518)
Follow-up to #516, which was merged before these two commits had landed on the branch, so they did not make it into `main`.

Adds the remaining parts of the overhaul:
- Collapse **Commands**, **Features**, **Reading the dashboard**, **How it reads your data**, and **Environment Variables** behind `<details>` toggles so the README is short by default.
- Replace "AI coding tools" with "AI tools" in the README (3 places) and the `package.json` description.
2026-06-19 12:58:21 +02:00
Resham Joshi
5dc601f633
docs: overhaul README (#516)
- Lead with a story: hook, hero dashboard, and a quick start that highlights npx, global install, and the menubar app
- Restore the honeycomb logo to the top and recolor all badges to brand orange
- Replace the 28-row supported-tools table with a compact logo wall, adding icons for Warp, Mux, and Vercel AI Gateway
- Group CLI commands into copy-friendly tables
- Merge "Provider notes" and "How it reads your data" into one Provider / Data location / Notes table
- Rewrite the sponsor section
- Change the tagline to "See where your AI spend goes."
- Correct the Node version (22.13+) and the provider count
2026-06-19 12:31:30 +02:00
Resham Joshi
808a149bd6
feat(export): surface MCP server usage in JSON and CSV exports (#514)
Some checks are pending
CI / semgrep (push) Waiting to run
The export schema emitted tools and shell-commands but no MCP section, and
the tools list is built from extractCoreTools which strips mcp__ names, so
MCP usage never appeared in codeburn export output even when sessions had
MCP activity (issue #496).

Add an mcp section to the JSON export and an mcp.csv to the CSV export,
sourced from session.mcpBreakdown (server -> calls, with share). Mirrors the
existing tools section.

The underlying parse fix (Codex mcp_tool_call_end attribution) landed in
#513; this makes that data visible in exports. Validated on real local data:
the JSON export now reports mcp: [{Server: node_repl, Calls: 5, ...}].

Fixes #496
2026-06-18 23:13:48 +02:00
Resham Joshi
80e3de3bab
fix(codex): attribute MCP calls emitted as event_msg/mcp_tool_call_end (#513)
Recent Codex sessions report completed MCP tool calls as an event_msg with
type mcp_tool_call_end instead of a function_call response_item. The parser
only built its tool list from function_call and patch_apply_end events, so
these MCP calls were never attributed: token usage was still counted, but the
MCP tool itself was missing from recent lookback windows (today/week/month)
while older history still showed it.

Add a handler that reads invocation.server and invocation.tool from the
event and rebuilds the canonical mcp__<server>__<tool> name the classifier
recognizes. Bump the Codex result cache to v4 so sessions cached under v3
re-parse and pick up the previously dropped MCP calls.

Validated on real local data: 5 mcp_tool_call_end events that were dropped
before are now attributed as mcp__node_repl__js, with no change to the total
call count.

Fixes #478
2026-06-18 22:32:40 +02:00
Resham Joshi
9e997ddb15
fix(cursor): scan the requested window instead of a blind ROWID cap (#482) (#512)
Some checks are pending
CI / semgrep (push) Waiting to run
Large Cursor DBs were scanned as "the most-recent 250k bubbles by ROWID",
which dropped in-range older sessions from long-range reports and warned even
when the requested window fit comfortably. The bubble timestamp lives in the
JSON value (no index), so the date filter forces a full decode per row, which
is why a scan bound exists.

Replace the blind cap with a range-aware paged scan: for DBs over the budget,
page ROWID-descending, keep only rows within the window (createdAt > timeFloor),
and stop once a full page falls past the window floor. The hard budget remains
as a backstop for genuinely enormous in-range scans, and the "older sessions
may be missing" warning now fires only when that budget is actually hit.

Effect: short ranges decode far fewer rows and no longer warn; long ranges
return the full window when it fits the budget; truncation keeps the newest
in-range bubbles and warns only then. Small DBs are unchanged (un-paged query).
Budget is overridable via CODEBURN_CURSOR_MAX_BUBBLES for tests.
2026-06-18 18:45:38 +02:00
Resham Joshi
844fdde4cd
fix(menubar): drop the ' tok' suffix from the Total Tokens metric (#511)
Per #497 feedback: with Total Tokens selected the bar showed e.g. '0 tok'.
The user picked the metric, so the unit is redundant; show just the number
(the flame already disambiguates it from cost). Compact mode already had no
suffix; this only changes the wide rendering.
2026-06-18 17:59:03 +02:00
Resham Joshi
a3da2ded2a
feat(codex): compute Codex credit usage (#408, #495) (#510)
* feat(codex): compute Codex credit usage (#408, #495)

Codex/ChatGPT subscription users consume credits, a unit separate from API
dollars: usage is billed as credits-per-million-tokens at per-model rates that
differ from the API USD pricing CodeBurn uses for cost. So the reported dollar
cost does not match what credits actually consume.

Add a credit engine sourced from the official Codex credit rates
(developers.openai.com/codex/pricing): GPT-5.5 125/12.5/750, GPT-5.4
62.5/6.25/375, GPT-5.4 mini 18.75/1.875/113 credits per 1M input/cached/output
tokens. Surface per-model credit usage in `codeburn models` JSON output
(credits field; null for non-Codex or unknown models). models-report already
folds reasoning into output and keeps non-cached input + cached-read separately,
which is exactly what the credit rates expect, so the figure is exact.

Engine + computation are unit-tested. UI display surfaces (the models table,
the TUI dashboard, the menubar "credits" view) are intentionally left for a
follow-up so the display choice can be decided.

* feat(menubar): opt-in Codex credits display metric (#408, #495)

Surface Codex credit usage in the menubar as a selectable metric, without
changing the default. Cost ($) stays the default in both the menubar and the
CLI; credits only appear when explicitly chosen.

- TS: buildMenubarPayloadForRange computes the period's Codex credits (via the
  tested aggregateModels, so reasoning/cached are handled) and exposes
  current.codexCredits in the menubar JSON.
- Swift: new DisplayMetric.credits, a "Credits (Codex)" option in the metric
  picker, decodes codexCredits, and renders it in the menu-bar title. Default
  metric remains .cost.
2026-06-18 17:03:46 +02:00
Resham Joshi
96d74a04b3
feat(menubar): polish the status-item menu and About tab (#509)
Right-click menu:
- Add a dimmed "Today · $X · N calls" usage row at the top.
- Give Settings an explicit gearshape icon (instead of relying on the macOS
  Tahoe auto-decoration).
- Add an "About CodeBurn" item that opens Settings on the About tab.
- Drop the per-item keyboard-shortcut labels and the section separators for a
  flat, compact list.
- Open the menu a few px below the status item so it clears the menu bar
  (anchoring flush clipped the top edge and triggered menu scrolling on hover).

Settings:
- Add a settingsTab selection to AppStore and bind SettingsView's TabView to
  it (tagged tabs) so "About CodeBurn" can jump straight to the About tab.
- About tab: add a "Star on GitHub" button and a "Sponsor" link.
2026-06-18 14:25:29 +02:00
Resham Joshi
dbe4e94322
fix(menubar): keep cost budget in USD and flag empty custom budget (#508)
Two follow-ups from the budget review:

- Currency consistency: the daily cost budget is defined in USD (the presets and
  the custom field are labeled "$"), but the "exceeded" banner ran the value
  through the display-currency rate, so a non-USD user saw the field and banner
  disagree (e.g. field "$100", banner "EUR 92"). Render the budget label in USD
  via a new asUSD() helper so the picker, field, and banner all agree. The
  over-budget comparison was already USD vs USD and is unchanged.

- Empty custom cue: selecting "Custom..." and leaving the field blank stores 0,
  which silently disables the alert while the picker still shows "Custom...".
  The help text now says "Enter an amount above, or the alert stays off." in
  that state so it does not look armed when it isn't.
2026-06-18 13:30:58 +02:00
Resham Joshi
8794ddeb83
fix(menubar): track isOverDailyBudget so the flame re-tints on budget crossings (#507)
The menubar refresh observation block read displayMetric and the two budget
values, but not the derived isOverDailyBudget (which also depends on
todayPayload). It worked only because payload/menubarPayload happened to touch
the same cache storage. Read isOverDailyBudget explicitly so the flame re-tints
when today's usage crosses the budget, and so the dependency survives any future
cache refactor.
2026-06-18 13:23:57 +02:00
Resham Joshi
8467b104cc
feat(menubar): allow a custom daily budget amount (#497) (#506)
The daily budget could only be one of a few fixed presets. Add a "Custom…"
option to the budget picker that reveals a text field for an exact amount:
dollars for the cost metric, millions of tokens for the token metrics. The
typed value is stored in the same dailyBudget / dailyTokenBudget fields, so
the flame tint and banner pick it up unchanged. On open, a previously saved
non-preset value reselects "Custom…" and pre-fills the field.
2026-06-18 13:17:19 +02:00
Resham Joshi
dab1297001
feat(menubar): make the daily budget alert respect the display metric (#497) (#505)
The daily budget was always measured in dollars, so users tracking the
Tokens or Total Tokens metric had no token-based budget alert. Add a
separate token-denominated daily budget and route the flame tint and the
hero "budget exceeded" banner through one metric-aware check.

- New dailyTokenBudget (stored separately from the cost budget so switching
  metric never reinterprets a threshold).
- Centralize the logic on AppStore: isTokenMetric, activeDailyBudget,
  todayMetricTotal, isOverDailyBudget, dailyBudgetLabel.
- Settings shows a token-threshold picker (1M..100M) for token metrics and
  the existing dollar picker for the cost metric, with metric-aware help text.
- Flame tint and the banner now use isOverDailyBudget / dailyBudgetLabel.
2026-06-18 13:07:28 +02:00
Resham Joshi
b481f81fc4
test(cli-proxy-path): raise timeout for spawn-based tests to fix load flake (#504)
Each test spawns 'tsx src/cli.ts' several times, re-transpiling the CLI on
every spawn. Under full-suite parallel load these spawns contend for CPU and
the slowest test could exceed the 5s default timeout, even though it runs in
~1.7s in isolation and the spawnSync calls already allow 30s each. Set the
file testTimeout to 30s so the wrapper matches the per-spawn cap. Full suite
is green across repeated runs.
2026-06-18 12:57:44 +02:00
Resham Joshi
a9c0273a42
test(usage-aggregator): build payload against an empty HOME to fix timeout flake (#503)
buildMenubarPayloadForRange('today') parsed the developer's real on-disk
session data under a fixed 5s timeout, so on a heavy-usage day the test
timed out locally (it stayed green on CI, which has no real data). Point
HOME and the config dirs at an empty temp dir so the payload is built from
an empty dataset: deterministic and fast (~0.3s) while still asserting the
payload shape and the optimize:false short-circuit.
2026-06-18 12:50:39 +02:00
Resham Joshi
59efa7c824
Merge pull request #502 from getagentseal/kiro-cli-sessions-rebased
feat(kiro): add Kiro CLI session discovery and parsing (#476)
2026-06-18 12:44:29 +02:00
Wurzer, Wilhelm
89b365c269 feat(kiro): add Kiro CLI session discovery and parsing
The Kiro provider previously only detected sessions from the Kiro IDE
(VS Code-based) stored in globalStorage. This adds support for Kiro CLI
(`kiro-cli chat`) sessions stored in ~/.kiro/sessions/cli/.

CLI sessions use a different format:
- .jsonl files with Prompt/AssistantMessage/ToolResults entries
- Companion .json files with session metadata, model info, and
  per-turn metering usage (credits)

Changes:
- Add CLI session discovery (scans ~/.kiro/sessions/cli/ for .jsonl)
- Add CLI JSONL parser that extracts turns, tools, and cost from
  metering_usage credits
- Add CLI tool name mappings (read, write, shell, grep, glob)
- Make CLI sessions dir configurable via third param to
  createKiroProvider for testability
- Add unit tests for CLI session discovery and parsing
2026-06-18 12:42:58 +02:00
Resham Joshi
c5bf09921b
fix(cli): validate --provider and make non-TTY report deterministic (#501)
Two CLI polish fixes found during a full command sweep:

- Validate --provider against the known provider set (report, status, today,
  month, export, optimize, compare, models). An unknown provider previously
  produced a silently empty report; it now exits 1 with a clear message
  listing valid values, matching how --period and --format already behave.
  Provider names are exposed via a lazy allProviderNames() helper so importing
  the providers module never depends on every provider object being defined.

- Make the non-interactive report/today/month render deterministic: yield a
  tick so ink flushes the one-shot frame to stdout before unmounting, instead
  of unmounting synchronously which could race the flush and drop the output.

Adds CLI provider-validation tests (rejection, valid name, all sentinel, and
a drift guard that allProviderNames covers every loadable provider).
2026-06-18 12:39:22 +02:00
Resham Joshi
15536ed71b
Merge pull request #500 from getagentseal/json-optimize-yield-rebased
feat(cli): JSON output for optimize and yield (#492)
2026-06-18 12:14:55 +02:00
barry
a2947a48be Add JSON output for optimize and yield
Dashboard integrations need machine-readable optimize findings and yield ratios without parsing terminal output. This threads the existing analysis results into conservative JSON serializers while preserving text output as the default.

Constraint: Issue #419 asks for dashboard-friendly JSON for optimize and yield

Rejected: Build a separate analysis path | would risk drift from terminal output

Confidence: high

Scope-risk: narrow

Tested: npm test

Tested: npm run build

Tested: npm run dev -- optimize -p today --format json

Tested: npm run dev -- yield -p today --format json

Not-tested: Real downstream dashboard integration
2026-06-18 12:13:17 +02:00
Resham Joshi
49d1ddc376
Merge pull request #499 from getagentseal/reduce-status-parsing-patched
fix(menubar): reduce repeated status parsing (#486) + clock-skew guard
2026-06-18 12:08:51 +02:00
AgentSeal
7f44eeb1ba fix(daily-cache): purge cached today/future entries on hydration
Keeps the perf win from PR #486 (yesterday stays cached, no re-parse every
run) but restores a narrow safety guard: drop any cached entry dated today or
later before computing the gap range. The cache only ever stores complete past
days, so a >= today entry can only come from the clock moving backward or a
stale older cache; without this it would be served frozen instead of being
recomputed live. Yesterday and earlier are untouched, so this does not
re-parse already-cached days. Adds a regression test.
2026-06-18 12:07:35 +02:00
Resham Joshi
7b484c1644
Merge pull request #498 from getagentseal/copilot-otel-patched
feat(copilot): OTel cache-token parsing (#477) + maintainer review fixes
2026-06-18 11:57:27 +02:00
AgentSeal
9917a8a181 refactor(copilot): apply review fixes to OTel parsing PR #477
Maintainer review fixes on top of the OTel cache-token work:

- Remove all DEBUG_OTEL console.warn scaffolding from parser.ts and
  copilot.ts (gated but unlike the rest of the codebase).
- Parameterize the spans IN (...) query instead of string-interpolating
  trace IDs.
- Fold the per-chat-span metadata query into the trace-span query to drop
  the N+1 (one query per chat span -> one per conversation).
- Guard epochToISO against null/NaN/0 so a malformed start_time_ms row no
  longer throws on new Date(NaN).toISOString().
- Remove dead code: parseSpanAttributes, OTelSpanRow, and the unreachable
  `if (!db) return`; drop unused catch bindings.
- Note in parseProviderSources that the non-durable append path assumes
  unique source paths.
- Document the OTel source in docs/providers/copilot.md: Node 22+
  requirement, durable-cache monotonic totals, and the one-time
  parse-version cache reset on upgrade.
2026-06-18 11:56:04 +02:00
steelp02
d68a214c76
fix(copilot): Implement durable sources for OTel DB parsing.
Signed-off-by: steelp02 <pieter.steel@pfizer.com>
2026-06-17 21:42:40 +02:00
AgentSeal
a5e443ed38 docs: move Claude for Open Source badge to top of README
Some checks failed
CI / semgrep (push) Has been cancelled
2026-06-16 00:56:01 +02:00
AgentSeal
653f2e1271 docs: make Claude for Open Source recipient badge visible 2026-06-16 00:54:47 +02:00
AgentSeal
42222b4ab5 docs: note Claude for Open Source program support in README 2026-06-16 00:52:32 +02:00
Resham Joshi
450f6575b6
Update README.md 2026-06-16 00:40:23 +02:00
Resham Joshi
421eea3bef
Update README.md 2026-06-16 00:39:48 +02:00
Resham Joshi
694e8ce79b
fix(menubar): read Claude keychain via security CLI on silent refresh (#490) (#491)
Some checks failed
CI / semgrep (push) Has been cancelled
Background token refreshes re-read the "Claude Code-credentials" keychain
item via the Security framework. On macOS Sierra+, access is governed by the
item's partition list, not the legacy "Always Allow" ACL. Claude Code resets
that partition list every time it rotates the credential, dropping our app
from the allowed set, so the next read raises a fresh keychain password
prompt. On a heavy usage day this fires dozens of times. The LAContext
interactionNotAllowed flag we relied on does not suppress that prompt for a
plain generic-password item.

Route the silent path (proactive refresh and post-401 re-read) through
/usr/bin/security instead. The Apple-signed security binary sits in the
item's apple-tool: partition, so it reads the secret without prompting and
without depending on the user's ACL grant. It is read-only and never spends
the shared refresh token, preserving the existing invariant that the Claude
CLI owns the grant.

The user-initiated bootstrap keeps the framework read, where a single
consent prompt is expected. Drops the now-unused LocalAuthentication import.
2026-06-14 10:31:56 +02:00
Vaibhav Arora
0ac09d8909 fix(menubar): reduce repeated status parsing
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-13 00:07:04 +05:30
Stephan Leicht Vogt
04f3fc1060
fix(menubar): support installer HTTP proxies (#475)
Some checks failed
CI / semgrep (push) Has been cancelled
Route the menubar installer's GitHub downloads through an undici ProxyAgent when HTTP(S)_PROXY is set, honoring NO_PROXY for bypass. Falls back to direct fetch when no proxy is configured. Closes #473.
2026-06-11 14:13:10 +02:00
theparlor
81fdeee5d6
fix(mac): restore right-click status-item menu on macOS 27 (#472)
Present the right-click menu from a global right-mouse-down monitor (hit-tested against the status-item window) via NSMenu.popUp, since macOS 27 no longer routes right-mouse events to the status-item action. Legacy action path retained for macOS <= 26, guarded by a debounce. Remove the global monitor on terminate.
2026-06-11 09:56:56 +02:00
steelp02
7d067e82b2
feat(providers): [copilot.ts]Add OTel cache token parsing including tests and regression checks.
Signed-off-by: steelp02 <pieter.steel@pfizer.com>
2026-06-10 22:58:12 +02:00