Commit Graph

6 Commits

Author SHA1 Message Date
ocd
5393e4eab5 Add health subcommand; detect dead auth cookies
OpenCode rejects a dead auth cookie with HTTP 302 to /auth/authorize (or
401/403). The HTTP client now disables redirect following so this surfaces
as a FetchError::Unauthorized instead of a 200 login page that fails to parse.

ocd health probes each account's cookie against its default workspace Zen
page and reports alive/dead/errored/misconfigured with a total count. Exit is
0 only when all accounts are alive.

The main walk (pretty/raw/fetch) also classifies a dead cookie as the new
ReportStatus::CookieDead, labels it [cookie dead] in pretty output and
cookie_dead in raw output, and continues with the valid accounts rather than
aborting. Verified live: valid account surfaces [ok], dead account surfaces
[cookie dead], unfilled account surfaces [cookie dead].
2026-07-30 02:04:53 +03:00
ocd
bd36438abf Backlog: store extra Zen billing fields (reload/subscription/limits)
The Zen page embedded record streams reloadAmount, reloadTrigger,
subscriptionPlan, monthlyLimit, and monthlyUsage, none of which appear in the
rendered DOM. Note as an open item for a future 'ocd billing' command.
2026-07-30 01:53:06 +03:00
ocd
3bb9a75234 Scrape embedded SSR JS records first, fall back to DOM
OpenCode is React Router v7 with streaming SSR; loader data is inlined in the
HTML as JS resource objects (e.g. rollingUsage:$R[n]={status,resetInSec,
usagePercent}). There is no separate JSON HTTP endpoint. parse the embedded
records first (exact resetInSec integers + a status field), and fall back to the
data-slot DOM when the embedded path finds nothing.

Add optional status field to UsageFrame (serde default, schema-compatible).
embedded_usage_frame scans every key occurrence so a billing-record
'monthlyUsage:null' sibling does not shadow the real usage object. embedded_balance
scans past 'balance:null'. Verified live: all three frames parse, month no
longer blank, status captured, resets_at driven by resetInSec.
2026-07-30 01:42:10 +03:00
ocd
4bf3eb1e06 Implement conditional GET (inert until OpenCode emits validators)
HttpClient.get_text now accepts etag/last_modified and sends
If-None-Match/If-Modified-Since; a 304 returns no body and the caller reuses
the cached parse. fetch_target recovers validators from the stale cache entry
(via new cache::read_raw, TTL-agnostic) and stores whatever the server emits.
Verified live: OpenCode/Cloudflare returns no ETag or Last-Modified on either
page, so the slots stay None, no headers go out, and 304 never fires. The
machinery is correct and ready for the day they add validators.
2026-07-30 01:27:30 +03:00
ocd
41f35aec34 Wire real OpenCode selectors and URLs from captured pages
Go page (/workspace/{id}/go): parse three [data-slot=usage-item] frames
(Rolling/Weekly/Monthly) into usage percentages + computed resets_at from the
relative reset-duration text. Zen page (/workspace/{id}): parse
[data-slot=balance] b into remaining + currency unit. Add an offline unit test
against tmp/ captures. Fix pretty renderer (percent display, money format,
day-aware relative times). Update DESIGN.md, README, examples and config
template to the real wrk_ workspace ids.
2026-07-30 01:16:36 +03:00
ocd
769982e5c9 Add design doc for ocd limits/balance dashboard 2026-07-30 01:07:14 +03:00