--- name: console-autofill description: AI-assisted autofill of App Store Connect and Google Play Console forms that have no API (the New App dialog, App Privacy, Play's Create app form, the Play IARC content rating questionnaire, Target audience and content plus the Ads declaration; the App Store age rating is shown as guidance because the API sets it) in a visible browser with storebound's own profile that the owner signs in to once (never a copy of the owner's Google cookies unless they opt in); storebound opens the form itself. Answers come only from storebound.config.ts; anything the config does not prove stays empty and highlighted. storebound moves through the pages itself (Next; App Privacy: Get Started, Edit, each data type and the draft Save; content rating: Save draft) and never signs in, submits or publishes: the owner presses the final button. 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 autofill (forms without an API) CLI: `storebound` (or `bunx storebound` in a project that has it, or `bun /cli/index.ts`). **Read this to the owner first.** Console pages change without notice. This is AI-assisted autofill and it may put an answer in the wrong place. The owner reviews every field and presses the final button. storebound never types a password, never touches a sign-in page, never prints or sends a cookie, and never clicks Submit, Publish, Create, a final Save or anything that sends a form for review (English or Turkish console). It clicks only what moves through the form (see "What storebound clicks" below). It works in its own browser profile where the owner signs in once by hand. It never copies the owner's Google cookies unless the owner opts in (`--use-chrome-session google`), because Google binds them to the device and may sign the owner out of their real Chrome (step 3). It is not available over MCP. ## 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 1. `storebound console fill` lists the forms. `storebound console fill
--dry` prints every answer with its source; `[check]` lines are not proven by the config. Show them to the owner. 2. For each `[check]` line, ask the owner (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. The owner runs `storebound console fill ` in a terminal (or presses the button in the desktop app). The first log line says which browser it uses: - **profile** (the default): storebound's own profile (`~/.storebound/console-profile/`, or `--profile ` / `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:`): 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//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 owner moves on by hand (the + button, the New App item, the app in the list appear) it continues from that step; when the owner opens the form 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//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 " for each collected type in the Data Types section: purposes > Next > linked > Next > tracking > Save; a dialog step it does not know: a debug dump and `paused on ""` until the owner answered it; content rating: Save draft, then Next). It stops on a page only when an answer there is uncertain: it logs `answer needed on "": `, 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 yourself" (App Privacy: "review the summary, then press Publish yourself"). 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 ,