xiaotime 0.2.1 → 0.4.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.
Files changed (53) hide show
  1. package/dist/__tests__/approval_menu.test.d.ts +15 -0
  2. package/dist/__tests__/approval_menu.test.d.ts.map +1 -0
  3. package/dist/__tests__/approval_menu.test.js +107 -0
  4. package/dist/__tests__/approval_menu.test.js.map +1 -0
  5. package/dist/__tests__/legacy_compat.test.d.ts +2 -0
  6. package/dist/__tests__/legacy_compat.test.d.ts.map +1 -0
  7. package/dist/__tests__/legacy_compat.test.js +36 -0
  8. package/dist/__tests__/legacy_compat.test.js.map +1 -0
  9. package/dist/__tests__/server_tool_render.test.d.ts +17 -0
  10. package/dist/__tests__/server_tool_render.test.d.ts.map +1 -0
  11. package/dist/__tests__/server_tool_render.test.js +74 -0
  12. package/dist/__tests__/server_tool_render.test.js.map +1 -0
  13. package/dist/__tests__/settings.test.d.ts +16 -0
  14. package/dist/__tests__/settings.test.d.ts.map +1 -0
  15. package/dist/__tests__/settings.test.js +289 -0
  16. package/dist/__tests__/settings.test.js.map +1 -0
  17. package/dist/__tests__/tool_result_client.test.d.ts +20 -0
  18. package/dist/__tests__/tool_result_client.test.d.ts.map +1 -0
  19. package/dist/__tests__/tool_result_client.test.js +142 -0
  20. package/dist/__tests__/tool_result_client.test.js.map +1 -0
  21. package/dist/__tests__/tools.test.d.ts +17 -0
  22. package/dist/__tests__/tools.test.d.ts.map +1 -0
  23. package/dist/__tests__/tools.test.js +381 -0
  24. package/dist/__tests__/tools.test.js.map +1 -0
  25. package/dist/config.d.ts +7 -0
  26. package/dist/config.d.ts.map +1 -1
  27. package/dist/config.js +8 -1
  28. package/dist/config.js.map +1 -1
  29. package/dist/display.d.ts +19 -0
  30. package/dist/display.d.ts.map +1 -1
  31. package/dist/display.js +23 -0
  32. package/dist/display.js.map +1 -1
  33. package/dist/settings.d.ts +109 -0
  34. package/dist/settings.d.ts.map +1 -0
  35. package/dist/settings.js +332 -0
  36. package/dist/settings.js.map +1 -0
  37. package/dist/tool_result_client.d.ts +72 -0
  38. package/dist/tool_result_client.d.ts.map +1 -0
  39. package/dist/tool_result_client.js +123 -0
  40. package/dist/tool_result_client.js.map +1 -0
  41. package/dist/tools.d.ts +11 -3
  42. package/dist/tools.d.ts.map +1 -1
  43. package/dist/tools.js +96 -33
  44. package/dist/tools.js.map +1 -1
  45. package/dist/ui_state.d.ts +134 -0
  46. package/dist/ui_state.d.ts.map +1 -0
  47. package/dist/ui_state.js +460 -0
  48. package/dist/ui_state.js.map +1 -0
  49. package/dist/ws.d.ts +15 -0
  50. package/dist/ws.d.ts.map +1 -1
  51. package/dist/ws.js +180 -75
  52. package/dist/ws.js.map +1 -1
  53. package/package.json +6 -3
package/dist/display.d.ts CHANGED
@@ -8,4 +8,23 @@ export declare function printTurnEnd(costUsd: number): void;
8
8
  export declare function printError(message: string): void;
9
9
  export declare function printSessionList(sessions: Session[]): void;
10
10
  export declare function printPrompt(): void;
