desktop reads everything by itself: one background read scheduler (4 per store, starts spaced per account, App Store Connect X-Rate-Limit slows it below 20 % and pauses at 100 left, backoff with jitter on 429 / RESOURCE_EXHAUSTED, visible app first, one read per key), per-method TTLs over the saved reads, Settings auto-read (on) and interval (15 min, paused while hidden), Refresh all, a refresh icon and 'read 3 min ago' / 'couldn't refresh' per card, writes invalidate, status line with progress and quota; quota reaches the window through ToolResult.quota (cli/lib/quota.ts); Health top issues open with versions, sample stack (health --issue / --signature, read health_issue), Open in Play Console, Copy for AI and likely-cause hints (cli/lib/crash-hints.ts); tests/scheduler.test.ts, tests/quota-crash-hints.test.ts, boot check auto-read case

This commit is contained in:
CRTkafa 2026-10-13 23:36:30 +08:00
parent 89256f1e80
commit 0fb4ce0001
46 changed files with 1531 additions and 141 deletions

View file

@ -69,10 +69,33 @@ Types of the protocol: `cli/lib/desktop-protocol.ts`; pure portfolio logic: `cli
tools, first read.
- **Command palette** (Cmd/Ctrl+K): apps, tabs, screens and actions. Cmd/Ctrl+1 Portfolio, +2-5 workspace screens,
Cmd/Ctrl+Shift+[ ] app tabs, arrow keys in the tab list. Toasts show background jobs with progress and Cancel.
- **Unknown, never false.** Store reads run on demand (they use the account's API quota) and are saved in app data
(`cache.sqlite`, results only, never keys); a screen opens with the saved read and its time ("Last read 3 h ago,
saved copy"), a failed read keeps the previous one and says so, and nothing that was not read is shown as fine.
Settings clears the saved reads.
- **Unknown, never false.** Every read is saved in app data (`cache.sqlite`, keyed by app + method + arguments,
results only, never keys); a card opens with the saved read at once and "read 3 min ago", a small spinner while it
refreshes, and a failed read keeps the previous one with "couldn't refresh: <one-line reason>". Nothing that was not
read is shown as fine. Settings clears the saved reads.
- **Automatic reading** (`src/lib/scheduler.ts`, `src/lib/freshness.ts`, `store.tsx`). On start, when the window shows
again after a minute hidden, and every N minutes while it is visible (Settings > Store reads: auto-read on by
default, every 15 min; 0 = only on start; paused while hidden), the window reads everything of every app that is past
its TTL: status and submission state 5 min, TestFlight 5 min, reviews 15 min, catalog / prices / listings / checks
30 min, health / analytics / ASO 6 h. "Refresh all" in the toolbar reads everything now; each card has a refresh
icon. Writes invalidate the app's reads. All reads go through one scheduler:
- lanes `apple`, `play`, `local`, each with at most Settings > parallel (default 4) at once; a read that needs both
stores takes a slot in each;
- starts on one credential (account) at least 250 ms apart;
- App Store Connect's `X-Rate-Limit` header (user-hour-lim / user-hour-rem, about 3600 per key per hour) reaches the
window as the ToolResult's `quota` (`cli/lib/quota.ts`: a marked stderr line under `serve`, taken out of the
output by `runner.ts`). Below 20 % left, starts are spread over the rest of the hour; at 100 left (kept for the
owner's own commands) the key pauses 10 minutes;
- 429 / RESOURCE_EXHAUSTED (Play Developer API, Reporting API; the CLI already retries inside one command): the read
runs again with exponential backoff and equal jitter (5 s base, 5 min cap, 4 attempts), and its key waits too;
- priorities: the app on screen first, then the live portfolio status, then the rest; one in-flight read per key.
The sidebar's status line says "Reading 12 of 48 · App Store limit 3,412/3,600 left this hour". The boot check's
auto-read case (13 apps, cold cache) loads everything without a click and finds no "not read yet" on any tab.
- **Health issue detail.** A top issue (Play) or diagnostic signature (App Store) opens in place: full title and type,
reports / users, first and last versionCode, Android API range, last report, a sample stack (`health.issue` =
`health --issue <id>` via errorReports:search, or `health --signature <id>` via the signature's logs; cached like
every read), "Open in Play Console" (issueUri), "Copy for AI" (compact markdown with the top 15 frames) and a likely
cause with its fix for known signatures (`cli/lib/crash-hints.ts`, shared with `storebound health`).
- **Jobs.** The window tags a call with a job id; the host maps sidecar request ids to it, so progress lines reach the
right toast, and `job_cancel` sends `$/cancelRequest` (or stops a download). Live portfolio reads run up to four
apps at a time with their own accounts and report n / total.