Commit graph

235 commits

Author SHA1 Message Date
iamtoruk
e2ebdc92e4 Add UTC-based date helpers for period calculations 2026-04-29 14:13:03 -07:00
iamtoruk
d61ea56b3e Add collapsible models section with bar chart and token summary 2026-04-29 14:12:24 -07:00
iamtoruk
637c88a86d Add dynamic agent tab strip with per-tab cost and 7-provider support 2026-04-29 14:09:39 -07:00
iamtoruk
7206638a7d Add payload cache with 5-minute TTL and concurrent fetch guarding 2026-04-29 14:07:44 -07:00
AgentSeal
a05dabf12b feat(desktop): Windows MSI build + tray + CLI discovery
Windows-specific pieces that were stubbed or missing:

- icon.ico multi-resolution (16/24/32/48/64/128/256) so the MSI bundler
  and the Windows taskbar/installer get a proper app icon. Generated from
  icons/icon.png with ImageMagick; also listed in tauri.conf.json.
- cli.rs defaults program to 'codeburn.cmd' on Windows because npm installs
  a cmd shim, not an .exe, and std::process::Command does not guarantee
  PATHEXT resolution for extensionless names.
- cli.rs is_safe_arg now accepts '\\', ':', '(', ')' on Windows so a user
  supplied CODEBURN_BIN like C:\\Users\\...\\codeburn.cmd is not rejected.
  These are not shell metacharacters in a direct-argv spawn; we never
  invoke sh -c or cmd /c with string interpolation.
- spawn_in_terminal on Windows now passes an explicit empty title to
  'start' so the program name is not eaten as the window title.
- release-desktop-windows.yml mirrors the Linux release workflow on
  windows-latest. Triggered by 'win-v*' tag or workflow_dispatch. Caches
  Cargo, runs 'npm run tauri build', uploads .msi/.exe to a GitHub Release
  (or an artifact on manual runs).

