price changes of existing products need --allow-price-changes (play products, play/apple subscriptions, prices import): --yes and flow run --apply --yes confirm creates, first prices and other updates only; without the flag the run writes the rest, names each held change and exits 3 (declined), dry runs mark them owner, flows refuse the flag; lib/price-gate.ts, tests/price-gate.test.ts. CLI wording: console fill leaves what it cannot open or fill to the calling agent's browser tool (console plan / verify) instead of 'yourself'; other 'by hand' output points at the config; do-it-dont-ask test scans cli/ output

This commit is contained in:
CRTkafa 2026-10-12 09:38:56 +08:00
parent b5b8f2da9d
commit e01a27d7b7
33 changed files with 414 additions and 117 deletions

View file

@ -66,4 +66,36 @@ describe("first rule: do it, don't ask", () => {
'press Create app yourself', 'Granting them is the owner\'s (a permission grant)'];
for (const o of ok) expect(HANDOFF.some(re => re.test(o))).toBe(false);
});
// The CLI's own output: when storebound's scripted part cannot go on, what is left goes to the calling agent (its
// browser tool with `console plan` / `console verify`, or an edit of the config), not "do it yourself" to the owner.
// Sign-in, the final Submit / Create / Publish and agreements stay the owner's.
const CLI_HANDOFF: RegExp[] = [
/\b(open|fill|answer|check|do|merge|add|set)\b[^.\n]{0,40}\b(yourself|by hand)\b/i,
/\bdo (this|that) part\b/i,
/\bto check yourself\b/i,
];
const cliFiles = (dir: string): string[] => readdirSync(join(root, dir), { withFileTypes: true }).flatMap(e =>
e.isDirectory() ? (e.name === 'data' ? [] : cliFiles(`${dir}/${e.name}`)) : e.name.endsWith('.ts') ? [`${dir}/${e.name}`] : []);
test('CLI output never hands agent work to the owner', () => {
const hits: string[] = [];
for (const f of cliFiles('cli')) {
read(f).split('\n').forEach((line, i) => {
const t = line.trim();
if (t.startsWith('//') || t.startsWith('*') || t.startsWith('/*')) return; // comments are for the reader of the code
for (const re of CLI_HANDOFF) if (re.test(line)) hits.push(`${f}:${i + 1}: ${t.slice(0, 140)}`);
});
}
expect(hits).toEqual([]);
});
test('the CLI patterns catch the old console fill lines and keep the owner-only ones', () => {
for (const b of ['Open it yourself; storebound keeps watching', 'fill them yourself', 'do this part yourself; storebound keeps watching',
'3 answer(s) to check yourself.', 'not mapped (do these by hand)'])
expect([b, CLI_HANDOFF.some(re => re.test(b))]).toEqual([b, true]);
for (const o of ['review every field and press Submit yourself', 'sign in to App Store Connect yourself (Apple ID, password, two-factor code)',
'Read and tick them yourself, then press Create app.', 'storebound console plan <form> [--json] [--lookup] (then drive the page yourself'])
expect([o, CLI_HANDOFF.some(re => re.test(o))]).toEqual([o, false]);
});
});