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

This commit is contained in:
CRTkafa 2026-10-20 12:41:43 +07:00
parent 3949ff1531
commit e1e4e05237
15 changed files with 545 additions and 9 deletions

View file

@ -126,7 +126,8 @@ the icon, feature graphic and phone screenshots and prints `tablet screenshots:
`baseUrl`, fonts) via `.storebound/capture-cache.json`; `--only-missing` captures only missing files, `--force`
everything. Each progress line shows n/total, ETA and the time per step (goto, wait, eval, delay, shot); the end
lists the slowest frames and steps, and `.storebound/capture-report.json` keeps them. Put the slow step right
(a long `waitFor` = the game reaching the scene: add a URL switch that jumps there).
(a long `waitFor` = the game reaching the scene: add a URL switch that jumps there). Screens behind a login:
`capture.auth` (see Signed-in apps).
- `ios-sim` (Unity, Godot, Flutter, native, Capacitor: any Simulator build): `ios.device` (simulator name or UDID,
e.g. a 6.9" iPhone), `ios.bundleId`, optional `ios.app` (.app to install), `ios.args`. Per shot: `args` (launch
arguments, read them with UserDefaults / your engine) and / or `url` (deep link). Language goes in as
@ -190,6 +191,30 @@ the icon, feature graphic and phone screenshots and prints `tablet screenshots:
`play images` uploads only the sets that exist (tablet / TV / Wear when rendered) and never deletes a set Play has
but the renders lack: the dry run shows `keeps <type> on Play`; `--prune` (only on the owner's word) deletes them.
## Signed-in apps
Key screens behind a login (`source: 'web'`): capture against a **local or demo backend with fabricated data** and a
demo account. Never real users' data in store screenshots. The same frames feed both stores (App Store and Google
Play), so one sign-in setup covers both. Two ways to sign in, set in `shots.capture.auth`:
- **Sign-in request**: `{ request: { url: '/api/login', method: 'POST', body: { email: '${cred:reviewDemoAccount.username}',
password: '${cred:reviewDemoAccount.password}' } } }`. It runs once per browser context before its first shot, and
the context keeps the cookies. `${cred:NAME}` reads `.storebound/credentials.json` (`reviewDemoAccount.username` /
`.password`, also from REVIEW_DEMO_USER / REVIEW_DEMO_PASSWORD, or any key under `captureAuth`). `${env:NAME}`
reads the environment. A password-, token- or key-like field with a literal value is refused. A status outside
2xx/3xx stops the capture and shows the status, never the values.
- **Saved session**: `{ storageState: '.storebound/auth.json' }`, made by `storebound shots login [--url <url>]`. It
opens a visible browser with a fresh, empty profile. The owner signs in to the demo account by hand (signing in is
the owner's step; the agent never types it). Enter in the terminal (or closing the window) saves the state with
0600 permissions and prints how many cookies and origins it saved. The wait times out after 15 min
(`--timeout <s>`) and logs a heartbeat every 30 s. This one is for SSO, 2FA or captcha logins. MCP has no login
tool, because the owner signs in at a terminal.
If a shot lands on a sign-in page anyway, capture warns `<url> is a sign-in page: session expired? run storebound
shots login again` and fails that shot at once. `auth.loginUrl` (a regex) names the login page when the default
(login / signin / auth in the path) does not match. Resolved values and cookie values are masked in logs and
`.storebound/capture-report.json`; the gallery only shows the renders.
## A/B variants and store experiments
`shots.variants.<id>`: `scenes` (order / subset), `copy` (a JSON merged over the base copy, only what changes),
`palettes` (per scene). `storebound shots render --variant b` (or `--variants` for all) writes