apple signing: App Group association fails early under automatic signing too (API capability + newest profile, steps and deep link, exit before the archive); console plan/verify apple-app-group

This commit is contained in:
CRTkafa 2026-10-25 22:34:14 +07:00
parent d978bb7a8b
commit 304407e523
20 changed files with 590 additions and 60 deletions

View file

@ -40,7 +40,7 @@ export const notSentProp = { type: 'boolean', default: false, description: 'Comm
/** Every form `console plan` / `console verify` know (cli/lib/console-answers.ts PLAN_FORM_IDS; a test keeps them equal). */
export const CONSOLE_PLAN_FORMS = ['apple-app-record', 'apple-age-rating', 'apple-app-privacy', 'play-app-record', 'play-content-rating', 'play-target-audience',
'play-store-settings', 'play-app-content', 'play-privacy-policy', 'play-ads', 'play-sign-in-details', 'play-advertising-id', 'play-government-apps',
'play-financial-features', 'play-health-apps', 'play-production-countries', 'play-production-release', 'play-games-services', 'play-publish-changes'];
'play-financial-features', 'play-health-apps', 'play-production-countries', 'play-production-release', 'play-games-services', 'play-publish-changes', 'apple-app-group'];
export const TOOLS: ToolDef[] = [
{ name: 'check', title: 'Offline config check', command: ['check'], write: false, network: false,
@ -458,15 +458,19 @@ export const TOOLS: ToolDef[] = [
description: 'Upload the event card (16:9, 1920x1080 to 3840x2160) and details page (9:16) images of one draft in-app event per locale, size-checked first.',
props: { name: { type: 'string', description: 'Reference name from apple.events' } }, required: ['name'], args: i => opt('name', i.name) },
{ name: 'console_plan', title: 'Console form plan (agent-first)', command: ['console', 'plan'], write: false, network: false,
description: 'Read-only plan for filling a console form that has no API with YOUR OWN browser tool (Claude in Chrome, a built-in browser, Playwright): start URL (deep link when the ids are known), ordered steps (what to click by visible text, what to answer), the answer sheet from the config (each answer certain or ask-owner, with the console\'s exact labels, curly quotes noted), the buttons never to press (Publish, Submit, Create, agreements, help panels), known UI facts per form (Play: material radios and checkboxes take a JS click on the inner [role=radio] / [role=checkbox]), a ready-made in-page helper (inPageLoop: one evaluate call times out around 45 s, so long flows run in the page and you poll window.__log) and how to verify. Forms: the App Store and Play forms of `console fill`, plus agent-only Play pages: play-store-settings (the App category, which the Play API cannot set), play-app-content (every App content card in order with its page and one owner batch; all: true inlines every card) and 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 (the API cannot set them) and play-production-release (its final Send changes for review is the owner\'s). Drive the page yourself, stop before the final button and tell the owner what to press, then call console_verify.',
description: 'Read-only plan for filling a console form that has no API with YOUR OWN browser tool (Claude in Chrome, a built-in browser, Playwright): start URL (deep link when the ids are known), ordered steps (what to click by visible text, what to answer), the answer sheet from the config (each answer certain or ask-owner, with the console\'s exact labels, curly quotes noted), the buttons never to press (Publish, Submit, Create, agreements, help panels), known UI facts per form (Play: material radios and checkboxes take a JS click on the inner [role=radio] / [role=checkbox]), a ready-made in-page helper (inPageLoop: one evaluate call times out around 45 s, so long flows run in the page and you poll window.__log) and how to verify. Forms: the App Store and Play forms of `console fill`, plus agent-only Play pages: play-store-settings (the App category, which the Play API cannot set), play-app-content (every App content card in order with its page and one owner batch; all: true inlines every card) and 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 (the API cannot set them) and play-production-release (its final Send changes for review is the owner\'s); and apple-app-group (the developer portal step the App Store Connect API cannot do: assign App Groups to an extension\'s App ID; its final Save is the agent\'s own, buttons.finalByAgent; bundleId narrows it, lookup adds the portal deep link). Drive the page yourself, stop before the final button and tell the owner what to press, then call console_verify.',
props: { form: { type: 'string', enum: CONSOLE_PLAN_FORMS },
all: { type: 'boolean', description: 'play-app-content only: every card\'s steps inline in fix order, ending with the one owner list (tick the IARC Terms before, Send changes for review after)' } },
required: ['form'], args: i => [String(i.form), ...(i.all === true ? ['--all'] : []), '--json'] },
all: { type: 'boolean', description: 'play-app-content only: every card\'s steps inline in fix order, ending with the one owner list (tick the IARC Terms before, Send changes for review after)' },
bundleId: { type: 'string', description: 'apple-app-group only: the bundle id (an extension) whose App Group to assign' },
groups: { type: 'string', description: 'apple-app-group only: comma-separated group ids when the project does not show them (group.com.example.app)' },
lookup: { type: 'boolean', description: 'apple-app-group only: read the bundle id\'s resource id from the App Store Connect API (a GET) for the portal deep link' } },
required: ['form'], args: i => [String(i.form), ...(i.all === true ? ['--all'] : []), ...opt('bundle-id', i.bundleId), ...opt('groups', i.groups), ...flag('lookup', i.lookup), '--json'] },
{ name: 'console_verify', title: 'Verify a console form', command: ['console', 'verify'], write: false, network: true,
description: 'Read-only check of a filled console form against the config. Pass the page text you read (App Privacy: the App Privacy page\'s innerText, whose Data Types section shows per type "Used for …", linked and tracking; Play Create app: the JSON of the plan\'s capture snippet; Play content rating / target audience: the summary page text; play-app-content: the JSON of the plan\'s capture snippet; Play store settings and the privacy policy: the page text after Save; the other App content cards (ads, sign-in details, advertising ID, government, financial, health): the JSON of the plan\'s verify.capture snippet, which reads radios and boxes from the DOM: aria-checked, checked, aria-selected). apple-app-record and apple-age-rating without text read the App Store Connect API. Returns a per-item match / mismatch table; exit code 1 on a mismatch.',
description: 'Read-only check of a filled console form against the config. Pass the page text you read (App Privacy: the App Privacy page\'s innerText, whose Data Types section shows per type "Used for …", linked and tracking; Play Create app: the JSON of the plan\'s capture snippet; Play content rating / target audience: the summary page text; play-app-content: the JSON of the plan\'s capture snippet; Play store settings and the privacy policy: the page text after Save; the other App content cards (ads, sign-in details, advertising ID, government, financial, health): the JSON of the plan\'s verify.capture snippet, which reads radios and boxes from the DOM: aria-checked, checked, aria-selected). apple-app-record and apple-age-rating without text read the App Store Connect API, and so does apple-app-group (the App Groups on the App ID\'s newest readable profile). Returns a per-item match / mismatch table; exit code 1 on a mismatch.',
props: { form: { type: 'string', enum: CONSOLE_PLAN_FORMS },
text: { type: 'string', description: 'The page text (or the capture JSON) of the finished form' } },
required: ['form'], args: i => [String(i.form), ...(typeof i.text === 'string' && i.text ? [`--text=${i.text}`] : []), '--json'] },
text: { type: 'string', description: 'The page text (or the capture JSON) of the finished form' },
bundleId: { type: 'string', description: 'apple-app-group only: check this bundle id (default: every target with App Groups); it reads the API, no text' } },
required: ['form'], args: i => [String(i.form), ...(typeof i.text === 'string' && i.text ? [`--text=${i.text}`] : []), ...opt('bundle-id', i.bundleId), '--json'] },
{ name: 'apple_preorder_status', title: 'App Store pre-order status', command: ['apple', 'preorder', 'status'], write: false, network: true,
description: 'Read-only: per territory whether the app is available, its release date, pre-order on / off and its content status. Enabling or ending a pre-order is an owner-only terminal command.', args: () => ['--json'] },
];