00756e6908
Screenshot guardrails: name every screenshot not COMPLETE on 409 ASSET_IN_POST_PROCESSING (submit, validate; stuck after 5 min in UPLOAD_COMPLETE), apple screenshots --repair-stuck replaces only those in place, part uploads without Expect: 100-continue (failed part deletes the reservation, one retry, never committed), storebound api --confirm <operationId> for agents' plain writes (never owner-only)
2026-10-21 18:08:57 +07:00
9c1f146c05
flags regenerated
2026-10-21 05:36:12 +07:00
567d6ca181
gen-flags: Args.required reads are flags too (edit --ops, privacy-manifest --ios, apple listing --version were refused as unknown options); play games client listAll (no false flags)
2026-10-21 05:29:08 +07:00
26f0723641
games status: both stores' game services in one read-only report (CLI, MCP games_status, desktop read method); --json status output stays pure JSON
2026-10-21 05:28:47 +07:00
9f48dd57a4
MCP: apple_game_center, apple_game_center_status, play_games, play_games_status (writes dry by default)
2026-10-21 05:28:47 +07:00
ca7289b173
games: one config section for Game Center and Play Games Services (shared leaderboards and achievements); apple game-center (gameCenterDetails, gameCenterAppVersions, leaderboards, achievements and localizations, upsert by vendor id, read back); play games (Games Configuration API upsert, no POST retry); signing ensure adds GAME_CENTER when games.gameCenter
2026-10-21 05:28:47 +07:00
335a4124ab
console plans: guardrails from a real run (2026-10-21): health none label and Sign in details Yes/No + Add details (old labels as altLabels, verify accepts both), finance boxes on step 1, App Privacy Additional Setup Required OK, verify from DOM input states (verify.capture, --from), Save + confirm before every Next on Play pages, IARC Digital purchases boxes and an exact-label in-page helper that refuses patterns and blanket answers; review contact found in parent folders, the profile and more key shapes with the files checked listed; one-line errors for missing required values (Args.required); pitfalls 86-87, tests
2026-10-21 05:03:02 +07:00
e1e4e05237
shots capture: signed-in web apps (shots.capture.auth request with ${cred:}/${env:} references or storageState), shots login saves a demo sign-in to .storebound/auth.json (0600), expired-session warning, secrets and cookies masked
2026-10-20 12:41:43 +07:00
c2f34c62f3
apple creative-assets: place Header and Search Results assets once Apple approved them (the version page offers only approved assets); waiting files are reported per locale and placement with their upload date and the re-run command, in the run, --verify, validate and the report; iPhone Duo: the published API spec still has no Duo type, the note says the console takes Duo and the API does not yet
2026-10-20 03:04:14 +07:00
47dfd372fe
apple: macOS (tvOS, visionOS) versions are no longer invisible: every version and review submission lookup is per platform; status, listing, listing pull, resubmit, submit, release and screenshots take --platform and otherwise pick the platform that has the version they need (refuse naming both when several do); status, validate, release status, the report and the desktop label versions with their platform; MCP and owner actions get platform; pitfall 77
2026-10-20 03:00:05 +07:00
183579449c
apple creative-assets: select Header and Search Results assets once processed (Prepare for Submission), not once approved: Apple reviews an asset only in a review submission, so 0.8.13 waited forever; upload and select in one run with a bounded processing wait; --add-for-review puts unreviewed assets into the draft review submission (never submits); --verify reads every slot back read-only (MCP apple_creative_assets_verify); a rejected file or one that failed processing twice is not re-uploaded, an archived copy is unarchived; fake commits library assets to PREPARE_FOR_SUBMISSION; pitfall 76 rewritten; apple-name check test only looks at name hints
2026-10-19 05:42:53 +07:00
e5369090fa
apple creative-assets: App Store Header and Search Results (PRODUCT_PAGE_HEADER_ASSET, APP_STORE_SEARCH_RESULTS_ASSET) through the App Asset Library: local size / alpha / video checks, upload once by MD5, select only approved assets, replace on a changed file; check / validate note, MCP tool, gallery and desktop Art row, docs, pitfall 75
2026-10-18 13:10:20 +07:00
af349b3db1
report: storebound report writes one shareable, self-contained readiness page (.storebound/reports/readiness-<date>.html; --md, --json, --no-open, --offline, --out) from scan, check and validate: a verdict per store (Ready to submit / N blockers / Not checked with the reason), findings per store by severity with guideline, evidence, fix and console button, the owner's compliance questions as a checklist, secrets masked; storebound.dev look, light / dark, print; desktop Checks > Report (same code path, opens through project_reveal) and its Markdown export on the same renderer; read-only MCP tool report
2026-10-18 01:42:03 +08:00
220e9bb072
play: an edit Play will not send for review (400 'set changesNotSentForReview to true', a game 1.0.1 (5) after a rejection) is validated and committed again on the same edit with changesNotSentForReview=true; dry runs validate that way and say so; prints the Publishing overview link (owner's button), records it in .storebound/ for play status; --not-sent-for-review on release / upload / rollout / listing / images and MCP / desktop; pitfall 72
2026-10-16 05:50:37 +08:00
992f5bcc0e
desktop: a Fix button on scan and validate findings storebound can fix itself. Findings carry action { method, args } (cli/lib/fix-actions.ts); privacy-labels runs scan.write (scan --write-only, the dialog shows the config diff), Play listing gaps play.listing, a missing App Store localization or copyright apple.listing (editable version only), the App Review contact apple.review_details; each a dry run, the native dialog, then the confirmed run. --write-only is forbidden for MCP tools; tests pin the mapping, the round trip on a fixture and that no fix reaches a submit, release or publish
2026-10-15 06:14:49 +08:00
daae3715bf
apple signing ensure with automatic signing only adds capabilities (no profiles or certificates; the dry run says so) and lists unused storebound profiles with the exact delete command, never run; every capability write is read back (lag retries, timeout) and fails with the API answers when one did not stick; apple status, signing status and validate compare the App ID, apple.signing.capabilities and the iOS project's entitlements (CODE_SIGN_ENTITLEMENTS too) and name what is missing where; api writes outside a terminal say TTY by design and name the dedicated command; pitfalls 64-66; tests
2026-10-14 19:10:01 +08:00
357319a775
--json prints only JSON on stdout (withJsonOutput around every command: logs, tables and prompts to stderr; printJson everywhere; test runs every --json command against the fake stores and parses stdout); prices ppp compares like with like: par = the store's own US-equivalent price (Play convertRegionPrices, Apple equalizations; offline base x fx x (1 + VAT) rounded up) scaled by the PPP price level; defaults only lower, only price levels <= 0.5, World Bank high-income at par, CN at par on Apple and skipped on Play, never above par (--only-decreases, --max-ratio, --min-ratio, --max-level, --include-high-income; --floor / --ceiling kept), top-10 revenue market drops > 20 % warned (sales reports, else a static list), unchanged regions out of the sheet; price changes of existing products only with --allow-price-changes, TTY or not (a terminal yes covered them), then their own question listing each old -> new, held section in dry runs (play products, prices import, play / apple subscriptions); pitfalls 61-63; tests
2026-10-14 17:36:00 +08:00
bab3f63227
whats-new drafts from player-facing notes first (whatsNew.source, docs/RELEASE-<version>.md, the CHANGELOG section of the version or Unreleased; --release, --from-git) and only then from the git log; internal jargon warnings (SDK / vendor names, developer and version-control words, file paths, issue ids, code identifiers) with plain alternatives in the draft and whats-new check, one list in cli/lib/jargon.ts, whatsNew.allowWords keeps a word; skill: write from what players feel, never from commit messages; tests
2026-10-14 14:45:19 +08:00
7219ded3e8
template compliance answers never reach a store: init writes unanswered (dataSafety, appPrivacy, reviewNotes, ageRating, accounts, reviewAccess), older configs are recognised by the template values; check / validate / scan block, play data-safety (also --dry), console plan / fill, apple review-details and appinfo refuse. onboard --declarations replaces them on a live app: name and copyright from the stores (a copyright with a private term is left out), the rows the code proves (scan proposal, sign-up methods from auth code and login SDKs), the rest as one owner question list. play data-safety --dry prints the answers that change (old -> new, account and deletion first) against the last uploaded CSV; pitfall 60; tests
2026-10-14 11:36:30 +08:00
00be838b76
store art from the live stores: storebound art pull (App Store screenshot sets and previews per locale and display type from imageAsset.templateUrl at the original size, the App Store icon from the newest build or the public artwork; per Play language icon, feature graphic, phone / 7" / 10" / TV / Wear screenshots as the =s0 originals checked against Play's sha256) into store/renders with pulled.json (sha256, store, URL host, store checksum); upsert (a re-run changes nothing, differing local files kept unless --force, the upload's file of a slot compared: Play .jpg, App Store .png, the App Store icon as a picture); onboard runs it; apple screenshots / previews and play images see unchanged pulled files as the store's own; check: render sources are notes with pulled art; local renders check: pulled files pass, store-empty sets not demanded; art live / thumb / replace; desktop Store art tab: live thumbnails per store, language and set (same / differs / not pulled), Pull from stores, store-only apps read-only, Replace with a file (image_pick, art.replace) in the lightbox and the art editor; boot check store art case; tests/art-pull.test.ts; console parity 76 rows (57 complete, 19 with the reason); store-screenshots skill (pull first for live apps), pitfall 59
2026-10-14 05:25:37 +08:00
7ac028421a
store-only apps edit their listing on the store (storebound listing live / set: old -> new per field with the checks, a PATCH of only the edited fields; serve listing.set is the one store-only write, its token bound to the store ids; policy.rs STORE_ONLY_WRITES, write_with_confirm for store targets); screenshot captions per language and scene edited as a text edit of the shots copy JSON (JSON files keep their layout); Play content rating, target audience and App content answers in App info; docs/console-parity.md after the editors (73 rows: 55 complete, 18 with the reason) and tests/console-parity.test.ts keeping it honest
2026-10-14 00:54:56 +08:00
3cea2af4e1
desktop editors: edit in place what App Store Connect and Play Console let the owner edit (listings per language for both stores with live counters and the check / scan rules, store details, App info, age rating, App Review, Play store settings, Data safety and App Privacy answers, promotion blocks, catalog names / prices / offers, TestFlight information, scene order); storebound edit (project.edit) saves into the config by path keeping comments and formatting, the listing files and the review contact, dry run with the diff and the checks first; Apply shows each store's dry run in the window; unsaved edits survive tab switches with undo / redo; tests/edit.test.ts, apps/desktop/tests/edits.test.ts, boot check editor case
2026-10-14 00:37:08 +08:00
7b05e4b396
auto-read after the rebase: results through usable / failureText, issuesError notice kept, scheduler 'accounts' (no credentials word in the webview), fixtures and generated tables for health_issue
2026-10-13 23:41:36 +08:00
0e4f579899
apple build exports each build into build/ios/export/<version>-<build>/ (cleaned first), runs xcodebuild with Apple's tool folders first on PATH (Homebrew rsync 3.x broke Xcode's IPA step: exportArchive Copy failed), shows the IDEDistribution log's real error lines and exits non-zero on any export failure; apple upload fails with exit 1 when the IPA's build number is already on App Store Connect (stale IPA after a failed build), --allow-existing re-checks, --build N picks that build's IPA, release and beta flows use both; doctor flags a non-Apple rsync first on PATH; desktop publish calls rsync by absolute path without --chmod (publishSteps); site download notice per OS (macOS signed and notarized, Windows unsigned for now, Linux unsigned); game-report credential tests hermetic against a machine default profile (pitfalls 53, 54)
2026-10-13 09:45:48 +08:00
e5f148e534
Onboarding live apps without owner questions for what the stores and the code prove: storebound onboard imports the live products and subscriptions with USD / base prices (catalog diff clean), contact values, the App Review contact and demo account (credentials.json, masked), sign-in for review from Apple's review detail and auth code, and the account deletion page from the app's site; App Store keywords count characters (live fields up to 265 bytes accepted); validate accepts the 6.5-inch iPhone set and Play's default-language graphics fallback; Play Reporting API off gives the project's enable link and apps list falls back to local apps; accounts default <profile>; store-only apps (apps add --store --app-id) shown read-only in the desktop (pitfalls 43-52)
2026-10-13 07:25:01 +08:00
d2ce185441
One product per machine: the CLI and the desktop app share one version, the store accounts and the app folder list
...
- one version: package.json's version is the desktop app's too (desktopVersion removed); tools/desktop-version.ts
keeps the plugin manifests and the app's copies equal, desktop-release publish refuses another version, release
order app first then npm publish; the unpublished desktop 0.1.0 candidate is noted in the docs and CHANGELOG
- storebound desktop installs (per user, no admin: macOS DMG -> /Applications or ~/Applications, Windows NSIS /S,
Linux AppImage + .desktop with the storebound:// handler), updates an older app, notes a newer one (downgrade only
with --force), asks once ([Y/n], --yes, exit 3 without a terminal), opens on the folder; --dry / --json / --download;
the install steps are pure plans (cli/lib/desktop-install.ts); the version's own manifest dl/<version>/latest.json
- credentials: one store, the CLI's credential profiles in ~/.storebound/credentials; the desktop writes and reads
them (profiles.rs, files 0600), moves old keychain accounts into profiles on first start, shows profiles the CLI
made; precedence env > named profile > project file > default profile; config `account`, "account" in the
project's credentials.json; storebound accounts list | import (dedup by key id / service account e-mail, key files
copied 0600, project file keeps keystore and review contact, backup, --dry, never prints values) | use
- app folders: one list ~/.storebound/projects.json shared by the desktop (registry.rs), storebound desktop,
apps folders | add | remove, ws init and ws without a workspace file
- desktop: notice when a folder pins another storebound version; storebound-desktop --version
- tests: version drift, desktop decisions and per-OS plans, credential precedence and import, registry (TS + Rust),
keychain migration and profile round trip (Rust), boot check with old keychain state and the new shared state
2026-10-13 06:17:37 +08:00
f741c15829
console: Play App content card Saves are the agent's (declaration-save; Send changes for review, Submit, Publish, Restart review stay forbidden), one owner batch per plan with the IARC Terms tick before the run, console plan play-app-content --all, New App fields typed not set, play.appId from the Create app redirect (console verify --url --write) and storebound play app-id across app list pages
2026-10-13 05:44:29 +08:00
1366e7e231
scan, privacy answers and credentials from a second game's first release (lessons 35-37): review-prompt needs rating context, not a bare "rate"; account signals skip import lines and module paths, config-only accounts say "from config"; accounts.inApp without deletion code stays a medium 5.1.1(v) finding; privacy-manifest --propose builds NSPrivacyCollectedDataTypes from appPrivacy.types and check warns on an empty list; appPrivacy.types is authoritative in the App Privacy sheet (unlisted = not collected), ad / support / AR types default to not collected without tracking or ad SDKs; Play advertising ID from SDKs, the AD_ID permission, app-ads.txt or the flag, never from a Device ID alone; scan.ignore[].evidence and warnings for entries that match nothing; empty credentials template, placeholder values count as missing (check, doctor)
2026-10-13 05:39:09 +08:00
33eb24a3dc
apple submit --build <n> (first submission, the resubmit steps; plan table with --json under serve); desktop owner actions to send for review: owner.apple_submit, owner.apple_resubmit, owner.apple_iap_submit, owner.play_production, and the price gate owner actions (owner.prices_play_products / _play_subscriptions / _apple_subscriptions; owner.prices_import passes --allow-price-changes after the dialog); OWNER_ALLOWED_ARGS ties each forbidden flag to its one action, dry runs never carry owner flags, MCP still has no submit or production release; submit dialog text in the host; system.tools androidSdk, project.inspect playRelease, declarations dataDeletion
2026-10-12 12:08:07 +08:00
41d2ffcc24
apple testflight groups: bare lists the groups and what the config would create (read-only); groups sync (or --yes) creates; flows: bare = read, sync = write; MCP tool runs groups sync
2026-10-12 10:48:59 +08:00
1724296e59
apple resubmit: REJECTED review submission items are marked resolved before the submit (PATCH reviewSubmissionItems resolved: true; the version item by default, IAP / subscription items with --resolve-iaps), plan table 'mark resolved' rows; on 409 'not ready yet' the items are re-read and a REJECTED item out of scope stops the run at once naming it and how to resolve it, backoff only when none is left; an empty READY_FOR_REVIEW draft is never canceled (409 'not in cancellable state'): left as is, no cancel command, apple status labels it harmless and treats the cancel 409 as such; fake ASC blocks submit on REJECTED items and refuses to cancel empty drafts. Pitfall 30
2026-10-12 10:45:05 +08:00
0da5ee9d01
apple resubmit after a rejection: iap-submit reuses the open review submission (UNRESOLVED_ISSUES gets only missing items and is resubmitted, a draft is reused, one waiting for or in review means nothing is sent), never a second draft; 409 ITEM_PART_OF_ANOTHER_SUBMISSION names the other submission instead of the list-lag message, a draft the run created is canceled and the named one used; submit retried with backoff and heartbeat while Apple says not ready yet; new apple resubmit --build <n> (build by number, plan table, --yes); apple status lists UNRESOLVED_ISSUES items and the resubmit command. Pitfall 30
2026-10-12 10:34:12 +08:00
c8213be2b6
ITMS-91064: NSPrivacyTracking and NSPrivacyTrackingDomains must agree in every privacy manifest (true needs domains, domains need true); check, scan and privacy-manifest fail on either mismatch with the exact ITMS text; App Privacy tracking no longer demands the app manifest's true: SDK manifests that declare tracking (Pods, frameworks, SwiftPM, Carthage, plugins, the built IPA) are info, known tracking SDKs info, else a warning; privacy-manifest --propose writes true only with appPrivacy.trackingDomains; apple upload checks every manifest in the IPA (XML or binary) before any network call, apple build after the export. Pitfall 29
2026-10-12 10:27:21 +08:00
b7bfe89151
Play console steps without an API as agent plans: play-store-settings (App category; play listing cannot set it), play-app-content (every card in order, one owner batch) and its cards (privacy policy, ads, sign in details, advertising ID, government, financial, health), play-production-countries and play-production-release; IARC plan with the Terms of Use agreement, follow-ups (console.contentRating.details, products[].randomRewards, social) and the Summary's final Save; config play.category / countries / managedPublishing, console.appContent; check, validate, play status and play release reminders; inPageLoop helper and material checkbox clicks; App Privacy published signal; first IAPs via apple iap-submit --with-version. Pitfalls 27, 28
2026-10-12 10:16:34 +08:00
e01a27d7b7
price changes of existing products need --allow-price-changes (play products, play/apple subscriptions, prices import): --yes and flow run --apply --yes confirm creates, first prices and other updates only; without the flag the run writes the rest, names each held change and exits 3 (declined), dry runs mark them owner, flows refuse the flag; lib/price-gate.ts, tests/price-gate.test.ts. CLI wording: console fill leaves what it cannot open or fill to the calling agent's browser tool (console plan / verify) instead of 'yourself'; other 'by hand' output points at the config; do-it-dont-ask test scans cli/ output
2026-10-12 09:38:57 +08:00
366362d62b
flow run: a declined confirmation stops the flow; --apply --yes confirms write steps
...
confirm() prints 'declined: <reason>' and exits 3 whenever the answer is no or nobody can answer (not a
terminal without --yes, serve without the owner's dialog), so no command reports success after declining.
flow run stops at such a step with 'step N declined: <reason>' (status declined), never ok. flow run
--apply --yes passes --yes to write steps only; --yes without --apply is refused. Pitfall 24.
2026-10-12 08:51:58 +08:00
5fbb9e63e3
build.prebuild and a Capacitor web-asset staleness guard for apple build / play build
...
apple build and play build run build.prebuild (or apple.prebuild / play.prebuild) first, once per flow run;
--no-prebuild skips it, --dry only prints it. A Capacitor project's copied public/index.html must then be newer
than the last source commit and <webDir>/index.html, else the build stops (--allow-stale-web overrides). check
warns when stale without a prebuild; the build output and dry plan show the web bundle's sha256 and mtime.
The Capacitor project is also found in a monorepo (apps/native etc.); several ask for play.build.dir /
apple.build.*. Pitfall 23.
2026-10-12 08:51:38 +08:00
720cb2a13a
play images: same languages as play listing (drafts skipped unless --include-drafts); a language with no Play listing (404) is skipped with an info line, never fatal
2026-10-12 08:45:43 +08:00
c9a7f095c4
console: agent-first mode (console plan / console verify), verified App Privacy and Play Create app facts
...
- `storebound console plan <form> [--json] [--lookup]`: read-only plan for an AI agent that drives the browser
itself: start URL (deep links), ordered steps, answer sheet with the console's exact labels (certain / ask owner,
curly quotes noted), never-press list, verified UI facts, driving guidance (in-page async loop + window.__log),
verify instructions. All six forms.
- `storebound console verify <form> [--from-text <file|->]`: App Privacy per-type summaries from page text, Play
Create app from input states, content rating / target audience summaries, App Store record and age rating from
the API; exit 1 on a mismatch.
- MCP: console_plan, console_verify (read-only); console fill stays off MCP.
- App Privacy (verified 2026-10-12): two information steps, unlabeled purpose checkboxes (nearest parent text),
"Definitions and Examples" never pressed; fixture and engine follow.
- Play Create app (verified 2026-10-12): /developers/<id>/create-new-app, App package name + read-only Check
availability, material-radio inner [role=radio] click, app dashboard URL and play.appId, service account
"Add app" as an owner step.
- console-autofill skill rewritten agent-first; agents-core rule 10; README; pitfalls 12, 18 (confirmed sign-out),
19; CHANGELOG; play-first-release.
2026-10-12 08:39:12 +08:00
a9261a8052
play images: tablet, TV and Wear sets are optional; play.tablets switch (default: tablet renders exist); a missing optional set never deletes Play's images without --prune
...
A phone-only project aborted with 'no sevenInchScreenshots'. Play requires the 512 icon, the 1024x500 feature graphic and at least 2 screenshots (Play Console Help answer 9866151). play images uploads what exists and prints an info line for missing optional sets; play.tablets true makes missing tablet sets an error in shots render/check/gallery, validate and play images. Pitfall 19.
2026-10-12 08:21:03 +08:00
0035931734
console fill: never copy Google session cookies by default (device-bound); own profile in a Chrome started without automation switches
...
Google binds its session cookies to the device (Device Bound Session Credentials) and signs out
sessions whose cookies look stolen, which can sign the owner out of their real Chrome. The default is
now storebound's own profile per store; storebound starts Chrome itself (--user-data-dir,
--remote-debugging-port=0, no --enable-automation, navigator.webdriver false) and attaches over CDP.
--use-chrome-session google|apple is an explicit opt-in that warns. /signin/rejected is said plainly.
2026-10-12 07:46:38 +08:00
81313b6fa4
console fill: no silent waits; an unknown page gets a debug dump and a bounded stop
...
A real `console fill apple-app-privacy` clicked Next onto a console page
the engine did not recognise and logged nothing for 8 hours.
- Step watchdog: after a click, or while the form is expected, no known
page within --step-timeout seconds (default 30) -> "the page after
"Next" is not a page storebound knows ... Buttons there: ...", a debug
dump, and the manual steps. It keeps watching, but 20 min without
progress closes the window and exits 1.
- A heartbeat line every minute while waiting (also during sign-in); the
final review wait is bounded (2 h) with a reminder every 10 min.
- Debug dump (cli/console/debug.ts) in .storebound/console-debug/
<form>-<time>/: page.json (URL without query, headings, dialogs, every
visible control with role and name), page.html (rebuilt from a clone:
no scripts, values, hidden inputs or non-whitelisted attributes) and a
small screenshot. Emails, phone numbers and the header / account area
are replaced; cookies, storage and tokens are never read.
--debug-dump writes one at every step for bug reports.
- Guardrail tests: an unknown-page fixture (dump sanitized, bounded exit)
and a scan of cli/console for while loops without a deadline and
Playwright waits without a timeout (one waitForLoadState fixed).
- Pitfall 14 (docs/pitfalls.md, store-pitfalls skill); the
console-autofill skill says how to send a dump.
2026-10-12 07:08:10 +08:00
db363be37b
flags: regenerate (check reads the iOS project through the scan modules)
2026-10-12 05:27:03 +08:00
7746a984ec
Merge remote-tracking branch 'origin/main' into shots-liga
...
# Conflicts:
# CHANGELOG.md
# cli/index.ts
2026-10-12 05:24:19 +08:00
af08d7113a
shots check: ligatures no longer read as system-font glyphs (probe spans shaped as one run); --help on every command and group, unknown flags rejected before anything runs
...
Root cause: the per-character font probes were inline spans side by side, so Chrome shaped f + i into the fi ligature across them and the lone i probe reported no glyph of ours. Each probe is now its own block. The Google Fonts text= subsets keep the GSUB ligatures; regression test with a real Montserrat text= subset.
--help / -h prints the command's usage and exits 0 before any config, render or network; unknown flags exit 2 with the usage (cli/data/flags.json from tools/gen-flags.ts). Command tables moved to cli/commands.ts; ads apple reachable again; shots render --scenes rejects unknown ids at once.
2026-10-12 05:23:37 +08:00