// Checks a console form against the config after the agent (or the owner) filled it. Read-only. // storebound console verify
[--from ] [--from-text ] [--json] // --from takes the JSON of the plan's verify.capture snippet (input states: aria-checked, checked, aria-selected) or page text. // storebound console verify play-app-record --url [--write] // storebound console verify apple-app-group [--bundle-id ] (API: the App Groups on the App ID's newest readable profile) // apple-app-privacy: the page text of the App Privacy page (the Data Types section's per-type summaries; the App Store // Connect API has no App Privacy endpoint). play-app-record: the JSON of the plan's capture snippet (input states) or // the page text. play-content-rating, play-target-audience: the summary page text. play-app-content: the plan's // capture JSON (cards still showing "Start declaration"); play-store-settings, the other App content cards and // play-production-countries / -release: the page text after Save. apple-app-record: the App Store // Connect API (the app by bundle id; a GET) or, with --from-text, the page text. apple-age-rating: the API. // play-app-record also reads the Play Console app id from the URL Create app redirects to (/app//app-dashboard; // --url, or the capture JSON's url) and shows the play.appId change as a dry diff; --write saves it into the config (an // upsert; the only write of this command, and only to the local config file). // Exit 1 on any mismatch, and when nothing could be matched. Pure logic in cli/lib/console-verify.ts. MCP: console_verify. import { readFileSync } from 'node:fs'; import type { Ctx } from '../lib/ctx'; import { isPlanForm, PLAN_FORM_IDS } from '../lib/console-answers'; import { verifyAgeRatingApi, verifyAppRecordApi, type VerifyResult, verifyTable, verifyText } from '../lib/console-verify'; import { log, printJson } from '../lib/log'; import { appIdChange, appIdDiff, writePlayAppId } from '../lib/play-app-id'; import { basename } from 'node:path'; import * as rt from '../lib/rt'; async function readText(src: string): Promise { if (src === '-' || src === 'true') return rt.stdinText(); return readFileSync(src, 'utf8'); } export async function run(ctx: Ctx): Promise { const { config, root } = ctx.loaded; const args = ctx.args, json = args.has('json'); const form = args._[0]; if (!isPlanForm(form)) throw new Error(`console verify [--from-text ]: one of ${PLAN_FORM_IDS.join(', ')}`); const from = args.get('from') ?? args.get('from-text'), url = args.get('url'); const inline = args.get('text') ?? (url !== undefined ? url : undefined); if (url !== undefined && form !== 'play-app-record') throw new Error('console verify: --url is for play-app-record (the URL Create app redirects to)'); let r: VerifyResult; if (from !== undefined || inline !== undefined) { const text = inline ?? await readText(from!); if (!text.trim()) throw new Error('console verify: the page text is empty'); r = verifyText(form, config, text); } else if (form === 'play-publish-changes') { const { readyFromApi } = await import('../play/publish-ready'); const { verifyPublishChangesApi } = await import('../lib/console-verify'); r = verifyPublishChangesApi(await readyFromApi(root, config)); } else if (form === 'apple-app-group') { const { discoverTargets, readGroupEvidence } = await import('../apple/signing'); const { appGroupVerdicts } = await import('../lib/targets'); const { verifyAppGroupsApi } = await import('../lib/console-verify'); const want = args.get('bundle-id'); const scan = discoverTargets(root, config); const targets = [...(scan.main ? [scan.main] : []), ...scan.extensions].filter(t => t.appGroups.length && (!want || t.bundleId === want)); if (!targets.length) throw new Error(`console verify apple-app-group: no target${want ? ` "${want}"` : ''} with App Groups in the project's entitlements or apple.signing.extensions`); const bundleId = config.apple?.bundleId; if (!bundleId) throw new Error('console verify: apple.bundleId is not set'); const { ascClient } = await import('../apple/asc'); const c = await ascClient(root, bundleId, config.apple?.appId); const style = config.apple?.signing?.style ?? 'automatic'; const evidence = await readGroupEvidence(c, targets, style, config.apple?.signing?.profileType ?? 'IOS_APP_STORE'); r = verifyAppGroupsApi(targets, appGroupVerdicts(targets, evidence)); } else if (form === 'apple-app-record' || form === 'apple-age-rating') { const bundleId = config.apple?.bundleId; if (!bundleId) throw new Error('console verify: apple.bundleId is not set'); const { ascClient } = await import('../apple/asc'); const c = await ascClient(root, bundleId, config.apple?.appId); if (form === 'apple-app-record') { const found = await c.asc('GET', `/v1/apps?filter[bundleId]=${encodeURIComponent(bundleId)}`); const hit = (found.data as { attributes: { bundleId: string; name?: string; sku?: string; primaryLocale?: string } }[]).find(a => a.attributes.bundleId === bundleId); r = verifyAppRecordApi(config, hit?.attributes ?? null); } else { const info = await c.editableAppInfo(); const age = await c.maybe(`/v1/appInfos/${info.id}/ageRatingDeclaration`); r = verifyAgeRatingApi(config, (age?.data?.attributes ?? null) as Record | null); } } else throw new Error(`console verify ${form}: pass the page text with --from-text (run \`storebound console plan ${form}\` for what to capture)`); // play-app-record: the app id from the redirect URL -> play.appId (dry diff; --write saves it) let appId: { change: ReturnType; written: boolean } | null = null; if (r.playAppId && config.play) { const change = appIdChange(config, r.playAppId); const written = args.has('write') && !args.has('dry') && !!ctx.loaded.path ? writePlayAppId(ctx.loaded.path, config, r.playAppId) : false; appId = { change, written }; } if (json) printJson(appId ? { ...r, appId } : r); else { console.log(verifyTable(r)); if (appId) { const file = basename(ctx.loaded.path || 'storebound.config.ts'); console.log(`\n${appIdDiff(appId.change, file)}`); if (appId.written) log('console', `wrote play.appId '${appId.change.to}' into ${file}`); else if (appId.change.changed) console.log('dry: run the same command with --write to save it'); } } if (!r.ok) process.exitCode = 1; }