11
+ /**
12
+ * Render a single inline log line for a server-side tool invocation.
13
+ *
14
+ * SPRINT-CLI-APPROVAL-MENU-1 (#6509) PR3 — pre-#6509 the 15 tools in
15
+ * terminal_orchestrator/main.py's SERVER_TOOLS set executed silently
16
+ * from the user's perspective. This helper renders the
17
+ * `server_tool_call` WS event the orchestrator now emits before each
18
+ * server-side dispatch, matching the visual style of the existing
19
+ * client-tool inline logs in cli/src/tools.ts (`[read_file] /path
20
+ * (N lines)`, `[git_status]`, etc.).
21
+ *
22
+ * `inputSummary` is built server-side (see
23
+ * `terminal_orchestrator/main.py::_build_server_tool_input_summary`)
24
+ * — ≤120 chars, preferred-keys-per-tool, middle-truncated values,
25
+ * no newlines. Empty inputSummary (e.g. librarian_list_collections,
26
+ * which has no meaningful input to render) drops to just the
27
+ * `[server:<name>]` prefix.
28
+ */
29
+ export declare function printServerToolCall(name: string, inputSummary: string): void;
11
30
  //# sourceMappingURL=display.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"display.d.ts","sourceRoot":"","sources":["../src/display.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAE5C,wBAAgB,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,QAQjH;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,QAE1C;AAED,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,QAG3C;AAED,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,QAEzC;AAED,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,OAAO,EAAE,QAoBnD;AAED,wBAAgB,WAAW,SAE1B"}
