356 lines
34 KiB
Markdown
356 lines
34 KiB
Markdown
---
|
|
name: console-autofill
|
|
description: Fill App Store Connect and Google Play Console forms that have no API (New App, App Privacy, Play Create app, IARC content rating, every Play App content card, Play Store settings category, first production release countries and send-for-review). Agent-first by default - `storebound console plan <form> --json` (MCP console_plan) gives the start URL, steps, answer sheet with the console's exact labels, never-press list and verified UI facts; you drive the page with your own browser tool, stop before the final button and tell the owner what to press, then `storebound console verify <form>` (MCP console_verify). `storebound console fill` is the optional scripted fast path. Answers come only from storebound.config.ts. Never sign in, submit, publish, accept agreements or grant permissions. Use when the user wants help filling one of these console forms, asks what to answer in them, or wants the answers as a checklist.
|
|
---
|
|
|
|
# Console forms without an API
|
|
|
|
CLI: `storebound` (or `bunx storebound` in a project that has it, or `bun <toolchain>/cli/index.ts`).
|
|
|
|
**Do it, don't ask** (first rule in AGENTS.md / `docs/agents-core.md`): you open the console, navigate, fill the form,
|
|
save drafts and Play App content declarations, and verify; the owner only signs in once, does the plan's
|
|
`ownerBatch` "before" steps (answers, the IARC Terms of Use box) and presses the final button at the end. Hand the
|
|
owner the whole `ownerBatch` in ONE message before you start, never one item at a time on the page.
|
|
|
|
**Read this to the owner first.** Console pages change without notice. You (the agent) fill the form from storebound's
|
|
answer sheet and the owner reviews every field and presses the final button. Nobody types a password or touches a
|
|
sign-in page except the owner; nothing that submits, publishes, creates, accepts an agreement or grants a permission
|
|
is pressed by you or by storebound.
|
|
|
|
## Default: agent-first (you drive the browser)
|
|
|
|
storebound supplies the knowledge and the check; you drive the page with your own browser tool and adapt to whatever
|
|
the UI shows. This is the primary mode.
|
|
|
|
1. `storebound console plan <form> --json` (MCP: `console_plan`; read-only, offline; `--lookup` asks the App Store
|
|
Connect API for the app id). It returns:
|
|
- `ownerBatch` (printed first): EVERY owner step of the plan in ONE list, in the order you need them: `before`
|
|
(open answers to write into the config, the IARC Terms of Use box with its exact place, the demo account when it
|
|
is not in `.storebound/credentials.json`) and `after` (the final button, e.g. "Send changes for review");
|
|
- `start`: the URL (a deep link when `apple.appId`, `play.developerId` or `play.appId` is known, or the ids
|
|
storebound remembered) and how to reach the form otherwise;
|
|
- `steps`: plain language plus what to `click` (by visible text) and which `answers` go on each step;
|
|
- `answers`: every answer with `status` (`certain`, `ask-owner`, `owner-check`, `agreement`), the console's own
|
|
`label` (curly quotes as shown, `curlyQuotes: true`), `action` (tick / untick / pick / type / choose) and `where`;
|
|
- `buttons.neverPress` (Publish, Submit, Create, Create app, Send, Release, "Send changes for review", "Restart
|
|
review", "Definitions and Examples", "?" ...), `buttons.final` and `buttons.allowed` (navigation, draft saves and
|
|
Play App content declaration saves);
|
|
- `uiFacts` (verified facts per form, see below), `driving`, `inPageLoop` (a ready-made in-page helper, see 5) and
|
|
`verify` (the command and what to capture).
|
|
|
|
Forms: `apple-app-record`, `apple-age-rating`, `apple-app-privacy`, `play-app-record`, `play-content-rating`,
|
|
`play-target-audience` (these also have the scripted `console fill`), and agent-only Play pages:
|
|
`play-store-settings` (the App category: the Play API has no category field, so `play listing` leaves it "Not
|
|
selected"), `play-app-content` (every App content card in order, its page, its plan and ONE owner batch;
|
|
`--all` / MCP `all: true` puts every card's steps inline in fix order and ends with the one owner list: tick the
|
|
IARC Terms of Use box (before), Send changes for review (after)), its cards `play-privacy-policy`, `play-ads`, `play-sign-in-details`, `play-advertising-id`,
|
|
`play-government-apps`, `play-financial-features`, `play-health-apps`, and the first production release:
|
|
`play-production-countries` and `play-production-release`. `storebound console plan` lists them.
|
|
2. Send the owner the `ownerBatch` of every form you will fill in ONE message before you start (AGENTS.md rule 5): the
|
|
`ask-owner` answers (write them into the config, so the plan has them; never guess them) and the `before` steps
|
|
(Play content rating: tick the IARC Terms of Use box on the questionnaire's page 1 first; page 1's Next does
|
|
nothing until it is ticked). Then run without stopping until the `after` steps.
|
|
3. Open `start.url` in your browser tool. If a sign-in page shows, the owner signs in; you wait.
|
|
4. Follow `steps`. Read the page (text or accessibility tree) before each action, click by visible text, set each
|
|
answer as the sheet says, wait for the next step to render. If the page differs from the plan, adapt (the console
|
|
changes), but never press anything in `buttons.neverPress` and never tick an agreement.
|
|
5. Long flows (App Privacy with many data types, the IARC questionnaire, App content): one Runtime.evaluate call
|
|
times out at about 45 s on big console pages, so never loop over many controls in one call. Paste the plan's
|
|
`inPageLoop` once per page (one short call; it defines `window.__sb`), start the job and poll in short calls:
|
|
```js
|
|
window.__sb.run([{ role: 'radio', label: 'No', within: '<question text>' }, { role: 'checkbox', label: '<box label>', want: true }])
|
|
// then, every 2-5 s: window.__log.slice(-20), window.__done
|
|
```
|
|
The helper finds each control by its label (aria-label, else the nearest parent with non-empty innerText), clicks
|
|
the inner `[role=radio]` / `[role=checkbox]` of a material control and reads it back; it never clicks buttons and
|
|
refuses agreements. Press Next / Save yourself afterwards. Never click past a step you have not read.
|
|
Text fields: click and TYPE (key presses); never set `value` programmatically (App Store Connect's New App kept an
|
|
empty Name / SKU after a `form_input` set, verified 2026-10-13).
|
|
6. Play App content cards (Privacy policy, Ads, Sign in details, Advertising ID, Government apps, Financial features,
|
|
Health apps, Target audience, the content rating questionnaire and its Summary, Data safety): press each card's
|
|
**Save yourself**. Saving a declaration is not public: Google gets it only with the next "Send changes for review"
|
|
in Publishing overview, which stays the owner's (verified 2026-10-13). The Sign in details demo account: type it
|
|
from `.storebound/credentials.json` (`reviewDemoAccount`) without echoing it; only when it is not there does the
|
|
owner type it.
|
|
7. Stop before `buttons.final` and tell the owner exactly what to press (App Privacy: review the summary, press
|
|
Publish, then Publish in the "Publish Your App Privacy Responses?" dialog; Play Create app: tick the two
|
|
declarations, press Create app; Play App content, content rating, target audience, Store settings and production:
|
|
"Submit N changes for review", then "Send changes for review" in Publishing overview, once at the end; New App:
|
|
Create).
|
|
8. `storebound console verify <form> --from-text -` (MCP: `console_verify` with `text`) with what `verify.capture`
|
|
says: App Privacy the page's `innerText`, Play Create app the JSON of the capture snippet; after Create app,
|
|
`storebound console verify play-app-record --url "<location.href>"` reads the new app's id from the redirect
|
|
(`/app/<appId>/app-dashboard`), shows the `play.appId` change as a dry diff, and `--write` saves it (an upsert).
|
|
Missed the redirect: `storebound play app-id` (the app list is paginated, 10 per page, a new app is not on page 1:
|
|
search the package name or walk the pages, pipe the captures in). Exit 1 / mismatches:
|
|
fix them in the page (or the config), verify again. `apple-app-record` and `apple-age-rating` verify from the API
|
|
without text.
|
|
|
|
### Verified UI facts
|
|
|
|
- **App Privacy** (`/apps/<id>/distribution/privacy`, 2026-10-12): "Set Up <type>" is a plain `<p>` with a click
|
|
handler (click it by exact text); it opens a `<dialog>` whose h2 is the type. Step 1: 6 purpose checkboxes in the
|
|
order Third-Party Advertising, Developer’s Advertising or Marketing (curly ’), Analytics, Product Personalization,
|
|
App Functionality, Other Purposes; an input has no aria label, its label is the nearest non-empty parent text
|
|
(label and help text run together). Step 2: radios "Yes, <type> data collected from this app is linked to the
|
|
user’s identity" / "No, …". Steps 3 and 4: two information pages ("Let’s define two important terms…", "To help put
|
|
tracking into context, here are a few examples:"), no inputs: Next on each. Step 5: radios "Yes, we use <type> data
|
|
for tracking purposes" / "No, …", plus "Definitions and Examples" (never press), Back, Cancel, Save. Save closes the
|
|
dialog; the type then shows Edit and a summary ("Used for …", linked, tracking), which `console verify` reads.
|
|
- **Play Create app** (2026-10-12, through Claude in Chrome): `/console/u/0/developers/<developerId>/create-new-app`.
|
|
App name (max 30), App package name (max 150) with a read-only "Check availability" ("Package name available";
|
|
press it), Default language (default "English (United States) – en-US"), App or game and Free or paid as
|
|
`<material-radio>`s, two declarations (the owner's). In a background window computer-tool clicks on the material
|
|
radios did nothing and `form_input` on the native inputs did not update the form ("Select app or game" stayed):
|
|
click the inner `[role=radio]` with a JS click, then check `aria-checked="true"` and that no "Select …" hint is
|
|
left. Text inputs set by `form_input` are fine. After Create app the app lives at
|
|
`/console/u/0/developers/<developerId>/app/<appId>/app-dashboard`: `console verify play-app-record --url <url>
|
|
--write` puts the id in `play.appId` (2026-10-13: the app list shows 10 apps per page and the new app was not on
|
|
page 1, so take the id from the redirect).
|
|
- **App Store Connect New App** (2026-10-13): a programmatic value set (`form_input`, `el.value = …`) on the Name and
|
|
SKU textboxes left the dialog's React state empty (""); clicking the field and typing worked. Type, don't set
|
|
value. `console fill` types every text field the same way.
|
|
- **Play material radios and checkboxes** (Create app, App content, IARC, 2026-10-12): `<material-radio>` /
|
|
`<material-checkbox>`; click the inner `[role=radio]` / `[role=checkbox]` (or the `<input>`) with a JS click and read
|
|
`aria-checked` back. Their labels sit in sibling nodes: read the nearest parent with non-empty `innerText`.
|
|
- **Play Store settings** (2026-10-12): `/app/<appId>/store-settings` > the FIRST Edit (App category) > App or game >
|
|
the Category dropdown's `[role=option]` with exactly the category name (`play.category`) > Save.
|
|
- **Play App content** (2026-10-12): `/app-content/overview`; each card has a button aria-label="Start <Name>
|
|
declaration". Pages: privacy-policy, ads-declaration, testing-credentials (Sign in details), ad-id-declaration,
|
|
government-apps, finance (2 steps, 2026-10-21: the checkboxes, "My app doesn't provide any financial features"
|
|
among them, are on step 1; step 2 is an information page only), health (2 steps; 2026-10-21: the last step offers
|
|
"My app does not have any health features", tick that; only an older page without it took "Other"),
|
|
target-audience-content (age checkboxes; with 13+ only it went straight to the Summary, no appeal question; Save),
|
|
content-rating-overview > "Start questionnaire" > content-rating-iarc-questionnaire. Each card's Save is yours
|
|
(2026-10-13: nothing goes to Google before the owner's "Send changes for review").
|
|
- **IARC questionnaire** (2026-10-12): page 1 has the email, the category radio and an IARC Terms of Use checkbox (an
|
|
agreement: the owner ticks it, never you; page 1's Next does nothing until it is ticked, 2026-10-13, so it is in the
|
|
owner batch before the run). Then ONE long page of sections; each Yes reveals follow-ups (Violence:
|
|
humans / non-humans, setting, childlike style, reactions, presentation, blood level, historical war, innocent
|
|
characters, fierce sounds; Digital Purchases: digital goods, then loot boxes, then player trading; Miscellaneous:
|
|
user interaction then block / report / moderation / friends-only, location sharing, Nazi symbols, Korea history,
|
|
terrorism, crimes). The answers come from `console.contentRating.answers` and `.details`, `products[].randomRewards`
|
|
and `social`. Save, then Next to the Summary with per-authority ratings; its button is **Save**, not Submit: yours
|
|
(a declaration save). Example: PEGI 7, ESRB E10+, USK 12, ClassInd 18 (paid random items), Google Play 7+.
|
|
- **Save after every page** (Play, 2026-10-21): on a multi-step Play form with a Save next to Next (the IARC
|
|
questionnaire, finance, target audience), answer the page, press **Save**, wait for "Your changes have been saved",
|
|
and only then press Next. The plan steps and the rule line say so on every such step.
|
|
- **IARC Digital purchases** (2026-10-21): Yes on "Digital purchases" reveals checkboxes ("Purchases of digital goods"
|
|
and others); tick them first, the loot-box and trading follow-ups show only after that. Answer every IARC question
|
|
by its exact console label: a keyword helper answered this one No because its text also mentions "cash rewards". The
|
|
in-page helper refuses patterns, radios without their exact question (`within`) and blanket group answers.
|
|
- **Sign in details** (2026-10-21): the first question reads "Is any part of your app restricted?" with Yes / No; the
|
|
dialog button is "Add details" (older pages: the long radio labels and "Add instructions"; the plan keeps both).
|
|
- **App Privacy "Additional Setup Required"** (2026-10-21): after saving the data types this message appears; its OK
|
|
sometimes opens the first type's setup dialog by itself. That dialog IS the first type's setup: carry on from step 1
|
|
there, do not press its Set Up again.
|
|
- **Verify reads input states** (2026-10-21): Yes / No radios and unticked boxes are not in page text. Run the plan's
|
|
`verify.capture` snippet (it returns every radio / checkbox with aria-checked, checked or aria-selected as JSON) and
|
|
pipe it to `storebound console verify <form> --from -`; the page-text path stays for text answers.
|
|
- **App Privacy Publish** (2026-10-12): a `<dialog>` "Publish Your App Privacy Responses?" with Cancel / Publish (the
|
|
owner's); afterwards the page shows "Published a few seconds ago by …", which `console verify` reads.
|
|
- **Play first production release** (2026-10-12): countries are console-only (`edits.countryAvailability` is
|
|
read-only): tracks/production > Countries / regions > Edit countries / regions > "Select all rows" > Save. Then
|
|
Releases > Edit release > Next > Save; Publishing overview > "Submit N changes for review" > "Send changes for
|
|
review" (the owner's; a JS `.click()` did nothing, it needs a real click). Managed publishing is off by default:
|
|
approved means live.
|
|
- **After Play Create app, an owner step:** a service account's app-level permissions do NOT cover apps created
|
|
later. Granting them is the owner's (a permission grant; auto mode blocks it too): give the exact path together
|
|
with the Create app button, Users and permissions > the service account > Add app > the new app > Admin (or the
|
|
minimal set the plan lists) > Apply > Save changes; then run `storebound apps check --store play` yourself.
|
|
|
|
## Optional: the scripted fast path (`storebound console fill`)
|
|
|
|
`console fill` drives a visible browser by itself with the same answers and click rules. It is fast when the page
|
|
matches its model; when it stalls on a changed page, switch to the agent-first mode above instead of waiting for a
|
|
fix. It is not available over MCP: run it from your shell (or the owner uses the desktop app).
|
|
|
|
## Forms
|
|
|
|
| form | where | filled from |
|
|
|---|---|---|
|
|
| `apple-app-record` | App Store Connect > Apps > + > New App | apple.name (else app.name), apple.primaryLocale, apple.bundleId, console.appRecord (SKU, user access) |
|
|
| `apple-age-rating` | App Information > Age Ratings | guide only: `storebound apple appinfo` sets every question through the API |
|
|
| `apple-app-privacy` | the app > App Privacy | appPrivacy.types (the owner's answers per Apple type id: collected, purposes, linked, tracking; certain), else dataSafety (types and purposes; Play advertising = Third-Party Advertising only); unset linked / tracking stay the owner's |
|
|
| `play-app-record` | Play Console > Home (All apps) > Create app | play.name or app.name (30 chars), play.defaultLanguage or the first language's play code, app.profile (App / Game), play.free or monetization (Free, owner check); the declarations are never ticked |
|
|
| `play-content-rating` | Policy > App content > Content rating | app.contactEmail, app.profile, products, console.contentRating |
|
|
| `play-target-audience` | Policy > App content > Target audience and content (+ Ads) | console.targetAudience, ads.appAdsTxt |
|
|
|
|
## Steps (scripted fast path)
|
|
|
|
1. `storebound console fill` lists the forms. `storebound console fill <form> --dry` prints every answer with its
|
|
source; `[check]` lines are not proven by the config. Show them to the owner.
|
|
2. Ask the owner about every `[check]` line in one batch before the run (AGENTS.md rule 5: never guess a compliance answer). If they want it filled
|
|
next time, add it to the config, for example:
|
|
```ts
|
|
console: {
|
|
appRecord: { sku: 'com.example.game', userAccess: 'full' },
|
|
contentRating: { category: 'game', answers: { violence: false, lootBoxes: false, userInteraction: false } },
|
|
targetAudience: { ages: ['13-15', '16-17', '18-over'], appealsToChildren: false, containsAds: true },
|
|
},
|
|
```
|
|
Age rating answers live in `apple.info.ageRating` (also `socialMedia`, `socialMediaAgeRestricted`).
|
|
3. Run `storebound console fill <form>` yourself from a shell (the desktop app has a button for it too). The first
|
|
log line says which browser it uses:
|
|
- **profile** (the default): storebound's own profile (`~/.storebound/console-profile/<store>`, or `--profile <dir>`
|
|
/ `STOREBOUND_CONSOLE_PROFILE`; one per store, not per account: every studio shares the one login). Nothing is
|
|
read or copied from the owner's own Chrome. storebound starts Chrome itself as a normal window (its own
|
|
`--user-data-dir`, a DevTools port on 127.0.0.1, no `--enable-automation`, `navigator.webdriver` false, Chrome's
|
|
own user agent) and attaches to it. **First run:** the owner signs in there by hand, once (storebound never
|
|
types passwords): Play: the Google account that owns the developer account, then "Stay signed in"; App Store
|
|
Connect: Apple ID, password, two-factor code, "Keep me signed in" and "Trust" this browser. It is kept for the
|
|
next runs. If Google shows "Couldn't sign you in" (`/signin/rejected`), storebound says "Google refused sign-in in
|
|
this window"; try again, or open accounts.google.com in that window and sign in there first.
|
|
- **attach**: Chrome already runs with remote debugging (a `DevToolsActivePort` file in Chrome's user data folder,
|
|
or `--cdp http://127.0.0.1:<port>`): storebound opens one new tab there and closes only that tab. That is the
|
|
owner's own browser in place: no cookie leaves it.
|
|
- **copy: opt-in only, NOT recommended for Google** (`--use-chrome-session google|apple`, macOS): a temporary copy
|
|
of ONLY `Local State` and the profile's `Cookies` (+ journal), filtered to the ONE store's sign-in cookies
|
|
(google: play.google.com / accounts.google.com; apple: apple.com hosts), opened in real Google Chrome and deleted
|
|
when the run ends. Never copied: passwords, history, extensions, anything else. `--chrome-profile "Profile 1"`
|
|
picks the Chrome profile. **Why opt-in:** Google binds its sign-in cookies to the device (Chrome's Device Bound
|
|
Session Credentials: a key in the TPM / Secure Enclave that never leaves the machine; Google only renews the
|
|
short-lived cookies for the browser that holds it; public on Windows from Chrome 146, rolling out on macOS from
|
|
Chrome 147), and Google signs out sessions whose cookies look stolen: "the user is logged out of their account for
|
|
that session and any related suspicious sessions on that device" and must verify again. A copy of the owner's
|
|
Google cookies in a second Chrome is what that detection looks for: Google may answer `/signin/rejected`, AND it
|
|
may sign the owner out of their real Chrome or flag the account. In real use (October 2026) a copied Google
|
|
session reached Play Console once, then landed on `/v3/signin/rejected` and the account chooser. Apple publishes
|
|
no device binding for its web sign-in (fastlane's `spaceauth` reuses Apple sessions on other machines; Apple ties
|
|
them to a region), but it does not rule out a security check either, so the Apple copy is opt-in too. An agent
|
|
never adds `--use-chrome-session` on its own; the owner chooses it after reading this.
|
|
Sources: https://blog.google/security/protecting-cookies-with-device-bound-session-credentials/ ,
|
|
https://developer.chrome.com/docs/web-platform/device-bound-session-credentials ,
|
|
https://knowledge.workspace.google.com/admin/security/investigate-and-take-action-on-suspicious-session-cookies ,
|
|
https://docs.fastlane.tools/getting-started/ios/authentication/
|
|
`--fresh-profile` still works: it skips attach and copy.
|
|
Keeping the sign-in: Apple's and Google's sign-in cookies are partly session cookies. storebound starts Chrome with
|
|
`--restore-last-session` (so Chromium loads them again) and always closes it gracefully (CDP `Browser.close`,
|
|
window closed or Ctrl+C), so they are written to disk. Apple keeps a long sign-in only with "Keep me signed in"
|
|
ticked and "Trust" chosen for the browser; Google with "Stay signed in"; storebound reminds the owner on the
|
|
sign-in page.
|
|
Sign-in is detected by URL and by frame (App Store Connect `/login`, a frame from idmsa.apple.com /
|
|
appleid.apple.com, accounts.google.com, Google `/signin`), by a visible password field in any frame, and by the
|
|
form's own texts ("Sign in with Passkey", "Email or Phone Number", "Keep me signed in" and the Turkish ones):
|
|
Apple's form starts with no password field. storebound checks before every navigation step, after each click or
|
|
load, and while a step waits. Then it prints "sign in in the browser window; storebound waits" and waits up to
|
|
10 minutes, never typing or reading anything there; when the console is back (no sign-in page, the console URL)
|
|
it opens the form again from step 1.
|
|
4. After sign-in storebound opens the form itself and logs every step ("opening the form, step 2/4: the add button
|
|
(+)"): New App: Apps > + > New App; App Privacy: `/apps/<apple.appId>/distribution/privacy` (without `apple.appId`
|
|
but with App Store Connect API credentials, storebound looks the id up from `apple.bundleId` and caches it in
|
|
`.storebound/console-app-ids.json`), else the app list > the app (by name / bundle id; one app shown twice on its
|
|
card, title and icon, is one app) > App Privacy; Play forms: the app list > the app (by `play.packageName`) > Policy and
|
|
programs > App content > the form's Start or Manage link. These navigation clicks pass the same click gate: never
|
|
Save, Submit, Publish, Create, Send, Next or the Turkish equivalents; only a LINK named exactly Start / Manage /
|
|
Başla / Yönet is let through. If a step is not found in time, or two apps match (they are listed: set
|
|
`apple.appId` / `play.packageName`), storebound says which step failed, prints what to click, and keeps watching:
|
|
a sign-in that finishes restarts navigation at step 1; when the page moves on (you click the step with your
|
|
browser tool: the + button, the New App item, the app in the list appear) it continues from that step; when the
|
|
form is open it fills it. Watching
|
|
ends when the window is closed. When the form appears storebound fills the certain answers, outlines every
|
|
unproven or unfillable field with a "storebound: check this" label, and lists them in the terminal.
|
|
5. **One login, many developer accounts or teams.** storebound never asks for a second sign-in; it finds the
|
|
account that has the app and remembers it in `~/.storebound/console-accounts.json` (ids only, never cookies;
|
|
shared with the desktop app):
|
|
- Play: the app list of one account is `/console/u/0/developers/<id>/app-list`. The id is `play.developerId` in
|
|
the config, else the workspace account's `developerId` (`storebound.workspace.json`, passed as
|
|
`STOREBOUND_PLAY_DEVELOPER_ID`), else the remembered one; else storebound reads every developer account on the
|
|
"Choose a developer account" page and looks for `play.packageName` in each app list ("developer account 4/5:
|
|
..."). A package in none of them is said plainly (create the app, or ask to be invited).
|
|
- App Store Connect: the team is switched in the account menu at the top right. Order: the remembered team, the
|
|
menu entry that names `apple.teamId` (or the workspace account's `teamId`), then every other team. The app is
|
|
recognized by its entry in the app list (App Privacy) or, for a new app, by the bundle id in the New App dialog's
|
|
Bundle ID list (Cancel closes the dialog in a team without it). App Store Connect has no documented URL
|
|
parameter that picks a team, so storebound does not use one.
|
|
- Play Create app (`play-app-record`) is a first create: no account has the package yet, so storebound does not
|
|
search, it picks: `play.developerId` (or the workspace account's id, or the remembered one), else the only
|
|
account the login sees (picked even when `play.developerAccount` names another; the log says so), else the
|
|
chooser entry named `play.developerAccount`: the exact spelling first, then spaces and case aside, then a unique
|
|
prefix ("ACME" and "Acme" are two accounts: only the exact spelling picks one). Several accounts and none of
|
|
these set, or a name that matches none or two: it stops and lists every account with its developer id. Ask the
|
|
owner which one; never guess. The choice is remembered under
|
|
`play.packageName`.
|
|
- Switching only changes what the console shows. Each switch click must be an account name storebound just read
|
|
from the chooser or the menu, and never Sign Out or a deny-listed name (a studio called "Next Games" is left
|
|
for the owner to pick).
|
|
6. Multi-page forms: storebound fills a page and clicks its Next itself (App Privacy: Get Started or Edit next to
|
|
Data Types, Next, the data types' Save, then "Set Up <type>" for each collected type in the Data Types section:
|
|
purposes > Next > linked > Next > two information pages > Next > Next > tracking > Save; a dialog step it does not know: a debug dump and
|
|
`paused on "<type>"` until the owner answered it; content rating: Save draft, then Next). It stops on a page only when an answer there is uncertain, or (content rating page 1) the IARC Terms of Use box is not ticked yet (`agreement needed on …`; Next does nothing until the owner ticks it): it logs
|
|
`answer needed on "<page>": <the answers>`, leaves them outlined, and goes on by itself once the owner has set
|
|
each one (a box that should stay empty: click it twice). It never clicks Next past an open question.
|
|
7. On the last page storebound prints "review every field and press <Submit | Publish | Create> yourself"
|
|
(App Privacy: "review the summary, then press Publish yourself"). Content rating and target audience: storebound
|
|
presses the declaration's Save itself and says "storebound pressed Save … press Send changes for review in
|
|
Publishing overview yourself" (once every App content card is done). Text fields are typed key by key, never set
|
|
(New App kept an empty Name / SKU after a value set). The
|
|
command ends when the owner closes the window (`--json` then prints the result with `filled`, `highlighted`,
|
|
`fallback`, `stoppedAt`).
|
|
8. Nothing waits silently. While storebound waits it logs `still waiting for <what>, <time>` once a minute. When the
|
|
page after a click (or the form) is not one it knows within `--step-timeout` seconds (default 30), it says so with
|
|
the page's buttons, writes a debug dump to `.storebound/console-debug/<form>-<time>/` (page.json: headings, dialogs,
|
|
every visible control with role and name; page.html: the sanitized structure; screen.jpg) and prints the manual
|
|
steps. After 20 minutes without progress it closes the window and exits 1. `--debug-dump` writes a dump at every
|
|
step: attach that folder to a bug report after looking at screen.jpg (labels only; no values, cookies or tokens;
|
|
emails, phone numbers and the account area are replaced).
|
|
9. If a field cannot be found or matches twice (for more than a moment: a page that is still loading does not count), the console UI changed: storebound stops filling and prints the
|
|
step-by-step list ("label: answer"). That is the expected safe behavior, not an error: finish that part yourself
|
|
with your browser tool (agent-first mode above), not by handing the list to the owner.
|
|
|
|
## Play Create app (`play-app-record`)
|
|
|
|
- Navigation: the developer account's app list (Home / All apps) > the page-level **Create app** button. That button
|
|
only opens the form, but the form's final submit has the same words. storebound clicks it only as that one
|
|
navigation step: whole name exactly "Create app" / "Uygulama oluştur", a button or link, on the app list (All apps
|
|
heading or an /app-list URL), with no Create app form open and not inside a form or dialog. Inside the form it is
|
|
refused like every other submit.
|
|
- Fills App name (`play.name`, else `app.name`; over 30 characters it is left to the owner), Default language
|
|
(`play.defaultLanguage`, else the first language's play code, shown as "English (United States) – en-US"), App or
|
|
game (`app.profile`: game -> Game, app / subscription-app -> App) and Free or paid (`play.free`, else Free when the
|
|
config earns from IAP / subscriptions / ads only).
|
|
- **Owner check:** Free or paid is filled but outlined: a free app can never become paid on Google Play. Tell the
|
|
owner before they press Create app.
|
|
- **Declarations** (Developer Program Policies, US export laws) are agreements: storebound never ticks them, it
|
|
outlines them. The owner reads and ticks them and presses Create app.
|
|
- App package name (`play.packageName`, max 150) is in the form: storebound fills it and presses the read-only
|
|
"Check availability" ("Package name available"). It is fixed for good once the app is created.
|
|
- App or game and Free or paid are `<material-radio>`s: storebound clicks the inner `[role=radio]` (the native input
|
|
does not update the form's model). With `play.developerId` the form opens by its own URL (`.../create-new-app`).
|
|
- After Create app: the owner grants the service account access to the new app (a permission grant; see "Verified UI
|
|
facts" above), then you run `storebound apps check --store play`.
|
|
|
|
## What storebound clicks, and what stays with the owner
|
|
|
|
Every button of each form is classified in `cli/lib/console-forms.ts` (`FORM_BUTTONS`), from Apple's and Google's
|
|
help pages ("Manage app privacy", "Add a new app", "Set an app age rating"; "Content ratings", "Target audience and
|
|
content", "Create and set up your app"):
|
|
|
|
| form | storebound clicks (navigation) | stays with the owner, and why |
|
|
|---|---|---|
|
|
| App Privacy | Get Started; Edit next to Data Types (never the Privacy Policy Edit; skipped when the section already lists exactly the config's types); "Set Up <type>" for each collected type (a plain text, clicked by its exact text); a type's own Edit only when its purposes differ from the config; Next; **Save inside the questionnaire dialogs** (a draft: Apple publishes the answers only after Publish) | **Publish** and its confirmation: it puts the answers on the App Store product page; the "?" help buttons and "Definitions and Examples" are never clicked |
|
|
| New App (App Store) | + and New App (Cancel only to leave a team without the bundle id) | **Create**: creates the app record for good |
|
|
| Age rating (guide only) | nothing (`storebound apple appinfo` sets it by API) | Set Up / Next / Done, if the owner wants to look |
|
|
| Create app (Play) | Create app on the app list (opens the form); Check availability next to the package name (read-only) | the two **declarations** (agreements, never ticked) and **Create app** in the form: creates the app |
|
|
| Content rating (Play) | Start; **Save draft** then Next on each page (a draft); **Save** on the Summary (not "Submit"; a declaration save: Google gets it only with the next Send changes for review, verified 2026-10-13) | the **IARC Terms of Use** box (an agreement; ticked by the owner before the run) and **Send changes for review** in Publishing overview |
|
|
| Target audience (Play) | Start / Manage, Next between the pages, **Save** on the last page (a declaration save) | **Send changes for review** in Publishing overview: sends the declarations to Google |
|
|
| Other App content cards (agent-only plans) | Start <Name> declaration, Next, **Save** (a declaration save) | **Send changes for review**; the demo account only when it is not in `.storebound/credentials.json` |
|
|
|
|
Mid-way storebound stops only for an uncertain answer (see 6). A navigation button is never the owner's: when the
|
|
console page changed so that storebound cannot find it, it says so and you click it with your browser tool.
|
|
|
|
## The owner decides
|
|
|
|
Every `[check]` answer, linked-to-identity and tracking for each App Privacy data type (unless `appPrivacy.types`
|
|
answers them), Developer's Advertising or Marketing, the IARC answers, the target
|
|
ages, Play's Free or paid and its declarations, the IARC Terms of Use box (before your run), the Sign in details
|
|
username and password only when they are not in `.storebound/credentials.json`, and pressing the final button (App
|
|
Privacy Publish, New App Create, Play Create app, Play's Send changes for review). The App content cards' Save and
|
|
the IARC Summary's Save are yours (nothing goes to Google before Send changes for review).
|
|
|
|
## Out of scope
|
|
|
|
- Signing in, two-factor codes, accepting agreements, creating developer accounts or teams.
|
|
- Forms with an API: Data safety (`storebound play data-safety`), age rating, categories and content rights
|
|
(`storebound apple appinfo`), export compliance (`storebound apple encryption`).
|
|
- Recording or reading console data: storebound only fills the visible form.
|