Commit graph

5 commits

Author SHA1 Message Date
iamtoruk
527e58078d menubar: install and launch the Windows tray app from codeburn menubar
Shares the mac release resolution behind a per-platform spec (tag prefix, asset name, error text), so the Windows path reuses the pinned-version URL, the release-API fallback scan, the retrying download and the sha256 verify unchanged. Windows then runs msiexec out of %SystemRoot%\System32 with /i /passive /norestart, treats 3010 and 1602 as non-failures, and launches the exe named by the product's Uninstall registry key.
2026-08-18 06:55:09 -07:00
iamtoruk
76c63cbfd1 windows: cfg-gate the tray badge so the Linux build has no dead code
The spend badge is a second tray icon carrying the number as its bitmap, which
only the Tauri tray backend provides; Linux runs its own SNI tray and has no
equivalent. The module was compiled there anyway, so every item in it - and the
two tray ids in lib.rs - tripped dead_code under clippy -D warnings on the
ubuntu leg.

- `mod tray_badge` and TRAY_ID / BADGE_TRAY_ID are now cfg(not(linux)), so the
  code is absent on Linux rather than present and unused.
- `set_tray_badge` reports the badge as unsupported on Linux instead of
  returning a success that never happened.
- The frontend hides the control wherever it is unsupported, behind
  TRAY_BADGE_SUPPORTED in lib/platform.ts - one constant, three call sites
  (the settings row, the footer menu item, and the effect that would otherwise
  invoke the command).
- AppState's `linux_tray` field is dropped: it was written and never read, which
  is the same lint one file over. init_tray_linux already owns the handle.

Checked rather than reasoned: `cargo clippy --all-targets -- -D warnings` passes
on macOS, and a scratch copy of the crate with the linux/macos cfg arms swapped
(so a normal macOS clippy selects everything a Linux build would keep, and drops
everything it would drop) also passes. Reinstating the ungated `mod tray_badge`
in that copy reproduces the exact CI failure, so the check is real. It stubs
tray_linux.rs, whose ksni and png deps do not build here - that file is still
only covered by CI's ubuntu leg.
2026-08-18 04:37:27 -07:00
iamtoruk
120747d2fa ci: build and release the Windows menubar
windows-menubar-ci.yml runs on windows/** changes: tsc, clippy with -D warnings,
and cargo test on both windows-latest and ubuntu-latest, plus a release-profile
`tauri build --no-bundle` on Windows. The Linux leg exists because most of the
crate's cfg(windows) code cannot be compiled anywhere else, so the reverse -
keeping the ksni paths and every shared helper clean off Windows - has to be
checked somewhere too.

release-menubar-windows.yml mirrors release-menubar.yml: a `windows-v*` tag (or
a manual dispatch) builds the MSI and publishes it, with a sha256, to a
"Windows Menubar vX" release.

The two release-desktop-*.yml files on the source branch are deliberately not
imported: their tag names and "Desktop" release titles would collide with the
Electron app in app/, which already owns `desktop-v*`.
2026-08-18 04:11:32 -07:00
iamtoruk
7e57fb8d4f windows: fix the security and correctness findings from the import audit
Process spawning (Windows searches the current directory before PATH):

- reg.exe and cmd.exe are now spawned by absolute System32 path via
  cli::system_command, which also carries the CREATE_NO_WINDOW flag the three
  previous copies each set by hand. The tray badge re-ran `reg query` on every
  refresh, so this was the most reliably reachable planted-binary path.
- "Connect Claude" resolves the claude binary itself instead of handing a bare
  name to the console shell.
- CLI lookup ignores empty and relative PATH entries. `;;` or a trailing `;`
  used to yield PathBuf::from("").join("codeburn.cmd"), a current-directory
  lookup, at startup. The filter lives in one place (find_in_dirs) that every
  search - codeburn, claude, and the Linux terminal probe - goes through.
- The Linux terminal path re-validates the whole command against the argument
  allowlist before joining it into the string `bash -lc` parses; anything that
  fails falls through to the argv-only detached spawn.

CLI version gate:

- MIN_CLI_VERSION moves from 0.7.0 to 0.9.9, the first release accepting
  `status --format menubar-json --no-optimize` (every quiet refresh passes it)
  and emitting all the payload fields the popover reads.
- The gate is probed on mount, before the first fetch. It previously only ran
  when a fetch failed with the literal "CLI not found", so an old CLI produced
  a payload whose missing history.daily threw and blanked the popover.
- A successful fetch no longer flips an incompatible CLI to compatible, and the
  settings panel no longer probes on its own - App owns the verdict, so a
  transient probe failure there cannot drop a working app onto the setup screen.
- The payload reads App makes are optional now, so a surprising payload lands on
  an empty state rather than a blank window.

Refresh cadence, mirroring mac RefreshCadence.swift: 60s with optimize findings
while the popover is visible, 120s today/all without them while it is hidden,
and an immediate refresh on show when the visible key is stale. Every hide path
in lib.rs now goes through mark_hidden so the debounce stamp and the frontend
signal cannot drift. Previously it was 60s with optimize regardless of
visibility - about 2880 CLI spawns a day.

Claude quota (plan.rs) stops calling the token refresh endpoint. Claude's
refresh token is single-use and rotates, so spending it invalidated the token
Claude Code itself holds and broke the user's login. Parity with
ClaudeCredentialStore.refreshAfter401: re-read Claude's own store for a token it
has already rotated, and report a transient failure when there is not one yet.

Smaller:

- Snapshot writes refuse a symlinked target and are 0600 on unix, mirroring
  mac SafeFile; no home directory now means no snapshots rather than a file
  dropped in whatever directory the tray was launched from.
- The Windows config lock keeps its file handle open, so the stale sweep can
  only ever unlink a lock whose owner is gone. The doc comment now says what the
  lock actually buys (the CLI never takes it).
- "updated Xs ago" is only stamped by a fetch of the key on screen.
- External links go through tauri-plugin-opener instead of target=_blank.
- The tray badge font is loaded once into a OnceLock instead of read and parsed
  on every render.
- autostart's shared import and constant are cfg-gated so clippy is clean on
  every OS.

Adds unit tests for the PATH filter and the version gate.
2026-08-18 04:10:07 -07:00
iamtoruk
b199af182e windows: import the Tauri tray app as the Windows menubar
Brings the Tauri 2.x tray popover from #1022 onto main as windows/, mirroring
mac/. Product name, bundle identifier, and version line up with the macOS
menubar (org.agentseal.codeburn-menubar, 0.9.20); the crate is renamed off
"desktop" so it no longer collides with the Electron app in app/.

Linux (ksni) stays compiled and dev-usable but is documented as experimental:
gnome/ is the shipping Linux surface.

The five src/ CLI commits on that branch are dropped - they re-implement a
daily-bucketing fix main already carries.
2026-08-18 04:00:59 -07:00