Bypass mode (owner opt-in, default off): storebound settings bypass on|off|status and a one-time question in init; MCP owner_step (owner-only commands, dry run first), owner-only api operations with --yes --confirm, console plans may press buttons.final; every action logged to .storebound/owner-actions.log; docs/bypass.md, agent rules, tests
This commit is contained in:
parent
2598c4953a
commit
9bd17c401d
21 changed files with 218 additions and 13 deletions
39
tests/bypass.test.ts
Normal file
39
tests/bypass.test.ts
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
// Bypass mode: off by default; when the owner turns it on, owner-only steps open to the agent and every one is logged.
|
||||
import { describe, expect, test } from 'bun:test';
|
||||
import { mkdtempSync, readFileSync } from 'node:fs';
|
||||
import { tmpdir } from 'node:os';
|
||||
import { join } from 'node:path';
|
||||
import { decide } from '../cli/api/gate';
|
||||
import { bypassOn, logOwnerAction, setBypass } from '../cli/lib/bypass';
|
||||
import { listTools, OWNER_STEP, toArgv } from '../cli/mcp-tools';
|
||||
|
||||
const owner = { kind: 'owner' as const, method: 'POST', dry: false, yes: true, tty: false, agent: true, getOnly: false, confirm: 'reviewSubmissions_createInstance', opId: 'reviewSubmissions_createInstance' };
|
||||
|
||||
describe('bypass mode', () => {
|
||||
test('off by default; on / off per machine (settings.json)', () => {
|
||||
const env = { STOREBOUND_HOME: mkdtempSync(join(tmpdir(), 'sb-home-')) };
|
||||
expect(bypassOn(env)).toBe(false);
|
||||
setBypass(true, env);
|
||||
expect(bypassOn(env)).toBe(true);
|
||||
setBypass(false, env);
|
||||
expect(bypassOn(env)).toBe(false);
|
||||
});
|
||||
test('the api gate: owner operations refused for an agent unless bypass, then only with --yes --confirm <operationId>', () => {
|
||||
expect(decide(owner).action).toBe('refuse');
|
||||
expect(decide({ ...owner, bypass: true }).action).toBe('send');
|
||||
expect(decide({ ...owner, bypass: true, confirm: 'other' }).action).toBe('refuse');
|
||||
expect(decide({ ...owner, bypass: true, confirm: undefined }).action).toBe('refuse');
|
||||
expect(decide({ ...owner, bypass: true, yes: false }).action).toBe('dry');
|
||||
});
|
||||
test('MCP lists owner_step only in bypass mode; dry run first', () => {
|
||||
expect(listTools(false).some(t => t.name === 'owner_step')).toBe(false);
|
||||
expect(listTools(true).some(t => t.name === 'owner_step')).toBe(true);
|
||||
expect(toArgv(OWNER_STEP, { command: 'apple submit', args: ['--build', '42'] }).argv).toEqual(['apple', 'submit', '--build', '42', '--dry']);
|
||||
expect(toArgv(OWNER_STEP, { command: 'play rollout', args: ['--percent', '20'], dry_run: false })).toEqual({ argv: ['play', 'rollout', '--percent', '20'], dryRun: false });
|
||||
});
|
||||
test('every owner action is logged in the project', () => {
|
||||
const root = mkdtempSync(join(tmpdir(), 'sb-owner-'));
|
||||
const f = logOwnerAction(root, { command: 'storebound apple submit --build 42 --yes', dry: false, exit: 0, summary: 'submitted' });
|
||||
expect(readFileSync(f, 'utf8')).toMatch(/\tRAN\texit 0\tstorebound apple submit --build 42 --yes\tsubmitted\n$/);
|
||||
});
|
||||
});
|
||||
|
|
@ -8,7 +8,7 @@ import { describeMethods } from '../cli/serve';
|
|||
import { analyze, render } from '../tools/gen-desktop-coverage';
|
||||
|
||||
/** The only commands that make no sense in a window. Adding one here needs a reason in the coverage map. */
|
||||
const NOT_FOR_DESKTOP = ['mcp', 'serve', 'desktop', 'api'];
|
||||
const NOT_FOR_DESKTOP = ['mcp', 'serve', 'desktop', 'api', 'settings'];
|
||||
|
||||
describe('desktop coverage', () => {
|
||||
const { rows, extra } = analyze();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue