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.
- package/dist/__tests__/approval_menu.test.d.ts +15 -0
- package/dist/__tests__/approval_menu.test.d.ts.map +1 -0
- package/dist/__tests__/approval_menu.test.js +107 -0
- package/dist/__tests__/approval_menu.test.js.map +1 -0
- package/dist/__tests__/legacy_compat.test.d.ts +2 -0
- package/dist/__tests__/legacy_compat.test.d.ts.map +1 -0
- package/dist/__tests__/legacy_compat.test.js +36 -0
- package/dist/__tests__/legacy_compat.test.js.map +1 -0
- package/dist/__tests__/server_tool_render.test.d.ts +17 -0
- package/dist/__tests__/server_tool_render.test.d.ts.map +1 -0
- package/dist/__tests__/server_tool_render.test.js +74 -0
- package/dist/__tests__/server_tool_render.test.js.map +1 -0
- package/dist/__tests__/settings.test.d.ts +16 -0
- package/dist/__tests__/settings.test.d.ts.map +1 -0
- package/dist/__tests__/settings.test.js +289 -0
- package/dist/__tests__/settings.test.js.map +1 -0
- package/dist/__tests__/tool_result_client.test.d.ts +20 -0
- package/dist/__tests__/tool_result_client.test.d.ts.map +1 -0
- package/dist/__tests__/tool_result_client.test.js +142 -0
- package/dist/__tests__/tool_result_client.test.js.map +1 -0
- package/dist/__tests__/tools.test.d.ts +17 -0
- package/dist/__tests__/tools.test.d.ts.map +1 -0
- package/dist/__tests__/tools.test.js +381 -0
- package/dist/__tests__/tools.test.js.map +1 -0
- package/dist/config.d.ts +7 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +8 -1
- package/dist/config.js.map +1 -1
- package/dist/display.d.ts +19 -0
- package/dist/display.d.ts.map +1 -1
- package/dist/display.js +23 -0
- package/dist/display.js.map +1 -1
- package/dist/settings.d.ts +109 -0
- package/dist/settings.d.ts.map +1 -0
- package/dist/settings.js +332 -0
- package/dist/settings.js.map +1 -0
- package/dist/tool_result_client.d.ts +72 -0
- package/dist/tool_result_client.d.ts.map +1 -0
- package/dist/tool_result_client.js +123 -0
- package/dist/tool_result_client.js.map +1 -0
- package/dist/tools.d.ts +11 -3
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +96 -33
- package/dist/tools.js.map +1 -1
- package/dist/ui_state.d.ts +134 -0
- package/dist/ui_state.d.ts.map +1 -0
- package/dist/ui_state.js +460 -0
- package/dist/ui_state.js.map +1 -0
- package/dist/ws.d.ts +15 -0
- package/dist/ws.d.ts.map +1 -1
- package/dist/ws.js +180 -75
- package/dist/ws.js.map +1 -1
- 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
|
package/dist/display.d.ts.map
CHANGED
|
@@ -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
|
package/dist/display.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"display.js","sourceRoot":"","sources":["../src/display.ts"],"names":[],"mappings":";AAAA;;GAEG;;;;;AAKH,oCAQC;AAED,wCAEC;AAED,oCAGC;AAED,gCAEC;AAED,4CAoBC;AAED,kCAEC;
|
|
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"}
|
package/dist/settings.js
ADDED
|
@@ -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"}
|