Commit graph

3 commits

Author SHA1 Message Date
iamtoruk
916aa6430f harden(app): Electron security + spawn efficiency batch
- will-navigate/window-open denied; openExternal restricted to http(s);
  production CSP drops 'unsafe-inline' scripts (dev-only Vite plugin
  re-adds it for the Fast Refresh preamble)
- single-instance lock; before-quit reaps in-flight CLI children; 16MB
  output cap rejects with CliError 'too-large'
- read-only CLI spawns coalesce: concurrent identical calls share one
  child, a 5s result cache absorbs same-cadence pollers (six sections
  polled getOverview independently); actions bypass and flush the cache
  on completion so post-action refetches are fresh
- every renderer-supplied argv string validated (period/provider/range/
  currency allowlists, no flag-shaped tokens, absolute export path) with
  new CliError kind 'bad-args'; envelope + action stderr routed through
  sanitizeError
- quota force-refresh: manual refresh (refreshToken) passes force which
  invalidates QuotaService's cache; steady polls do not
- POSIX credential mode check gated off on win32

App suite 190/190, build green (production index.html verified strict).
2026-07-16 01:57:44 -07:00
iamtoruk
b36253d2e2 fix(app): resolve usePolled stale-response race + review nits
Review of T0 (Electron scaffold) approved with one Important finding + cheap
Minors. All fixes scoped to app/.

- usePolled: replace per-call cancel closure with a generation/epoch counter so
  an orphaned in-flight fetch (from refresh() or an interval tick, discarded
  cancel handle) can't resolve after a newer fetch and clobber fresh data. New
  TDD race test (slow deps-A resolves after fast deps-B; keeps B).
- cli: nvm resolution now scans version dirs descending and takes the first
  whose bin actually contains codeburn (was lexicographic max, unverified),
  matching CodeburnCLI.swift. Export nodeManagerDirs + hermetic nvm test.
- index.html: tighten CSP connect-src ws: -> ws://localhost:5173.
- preload: import type { Envelope } from main instead of redeclaring it.
- main.test: table-driven channel->argv assertion over all 9 codeburn:* channels
  plus a keys check and a non-spawning cliStatus case.
2026-07-10 15:40:11 -07:00
iamtoruk
6e3729cfa9 feat(app): window chrome, sidebar nav, and Overview smoke view
Ports the wireframe window shell into React with exact markup/classes:
- Shared components: Window, Sidebar (traffic lights, gradient mark, six
  nav items with ⌘ keycaps + active `on` rail, status line), TopBar
  (title, scope, period SegTabs Today/7D/30D/Month/6M/Custom, ProviderPop),
  Panel, Stat, SegTabs, ProviderPop, Hint.
- usePolled(fetcher, deps): generic 30s poll + loading/error state,
  errors normalized to CliError.
- App.tsx: local section state switches the content outlet; the Overview
  placeholder round-trips getOverview('30days','all') and renders the raw
  current.cost, with an honest first-run "locate CLI" state when the
  codeburn binary isn't found (never a crash).
- Sidebar.test.tsx: six nav items, click routes onNavigate, active `on`.
2026-07-10 15:17:11 -07:00