Also drops the AgentSeal/codeburn URLs that pre-date the getagentseal org
rename so new pulls hit the canonical URL instead of the 301.
2026-04-18 15:52:22 -07:00
AgentSeal
c179118aa6 merge: bring main into Windows menubar work
Picks up all 0.7.3 npm fixes, mac-v0.7.3-0.7.5 menubar releases,
semgrep CI guard (#78), --from/--to date filtering (#80), org rename
to getagentseal (#97), and docs catch-up (#99).

# Conflicts:
#	src/menubar-installer.ts
2026-04-18 15:48:59 -07:00
AgentSeal
82df214958
docs: cover --from/--to, avgCostPerSession, and semgrep guard (#99)
Some checks are pending
CI / semgrep (push) Waiting to run
README gains a --from/--to example in the Usage block, a dedicated
'Date range filtering' subsection, and a note that JSON projects[]
now includes avgCostPerSession.

CHANGELOG opens an Unreleased section crediting @lfl1337 for PRs #78
and #80. Flags the projects.csv column-order shift (Avg/Session now
between Cost and Share) so consumers parsing by position read by
header instead.

Co-authored-by: AgentSeal <hello@agentseal.org>
2026-04-18 15:45:45 -07:00
Ninym
c634b10560
feat(report): add --from/--to date range filtering and avgCostPerSession (#80)
* test(cli): failing tests for parseDateRangeFlags helper

* feat(cli): add parseDateRangeFlags helper with local-time dates

* feat(report): add --from/--to date range filtering

* feat(report): add avgCostPerSession to JSON report and CSV/JSON export
2026-04-18 15:11:33 -07:00
Ninym
5932a273a1
chore(ci): add semgrep guard against prototype pollution regressions in provider hot paths (#78)
* chore(ci): add semgrep rule no-bracket-assign-on-literal-object-map

* chore(ci): add workflow running semgrep bracket-assign guard on push/PR

* fix(parser): use Object.create(null) for categoryBreakdown map

* chore(ci): expand semgrep rule to cover ||, ??=, and if-guard variants

* chore(ci): limit push trigger to main and add semgrep --strict

* chore(ci): use jq to enforce finding count (--error unreliable in semgrep 1.x)
2026-04-18 15:10:24 -07:00
AgentSeal
a031c8d32d
chore: point repo URLs at getagentseal org (#97)
Add package.json repository/bugs/homepage fields. Swap hardcoded
AgentSeal/codeburn URLs to getagentseal/codeburn across README,
mac README, macOS menubar star banner, and the menubar installer's
release-API endpoint. 301 redirects keep old URLs working, but
canonical links now point at the current org.

Co-authored-by: AgentSeal <hello@agentseal.org>
2026-04-18 14:55:44 -07:00
AgentSeal
af3676a2b1
Merge pull request #95 from getagentseal/fix/trend-tooltip-per-provider
fix(mac): show correct cost in trend tooltip for per-provider views
2026-04-18 13:41:49 -07:00
AgentSeal
94240f5341 fix(mac): show correct cost in trend tooltip for per-provider views
The trend chart tooltip always displayed `bar.tokens` in its header,
which is zero for provider-filtered history (the CLI only carries
per-provider cost+calls in the daily cache, not tokens). Result: when
you selected Claude/Codex/Cursor/Pi, hovering a bar showed $0.00 even
on days with real spend.

The trend chart's main metric already falls back to cost when tokens
are zero. Pass that same metric value through to the tooltip so both
stay consistent.

Also removed the misleading "No model breakdown available" fallback
line. For provider-filtered views the per-model breakdown legitimately
doesn't exist in the payload, so the tooltip now just shows date +
cost without the error-sounding message.
2026-04-18 13:18:11 -07:00
AgentSeal
70f47f8d9e
Merge pull request #94 from getagentseal/fix/menubar-today-cache-staleness
fix(mac): keep (today, all) cache fresh for menubar title and tab labels
2026-04-18 13:03:07 -07:00
AgentSeal
7ee8b679f9 fix(mac): keep (today, all) cache fresh for menubar title and tab labels
The refresh loop previously skipped `refreshQuietly(.today)` when the
user was already viewing the Today period. That guard meant while the
user was on (today, claude) or any other non-.all provider, the
(today, all) cache went stale. The menubar title and the agent tab
strip both read from that cache, so they displayed stale costs while
the hero section (which reads the currently-viewed payload) showed
the correct fresh value.

Remove the guard so the (today, all) cache refreshes every cycle
regardless of the currently selected period/provider.

Shipped as mac-v0.7.4.
2026-04-18 12:58:50 -07:00
AgentSeal
8ee1f38f86
Merge pull request #92 from AgentSeal/chore/reset-version-to-0.7.3
chore: reset version to 0.7.3 to match published npm
2026-04-18 09:57:52 -07:00
AgentSeal
c83a12efed chore: reset version to 0.7.3 to match published npm 2026-04-18 09:54:03 -07:00
AgentSeal
476b3c51ee
Merge pull request #91 from AgentSeal/revert/remove-npm-oidc
revert: remove npm OIDC publish workflow
2026-04-18 09:53:53 -07:00
AgentSeal
9ac2144950 revert: remove npm OIDC publish workflow
Three consecutive failed publish attempts on a live repo are not
acceptable. Reverting to manual `npm publish` from the laptop, which
has always worked. OIDC can be revisited later in a staging
environment, not on the production package.
2026-04-18 09:51:58 -07:00
AgentSeal
c62a1cf21f
Merge pull request #90 from AgentSeal/chore/bump-0.7.4-rc.2
chore: bump to 0.7.4-rc.2
2026-04-18 09:47:25 -07:00
AgentSeal
35d4d32955 chore: bump to 0.7.4-rc.2 for Node 24 OIDC retry 2026-04-18 09:47:21 -07:00
AgentSeal
ec130037f5
Merge pull request #89 from AgentSeal/fix/node-24-for-oidc
fix(ci): use Node 24 for npm OIDC trusted publishing
2026-04-18 09:47:18 -07:00
AgentSeal
4fccca47d2 fix(ci): use Node 24 for npm OIDC trusted publishing
Node 22 on GitHub's hosted runners currently pins to a broken npm
10.9.7 whose internal `promise-retry` module is missing from the
toolcache (runner-images#13883, nodejs/node#62430). Self-upgrading
via `npm install -g npm@latest` crashes before the install can run,
because `@npmcli/arborist` cannot start without that module.

Node 24 LTS bundles npm 11.x natively, which supports OIDC trusted
publishing out of the box (minimum is 11.5.1, per npm docs). Bumping
the runtime lets us delete the fragile upgrade step entirely.

Test: tag `v0.7.4-rc.2` after merge to validate the flow publishes
successfully with provenance.
2026-04-18 09:46:13 -07:00
AgentSeal
27af2ef96a
Merge pull request #88 from AgentSeal/chore/bump-0.7.4-rc.1
chore: bump to 0.7.4-rc.1
2026-04-18 09:36:52 -07:00
AgentSeal
e7f1b33196 chore: bump to 0.7.4-rc.1 for OIDC retry after npm upgrade fix 2026-04-18 09:36:38 -07:00
AgentSeal
679363a25c
Merge pull request #87 from AgentSeal/fix/npm-version-for-oidc
fix(ci): upgrade npm to 11.5.1+ for OIDC trusted publishing
2026-04-18 09:36:36 -07:00
AgentSeal
832dd4ada1 fix(ci): upgrade npm to 11.5.1+ for OIDC trusted publishing
Node 22 ships with npm 10.x, which does not know how to exchange the
GitHub OIDC id-token for a short-lived npm token. Without this upgrade,
the publish step silently falls back to the empty NODE_AUTH_TOKEN that
setup-node writes to .npmrc, and the registry returns 404.

First test publish (v0.7.4-rc.0) failed at exactly this point, even
though provenance signing via sigstore succeeded, confirming the OIDC
handshake with GitHub was fine and only the npm-side auth was broken.

Fix: `npm install -g npm@latest` before the publish step. Adds ~5s to
runtime.
2026-04-18 09:33:52 -07:00
AgentSeal
bed772b6a5
Merge pull request #86 from AgentSeal/chore/bump-0.7.4-rc.0
chore: bump to 0.7.4-rc.0 for OIDC test publish
2026-04-18 09:27:05 -07:00
AgentSeal
46f72ba348 chore: bump to 0.7.4-rc.0 for OIDC test publish
Pre-release bump to validate npm OIDC trusted publishing end to end:
workflow trigger, Environment approval gate, Trusted Publisher match,
provenance attestation. Will not be tagged as `latest` on npm (npm
auto-excludes SemVer pre-releases from dist-tags). After this RC
succeeds, cut 0.7.4 proper.
2026-04-18 09:25:59 -07:00
AgentSeal
882deafc2b
Merge pull request #84 from AgentSeal/feat/npm-oidc-publish
CI: npm OIDC trusted publishing workflow
2026-04-18 09:10:56 -07:00
AgentSeal
21a4627780
Merge pull request #85 from AgentSeal/chore/ignore-discord-brand-assets
chore: ignore local Discord brand assets
2026-04-18 09:10:53 -07:00
AgentSeal
c90340f205 chore: ignore local Discord brand assets
Adds `assets/discord-*.png` to .gitignore so local promo/branding
assets that aren't ready to publish don't show up as untracked noise
in `git status`. Any Discord asset that should be tracked later can be
added with `git add -f`.
2026-04-18 09:08:43 -07:00
AgentSeal
8c87947980
Merge pull request #83 from AgentSeal/chore/block-claude-coauthor-prs
CI: block Co-authored-by Claude/Anthropic trailers on PRs
2026-04-18 09:04:54 -07:00
AgentSeal
e834f64c22 ci: block Co-authored-by Claude/Anthropic trailers on PRs
New GitHub Actions check that scans every PR commit for
`Co-authored-by: ... claude ...` or `... anthropic ...` trailers and
fails the PR with a clear remediation message if found. Contributors
can still use AI tools; the trailer attribution must be removed before
the PR is eligible to merge, consistent with the project contributor
guidelines.

The workflow scans only commits introduced by the PR
(base.sha..head.sha), so existing history is untouched.
2026-04-18 09:02:48 -07:00
AgentSeal
d80f68928b ci: add npm OIDC trusted-publish workflow
Triggers on v* tag push or manual dispatch. Builds, tests, then publishes
codeburn to npm with provenance attestation. Uses OIDC so no NPM_TOKEN is
stored in repo secrets. The npm-publish GitHub Environment gates the
publish step behind a required reviewer, so every release needs explicit
human approval before it reaches the registry.

Tag/package version mismatch fails fast before any build work. Tests run
before publish to prevent shipping a broken release.
2026-04-18 07:43:06 -07:00
AgentSeal
7a5cb32e4c Merge: restore agent tabs + Connect Claude button 2026-04-18 07:17:49 -07:00
AgentSeal
43a938ff9e feat(mac): add Connect Claude button to Plan pane
The Plan pane previously told users to "run claude login in your
terminal, then retry" with no way to start the flow from the app.
Added a primary Connect Claude button on both the no-credentials and
failed states that launches Terminal.app with `claude login`, so the
OAuth flow is one click away.

TerminalLauncher.openClaudeLogin() uses a hardcoded literal, so no
user input reaches AppleScript. Refactored the common path into
runInTerminal(command:preValidated:) which re-validates any non-
literal input against CodeburnCLI.isSafe as defense-in-depth.

On machines without Terminal.app (iTerm/Ghostty/Warp), the button
surfaces an inline instruction to run `claude login` manually instead
of failing silently.
2026-04-18 06:54:57 -07:00
AgentSeal
9483d66e65 fix(mac): restore agent tab strip to show all detected providers
Tabs were filtering on `value > 0` (today's spend), which hid the row
whenever only one provider had activity today. The CLI's providers map
already contains only providers detected on the system, so showing the
map as-is matches user intent: a tab for each installed tool,
regardless of today's spend. Tab strip only hides when nothing is
detected.

This also makes the Plan pill reachable again: it gates on
`selectedProvider == .claude`, which required clicking the Claude tab
to select.
2026-04-18 06:54:06 -07:00
AgentSeal
c155ba3c98 perf(extensions): cache payloads by (period, provider) for instant switches
Every period/provider tab tap was spawning a fresh codeburn status CLI,
which ran the whole sessions parser again — roughly 500 ms to 2 s per
call. The Mac app caches by (period, provider) with a 60 s TTL; port the
same pattern to GNOME.

* New payloadCache Map keyed by "period|provider". A switch that lands
  on a cached key renders from memory immediately and skips the CLI.
* inFlightKeys Set de-duplicates concurrent fetches for the same key —
  rapid clicks don't fan out into parallel subprocesses.
* When a fetch returns but the user has since switched to a different
  key, cache the payload anyway (warms it for a later tap) and skip the
  render so the stale response can't overwrite the new one.
* Currency switch no longer re-fetches. Payloads are raw USD; formatCost
  applies the FX multiplier at render time. Dispatch the CLI currency
  write in the background so other tools (TUI, menubar subcommand) see
  the same symbol on next run, then re-render the cached payload.
2026-04-18 06:00:39 -07:00
AgentSeal
933a93d90a fix(extensions): token chart, single-provider badge, plan text wrap
Three visual fixes on the GNOME popup:

* Token histogram chart was showing "0 tokens" with no bars because the
  reduce-and-spread pipeline hit an edge case; replace with an explicit
  loop that coerces each field to Number and accumulates the total.
* Single-provider state used to stretch a full-width button across the
  whole popup. Render a compact brand-orange pill badge instead so it
  reads as "showing: Claude" rather than a filter tab with nothing to
  switch to.
* Plan placeholder text was being ellipsized to "Claude OAuth ... macOS-..."
  because St.Label defaults to single-line rendering. Set clutter_text
  line_wrap + WrapMode.WORD + EllipsizeMode.NONE so the message flows
  across the popup width.
2026-04-18 05:50:34 -07:00
AgentSeal
e246aa9ece feat(extensions): insight pills + token histogram chart on GNOME
Port Wave 1 of the Mac popover to the GNOME extension:

* Insight pills row (Activity, Trend, Forecast, Pulse, Stats, Plan) below
  period tabs. Clicking a pill swaps the content area between views.
* 19-day token histogram chart between period tabs and content area.
  Each bar scales to the top-token day, rendered as a St.Widget with a
  brand-orange linear gradient.
* Six view implementations:
  - Activity (default): current activity rows + top 3 models
  - Trend: last 7 days, per-day cost and call count
  - Forecast: 7-day avg, yesterday, day-over-day %, month projection
  - Pulse: three KPI tiles (cost, calls, cache hit) + last-7-day summary
  - Stats: favorite model, active days, current streak, peak day
  - Plan: placeholder (Claude OAuth is macOS-only until Wave 3)

Shared helpers:
* _sectionTitle, _kvRow, _pulseTile for consistent row layouts
* formatTokensCompact for 1.2k / 4.5M / 2.3B token totals
2026-04-18 05:44:10 -07:00
AgentSeal
6358100d3d fix(extensions): period switching and currency conversion on GNOME
Period bug:
_refresh() returned early when a previous fetch was still in flight, so
clicking a new period tab while the initial load was running silently
dropped the second click. Swap the loading-guard for a generation counter:
every refresh increments a counter, and only the callback whose generation
matches the latest value applies the result. Older responses are dropped,
newer ones win.

Currency bug:
codeburn status --format menubar-json is a raw USD payload; the CLI does
not convert it. The popup was only changing the symbol prefix, not the
values. Fetch the USD->target rate from Frankfurter via Soup and apply it
in formatCost(). Rate is cached per-session so tab switches don't hit the
network; on currency change we kick off a fresh fetch (or reuse the cached
rate) and re-render with the new multiplier.

Other small fixes:
* Show a single-provider tab row (when exactly one provider is installed)
  instead of hiding it, so the user still sees which agent the numbers
  are for.
* Activity bar chart track is now a BoxLayout so the fill width takes
  effect; previously every bar rendered as 100% because the St.Widget
  track stretched its only child.
* Findings CTA no longer runs labels together ("findingssave"). Adds
  8px spacing between count and savings.
2026-04-18 05:21:09 -07:00
AgentSeal
ce94786185 feat(extensions): detect Pi and OpenCode in GNOME agent tab filter
Mirror the Mac ProviderFilter expansion by letting the GNOME popup
surface Pi and OpenCode tabs when their session stores are present.
Extends the provider detection paths with:

  opencode: $XDG_DATA_HOME/opencode (defaults to ~/.local/share/opencode)
  pi:       ~/.pi/agent/sessions

When two or more providers are detected the agent tab row renders with
All plus the detected providers; otherwise the row stays hidden so the
popup doesn't show a filter UI with nothing to filter against.
2026-04-18 05:10:29 -07:00
AgentSeal
29e175c735 Merge: hide Mac agent tabs when fewer than two providers have spend
Brings in the Mac menubar's agent tab visibility rule: tabs only appear when
two or more providers have non-zero spend in the all-provider today view.
Also expands ProviderFilter to include every provider the CLI supports
(OpenCode, Pi) so their tabs appear when those tools produce sessions.
2026-04-18 05:08:10 -07:00
AgentSeal
85d7bea7ea feat(mac): hide agent tabs when fewer than two providers have spend
The tab strip was visible for everyone regardless of which tools they
actually run, which produced a row of All + one provider for Claude-only
users and a row of All + zeros for users on exotic stacks. Hide the
whole row until a second provider has real spend, matching the behavior
the GNOME extension ships with.

Also expand ProviderFilter to include every provider the CLI supports
(OpenCode and Pi were missing) so their tabs appear when those tools
produce sessions. The CLI already emits pi and opencode in the payload's
providers map; the Mac app just wasn't offering a tab for them.

visibleFilters now filters on value > 0 instead of key presence, because
the CLI includes zero-cost entries for discovered-but-unused providers
and we don't want those rendering as blank tabs.
2026-04-18 05:07:36 -07:00
AgentSeal
6e354c17ed feat(extensions): hide agent tabs for providers with no data
The All / Claude / Codex / Cursor / Copilot tab row was fixed regardless of
which providers the user actually had installed, which made most of the
tabs render as $0 for people who only run one tool. Scan the expected
session directories on startup and only build tabs for providers that are
present. If two or more are detected, show "All" plus those providers;
if fewer than two are detected, skip the row entirely since there is
nothing to filter.

Paths checked:
  $HOME/.claude/projects         (Claude Code)
  $HOME/.codex/sessions          (Codex CLI)
  $HOME/.config/Cursor/User/globalStorage/state.vscdb (Cursor IDE)
  $HOME/.copilot/session-state   (GitHub Copilot)
2026-04-18 04:59:45 -07:00
AgentSeal
d47658c91d feat(extensions): rebuild GNOME popup with native widgets for Mac parity
Replace the PopupMenu text-item list with a custom St.Widget tree that
mirrors the macOS popover pixel for pixel. Same layout the native Quick
Settings panel uses: horizontal tab rows, branded header, hero
typography, inline bar-chart activity rows, and a pill-styled footer.

Structure:
  * Branded header with Code + Burn typography and subhead
  * Agent tab row (All / Claude / Codex / Cursor / Copilot) with active
    pill state and hover feedback
  * Hero block with period dot, label, large cost figure, calls and
    sessions meta
  * Period tab row (Today / 7 Days / 30 Days / Month / All) with same
    active-pill treatment
  * Activity section: bar chart per row scaled to the top cost,
    one-shot rate in brand green, cache and session numbers inline
  * Findings CTA as a full-width branded button, hidden when
    findingCount is zero
  * Footer with currency pill (cycles through 17 ISO codes), Refresh,
    Open Full Report, and an Updated timestamp

Dark and light theme classes drive the track and button palettes so the
popup stays legible regardless of GNOME system theme. Brand orange
(#ff8c42 to #c9521d) is the only hardcoded palette; everything else
inherits from the shell.
2026-04-18 04:52:33 -07:00
AgentSeal
e78a56b348 fix(extensions): stop truncating section titles in GNOME popup
The Activity and Models section headers were being clipped to "Activi..."
and "Mod..." because St.Label ellipsized the combination of
text-transform: uppercase plus letter-spacing in the available popup width.
Drop the uppercase transform and the letter-spacing so the labels render
in full and read consistently across themes.
2026-04-18 04:44:22 -07:00
AgentSeal
5e68981723 feat(extensions): add agent filter and currency switcher to GNOME popup
Adds two more submenus so the GNOME popup matches the agent tabs and
currency picker the Mac menubar app ships with.

* Agent submenu: All / Claude / Codex / Cursor / Copilot. Passes --provider
  through to codeburn status and codeburn report so the numbers and the
  full terminal report both follow the selected filter.
* Currency submenu: 17 ISO codes (USD, EUR, GBP, CAD, AUD, JPY, INR, BRL,
  CHF, SEK, SGD, HKD, KRW, MXN, ZAR, DKK, CNY). Selecting a code shells
  out to codeburn currency <code> and refreshes the popup with the new
  symbol. The current currency is read from ~/.config/codeburn/config.json
  on startup and after each change so all of our surfaces agree.
* formatCost takes the active currency so activity, model, provider and
  findings rows render with the right symbol instead of hardcoded $.
2026-04-18 04:35:29 -07:00
AgentSeal
4c5ec0f985 feat(extensions): polish GNOME extension and follow system theme
Expand the popup to show everything the TUI does: period switcher submenu,
top activities with one-shot rate, top models, provider breakdown when more
than one provider has data, optimize findings, and an updated-timestamp row.
Add per-period Open Full Report so the terminal view matches what the popup
was showing.

For theme compatibility, stop hardcoding colors that don't work on both
light and dark GNOME themes. Keep the brand orange (#ff8c42) for the header
and findings, let everything else inherit the shell palette, and express
dimming through opacity instead of rgba so the popup reads correctly on
Yaru Light, Yaru Dark, Adwaita, Adwaita Dark, and any other theme.

Add a listener on org.gnome.desktop.interface color-scheme so the indicator
re-applies its .codeburn-dark / .codeburn-light class when the user flips
the system theme, without waiting for a reload.
2026-04-18 04:20:14 -07:00
AgentSeal
291c376f06 feat(extensions): add GNOME Shell extension for native panel feel
Ship a GJS extension in extensions/gnome-shell/codeburn@agentseal.org so
GNOME users get the same panel-anchored popover Ubuntu's Quick Settings
uses, rather than the floating window the Tauri tray is limited to through
StatusNotifierItem.

The extension lives inside gnome-shell as a PanelMenu.Button, so it has
direct access to its own icon coordinates and can open a PopupMenu docked
under it without any IPC or DBus plumbing. The tradeoff is it only works
on GNOME 45+; the Tauri app in desktop/ stays the cross-platform option
for KDE, Unity, wlroots, and Windows users.

Data flow: the extension shells out to codeburn status --format menubar-json
every 60 seconds, parses the payload, and renders a header, top 5 activities,
and optimize findings count. Refresh and Open Full Report actions live in
the popup menu; the Open Full Report action spawns gnome-terminal with the
codeburn report TUI.
2026-04-18 04:06:44 -07:00