1
+ {"version":3,"file":"display.d.ts","sourceRoot":"","sources":["../src/display.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAE5C,wBAAgB,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,QAQjH;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,QAE1C;AAED,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,QAG3C;AAED,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,QAEzC;AAED,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,OAAO,EAAE,QAoBnD;AAED,wBAAgB,WAAW,SAE1B;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,IAAI,CAG5E"}
package/dist/display.js CHANGED
@@ -12,6 +12,7 @@ exports.printTurnEnd = printTurnEnd;
12
12
  exports.printError = printError;
13
13
  exports.printSessionList = printSessionList;
14
14
  exports.printPrompt = printPrompt;
15
+ exports.printServerToolCall = printServerToolCall;
15
16
  const chalk_1 = __importDefault(require("chalk"));
16
17
  function printWelcome(sessionId, sessionName, resumed, messageCount) {
17
18
  const name = sessionName || sessionId.slice(0, 8);
@@ -55,4 +56,26 @@ function printSessionList(sessions) {
55
56
  function printPrompt() {
56
57
  process.stdout.write(chalk_1.default.bold("you: "));
57
58
  }
59
+ /**
60
+ * Render a single inline log line for a server-side tool invocation.
61
+ *
62
+ * SPRINT-CLI-APPROVAL-MENU-1 (#6509) PR3 — pre-#6509 the 15 tools in
63
+ * terminal_orchestrator/main.py's SERVER_TOOLS set executed silently
64
+ * from the user's perspective. This helper renders the
65
+ * `server_tool_call` WS event the orchestrator now emits before each
66
+ * server-side dispatch, matching the visual style of the existing
67
+ * client-tool inline logs in cli/src/tools.ts (`[read_file] /path
68
+ * (N lines)`, `[git_status]`, etc.).
69
+ *
70
+ * `inputSummary` is built server-side (see
71
+ * `terminal_orchestrator/main.py::_build_server_tool_input_summary`)
72
+ * — ≤120 chars, preferred-keys-per-tool, middle-truncated values,
73
+ * no newlines. Empty inputSummary (e.g. librarian_list_collections,
74
+ * which has no meaningful input to render) drops to just the
75
+ * `[server:<name>]` prefix.
76
+ */
77
+ function printServerToolCall(name, inputSummary) {
78
+ const tail = inputSummary ? ` ${inputSummary}` : "";
79
+ console.log(chalk_1.default.dim(` [server:${name}]${tail}`));
80
+ }
58
81
  //# sourceMappingURL=display.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"display.js","sourceRoot":"","sources":["../src/display.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"display.js","sourceRoot":"","sources":["../src/display.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,109 @@
1
+ /**
2
+ * Per-user CLI settings + approval allowlist.
3
+ *
4
+ * SPRINT-CLI-APPROVAL-MENU-1 (#6509) PR2.
5
+ *
6
+ * Backs the option-2 "Run and don't ask again" path of
7
+ * `promptApprovalMenu`. Allowlist is persisted to
8
+ * `~/.xiaotime/settings.json` (mode 0600) and consulted before every
9
+ * `executeTool` invocation of the three prompting client-side tools
10
+ * (`shell_command` / `write_file` / `git_commit`). A match short-
11
+ * circuits the menu entirely; the inline log line gets an
12
+ * ` (auto-approved)` suffix so the user can still see what ran.
13
+ *
14
+ * Schema is `version: 1`; missing/malformed/unknown-version files
15
+ * fall back to an empty allowlist (with a stderr warning for
16
+ * malformed/unknown cases) — never crash the CLI on corrupted local
17
+ * config (ISC-6509-007).
18
+ *
19
+ * Pattern derivation differs per tool (see D2 in the sprint spec):
20
+ * - shell_command: argv[0] + leading flags + first subcommand
21
+ * token. `npm install --no-save lodash` → "npm install".
22
+ * Multi-level subcommand grammars (gh issue list) collapse to
23
+ * the first 2 tokens; users can hand-edit settings.json for
24
+ * finer-grained patterns.
25
+ * - write_file: `dirname(path) + "/**"`. Glob-matched against
26
+ * subsequent write_file invocations so allowlisting a write to
27
+ * `/repo/foo.ts` covers `/repo/bar.ts`.
28
+ * - git_commit: literal `"*"` — allowlisting one commit
29
+ * allowlists all future commits (the commit message + staged
30
+ * files vary by definition; there's no meaningful pattern
31
+ * parameter to narrow on).
32
+ *
33
+ * Distinct from `terminal_orchestrator/task_manager.py:57`'s
34
+ * server-side allowlist (EXACT-NAME on argv[0] basename, binary `gh`
35
+ * only). That's a security perimeter for `task_create`'s long-running
36
+ * managed-task envelope; this is a UX gate for client-side tool
37
+ * approvals. Two allowlists, two audiences, two update cadences.
38
+ */
39
+ export type AllowlistTool = "shell_command" | "write_file" | "git_commit";
40
+ export interface AllowlistEntry {
41
+ tool: AllowlistTool;
42
+ pattern: string;
43
+ added_at: string;
44
+ }
45
+ /**
46
+ * Settings-file location. Resolved lazily per call so the
47
+ * `XIAOTIME_SETTINGS_PATH` env-var override can redirect (used by
48
+ * tests to avoid touching the real `~/.xiaotime/settings.json`).
49
+ * Default: `~/.xiaotime/settings.json`.
50
+ */
51
+ export declare function _settingsPath(): string;
52
+ /**
53
+ * Read the allowlist from disk. Returns [] for any of:
54
+ * - file missing (first run; not an error)
55
+ * - file unreadable (permissions; logged + treated as empty)
56
+ * - malformed JSON (logged + treated as empty)
57
+ * - unknown schema version (logged + treated as empty)
58
+ * - shape doesn't match SettingsV1 (silently treated as empty)
59
+ *
60
+ * Never throws. The CLI must remain usable when the user has a
61
+ * corrupted local config (ISC-6509-007).
62
+ */
63
+ export declare function loadAllowlist(): AllowlistEntry[];
64
+ /**
65
+ * Append a new entry to the allowlist + persist. Creates
66
+ * ~/.xiaotime/ with mode 0700 if it doesn't exist; writes
67
+ * settings.json with mode 0600. Idempotent on duplicate
68
+ * (tool, pattern) — duplicate appends are silently dropped.
69
+ */
70
+ export declare function appendAllowlist(entry: {
71
+ tool: AllowlistTool;
72
+ pattern: string;
73
+ }): void;
74
+ /**
75
+ * Derive the allowlist pattern for a tool invocation. Returns null
76
+ * if the input shape is unusable (empty command, missing path). The
77
+ * pattern is used both at write time (option-2 selection persists
78
+ * `derivePattern(tool, input)`) and at read time (the next call's
79
+ * derived pattern feeds `matchAllowlist`).
80
+ *
81
+ * Per D2 in the sprint spec, derivation is coarse on purpose. For
82
+ * shell_command, the v1 rule captures argv[0] + leading flags + at
83
+ * most one subcommand token. Multi-level grammars (gh issue list,
84
+ * git remote add) collapse to 2-token prefixes; users who want finer
85
+ * patterns can hand-edit settings.json. For write_file, the directory
86
+ * the file lives in is allowlisted (`/**` suffix); writes deeper in
87
+ * the tree match. For git_commit, the literal `"*"` allowlists all
88
+ * future commits.
89
+ */
90
+ export declare function derivePattern(tool: AllowlistTool, input: Record<string, unknown>): string | null;
91
+ /**
92
+ * Check whether a tool invocation matches any entry in the
93
+ * allowlist. Matching semantics differ per tool:
94
+ * - shell_command: exact equality between derived pattern and
95
+ * stored pattern (`npm install` matches only `npm install`,
96
+ * not `npm install lodash` directly — but the derived pattern
97
+ * for `npm install lodash` IS `npm install`, so it matches.)
98
+ * - write_file: glob-match the incoming path against each
99
+ * `write_file` entry's stored glob.
100
+ * - git_commit: any `git_commit` entry matches (stored pattern
101
+ * is `"*"`).
102
+ *
103
+ * The cross-tool isolation invariant (PR3 ISC-spec): an entry with
104
+ * `tool === "shell_command"` can never authorize a `write_file` call
105
+ * even if patterns coincidentally match — the tool field is checked
106
+ * first.
107
+ */
108
+ export declare function matchAllowlist(tool: AllowlistTool, input: Record<string, unknown>, allowlist: AllowlistEntry[]): AllowlistEntry | null;
109
+ //# sourceMappingURL=settings.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"settings.d.ts","sourceRoot":"","sources":["../src/settings.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AAMH,MAAM,MAAM,aAAa,GAAG,eAAe,GAAG,YAAY,GAAG,YAAY,CAAC;AAQ1E,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,aAAa,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;CAClB;AASD;;;;;GAKG;AACH,wBAAgB,aAAa,IAAI,MAAM,CAKtC;AAMD;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,IAAI,cAAc,EAAE,CAoChD;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAC7B,KAAK,EAAE;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAC9C,IAAI,CAwCN;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,aAAa,CAC3B,IAAI,EAAE,aAAa,EACnB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC7B,MAAM,GAAG,IAAI,CA+Cf;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,cAAc,CAC5B,IAAI,EAAE,aAAa,EACnB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,SAAS,EAAE,cAAc,EAAE,GAC1B,cAAc,GAAG,IAAI,CAsBvB"}
@@ -0,0 +1,332 @@
1
+ "use strict";
2
+ /**
3
+ * Per-user CLI settings + approval allowlist.
4
+ *
5
+ * SPRINT-CLI-APPROVAL-MENU-1 (#6509) PR2.
6
+ *
7
+ * Backs the option-2 "Run and don't ask again" path of
8
+ * `promptApprovalMenu`. Allowlist is persisted to
9
+ * `~/.xiaotime/settings.json` (mode 0600) and consulted before every
10
+ * `executeTool` invocation of the three prompting client-side tools
11
+ * (`shell_command` / `write_file` / `git_commit`). A match short-
12
+ * circuits the menu entirely; the inline log line gets an
13
+ * ` (auto-approved)` suffix so the user can still see what ran.
14
+ *
15
+ * Schema is `version: 1`; missing/malformed/unknown-version files
16
+ * fall back to an empty allowlist (with a stderr warning for
17
+ * malformed/unknown cases) — never crash the CLI on corrupted local
18
+ * config (ISC-6509-007).
19
+ *
20
+ * Pattern derivation differs per tool (see D2 in the sprint spec):
21
+ * - shell_command: argv[0] + leading flags + first subcommand
22
+ * token. `npm install --no-save lodash` → "npm install".
23
+ * Multi-level subcommand grammars (gh issue list) collapse to
24
+ * the first 2 tokens; users can hand-edit settings.json for
25
+ * finer-grained patterns.
26
+ * - write_file: `dirname(path) + "/**"`. Glob-matched against
27
+ * subsequent write_file invocations so allowlisting a write to
28
+ * `/repo/foo.ts` covers `/repo/bar.ts`.
29
+ * - git_commit: literal `"*"` — allowlisting one commit
30
+ * allowlists all future commits (the commit message + staged
31
+ * files vary by definition; there's no meaningful pattern
32
+ * parameter to narrow on).
33
+ *
34
+ * Distinct from `terminal_orchestrator/task_manager.py:57`'s
35
+ * server-side allowlist (EXACT-NAME on argv[0] basename, binary `gh`
36
+ * only). That's a security perimeter for `task_create`'s long-running
37
+ * managed-task envelope; this is a UX gate for client-side tool
38
+ * approvals. Two allowlists, two audiences, two update cadences.
39
+ */
40
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
41
+ if (k2 === undefined) k2 = k;
42
+ var desc = Object.getOwnPropertyDescriptor(m, k);
43
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
44
+ desc = { enumerable: true, get: function() { return m[k]; } };
45
+ }
46
+ Object.defineProperty(o, k2, desc);
47
+ }) : (function(o, m, k, k2) {
48
+ if (k2 === undefined) k2 = k;
49
+ o[k2] = m[k];
50
+ }));
51
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
52
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
53
+ }) : function(o, v) {
54
+ o["default"] = v;
55
+ });
56
+ var __importStar = (this && this.__importStar) || (function () {
57
+ var ownKeys = function(o) {
58
+ ownKeys = Object.getOwnPropertyNames || function (o) {
59
+ var ar = [];
60
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
61
+ return ar;
62
+ };
63
+ return ownKeys(o);
64
+ };
65
+ return function (mod) {
66
+ if (mod && mod.__esModule) return mod;
67
+ var result = {};
68
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
69
+ __setModuleDefault(result, mod);
70
+ return result;
71
+ };
72
+ })();
73
+ Object.defineProperty(exports, "__esModule", { value: true });
74
+ exports._settingsPath = _settingsPath;
75
+ exports.loadAllowlist = loadAllowlist;
76
+ exports.appendAllowlist = appendAllowlist;
77
+ exports.derivePattern = derivePattern;
78
+ exports.matchAllowlist = matchAllowlist;
79
+ const fs = __importStar(require("fs"));
80
+ const path = __importStar(require("path"));
81
+ const os = __importStar(require("os"));
82
+ const ALLOWLIST_TOOLS = new Set([
83
+ "shell_command",
84
+ "write_file",
85
+ "git_commit",
86
+ ]);
87
+ /**
88
+ * Settings-file location. Resolved lazily per call so the
89
+ * `XIAOTIME_SETTINGS_PATH` env-var override can redirect (used by
90
+ * tests to avoid touching the real `~/.xiaotime/settings.json`).
91
+ * Default: `~/.xiaotime/settings.json`.
92
+ */
93
+ function _settingsPath() {
94
+ return (process.env.XIAOTIME_SETTINGS_PATH
95
+ ?? path.join(os.homedir(), ".xiaotime", "settings.json"));
96
+ }
97
+ function _settingsDir() {
98
+ return path.dirname(_settingsPath());
99
+ }
100
+ /**
101
+ * Read the allowlist from disk. Returns [] for any of:
102
+ * - file missing (first run; not an error)
103
+ * - file unreadable (permissions; logged + treated as empty)
104
+ * - malformed JSON (logged + treated as empty)
105
+ * - unknown schema version (logged + treated as empty)
106
+ * - shape doesn't match SettingsV1 (silently treated as empty)
107
+ *
108
+ * Never throws. The CLI must remain usable when the user has a
109
+ * corrupted local config (ISC-6509-007).
110
+ */
111
+ function loadAllowlist() {
112
+ if (!fs.existsSync(_settingsPath()))
113
+ return [];
114
+ let raw;
115
+ try {
116
+ raw = fs.readFileSync(_settingsPath(), "utf-8");
117
+ }
118
+ catch (err) {
119
+ process.stderr.write(`[xiaotime] ~/.xiaotime/settings.json unreadable: ${err.message}\n`);
120
+ return [];
121
+ }
122
+ let parsed;
123
+ try {
124
+ parsed = JSON.parse(raw);
125
+ }
126
+ catch (err) {
127
+ process.stderr.write(`[xiaotime] ~/.xiaotime/settings.json malformed JSON, treating as empty: ${err.message}\n`);
128
+ return [];
129
+ }
130
+ const obj = parsed;
131
+ if (!obj || typeof obj !== "object")
132
+ return [];
133
+ if (obj.version !== 1) {
134
+ process.stderr.write(`[xiaotime] ~/.xiaotime/settings.json: unrecognized schema version ${JSON.stringify(obj.version)}, treating as empty\n`);
135
+ return [];
136
+ }
137
+ const entries = obj.permissions?.allow;
138
+ if (!Array.isArray(entries))
139
+ return [];
140
+ return entries.filter(_isValidEntry);
141
+ }
142
+ /**
143
+ * Append a new entry to the allowlist + persist. Creates
144
+ * ~/.xiaotime/ with mode 0700 if it doesn't exist; writes
145
+ * settings.json with mode 0600. Idempotent on duplicate
146
+ * (tool, pattern) — duplicate appends are silently dropped.
147
+ */
148
+ function appendAllowlist(entry) {
149
+ const existing = loadAllowlist();
150
+ const isDup = existing.some((e) => e.tool === entry.tool && e.pattern === entry.pattern);
151
+ if (isDup)
152
+ return;
153
+ const newEntries = [
154
+ ...existing,
155
+ {
156
+ tool: entry.tool,
157
+ pattern: entry.pattern,
158
+ added_at: new Date().toISOString(),
159
+ },
160
+ ];
161
+ const settings = {
162
+ version: 1,
163
+ permissions: { allow: newEntries },
164
+ };
165
+ // Directory: mode 0700 (user-only). The settings file's mode is the
166
+ // load-bearing one but the parent permission tightens the surface
167
+ // against a hostile-co-tenant scenario on shared boxes.
168
+ fs.mkdirSync(_settingsDir(), { recursive: true, mode: 0o700 });
169
+ // openSync + writeSync + closeSync (with O_TRUNC + O_CREAT + 0o600
170
+ // mode) so the file is created with 0600 from the first byte. Using
171
+ // writeFileSync's `mode` option only sets mode on CREATE — but on
172
+ // overwrite of an existing file Node skips chmod entirely on some
173
+ // platforms. Explicit fchmod closes that gap.
174
+ const fd = fs.openSync(_settingsPath(), fs.constants.O_WRONLY | fs.constants.O_CREAT | fs.constants.O_TRUNC, 0o600);
175
+ try {
176
+ fs.writeSync(fd, JSON.stringify(settings, null, 2) + "\n");
177
+ fs.fchmodSync(fd, 0o600);
178
+ }
179
+ finally {
180
+ fs.closeSync(fd);
181
+ }
182
+ }
183
+ /**
184
+ * Derive the allowlist pattern for a tool invocation. Returns null
185
+ * if the input shape is unusable (empty command, missing path). The
186
+ * pattern is used both at write time (option-2 selection persists
187
+ * `derivePattern(tool, input)`) and at read time (the next call's
188
+ * derived pattern feeds `matchAllowlist`).
189
+ *
190
+ * Per D2 in the sprint spec, derivation is coarse on purpose. For
191
+ * shell_command, the v1 rule captures argv[0] + leading flags + at
192
+ * most one subcommand token. Multi-level grammars (gh issue list,
193
+ * git remote add) collapse to 2-token prefixes; users who want finer
194
+ * patterns can hand-edit settings.json. For write_file, the directory
195
+ * the file lives in is allowlisted (`/**` suffix); writes deeper in
196
+ * the tree match. For git_commit, the literal `"*"` allowlists all
197
+ * future commits.
198
+ */
199
+ function derivePattern(tool, input) {
200
+ if (tool === "shell_command") {
201
+ const cmd = String(input.command ?? "").trim();
202
+ if (!cmd)
203
+ return null;
204
+ const tokens = cmd.split(/\s+/);
205
+ // #6538 CR-r1: guard flag-only input. `"-la -h"` would
206
+ // otherwise produce pattern `"-la -h"`, which is nonsensical
207
+ // (no argv[0]) and would persist as a broken allowlist entry.
208
+ if (tokens[0].startsWith("-"))
209
+ return null;
210
+ const result = [tokens[0]];
211
+ for (let i = 1; i < tokens.length; i++) {
212
+ const tok = tokens[i];
213
+ if (tok.startsWith("-")) {
214
+ result.push(tok);
215
+ continue;
216
+ }
217
+ // First non-flag arg. Treat as a subcommand iff it's at
218
+ // position 1 (immediately after argv[0]). Otherwise stop —
219
+ // it's a data argument (path / value / package name) we
220
+ // don't want to bake into the pattern.
221
+ if (i === 1)
222
+ result.push(tok);
223
+ break;
224
+ }
225
+ return result.join(" ");
226
+ }
227
+ if (tool === "write_file") {
228
+ const p = String(input.path ?? "");
229
+ if (!p)
230
+ return null;
231
+ // #6538 CR-r1: normalize BEFORE deriving the pattern. Without
232
+ // this, a malicious or careless `input.path` like
233
+ // `"../../etc/passwd"` would persist as allowlist pattern
234
+ // `"../../etc/**"`, opening every future write to that
235
+ // resolved location to bypass the prompt. `path.resolve`
236
+ // anchors against the process cwd; tools.ts calls
237
+ // `resolvePath` (which uses the session cwd) before the file
238
+ // write itself, but derivePattern was operating on raw input
239
+ // — closing the gap here.
240
+ const normalized = path.resolve(p);
241
+ return path.dirname(normalized) + path.sep + "**";
242
+ }
243
+ if (tool === "git_commit") {
244
+ return "*";
245
+ }
246
+ return null;
247
+ }
248
+ /**
249
+ * Check whether a tool invocation matches any entry in the
250
+ * allowlist. Matching semantics differ per tool:
251
+ * - shell_command: exact equality between derived pattern and
252
+ * stored pattern (`npm install` matches only `npm install`,
253
+ * not `npm install lodash` directly — but the derived pattern
254
+ * for `npm install lodash` IS `npm install`, so it matches.)
255
+ * - write_file: glob-match the incoming path against each
256
+ * `write_file` entry's stored glob.
257
+ * - git_commit: any `git_commit` entry matches (stored pattern
258
+ * is `"*"`).
259
+ *
260
+ * The cross-tool isolation invariant (PR3 ISC-spec): an entry with
261
+ * `tool === "shell_command"` can never authorize a `write_file` call
262
+ * even if patterns coincidentally match — the tool field is checked
263
+ * first.
264
+ */
265
+ function matchAllowlist(tool, input, allowlist) {
266
+ for (const entry of allowlist) {
267
+ if (entry.tool !== tool)
268
+ continue;
269
+ if (tool === "git_commit")
270
+ return entry;
271
+ if (tool === "shell_command") {
272
+ const derived = derivePattern(tool, input);
273
+ if (derived !== null && derived === entry.pattern)
274
+ return entry;
275
+ continue;
276
+ }
277
+ if (tool === "write_file") {
278
+ const raw = String(input.path ?? "");
279
+ if (!raw)
280
+ continue;
281
+ // #6538 CR-r1: normalize the input path before matching for
282
+ // the same reason derivePattern does — so `../foo.txt`
283
+ // resolves to its absolute canonical form before being
284
+ // compared against stored absolute patterns.
285
+ const p = path.resolve(raw);
286
+ if (_globMatch(p, entry.pattern))
287
+ return entry;
288
+ continue;
289
+ }
290
+ }
291
+ return null;
292
+ }
293
+ // ── Internals ────────────────────────────────────────────────────
294
+ function _isValidEntry(e) {
295
+ if (!e || typeof e !== "object")
296
+ return false;
297
+ const entry = e;
298
+ return (typeof entry.tool === "string" &&
299
+ ALLOWLIST_TOOLS.has(entry.tool) &&
300
+ typeof entry.pattern === "string" &&
301
+ typeof entry.added_at === "string");
302
+ }
303
+ /**
304
+ * Tiny glob matcher. Supports `*` (matches everything) and
305
+ * `<prefix><sep>**` (matches `<prefix>` and any path under it),
306
+ * where `<sep>` is the platform path separator (`/` on Unix, `\`
307
+ * on Windows). Plain patterns require exact equality. Sufficient
308
+ * for write_file's directory-scope allowlist; not a general-
309
+ * purpose glob engine.
310
+ *
311
+ * #6538 CR-r1: was hardcoded to `/` — broken on Windows where
312
+ * `path.dirname` returns backslash-separated paths. Now uses
313
+ * `path.sep` so the comparison matches the form derivePattern
314
+ * stores (also using `path.sep`).
315
+ */
316
+ function _globMatch(p, pattern) {
317
+ if (pattern === "*")
318
+ return true;
319
+ const sepGlob = path.sep + "**";
320
+ if (pattern.endsWith(sepGlob)) {
321
+ const prefix = pattern.slice(0, -sepGlob.length);
322
+ return p === prefix || p.startsWith(prefix + path.sep);
323
+ }
324
+ // Tolerant of legacy entries that stored `/**` on Unix (no-op
325
+ // on Linux/macOS; harmless on Windows since path.sep is `\`).
326
+ if (pattern.endsWith("/**")) {
327
+ const prefix = pattern.slice(0, -3);
328
+ return p === prefix || p.startsWith(prefix + "/");
329
+ }
330
+ return p === pattern;
331
+ }
332
+ //# sourceMappingURL=settings.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"settings.js","sourceRoot":"","sources":["../src/settings.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,72 @@
1
+ /**
2
+ * REST POST client for tool_result delivery.
3
+ *
4
+ * SPRINT-TERMINAL-CLIENT-OWNED-DISPATCH-1 (#6565) PR3.
5
+ *
6
+ * Pre-#6565 the CLI sent tool_result back to terminal-orchestrator
7
+ * over the WebSocket — the server awaited each one inline with a
8
+ * 120s timeout. Any approval taking >2 min wedged the session
9
+ * (the wedge class observed live 2026-05-20 during #6509 PR1
10
+ * canary).
11
+ *
12
+ * Post-#6565 the CLI POSTs results to the new REST endpoint:
13
+ *
14
+ * POST <ORCHESTRATOR_URL>/terminal/sessions/{session_id}/tool_results
15
+ * Headers: x-api-key: <cli_token>
16
+ * Body: {"results": [{tool_use_id, content, is_error}, ...]}
17
+ *
18
+ * Server-side: atomic across the batch. Any unknown `tool_use_id`
19
+ * → 400 `unknown_tool_use_id` + no rows mutated. On success,
20
+ * ``mark_completed_batch.all_completed=True`` signals the WS
21
+ * handler (waiting on the per-session resume queue) to assemble
22
+ * tool_result blocks + resume the model stream.
23
+ *
24
+ * Retry policy: 3 attempts with 100ms→200ms→400ms exponential
25
+ * backoff for transient 5xx + network errors. 4xx errors are
26
+ * non-retriable (the server's rejection is durable — the
27
+ * tool_use_id IS unknown, retrying won't change that).
28
+ *
29
+ * Failure mode if all retries exhaust: the tool_result is lost
30
+ * from the server's perspective; the corresponding row stays as
31
+ * `status='pending'` in `terminal_pending_tool_calls` until the
32
+ * future expiry sweep (PR5). The user-visible symptom is a
33
+ * session that appears frozen (model never resumes). Tightening
34
+ * this is the WS-reconnect-replay work in PR4 — on the next WS
35
+ * connect, server re-emits pending tool_call events and the CLI
36
+ * can re-attempt the POST after re-rendering the menu.
37
+ */
38
+ export interface ToolResultEntry {
39
+ tool_use_id: string;
40
+ content: string;
41
+ is_error: boolean;
42
+ }
43
+ export interface PostToolResultsResponse {
44
+ completed: string[];
45
+ all_completed: boolean;
46
+ resume_signaled: boolean;
47
+ }
48
+ /**
49
+ * Thrown on durable (4xx) HTTP rejections that retrying won't fix.
50
+ * #6573 CR-r1: replaces the pre-r1 string-prefix matching on the
51
+ * Error message (`startsWith("POST tool_results rejected")`) which
52
+ * was fragile to message-format drift. The retry loop checks
53
+ * `instanceof DurableHttpError` and re-throws without retry; all
54
+ * other thrown errors are treated as transient + retried.
55
+ */
56
+ export declare class DurableHttpError extends Error {
57
+ readonly status: number;
58
+ readonly body: string;
59
+ constructor(status: number, body: string);
60
+ }
61
+ /**
62
+ * POST a batch of tool_results to the orchestrator. Retries on
63
+ * 5xx / network errors with exponential backoff; throws on
64
+ * 4xx (non-retriable) or after retries exhaust.
65
+ *
66
+ * Most callers pass a single-result batch (one tool finished, POST
67
+ * the result, move to next). Batch form exists so a future
68
+ * client-side allowlist-auto-execute path could fire multiple
69
+ * results in one POST.
70
+ */
71
+ export declare function postToolResults(sessionId: string, results: ToolResultEntry[]): Promise<PostToolResultsResponse>;
72
+ //# sourceMappingURL=tool_result_client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool_result_client.d.ts","sourceRoot":"","sources":["../src/tool_result_client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AAKH,MAAM,WAAW,eAAe;IAC9B,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,uBAAuB;IACtC,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,aAAa,EAAE,OAAO,CAAC;IACvB,eAAe,EAAE,OAAO,CAAC;CAC1B;AAED;;;;;;;GAOG;AACH,qBAAa,gBAAiB,SAAQ,KAAK;IACzC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;gBACV,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM;CAMzC;AAKD;;;;;;;;;GASG;AACH,wBAAsB,eAAe,CACnC,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,eAAe,EAAE,GACzB,OAAO,CAAC,uBAAuB,CAAC,CAmDlC"}