@namzu/cli 0.5.1 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/__tests__/permission-rules-reach-the-turn.test.d.ts +31 -0
- package/dist/__tests__/permission-rules-reach-the-turn.test.d.ts.map +1 -0
- package/dist/__tests__/permission-rules-reach-the-turn.test.js +132 -0
- package/dist/__tests__/permission-rules-reach-the-turn.test.js.map +1 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +16 -3
- package/dist/cli.js.map +1 -1
- package/dist/cli.test.js +7 -1
- package/dist/cli.test.js.map +1 -1
- package/dist/commands/__tests__/resume.test.d.ts +15 -0
- package/dist/commands/__tests__/resume.test.d.ts.map +1 -0
- package/dist/commands/__tests__/resume.test.js +116 -0
- package/dist/commands/__tests__/resume.test.js.map +1 -0
- package/dist/commands/__tests__/run-flags.test.d.ts +11 -0
- package/dist/commands/__tests__/run-flags.test.d.ts.map +1 -0
- package/dist/commands/__tests__/run-flags.test.js +189 -0
- package/dist/commands/__tests__/run-flags.test.js.map +1 -0
- package/dist/commands/__tests__/run-stream-flags.test.js +160 -11
- package/dist/commands/__tests__/run-stream-flags.test.js.map +1 -1
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/resume.d.ts +60 -0
- package/dist/commands/resume.d.ts.map +1 -0
- package/dist/commands/resume.js +107 -0
- package/dist/commands/resume.js.map +1 -0
- package/dist/commands/run-flags.d.ts +76 -0
- package/dist/commands/run-flags.d.ts.map +1 -0
- package/dist/commands/run-flags.js +163 -0
- package/dist/commands/run-flags.js.map +1 -0
- package/dist/commands/run-stream.d.ts +3 -39
- package/dist/commands/run-stream.d.ts.map +1 -1
- package/dist/commands/run-stream.js +74 -101
- package/dist/commands/run-stream.js.map +1 -1
- package/dist/commands/run.d.ts +25 -0
- package/dist/commands/run.d.ts.map +1 -1
- package/dist/commands/run.js +189 -16
- package/dist/commands/run.js.map +1 -1
- package/dist/commands/stubs.d.ts +10 -0
- package/dist/commands/stubs.d.ts.map +1 -1
- package/dist/commands/stubs.js +12 -2
- package/dist/commands/stubs.js.map +1 -1
- package/dist/config/load.d.ts.map +1 -1
- package/dist/config/load.js +20 -4
- package/dist/config/load.js.map +1 -1
- package/dist/config/schema.d.ts +9 -12
- package/dist/config/schema.d.ts.map +1 -1
- package/dist/config/schema.js.map +1 -1
- package/dist/doctor/checks/credentials.d.ts +20 -0
- package/dist/doctor/checks/credentials.d.ts.map +1 -0
- package/dist/doctor/checks/credentials.js +58 -0
- package/dist/doctor/checks/credentials.js.map +1 -0
- package/dist/doctor/checks/credentials.test.d.ts +10 -0
- package/dist/doctor/checks/credentials.test.d.ts.map +1 -0
- package/dist/doctor/checks/credentials.test.js +47 -0
- package/dist/doctor/checks/credentials.test.js.map +1 -0
- package/dist/doctor/checks/index.d.ts +2 -1
- package/dist/doctor/checks/index.d.ts.map +1 -1
- package/dist/doctor/checks/index.js +3 -1
- package/dist/doctor/checks/index.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/integrations/providers/discover.d.ts +3 -8
- package/dist/integrations/providers/discover.d.ts.map +1 -1
- package/dist/integrations/providers/discover.js +2 -12
- package/dist/integrations/providers/discover.js.map +1 -1
- package/dist/integrations/providers/discover.test.js +1 -28
- package/dist/integrations/providers/discover.test.js.map +1 -1
- package/dist/integrations/providers/index.d.ts +0 -1
- package/dist/integrations/providers/index.d.ts.map +1 -1
- package/dist/integrations/providers/index.js +0 -1
- package/dist/integrations/providers/index.js.map +1 -1
- package/dist/integrations/providers/oauth.d.ts +1 -1
- package/dist/integrations/providers/oauth.js +1 -1
- package/dist/integrations/providers/preferences.d.ts +2 -2
- package/dist/integrations/providers/preferences.js +2 -2
- package/dist/integrations/sessions/store.js +1 -1
- package/dist/integrations/sessions/store.js.map +1 -1
- package/dist/integrations/updates.d.ts +4 -12
- package/dist/integrations/updates.d.ts.map +1 -1
- package/dist/integrations/updates.js +6 -53
- package/dist/integrations/updates.js.map +1 -1
- package/dist/permissions/__tests__/mode.test.d.ts +10 -0
- package/dist/permissions/__tests__/mode.test.d.ts.map +1 -0
- package/dist/permissions/__tests__/mode.test.js +115 -0
- package/dist/permissions/__tests__/mode.test.js.map +1 -0
- package/dist/permissions/__tests__/rules.test.d.ts +10 -0
- package/dist/permissions/__tests__/rules.test.d.ts.map +1 -0
- package/dist/permissions/__tests__/rules.test.js +196 -0
- package/dist/permissions/__tests__/rules.test.js.map +1 -0
- package/dist/permissions/mode.d.ts +64 -0
- package/dist/permissions/mode.d.ts.map +1 -0
- package/dist/permissions/mode.js +32 -0
- package/dist/permissions/mode.js.map +1 -0
- package/dist/permissions/rules.d.ts +105 -0
- package/dist/permissions/rules.d.ts.map +1 -0
- package/dist/permissions/rules.js +163 -0
- package/dist/permissions/rules.js.map +1 -0
- package/dist/tui/App.d.ts.map +1 -1
- package/dist/tui/App.js +29 -133
- package/dist/tui/App.js.map +1 -1
- package/dist/tui/Picker.d.ts +4 -4
- package/dist/tui/Picker.d.ts.map +1 -1
- package/dist/tui/Picker.js +6 -8
- package/dist/tui/Picker.js.map +1 -1
- package/dist/tui/__tests__/deferred-tool-discovery.test.d.ts +24 -0
- package/dist/tui/__tests__/deferred-tool-discovery.test.d.ts.map +1 -0
- package/dist/tui/__tests__/deferred-tool-discovery.test.js +111 -0
- package/dist/tui/__tests__/deferred-tool-discovery.test.js.map +1 -0
- package/dist/tui/__tests__/to-agent-event.test.js +88 -0
- package/dist/tui/__tests__/to-agent-event.test.js.map +1 -1
- package/dist/tui/__tests__/working-directory.test.d.ts +13 -0
- package/dist/tui/__tests__/working-directory.test.d.ts.map +1 -0
- package/dist/tui/__tests__/working-directory.test.js +109 -0
- package/dist/tui/__tests__/working-directory.test.js.map +1 -0
- package/dist/tui/agent.d.ts +46 -5
- package/dist/tui/agent.d.ts.map +1 -1
- package/dist/tui/agent.js +140 -43
- package/dist/tui/agent.js.map +1 -1
- package/dist/tui/slashCommands.d.ts +0 -6
- package/dist/tui/slashCommands.d.ts.map +1 -1
- package/dist/tui/slashCommands.js +1 -21
- package/dist/tui/slashCommands.js.map +1 -1
- package/dist/tui/types.d.ts +12 -0
- package/dist/tui/types.d.ts.map +1 -1
- package/package.json +4 -5
- package/dist/commands/tools.d.ts +0 -16
- package/dist/commands/tools.d.ts.map +0 -1
- package/dist/commands/tools.js +0 -161
- package/dist/commands/tools.js.map +0 -1
- package/dist/integrations/clawtool/agents.d.ts +0 -31
- package/dist/integrations/clawtool/agents.d.ts.map +0 -1
- package/dist/integrations/clawtool/agents.js +0 -30
- package/dist/integrations/clawtool/agents.js.map +0 -1
- package/dist/integrations/clawtool/agents.test.d.ts +0 -2
- package/dist/integrations/clawtool/agents.test.d.ts.map +0 -1
- package/dist/integrations/clawtool/agents.test.js +0 -79
- package/dist/integrations/clawtool/agents.test.js.map +0 -1
- package/dist/integrations/clawtool/auth.d.ts +0 -26
- package/dist/integrations/clawtool/auth.d.ts.map +0 -1
- package/dist/integrations/clawtool/auth.js +0 -56
- package/dist/integrations/clawtool/auth.js.map +0 -1
- package/dist/integrations/clawtool/auth.test.d.ts +0 -2
- package/dist/integrations/clawtool/auth.test.d.ts.map +0 -1
- package/dist/integrations/clawtool/auth.test.js +0 -40
- package/dist/integrations/clawtool/auth.test.js.map +0 -1
- package/dist/integrations/clawtool/binary.d.ts +0 -25
- package/dist/integrations/clawtool/binary.d.ts.map +0 -1
- package/dist/integrations/clawtool/binary.js +0 -50
- package/dist/integrations/clawtool/binary.js.map +0 -1
- package/dist/integrations/clawtool/binary.test.d.ts +0 -2
- package/dist/integrations/clawtool/binary.test.d.ts.map +0 -1
- package/dist/integrations/clawtool/binary.test.js +0 -50
- package/dist/integrations/clawtool/binary.test.js.map +0 -1
- package/dist/integrations/clawtool/daemon.d.ts +0 -37
- package/dist/integrations/clawtool/daemon.d.ts.map +0 -1
- package/dist/integrations/clawtool/daemon.js +0 -109
- package/dist/integrations/clawtool/daemon.js.map +0 -1
- package/dist/integrations/clawtool/daemon.test.d.ts +0 -11
- package/dist/integrations/clawtool/daemon.test.d.ts.map +0 -1
- package/dist/integrations/clawtool/daemon.test.js +0 -118
- package/dist/integrations/clawtool/daemon.test.js.map +0 -1
- package/dist/integrations/clawtool/dispatch.d.ts +0 -33
- package/dist/integrations/clawtool/dispatch.d.ts.map +0 -1
- package/dist/integrations/clawtool/dispatch.js +0 -155
- package/dist/integrations/clawtool/dispatch.js.map +0 -1
- package/dist/integrations/clawtool/dispatch.test.d.ts +0 -2
- package/dist/integrations/clawtool/dispatch.test.d.ts.map +0 -1
- package/dist/integrations/clawtool/dispatch.test.js +0 -138
- package/dist/integrations/clawtool/dispatch.test.js.map +0 -1
- package/dist/integrations/clawtool/index.d.ts +0 -11
- package/dist/integrations/clawtool/index.d.ts.map +0 -1
- package/dist/integrations/clawtool/index.js +0 -10
- package/dist/integrations/clawtool/index.js.map +0 -1
- package/dist/integrations/clawtool/mcp.d.ts +0 -51
- package/dist/integrations/clawtool/mcp.d.ts.map +0 -1
- package/dist/integrations/clawtool/mcp.js +0 -174
- package/dist/integrations/clawtool/mcp.js.map +0 -1
- package/dist/integrations/clawtool/mcp.test.d.ts +0 -2
- package/dist/integrations/clawtool/mcp.test.d.ts.map +0 -1
- package/dist/integrations/clawtool/mcp.test.js +0 -126
- package/dist/integrations/clawtool/mcp.test.js.map +0 -1
- package/dist/integrations/clawtool/paths.d.ts +0 -8
- package/dist/integrations/clawtool/paths.d.ts.map +0 -1
- package/dist/integrations/clawtool/paths.js +0 -19
- package/dist/integrations/clawtool/paths.js.map +0 -1
- package/dist/integrations/clawtool/peers.d.ts +0 -67
- package/dist/integrations/clawtool/peers.d.ts.map +0 -1
- package/dist/integrations/clawtool/peers.js +0 -150
- package/dist/integrations/clawtool/peers.js.map +0 -1
- package/dist/integrations/clawtool/plugin.d.ts +0 -42
- package/dist/integrations/clawtool/plugin.d.ts.map +0 -1
- package/dist/integrations/clawtool/plugin.js +0 -38
- package/dist/integrations/clawtool/plugin.js.map +0 -1
- package/dist/integrations/clawtool/state.d.ts +0 -11
- package/dist/integrations/clawtool/state.d.ts.map +0 -1
- package/dist/integrations/clawtool/state.js +0 -41
- package/dist/integrations/clawtool/state.js.map +0 -1
- package/dist/integrations/clawtool/state.test.d.ts +0 -2
- package/dist/integrations/clawtool/state.test.d.ts.map +0 -1
- package/dist/integrations/clawtool/state.test.js +0 -38
- package/dist/integrations/clawtool/state.test.js.map +0 -1
- package/dist/integrations/clawtool/tooling.d.ts +0 -52
- package/dist/integrations/clawtool/tooling.d.ts.map +0 -1
- package/dist/integrations/clawtool/tooling.js +0 -91
- package/dist/integrations/clawtool/tooling.js.map +0 -1
- package/dist/integrations/clawtool/tooling.test.d.ts +0 -2
- package/dist/integrations/clawtool/tooling.test.d.ts.map +0 -1
- package/dist/integrations/clawtool/tooling.test.js +0 -58
- package/dist/integrations/clawtool/tooling.test.js.map +0 -1
- package/dist/integrations/clawtool/types.d.ts +0 -41
- package/dist/integrations/clawtool/types.d.ts.map +0 -1
- package/dist/integrations/clawtool/types.js +0 -9
- package/dist/integrations/clawtool/types.js.map +0 -1
- package/dist/integrations/providers/secrets.d.ts +0 -37
- package/dist/integrations/providers/secrets.d.ts.map +0 -1
- package/dist/integrations/providers/secrets.js +0 -69
- package/dist/integrations/providers/secrets.js.map +0 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* OAuth token refresh for a discovered subscription credential.
|
|
3
3
|
*
|
|
4
|
-
* The access token discovered from the macOS Keychain
|
|
4
|
+
* The access token discovered from the macOS Keychain
|
|
5
5
|
* is short-lived (~8h). When it lapses the provider answers 401 and the
|
|
6
6
|
* agent stream dies. Refreshing proactively against the public OAuth token
|
|
7
7
|
* endpoint, using the long-lived refresh token, renews a stale token before
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* OAuth token refresh for a discovered subscription credential.
|
|
3
3
|
*
|
|
4
|
-
* The access token discovered from the macOS Keychain
|
|
4
|
+
* The access token discovered from the macOS Keychain
|
|
5
5
|
* is short-lived (~8h). When it lapses the provider answers 401 and the
|
|
6
6
|
* agent stream dies. Refreshing proactively against the public OAuth token
|
|
7
7
|
* endpoint, using the long-lived refresh token, renews a stale token before
|
|
@@ -6,10 +6,10 @@
|
|
|
6
6
|
* "version": 2,
|
|
7
7
|
* "provider": "anthropic", // ProviderId
|
|
8
8
|
* "model": "claude-opus-4-7", // optional model override
|
|
9
|
-
* "subagents": { "active": [...] } //
|
|
9
|
+
* "subagents": { "active": [...] } // instances reserved for subagent dispatch
|
|
10
10
|
* }
|
|
11
11
|
*
|
|
12
|
-
* The previous schema (v1) stored a
|
|
12
|
+
* The previous schema (v1) stored a peer instance as the
|
|
13
13
|
* default — a different primitive (subagent dispatch, not primary
|
|
14
14
|
* chat). On-disk v1 files trigger a forced re-pick rather than an
|
|
15
15
|
* auto-migration: mapping between the two semantics would surprise
|
|
@@ -6,10 +6,10 @@
|
|
|
6
6
|
* "version": 2,
|
|
7
7
|
* "provider": "anthropic", // ProviderId
|
|
8
8
|
* "model": "claude-opus-4-7", // optional model override
|
|
9
|
-
* "subagents": { "active": [...] } //
|
|
9
|
+
* "subagents": { "active": [...] } // instances reserved for subagent dispatch
|
|
10
10
|
* }
|
|
11
11
|
*
|
|
12
|
-
* The previous schema (v1) stored a
|
|
12
|
+
* The previous schema (v1) stored a peer instance as the
|
|
13
13
|
* default — a different primitive (subagent dispatch, not primary
|
|
14
14
|
* chat). On-disk v1 files trigger a forced re-pick rather than an
|
|
15
15
|
* auto-migration: mapping between the two semantics would surprise
|
|
@@ -44,7 +44,7 @@ export async function openSessions(cwd) {
|
|
|
44
44
|
}
|
|
45
45
|
return { store, projectId, threadId: THREAD, tenantId: TENANT, root };
|
|
46
46
|
}
|
|
47
|
-
// Maps an embedder's own session key (e.g.
|
|
47
|
+
// Maps an embedder's own session key (e.g. a desktop host's uuid) to a
|
|
48
48
|
// namzu conversation id, so reopening that session resumes the same
|
|
49
49
|
// transcript. Kept as a small JSON pointer beside cli.json.
|
|
50
50
|
const DESKTOP_MAP = 'desktop-sessions.json';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store.js","sourceRoot":"","sources":["../../../src/integrations/sessions/store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AAChE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAChC,OAAO,EACN,gBAAgB,EAMhB,iBAAiB,GACjB,MAAM,YAAY,CAAA;AAEnB,MAAM,MAAM,GAAG,iBAA6B,CAAA;AAC5C,MAAM,MAAM,GAAG,eAA2B,CAAA;AAkB1C;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,GAAW;IAC7C,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAA;IAChC,MAAM,KAAK,GAAG,IAAI,gBAAgB,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAA;IACrD,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,EAAE,UAAU,CAAC,CAAA;IAE1C,IAAI,SAAgC,CAAA;IACpC,IAAI,CAAC;QACJ,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,WAAW,EAAE,MAAM,CAAC,CAA2B,CAAA;QACnF,IAAI,OAAO,GAAG,CAAC,SAAS,KAAK,QAAQ;YAAE,SAAS,GAAG,GAAG,CAAC,SAAsB,CAAA;IAC9E,CAAC;IAAC,MAAM,CAAC;QACR,iBAAiB;IAClB,CAAC;IACD,IAAI,SAAS,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,UAAU,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC,EAAE,CAAC;QAC/D,SAAS,GAAG,SAAS,CAAA,CAAC,+BAA+B;IACtD,CAAC;IACD,IAAI,CAAC,SAAS,EAAE,CAAC;QAChB,MAAM,OAAO,GAAG,MAAM,KAAK,CAAC,aAAa,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,EAAE,EAAE,MAAM,CAAC,CAAA;QAC1F,SAAS,GAAG,OAAO,CAAC,EAAE,CAAA;QACtB,SAAS,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;QACpC,aAAa,CAAC,WAAW,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,SAAS,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAA;IAC3F,CAAC;IACD,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,CAAA;AACtE,CAAC;AAED,
|
|
1
|
+
{"version":3,"file":"store.js","sourceRoot":"","sources":["../../../src/integrations/sessions/store.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Update
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* yields `null`, never an error or a hang.
|
|
2
|
+
* Update check for namzu (npm), surfaced in the TUI so the user knows when a
|
|
3
|
+
* newer version is out. Best-effort with a short timeout: offline or
|
|
4
|
+
* unpublished yields `null`, never an error or a hang.
|
|
6
5
|
*/
|
|
7
6
|
export interface UpdateInfo {
|
|
8
7
|
readonly name: string;
|
|
@@ -15,13 +14,6 @@ export interface UpdateInfo {
|
|
|
15
14
|
export declare function compareVersions(a: string, b: string): number;
|
|
16
15
|
/** Newer @namzu/cli on npm, or null when up to date / unreachable. */
|
|
17
16
|
export declare function checkNamzuUpdate(current: string): Promise<UpdateInfo | null>;
|
|
18
|
-
/**
|
|
19
|
-
* Newer clawtool, or null. Prefers the machine-readable
|
|
20
|
-
* `upgrade --check --json` (newer binaries); falls back to parsing the
|
|
21
|
-
* `current -> latest` line in the plain `upgrade --check` output (older
|
|
22
|
-
* binaries that predate `--json`).
|
|
23
|
-
*/
|
|
24
|
-
export declare function checkClawtoolUpdate(): Promise<UpdateInfo | null>;
|
|
25
|
-
/** Run both checks in parallel; returns whichever have an update available. */
|
|
17
|
+
/** Returns an update when one is available. */
|
|
26
18
|
export declare function checkUpdates(namzuVersion: string): Promise<readonly UpdateInfo[]>;
|
|
27
19
|
//# sourceMappingURL=updates.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"updates.d.ts","sourceRoot":"","sources":["../../src/integrations/updates.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"updates.d.ts","sourceRoot":"","sources":["../../src/integrations/updates.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,MAAM,WAAW,UAAU;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,yCAAyC;IACzC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;CACpB;AAID,yEAAyE;AACzE,wBAAgB,eAAe,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAc5D;AAED,sEAAsE;AACtE,wBAAsB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAkBlF;AAED,+CAA+C;AAC/C,wBAAsB,YAAY,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,UAAU,EAAE,CAAC,CAGvF"}
|
|
@@ -1,13 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Update
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* yields `null`, never an error or a hang.
|
|
2
|
+
* Update check for namzu (npm), surfaced in the TUI so the user knows when a
|
|
3
|
+
* newer version is out. Best-effort with a short timeout: offline or
|
|
4
|
+
* unpublished yields `null`, never an error or a hang.
|
|
6
5
|
*/
|
|
7
|
-
import { execFile } from 'node:child_process';
|
|
8
|
-
import { findBinary } from './clawtool/binary.js';
|
|
9
6
|
const NPM_TIMEOUT_MS = 2_500;
|
|
10
|
-
const CLAWTOOL_TIMEOUT_MS = 5_000;
|
|
11
7
|
/** Compare dotted numeric versions; >0 if a>b, <0 if a<b, 0 if equal. */
|
|
12
8
|
export function compareVersions(a, b) {
|
|
13
9
|
const parse = (v) => v
|
|
@@ -48,52 +44,9 @@ export async function checkNamzuUpdate(current) {
|
|
|
48
44
|
clearTimeout(timer);
|
|
49
45
|
}
|
|
50
46
|
}
|
|
51
|
-
|
|
52
|
-
return new Promise((resolve) => {
|
|
53
|
-
execFile(bin, args, { timeout: CLAWTOOL_TIMEOUT_MS }, (_err, stdout) => resolve(stdout ?? ''));
|
|
54
|
-
});
|
|
55
|
-
}
|
|
56
|
-
/**
|
|
57
|
-
* Newer clawtool, or null. Prefers the machine-readable
|
|
58
|
-
* `upgrade --check --json` (newer binaries); falls back to parsing the
|
|
59
|
-
* `current -> latest` line in the plain `upgrade --check` output (older
|
|
60
|
-
* binaries that predate `--json`).
|
|
61
|
-
*/
|
|
62
|
-
export async function checkClawtoolUpdate() {
|
|
63
|
-
let bin;
|
|
64
|
-
try {
|
|
65
|
-
bin = findBinary();
|
|
66
|
-
}
|
|
67
|
-
catch {
|
|
68
|
-
return null;
|
|
69
|
-
}
|
|
70
|
-
const how = 'clawtool upgrade';
|
|
71
|
-
// JSON path (preferred).
|
|
72
|
-
try {
|
|
73
|
-
const data = JSON.parse(await run(bin, ['upgrade', '--check', '--json']));
|
|
74
|
-
if (data.update_available && data.current && data.latest) {
|
|
75
|
-
return { name: 'clawtool', current: data.current, latest: data.latest, how };
|
|
76
|
-
}
|
|
77
|
-
if (data.current)
|
|
78
|
-
return null; // valid JSON, no update
|
|
79
|
-
}
|
|
80
|
-
catch {
|
|
81
|
-
// fall through to plain parsing
|
|
82
|
-
}
|
|
83
|
-
// Plain-output fallback: a line like `0.22.159 -> 0.22.160`.
|
|
84
|
-
const plain = await run(bin, ['upgrade', '--check']);
|
|
85
|
-
const m = plain.match(/(\d+\.\d+\.\d+)\s*->\s*(\d+\.\d+\.\d+)/);
|
|
86
|
-
if (m?.[1] && m[2] && compareVersions(m[2], m[1]) > 0) {
|
|
87
|
-
return { name: 'clawtool', current: m[1], latest: m[2], how };
|
|
88
|
-
}
|
|
89
|
-
return null;
|
|
90
|
-
}
|
|
91
|
-
/** Run both checks in parallel; returns whichever have an update available. */
|
|
47
|
+
/** Returns an update when one is available. */
|
|
92
48
|
export async function checkUpdates(namzuVersion) {
|
|
93
|
-
const
|
|
94
|
-
|
|
95
|
-
checkClawtoolUpdate(),
|
|
96
|
-
]);
|
|
97
|
-
return [namzu, clawtool].filter((u) => u !== null);
|
|
49
|
+
const namzu = await checkNamzuUpdate(namzuVersion);
|
|
50
|
+
return namzu ? [namzu] : [];
|
|
98
51
|
}
|
|
99
52
|
//# sourceMappingURL=updates.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"updates.js","sourceRoot":"","sources":["../../src/integrations/updates.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"updates.js","sourceRoot":"","sources":["../../src/integrations/updates.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAUH,MAAM,cAAc,GAAG,KAAK,CAAA;AAE5B,yEAAyE;AACzE,MAAM,UAAU,eAAe,CAAC,CAAS,EAAE,CAAS;IACnD,MAAM,KAAK,GAAG,CAAC,CAAS,EAAE,EAAE,CAC3B,CAAC;SACC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC;SACjB,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,0BAA0B;QACzC,EAAE,KAAK,CAAC,GAAG,CAAC;SACX,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAA;IAChD,MAAM,EAAE,GAAG,KAAK,CAAC,CAAC,CAAC,CAAA;IACnB,MAAM,EAAE,GAAG,KAAK,CAAC,CAAC,CAAC,CAAA;IACnB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QACzD,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAA;QACrC,IAAI,CAAC,KAAK,CAAC;YAAE,OAAO,CAAC,CAAA;IACtB,CAAC;IACD,OAAO,CAAC,CAAA;AACT,CAAC;AAED,sEAAsE;AACtE,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,OAAe;IACrD,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAA;IACxC,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,cAAc,CAAC,CAAA;IAClE,IAAI,CAAC;QACJ,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,8CAA8C,EAAE;YACvE,MAAM,EAAE,UAAU,CAAC,MAAM;YACzB,OAAO,EAAE,EAAE,MAAM,EAAE,kBAAkB,EAAE;SACvC,CAAC,CAAA;QACF,IAAI,CAAC,GAAG,CAAC,EAAE;YAAE,OAAO,IAAI,CAAA;QACxB,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAA0B,CAAA;QACxD,MAAM,MAAM,GAAG,OAAO,IAAI,CAAC,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAA;QACrE,IAAI,CAAC,MAAM,IAAI,eAAe,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC;YAAE,OAAO,IAAI,CAAA;QACjE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,qBAAqB,EAAE,CAAA;IACtE,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,IAAI,CAAA;IACZ,CAAC;YAAS,CAAC;QACV,YAAY,CAAC,KAAK,CAAC,CAAA;IACpB,CAAC;AACF,CAAC;AAED,+CAA+C;AAC/C,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,YAAoB;IACtD,MAAM,KAAK,GAAG,MAAM,gBAAgB,CAAC,YAAY,CAAC,CAAA;IAClD,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAA;AAC5B,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The mode, and its precedence against the config file.
|
|
3
|
+
*
|
|
4
|
+
* The rule being protected is one sentence: a mode governs only the calls no
|
|
5
|
+
* rule decided, so it can never reopen what a rule closed. The tests that
|
|
6
|
+
* matter are the ones that would catch a future change making
|
|
7
|
+
* `--permission-mode auto` able to run something the operator wrote `deny` for.
|
|
8
|
+
*/
|
|
9
|
+
export {};
|
|
10
|
+
//# sourceMappingURL=mode.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mode.test.d.ts","sourceRoot":"","sources":["../../../src/permissions/__tests__/mode.test.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG"}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The mode, and its precedence against the config file.
|
|
3
|
+
*
|
|
4
|
+
* The rule being protected is one sentence: a mode governs only the calls no
|
|
5
|
+
* rule decided, so it can never reopen what a rule closed. The tests that
|
|
6
|
+
* matter are the ones that would catch a future change making
|
|
7
|
+
* `--permission-mode auto` able to run something the operator wrote `deny` for.
|
|
8
|
+
*/
|
|
9
|
+
import { VerificationGate, configureLogger, getRootLogger } from '@namzu/sdk';
|
|
10
|
+
import { describe, expect, it } from 'vitest';
|
|
11
|
+
import { makeResumeHandler } from '../../tui/agent.js';
|
|
12
|
+
import { PERMISSION_MODES, isPermissionMode, resolvePermissionMode } from '../mode.js';
|
|
13
|
+
import { compilePermissions } from '../rules.js';
|
|
14
|
+
configureLogger({ level: 'silent' });
|
|
15
|
+
describe('resolving the mode from flag, alias and terminal', () => {
|
|
16
|
+
it('takes an explicit flag over everything else', () => {
|
|
17
|
+
expect(resolvePermissionMode({ flag: 'strict', interactive: true })).toEqual({ mode: 'strict' });
|
|
18
|
+
expect(resolvePermissionMode({ flag: 'prompt', skipPermissions: true, interactive: false })).toEqual({ mode: 'prompt' });
|
|
19
|
+
});
|
|
20
|
+
it('refuses a mode it does not know, and names the ones it does', () => {
|
|
21
|
+
const result = resolvePermissionMode({ flag: 'yolo', interactive: false });
|
|
22
|
+
expect('error' in result && result.error).toContain('prompt, auto, strict');
|
|
23
|
+
});
|
|
24
|
+
it('maps the bypass aliases to auto', () => {
|
|
25
|
+
expect(resolvePermissionMode({ skipPermissions: true, interactive: true })).toEqual({
|
|
26
|
+
mode: 'auto',
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
it('asks when there is someone to ask, and does not when there is not', () => {
|
|
30
|
+
// `prompt` without a terminal would silently become `auto` anyway;
|
|
31
|
+
// resolving it as `auto` makes the resolved mode honest in a log.
|
|
32
|
+
expect(resolvePermissionMode({ interactive: true })).toEqual({ mode: 'prompt' });
|
|
33
|
+
expect(resolvePermissionMode({ interactive: false })).toEqual({ mode: 'auto' });
|
|
34
|
+
});
|
|
35
|
+
it('exposes exactly the modes it accepts', () => {
|
|
36
|
+
expect(PERMISSION_MODES.every(isPermissionMode)).toBe(true);
|
|
37
|
+
expect(isPermissionMode('trusted')).toBe(false);
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
describe('a mode decides only what no rule decided', () => {
|
|
41
|
+
function gate(config) {
|
|
42
|
+
const { rules } = compilePermissions(config);
|
|
43
|
+
return new VerificationGate({
|
|
44
|
+
enabled: true,
|
|
45
|
+
rules: [...rules],
|
|
46
|
+
allowReadOnlyTools: false,
|
|
47
|
+
denyDangerousPatterns: false,
|
|
48
|
+
logDecisions: false,
|
|
49
|
+
}, getRootLogger());
|
|
50
|
+
}
|
|
51
|
+
it('a denied call never reaches the mode at all', async () => {
|
|
52
|
+
// THE precedence test. `deny` is settled by the gate, so no mode — not
|
|
53
|
+
// even the one spelled --yolo — is ever consulted about it. If this ever
|
|
54
|
+
// fails, a flag has become able to lift a prohibition someone wrote down.
|
|
55
|
+
const decision = gate({ bash: 'deny' }).evaluate({
|
|
56
|
+
toolName: 'bash',
|
|
57
|
+
toolInput: { command: 'rm -rf /tmp/x' },
|
|
58
|
+
toolDef: undefined,
|
|
59
|
+
});
|
|
60
|
+
expect(decision.decision).toBe('deny');
|
|
61
|
+
});
|
|
62
|
+
it('an allowed call never reaches the mode either', () => {
|
|
63
|
+
const decision = gate({ bash: { 'git status*': 'allow' } }).evaluate({
|
|
64
|
+
toolName: 'bash',
|
|
65
|
+
toolInput: { command: 'git status' },
|
|
66
|
+
toolDef: undefined,
|
|
67
|
+
});
|
|
68
|
+
expect(decision.decision).toBe('allow');
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
describe('what each mode does with an undecided call', () => {
|
|
72
|
+
const destructive = [
|
|
73
|
+
{ id: '1', name: 'write', input: { path: 'a.txt' }, isDestructive: true },
|
|
74
|
+
];
|
|
75
|
+
it('auto approves it', async () => {
|
|
76
|
+
const handler = makeResumeHandler({ all: false }, undefined, 'auto');
|
|
77
|
+
const decision = await handler({ type: 'tool_review', toolCalls: destructive });
|
|
78
|
+
expect(decision).toEqual({ action: 'approve_tools' });
|
|
79
|
+
});
|
|
80
|
+
it('strict refuses it, and says asking again will not help', async () => {
|
|
81
|
+
// The mode that did not exist: an unattended run could only be `auto`, so
|
|
82
|
+
// a CI job either trusted the agent with everything or could not use it.
|
|
83
|
+
const handler = makeResumeHandler({ all: false }, undefined, 'strict');
|
|
84
|
+
const decision = (await handler({
|
|
85
|
+
type: 'tool_review',
|
|
86
|
+
toolCalls: destructive,
|
|
87
|
+
}));
|
|
88
|
+
expect(decision.action).toBe('reject_tools');
|
|
89
|
+
expect(decision.feedback).toContain('Asking again will not change it');
|
|
90
|
+
});
|
|
91
|
+
it('prompt asks the human, and honours the answer', async () => {
|
|
92
|
+
let asked = false;
|
|
93
|
+
const handler = makeResumeHandler({ all: false }, async () => {
|
|
94
|
+
asked = true;
|
|
95
|
+
return { kind: 'reject', feedback: 'not that one' };
|
|
96
|
+
}, 'prompt');
|
|
97
|
+
const decision = (await handler({
|
|
98
|
+
type: 'tool_review',
|
|
99
|
+
toolCalls: destructive,
|
|
100
|
+
}));
|
|
101
|
+
expect(asked).toBe(true);
|
|
102
|
+
expect(decision.action).toBe('reject_tools');
|
|
103
|
+
expect(decision.feedback).toBe('not that one');
|
|
104
|
+
});
|
|
105
|
+
it('read-only batches run under every mode, including strict', async () => {
|
|
106
|
+
// `strict` refuses what needs asking, and a read never needed asking.
|
|
107
|
+
// Refusing reads would make the mode unusable rather than strict.
|
|
108
|
+
const readOnly = [{ id: '1', name: 'read', input: {}, isDestructive: false }];
|
|
109
|
+
const handler = makeResumeHandler({ all: false }, undefined, 'strict');
|
|
110
|
+
expect(await handler({ type: 'tool_review', toolCalls: readOnly })).toEqual({
|
|
111
|
+
action: 'approve_tools',
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
//# sourceMappingURL=mode.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mode.test.js","sourceRoot":"","sources":["../../../src/permissions/__tests__/mode.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The rule compiler.
|
|
3
|
+
*
|
|
4
|
+
* The load-bearing case is the LAST one: a tool nobody wrote a rule for must
|
|
5
|
+
* still be asked about. A declarative permission model that quietly widened the
|
|
6
|
+
* default would be a security downgrade wearing a feature's clothes, and it
|
|
7
|
+
* would look exactly like this change from the outside.
|
|
8
|
+
*/
|
|
9
|
+
export {};
|
|
10
|
+
//# sourceMappingURL=rules.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rules.test.d.ts","sourceRoot":"","sources":["../../../src/permissions/__tests__/rules.test.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG"}
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The rule compiler.
|
|
3
|
+
*
|
|
4
|
+
* The load-bearing case is the LAST one: a tool nobody wrote a rule for must
|
|
5
|
+
* still be asked about. A declarative permission model that quietly widened the
|
|
6
|
+
* default would be a security downgrade wearing a feature's clothes, and it
|
|
7
|
+
* would look exactly like this change from the outside.
|
|
8
|
+
*/
|
|
9
|
+
import { VerificationGate, configureLogger, getRootLogger } from '@namzu/sdk';
|
|
10
|
+
import { describe, expect, it } from 'vitest';
|
|
11
|
+
import { bySpecificity, compilePermissions, matchesPattern, patternToRegExpSource, } from '../rules.js';
|
|
12
|
+
describe('pattern matching', () => {
|
|
13
|
+
it('matches a literal command exactly', () => {
|
|
14
|
+
expect(matchesPattern('git status', 'git status')).toBe(true);
|
|
15
|
+
expect(matchesPattern('git statuses', 'git status')).toBe(false);
|
|
16
|
+
});
|
|
17
|
+
it('lets * stand for any run of characters and ? for one', () => {
|
|
18
|
+
expect(matchesPattern('git push --force', 'git push*')).toBe(true);
|
|
19
|
+
expect(matchesPattern('cat a.ts', 'cat ?.ts')).toBe(true);
|
|
20
|
+
expect(matchesPattern('cat ab.ts', 'cat ?.ts')).toBe(false);
|
|
21
|
+
});
|
|
22
|
+
it('matches the bare command for a trailing " *"', () => {
|
|
23
|
+
// `git push *` is the obvious way to write "any git push". Without this
|
|
24
|
+
// the rule silently misses the exact invocation it was written for.
|
|
25
|
+
expect(matchesPattern('git push', 'git push *')).toBe(true);
|
|
26
|
+
expect(matchesPattern('git push origin main', 'git push *')).toBe(true);
|
|
27
|
+
});
|
|
28
|
+
it('treats a backslash path as its forward-slash self, both sides', () => {
|
|
29
|
+
// One rule has to work on every platform; an operator should not have to
|
|
30
|
+
// write the same path twice.
|
|
31
|
+
expect(matchesPattern('src\\index.ts', 'src/*.ts')).toBe(true);
|
|
32
|
+
expect(matchesPattern('src/index.ts', 'src\\*.ts')).toBe(true);
|
|
33
|
+
});
|
|
34
|
+
it('does not let a pattern character smuggle in a regex', () => {
|
|
35
|
+
// `.` is a literal dot, not "any character", or a rule meant to allow
|
|
36
|
+
// `a.ts` would also allow `axts`.
|
|
37
|
+
expect(matchesPattern('axts', 'a.ts')).toBe(false);
|
|
38
|
+
expect(matchesPattern('a.ts', 'a.ts')).toBe(true);
|
|
39
|
+
expect(patternToRegExpSource('a+b')).toBe('^a\\+b$');
|
|
40
|
+
});
|
|
41
|
+
it('anchors, so a pattern is not a substring search', () => {
|
|
42
|
+
expect(matchesPattern('sudo git status', 'git status')).toBe(false);
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
describe('specificity ordering', () => {
|
|
46
|
+
// The kernel evaluates first-match-wins, so order decides meaning: a bare
|
|
47
|
+
// `*` sorted first would make every rule after it dead config.
|
|
48
|
+
it('puts a bare star last', () => {
|
|
49
|
+
expect(['*', 'git push*', 'git status'].sort(bySpecificity)).toEqual([
|
|
50
|
+
'git status',
|
|
51
|
+
'git push*',
|
|
52
|
+
'*',
|
|
53
|
+
]);
|
|
54
|
+
});
|
|
55
|
+
it('puts a longer literal prefix first', () => {
|
|
56
|
+
expect(['git*', 'git push*'].sort(bySpecificity)).toEqual(['git push*', 'git*']);
|
|
57
|
+
});
|
|
58
|
+
it('is stable for two patterns of equal specificity', () => {
|
|
59
|
+
expect(['b*', 'a*'].sort(bySpecificity)).toEqual(['a*', 'b*']);
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
describe('compiling a permissions table', () => {
|
|
63
|
+
it('turns a bare allow and deny into by-name rules', () => {
|
|
64
|
+
const { rules } = compilePermissions({ read: 'allow', bash: 'deny' });
|
|
65
|
+
expect(rules).toEqual([
|
|
66
|
+
{ type: 'allow_by_name', toolNames: ['read'] },
|
|
67
|
+
{ type: 'deny_by_name', toolNames: ['bash'] },
|
|
68
|
+
]);
|
|
69
|
+
});
|
|
70
|
+
it('emits pattern rules most-specific-first', () => {
|
|
71
|
+
const { rules } = compilePermissions({ bash: { '*': 'deny', 'git status*': 'allow' } });
|
|
72
|
+
expect(rules.map((r) => ('decision' in r ? r.decision : r.type))).toEqual(['allow', 'deny']);
|
|
73
|
+
});
|
|
74
|
+
it('emits nothing for ask, because the gate already asks by default', () => {
|
|
75
|
+
// If "ask" emitted a rule it would have to mean something different from
|
|
76
|
+
// the absence of a rule, and it does not.
|
|
77
|
+
const { rules } = compilePermissions({ edit: 'ask', bash: { 'rm *': 'ask' } });
|
|
78
|
+
expect(rules).toEqual([]);
|
|
79
|
+
});
|
|
80
|
+
it('reports a bad effect instead of dropping it in silence', () => {
|
|
81
|
+
// A permission someone believes is in force and which was quietly
|
|
82
|
+
// discarded is the worst outcome here — worse than refusing to start.
|
|
83
|
+
const { rules, diagnostics } = compilePermissions({
|
|
84
|
+
bash: { 'git push*': 'maybe' },
|
|
85
|
+
});
|
|
86
|
+
expect(rules).toEqual([]);
|
|
87
|
+
expect(diagnostics).toHaveLength(1);
|
|
88
|
+
expect(diagnostics[0]?.tool).toBe('bash');
|
|
89
|
+
expect(diagnostics[0]?.pattern).toBe('git push*');
|
|
90
|
+
});
|
|
91
|
+
it('keeps the other rules when one line is unreadable', () => {
|
|
92
|
+
const { rules, diagnostics } = compilePermissions({
|
|
93
|
+
read: 'allow',
|
|
94
|
+
bash: 7,
|
|
95
|
+
glob: 'allow',
|
|
96
|
+
});
|
|
97
|
+
expect(rules).toHaveLength(2);
|
|
98
|
+
expect(diagnostics).toHaveLength(1);
|
|
99
|
+
});
|
|
100
|
+
it('a tool nobody wrote a rule for produces no rule, so the gate asks', () => {
|
|
101
|
+
// THE case. The kernel's fallback for an unmatched call is `review`, so
|
|
102
|
+
// emitting nothing is what makes an unconfigured or newly bridged tool
|
|
103
|
+
// prompt. There is deliberately no way to spell "allow by omission":
|
|
104
|
+
// widening the default has to be something an operator typed.
|
|
105
|
+
const { rules } = compilePermissions({ read: 'allow' });
|
|
106
|
+
expect(rules.some((r) => 'toolNames' in r && r.toolNames.includes('some_new_tool'))).toBe(false);
|
|
107
|
+
expect(rules).toHaveLength(1);
|
|
108
|
+
});
|
|
109
|
+
it('is empty for absent config, which is the same as asking about everything', () => {
|
|
110
|
+
expect(compilePermissions(undefined)).toEqual({ rules: [], diagnostics: [] });
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
configureLogger({ level: 'silent' });
|
|
114
|
+
describe('the rules actually decide a real call', () => {
|
|
115
|
+
// The compiler tests above assert the SHAPE of the emitted rules. Shape is
|
|
116
|
+
// not behaviour: the first version of this compiler emitted `target: 'args'`
|
|
117
|
+
// with an anchored pattern, which matches `JSON.stringify(input)` — so
|
|
118
|
+
// `^git push.*$` could never match `{"command":"git push origin main"}` and
|
|
119
|
+
// every pattern rule decided nothing at all. It looked correct in every
|
|
120
|
+
// shape assertion. These drive the kernel's own gate instead.
|
|
121
|
+
function decide(config, tool, input) {
|
|
122
|
+
const { rules } = compilePermissions(config);
|
|
123
|
+
const gate = new VerificationGate({
|
|
124
|
+
enabled: true,
|
|
125
|
+
rules: [...rules],
|
|
126
|
+
allowReadOnlyTools: false,
|
|
127
|
+
denyDangerousPatterns: false,
|
|
128
|
+
logDecisions: false,
|
|
129
|
+
}, getRootLogger());
|
|
130
|
+
return gate.evaluate({ toolName: tool, toolInput: input, toolDef: undefined });
|
|
131
|
+
}
|
|
132
|
+
it('allows the command an operator allowed', () => {
|
|
133
|
+
expect(decide({ bash: { 'git status*': 'allow' } }, 'bash', { command: 'git status' }).decision).toBe('allow');
|
|
134
|
+
});
|
|
135
|
+
it('denies the command an operator denied', () => {
|
|
136
|
+
const result = decide({ bash: { 'git push*': 'deny' } }, 'bash', {
|
|
137
|
+
command: 'git push origin main',
|
|
138
|
+
});
|
|
139
|
+
expect(result.decision).toBe('deny');
|
|
140
|
+
});
|
|
141
|
+
it('does not let one tool rule decide another tool call', () => {
|
|
142
|
+
// A rule written under `bash` must not decide an `edit` call whose
|
|
143
|
+
// arguments happen to contain the same text. The kernel's pattern rules
|
|
144
|
+
// carry no tool scope, so this is the CLI's job and it is easy to lose.
|
|
145
|
+
const result = decide({ bash: { 'git push*': 'deny' } }, 'edit', {
|
|
146
|
+
content: 'run git push to deploy',
|
|
147
|
+
});
|
|
148
|
+
expect(result.decision).toBe('review');
|
|
149
|
+
});
|
|
150
|
+
it('asks about a tool nobody wrote a rule for', () => {
|
|
151
|
+
// The load-bearing case, driven end to end rather than inferred.
|
|
152
|
+
expect(decide({ read: 'allow' }, 'some_new_bridged_tool', { x: 1 }).decision).toBe('review');
|
|
153
|
+
});
|
|
154
|
+
it('prefers the specific rule over the catch-all, whatever the config order', () => {
|
|
155
|
+
const config = { bash: { '*': 'allow', 'git push*': 'deny' } };
|
|
156
|
+
expect(decide(config, 'bash', { command: 'git push --force' }).decision).toBe('deny');
|
|
157
|
+
expect(decide(config, 'bash', { command: 'ls' }).decision).toBe('allow');
|
|
158
|
+
});
|
|
159
|
+
});
|
|
160
|
+
describe('what a denial actually says to the model', () => {
|
|
161
|
+
// The decision was never the part that was broken. A model told only
|
|
162
|
+
// "denied" rewords the command and tries again; a model told WHICH rule
|
|
163
|
+
// stopped it, and whether a different input could ever pass, can stop and
|
|
164
|
+
// say so. These assert the sentence, not the verdict.
|
|
165
|
+
function reasonFor(config, tool, input) {
|
|
166
|
+
const { rules } = compilePermissions(config);
|
|
167
|
+
const gate = new VerificationGate({
|
|
168
|
+
enabled: true,
|
|
169
|
+
rules: [...rules],
|
|
170
|
+
allowReadOnlyTools: false,
|
|
171
|
+
denyDangerousPatterns: false,
|
|
172
|
+
logDecisions: false,
|
|
173
|
+
}, getRootLogger());
|
|
174
|
+
return gate.evaluate({ toolName: tool, toolInput: input, toolDef: undefined }).reason;
|
|
175
|
+
}
|
|
176
|
+
it('a by-name denial says the tool is refused, so a reworded input will not help', () => {
|
|
177
|
+
const reason = reasonFor({ bash: 'deny' }, 'bash', { command: 'ls' });
|
|
178
|
+
expect(reason).toContain('bash');
|
|
179
|
+
// The part that stops the retry loop.
|
|
180
|
+
expect(reason).toContain('a different input will not change it');
|
|
181
|
+
});
|
|
182
|
+
it('a pattern denial quotes the rule that matched', () => {
|
|
183
|
+
const reason = reasonFor({ bash: { 'git push*': 'deny' } }, 'bash', {
|
|
184
|
+
command: 'git push --force',
|
|
185
|
+
});
|
|
186
|
+
expect(reason).toContain('pattern rule');
|
|
187
|
+
// The model is shown the rule itself, not the name of its category, so
|
|
188
|
+
// it can tell which of its options are closed and which are not.
|
|
189
|
+
expect(reason).toContain('git push');
|
|
190
|
+
});
|
|
191
|
+
it('an unmatched call says nothing matched, rather than naming a rule', () => {
|
|
192
|
+
const reason = reasonFor({ read: 'allow' }, 'some_new_tool', {});
|
|
193
|
+
expect(reason).toBe('No matching rule found');
|
|
194
|
+
});
|
|
195
|
+
});
|
|
196
|
+
//# sourceMappingURL=rules.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rules.test.js","sourceRoot":"","sources":["../../../src/permissions/__tests__/rules.test.ts"],"names":[],"mappings":"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"}
|