jinzd-ai-cli 0.4.306 → 0.4.308
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/README.md +1 -0
- package/THIRD_PARTY_NOTICES.md +3 -0
- package/dist/{agent-client-6GX6QQDU.js → agent-client-TIEAYZXV.js} +4 -1
- package/dist/{auth-PE3Z5OHS.js → auth-QTQNZ6IX.js} +2 -2
- package/dist/{batch-JLGWQ7HU.js → batch-S7XLEEOH.js} +3 -3
- package/dist/{chat-index-2D5GK2DB.js → chat-index-7CLR4CBG.js} +4 -3
- package/dist/{chat-index-DGUOUUQW.js → chat-index-RLVLQRRC.js} +3 -2
- package/dist/{chunk-JDT6UG6R.js → chunk-2DYQDLLF.js} +1 -1
- package/dist/{chunk-T6WQEQI4.js → chunk-2MNUMLN4.js} +1 -1
- package/dist/chunk-2XMUCC42.js +67 -0
- package/dist/{chunk-7SSCSSC6.js → chunk-3537CQ5M.js} +2 -2
- package/dist/{chunk-OAXLMF3U.js → chunk-3YVNDYOY.js} +1 -1
- package/dist/{chunk-RYQBBEMM.js → chunk-4PR5AVJ5.js} +115 -19
- package/dist/{chunk-GHWZVPUV.js → chunk-6JHACXB3.js} +7 -5
- package/dist/{chunk-WZWRA7R2.js → chunk-7OHXFTZO.js} +2 -2
- package/dist/{chunk-IUVDZVZ4.js → chunk-AXTLYOA7.js} +24 -17
- package/dist/{chunk-XJOK5353.js → chunk-B3MPYI4E.js} +2 -2
- package/dist/{chunk-3RDJEYF7.js → chunk-BGZN4K7Q.js} +4 -4
- package/dist/chunk-BMLQPSHP.js +69 -0
- package/dist/{chunk-NRSYBKWA.js → chunk-C7CKDFJS.js} +1 -1
- package/dist/{chunk-JFNBJHR6.js → chunk-E2PNXNKI.js} +4 -4
- package/dist/chunk-FVXNEZEW.js +161 -0
- package/dist/{chunk-YKJO3SI4.js → chunk-G6DVGQ4I.js} +1 -1
- package/dist/{chunk-AYLK4EGA.js → chunk-HC33C4U3.js} +18 -18
- package/dist/chunk-JJFJWD35.js +162 -0
- package/dist/{chunk-W6PLV3EY.js → chunk-JJNUASMJ.js} +263 -55
- package/dist/{chunk-NYNSYLFY.js → chunk-JL44HGZC.js} +1 -1
- package/dist/{chunk-NUJTNH76.js → chunk-LBWKMTT3.js} +1 -1
- package/dist/{chunk-LAP7TPPK.js → chunk-LR3TGZWQ.js} +2 -2
- package/dist/{chunk-WQ3OMEC4.js → chunk-M6HLYG33.js} +1 -1
- package/dist/{chunk-I4GQRMFV.js → chunk-M6YV5KSS.js} +6 -4
- package/dist/{chunk-K5SDDTN7.js → chunk-NLK45GUS.js} +1 -1
- package/dist/{chunk-KBDKKHXB.js → chunk-O6CGOVD4.js} +2 -2
- package/dist/chunk-QMFMST2T.js +39 -0
- package/dist/{chunk-MI4SFZGX.js → chunk-RMEJR3AW.js} +5 -2
- package/dist/{chunk-UFW4S3RU.js → chunk-RVMCY6DP.js} +1 -1
- package/dist/{chunk-LF67HWWR.js → chunk-RWRZVQCB.js} +1 -1
- package/dist/chunk-SXIJLSVF.js +41 -0
- package/dist/chunk-TDQW2YHK.js +65 -0
- package/dist/{chunk-VLDB2SVN.js → chunk-TPTFV3Y6.js} +383 -513
- package/dist/{chunk-45UVJXFQ.js → chunk-W2UOVGRC.js} +1 -1
- package/dist/{chunk-VXEF2SWG.js → chunk-WT252ZDV.js} +2 -2
- package/dist/{chunk-NLB7JZSO.js → chunk-WVEIUSXE.js} +246 -55
- package/dist/{chunk-OC2XHG5L.js → chunk-YBZM7PHI.js} +4 -4
- package/dist/{ci-6PHEL3C7.js → ci-5HONSUAS.js} +7 -7
- package/dist/{ci-format-AZYUP6BL.js → ci-format-URUH3ZZ5.js} +2 -2
- package/dist/{constants-CPNWIUGZ.js → constants-BLDBI7IG.js} +1 -1
- package/dist/{constants-AXBD2EEB.js → constants-FG27DQMS.js} +1 -1
- package/dist/doctor-cli-QQLMNJN7.js +18 -0
- package/dist/electron-server.js +1003 -829
- package/dist/file-checkpoint-64A2AXHE.js +7 -0
- package/dist/{file-checkpoint-NKBHGC7L.js → file-checkpoint-VVPUJNCI.js} +2 -1
- package/dist/{hub-DDYAWLF2.js → hub-GGFW3DCC.js} +3 -3
- package/dist/{hub-server-CBKCP5VD.js → hub-server-JLC7CNP7.js} +55 -17
- package/dist/index.js +42 -40
- package/dist/{indexer-P3FPRTPJ.js → indexer-GKIWSSRP.js} +3 -3
- package/dist/indexer-VRXDZ74A.js +10 -0
- package/dist/{persist-S77WRVLD.js → persist-ZZXRWFBX.js} +4 -4
- package/dist/{persistent-memory-XZISXRWT.js → persistent-memory-UNLCXUWH.js} +3 -3
- package/dist/{persistent-memory-6XYYOMSC.js → persistent-memory-YYWIN6BG.js} +3 -3
- package/dist/{pr-UY4ZCZNI.js → pr-YCFGPFI7.js} +7 -7
- package/dist/{project-trust-MUG325AW.js → project-trust-6CJB5ZMX.js} +1 -1
- package/dist/{project-trust-NKYHL3VZ.js → project-trust-KGG2BWKC.js} +1 -1
- package/dist/run-tests-GHY4BYDE.js +13 -0
- package/dist/run-tests-KRNMFXAN.js +12 -0
- package/dist/{semantic-7GM5M4MU.js → semantic-N227CLSC.js} +4 -4
- package/dist/{semantic-QOBAXEQ4.js → semantic-TRQAZSRY.js} +4 -4
- package/dist/{server-6IC33CJK.js → server-73UBHSFV.js} +16 -14
- package/dist/{server-DBHLVQVP.js → server-QRAWSG4Z.js} +231 -103
- package/dist/{store-SIKHCUYJ.js → store-2N6PPPBP.js} +2 -2
- package/dist/{store-CMSSWZF7.js → store-76VVFDXM.js} +2 -2
- package/dist/{task-orchestrator-RDDETDW2.js → task-orchestrator-GI3RGLZI.js} +15 -14
- package/dist/{usage-D473ZVIP.js → usage-GWTIGZF5.js} +4 -4
- package/dist/{vector-store-JAXWDZYW.js → vector-store-T2DVUMY4.js} +2 -2
- package/dist/{vector-store-PWIOQFTP.js → vector-store-V6UGEYWK.js} +2 -2
- package/dist/web/client/app.js +7 -0
- package/dist/web/client/app.js.br +0 -0
- package/dist/web/client/app.js.gz +0 -0
- package/dist/web/client/util.js +14 -23
- package/dist/web/client/util.js.br +0 -0
- package/dist/web/client/util.js.gz +0 -0
- package/dist/web/client/vendor/DOMPurify-LICENSE.txt +202 -0
- package/dist/web/client/vendor/purify.es.mjs +2722 -0
- package/dist/web/client/vendor/purify.es.mjs.br +0 -0
- package/dist/web/client/vendor/purify.es.mjs.gz +0 -0
- package/package.json +4 -2
- package/dist/chunk-4BKXL7SM.js +0 -98
- package/dist/chunk-6BUTA5VW.js +0 -38
- package/dist/chunk-IW3Q7AE5.js +0 -40
- package/dist/chunk-SKET65WZ.js +0 -96
- package/dist/doctor-cli-DDHHDMMJ.js +0 -18
- package/dist/file-checkpoint-CGH6OJVI.js +0 -6
- package/dist/indexer-FPKY7LCT.js +0 -10
- package/dist/run-tests-BAMLVLO6.js +0 -10
- package/dist/run-tests-UEE3OMLS.js +0 -9
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import {
|
|
3
3
|
loadChatIndex,
|
|
4
4
|
searchChatMemory
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-6JHACXB3.js";
|
|
6
6
|
import {
|
|
7
7
|
addMemoryEntry,
|
|
8
8
|
findSimilarMemory,
|
|
@@ -11,17 +11,17 @@ import {
|
|
|
11
11
|
searchPersistentMemories,
|
|
12
12
|
touchMemoryReferences,
|
|
13
13
|
updateMemoryEntry
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-LR3TGZWQ.js";
|
|
15
15
|
import {
|
|
16
16
|
indexProject
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-RWRZVQCB.js";
|
|
18
18
|
import {
|
|
19
19
|
hasSemanticIndex,
|
|
20
20
|
semanticSearch
|
|
21
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-B3MPYI4E.js";
|
|
22
22
|
import {
|
|
23
23
|
loadIndex
|
|
24
|
-
} from "./chunk-
|
|
24
|
+
} from "./chunk-M6HLYG33.js";
|
|
25
25
|
import {
|
|
26
26
|
theme
|
|
27
27
|
} from "./chunk-5C76XDDF.js";
|
|
@@ -30,13 +30,13 @@ import {
|
|
|
30
30
|
runHook,
|
|
31
31
|
runLifecycleHooks,
|
|
32
32
|
runTool
|
|
33
|
-
} from "./chunk-
|
|
33
|
+
} from "./chunk-3537CQ5M.js";
|
|
34
34
|
import {
|
|
35
35
|
buildTruncatedArgumentsError,
|
|
36
36
|
getDangerLevel,
|
|
37
37
|
isFileWriteTool,
|
|
38
38
|
runLeanAgentLoop
|
|
39
|
-
} from "./chunk-
|
|
39
|
+
} from "./chunk-RMEJR3AW.js";
|
|
40
40
|
import {
|
|
41
41
|
EnvLoader,
|
|
42
42
|
NetworkError,
|
|
@@ -44,13 +44,26 @@ import {
|
|
|
44
44
|
} from "./chunk-TU3L3PW7.js";
|
|
45
45
|
import {
|
|
46
46
|
atomicWriteFileSync
|
|
47
|
-
} from "./chunk-
|
|
47
|
+
} from "./chunk-BMLQPSHP.js";
|
|
48
48
|
import {
|
|
49
|
-
|
|
50
|
-
|
|
49
|
+
MAX_SNAPSHOT_TOTAL_BYTES,
|
|
50
|
+
assertReadablePath,
|
|
51
|
+
canonicalPath,
|
|
52
|
+
fileCheckpoints,
|
|
53
|
+
isCredentialVault,
|
|
54
|
+
readSnapshot
|
|
55
|
+
} from "./chunk-JJFJWD35.js";
|
|
51
56
|
import {
|
|
52
|
-
|
|
53
|
-
|
|
57
|
+
IS_WINDOWS,
|
|
58
|
+
WIN_UTF8_PREAMBLE,
|
|
59
|
+
currentAbortSignal,
|
|
60
|
+
decodeWindowsBuffer,
|
|
61
|
+
killProcessTree,
|
|
62
|
+
looksLikeGbkMojibake,
|
|
63
|
+
recoverGbkMojibake,
|
|
64
|
+
runTestsTool,
|
|
65
|
+
shellInvocation
|
|
66
|
+
} from "./chunk-JJNUASMJ.js";
|
|
54
67
|
import {
|
|
55
68
|
CONFIG_DIR_NAME,
|
|
56
69
|
DEFAULT_MAX_TOOL_OUTPUT_CHARS_CAP,
|
|
@@ -59,170 +72,25 @@ import {
|
|
|
59
72
|
SUBAGENT_ALLOWED_TOOLS,
|
|
60
73
|
SUBAGENT_DEFAULT_MAX_ROUNDS,
|
|
61
74
|
SUBAGENT_MAX_ROUNDS_LIMIT
|
|
62
|
-
} from "./chunk-
|
|
75
|
+
} from "./chunk-2MNUMLN4.js";
|
|
76
|
+
import {
|
|
77
|
+
getCurrentSessionKey,
|
|
78
|
+
getExecutionConfigDir,
|
|
79
|
+
runWithSessionKey,
|
|
80
|
+
scopedState
|
|
81
|
+
} from "./chunk-SXIJLSVF.js";
|
|
63
82
|
|
|
64
83
|
// src/tools/shell-pool.ts
|
|
65
|
-
import { spawn as spawn2 } from "child_process";
|
|
66
|
-
import { platform as platform2 } from "os";
|
|
67
|
-
|
|
68
|
-
// src/tools/win-shell.ts
|
|
69
|
-
import { platform } from "os";
|
|
70
84
|
import { spawn } from "child_process";
|
|
71
|
-
|
|
72
|
-
var
|
|
73
|
-
function shellInvocation(command) {
|
|
74
|
-
if (IS_WINDOWS) {
|
|
75
|
-
return {
|
|
76
|
-
shell: "powershell.exe",
|
|
77
|
-
args: ["-NoProfile", "-NonInteractive", "-Command", WIN_UTF8_PREAMBLE + command]
|
|
78
|
-
};
|
|
79
|
-
}
|
|
80
|
-
return { shell: process.env["SHELL"] ?? "/bin/bash", args: ["-c", command] };
|
|
81
|
-
}
|
|
82
|
-
function decodeWindowsBuffer(buf) {
|
|
83
|
-
const utf8 = buf.toString("utf-8");
|
|
84
|
-
if (!utf8.includes("\uFFFD")) return utf8;
|
|
85
|
-
try {
|
|
86
|
-
return new TextDecoder("gbk").decode(buf);
|
|
87
|
-
} catch {
|
|
88
|
-
return utf8;
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
var GBK_MOJIBAKE_FINGERPRINTS = [
|
|
92
|
-
"\uFFFD",
|
|
93
|
-
"\u9215",
|
|
94
|
-
"\u951B",
|
|
95
|
-
"\u9983",
|
|
96
|
-
"\u9241",
|
|
97
|
-
"\u6D93",
|
|
98
|
-
"\u935B",
|
|
99
|
-
"\u9428",
|
|
100
|
-
"\u7EFE",
|
|
101
|
-
"\u95AB",
|
|
102
|
-
"\u9593",
|
|
103
|
-
"\u95BF",
|
|
104
|
-
"\u68E3",
|
|
105
|
-
"\u95B4",
|
|
106
|
-
"\u5A11",
|
|
107
|
-
"\u95B8",
|
|
108
|
-
"\u95BB",
|
|
109
|
-
"\u7F01",
|
|
110
|
-
"\u95C1"
|
|
111
|
-
];
|
|
112
|
-
var gbkReverse = null;
|
|
113
|
-
function gbkReverseMap() {
|
|
114
|
-
if (gbkReverse) return gbkReverse;
|
|
115
|
-
const map = /* @__PURE__ */ new Map();
|
|
116
|
-
try {
|
|
117
|
-
const dec = new TextDecoder("gbk");
|
|
118
|
-
for (let hi = 129; hi <= 254; hi++) {
|
|
119
|
-
for (let lo = 64; lo <= 254; lo++) {
|
|
120
|
-
if (lo === 127) continue;
|
|
121
|
-
const ch = dec.decode(new Uint8Array([hi, lo]));
|
|
122
|
-
if (ch.length === 1 && ch !== "\uFFFD" && !map.has(ch)) map.set(ch, [hi, lo]);
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
} catch {
|
|
126
|
-
}
|
|
127
|
-
gbkReverse = map;
|
|
128
|
-
return map;
|
|
129
|
-
}
|
|
130
|
-
function decodeRunAsChinese(bytes) {
|
|
131
|
-
for (let start = 0; start <= 2 && start < bytes.length; start++) {
|
|
132
|
-
for (let end = 0; end <= 2 && bytes.length - end > start; end++) {
|
|
133
|
-
const slice = bytes.slice(start, bytes.length - end);
|
|
134
|
-
if (slice.length < 6) continue;
|
|
135
|
-
try {
|
|
136
|
-
const out = new TextDecoder("utf-8", { fatal: true }).decode(new Uint8Array(slice));
|
|
137
|
-
if (/[一-鿿]/.test(out)) return out;
|
|
138
|
-
} catch {
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
return null;
|
|
143
|
-
}
|
|
144
|
-
function recoverGbkMojibake(text) {
|
|
145
|
-
if (!text) return null;
|
|
146
|
-
const map = gbkReverseMap();
|
|
147
|
-
if (map.size === 0) return null;
|
|
148
|
-
let run = [];
|
|
149
|
-
let best = null;
|
|
150
|
-
const flush = () => {
|
|
151
|
-
if (run.length >= 4) {
|
|
152
|
-
const bytes = [];
|
|
153
|
-
for (const p of run) bytes.push(p[0], p[1]);
|
|
154
|
-
const out = decodeRunAsChinese(bytes);
|
|
155
|
-
if (out && (!best || out.length > best.length)) best = out;
|
|
156
|
-
}
|
|
157
|
-
run = [];
|
|
158
|
-
};
|
|
159
|
-
for (const ch of text) {
|
|
160
|
-
const pair = ch.codePointAt(0) < 128 ? void 0 : map.get(ch);
|
|
161
|
-
if (pair) run.push(pair);
|
|
162
|
-
else flush();
|
|
163
|
-
}
|
|
164
|
-
flush();
|
|
165
|
-
return best;
|
|
166
|
-
}
|
|
167
|
-
function looksLikeGbkMojibake(text) {
|
|
168
|
-
if (recoverGbkMojibake(text) !== null) return true;
|
|
169
|
-
let hits = 0;
|
|
170
|
-
for (const ch of GBK_MOJIBAKE_FINGERPRINTS) {
|
|
171
|
-
if (text.includes(ch) && ++hits >= 2) return true;
|
|
172
|
-
}
|
|
173
|
-
return false;
|
|
174
|
-
}
|
|
175
|
-
function killProcessTree(proc) {
|
|
176
|
-
if (proc.killed || proc.exitCode !== null) return;
|
|
177
|
-
if (IS_WINDOWS && proc.pid) {
|
|
178
|
-
try {
|
|
179
|
-
const tk = spawn("taskkill", ["/PID", String(proc.pid), "/T", "/F"], {
|
|
180
|
-
windowsHide: true,
|
|
181
|
-
stdio: "ignore"
|
|
182
|
-
});
|
|
183
|
-
tk.on("exit", () => {
|
|
184
|
-
try {
|
|
185
|
-
proc.kill();
|
|
186
|
-
} catch {
|
|
187
|
-
}
|
|
188
|
-
});
|
|
189
|
-
tk.on("error", () => {
|
|
190
|
-
try {
|
|
191
|
-
proc.kill();
|
|
192
|
-
} catch {
|
|
193
|
-
}
|
|
194
|
-
});
|
|
195
|
-
} catch {
|
|
196
|
-
try {
|
|
197
|
-
proc.kill();
|
|
198
|
-
} catch {
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
return;
|
|
202
|
-
}
|
|
203
|
-
try {
|
|
204
|
-
proc.kill("SIGTERM");
|
|
205
|
-
} catch {
|
|
206
|
-
}
|
|
207
|
-
setTimeout(() => {
|
|
208
|
-
if (!proc.killed && proc.exitCode === null) {
|
|
209
|
-
try {
|
|
210
|
-
proc.kill("SIGKILL");
|
|
211
|
-
} catch {
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
}, 500).unref();
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
// src/tools/shell-pool.ts
|
|
218
|
-
var IS_WINDOWS2 = platform2() === "win32";
|
|
85
|
+
import { platform } from "os";
|
|
86
|
+
var IS_WINDOWS2 = platform() === "win32";
|
|
219
87
|
var SHELL = IS_WINDOWS2 ? "powershell.exe" : process.env["SHELL"] ?? "/bin/bash";
|
|
220
88
|
var pool = /* @__PURE__ */ new Map();
|
|
221
89
|
var IDLE_EVICT_MS = 5 * 6e4;
|
|
222
90
|
var evictionTimer = null;
|
|
223
91
|
function spawnShell(cwd) {
|
|
224
92
|
const shellArgs = IS_WINDOWS2 ? ["-NoProfile", "-NonInteractive", "-Command", "-"] : ["-s"];
|
|
225
|
-
const proc =
|
|
93
|
+
const proc = spawn(SHELL, shellArgs, {
|
|
226
94
|
cwd,
|
|
227
95
|
windowsHide: true,
|
|
228
96
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -433,16 +301,22 @@ function isToolCapableProvider(provider) {
|
|
|
433
301
|
}
|
|
434
302
|
|
|
435
303
|
// src/tools/builtin/bash.ts
|
|
436
|
-
import { spawn as
|
|
304
|
+
import { spawn as spawn2 } from "child_process";
|
|
437
305
|
import { existsSync as existsSync2, readdirSync, statSync } from "fs";
|
|
438
|
-
import { platform as
|
|
306
|
+
import { platform as platform2 } from "os";
|
|
439
307
|
import { resolve } from "path";
|
|
440
308
|
|
|
441
309
|
// src/tools/undo-stack.ts
|
|
442
|
-
import {
|
|
310
|
+
import { writeFileSync, unlinkSync, rmdirSync, existsSync } from "fs";
|
|
443
311
|
var MAX_UNDO_DEPTH = 20;
|
|
444
312
|
var UndoStack = class {
|
|
445
|
-
|
|
313
|
+
stateKey = /* @__PURE__ */ Symbol("stack");
|
|
314
|
+
get stack() {
|
|
315
|
+
return scopedState(this.stateKey, () => ({ items: [] })).items;
|
|
316
|
+
}
|
|
317
|
+
set stack(items) {
|
|
318
|
+
scopedState(this.stateKey, () => ({ items: [] })).items = items;
|
|
319
|
+
}
|
|
446
320
|
/**
|
|
447
321
|
* 在执行文件写入操作之前调用,保存当前状态。
|
|
448
322
|
* @param filePath 将要被写入的文件路径
|
|
@@ -451,11 +325,7 @@ var UndoStack = class {
|
|
|
451
325
|
push(filePath, description) {
|
|
452
326
|
let previousContent = null;
|
|
453
327
|
if (existsSync(filePath)) {
|
|
454
|
-
|
|
455
|
-
previousContent = readFileSync(filePath, "utf-8");
|
|
456
|
-
} catch {
|
|
457
|
-
return;
|
|
458
|
-
}
|
|
328
|
+
previousContent = readSnapshot(filePath);
|
|
459
329
|
}
|
|
460
330
|
this.pushEntry({
|
|
461
331
|
filePath,
|
|
@@ -492,7 +362,7 @@ var UndoStack = class {
|
|
|
492
362
|
/** 内部统一入栈方法,含溢出裁剪 */
|
|
493
363
|
pushEntry(entry) {
|
|
494
364
|
this.stack.push(entry);
|
|
495
|
-
|
|
365
|
+
while (this.stack.length > MAX_UNDO_DEPTH || this.stack.reduce((n, e) => n + Buffer.byteLength(e.previousContent ?? ""), 0) > MAX_SNAPSHOT_TOTAL_BYTES) {
|
|
496
366
|
this.stack.shift();
|
|
497
367
|
}
|
|
498
368
|
}
|
|
@@ -553,42 +423,8 @@ var UndoStack = class {
|
|
|
553
423
|
};
|
|
554
424
|
var undoStack = new UndoStack();
|
|
555
425
|
|
|
556
|
-
// src/core/interrupt.ts
|
|
557
|
-
var controller = new AbortController();
|
|
558
|
-
var interrupted = false;
|
|
559
|
-
function currentAbortSignal() {
|
|
560
|
-
return controller.signal;
|
|
561
|
-
}
|
|
562
|
-
function isInterrupted() {
|
|
563
|
-
return interrupted;
|
|
564
|
-
}
|
|
565
|
-
function requestInterrupt() {
|
|
566
|
-
interrupted = true;
|
|
567
|
-
if (!controller.signal.aborted) {
|
|
568
|
-
controller.abort(new DOMException("User interrupted (Ctrl+C)", "AbortError"));
|
|
569
|
-
}
|
|
570
|
-
}
|
|
571
|
-
function resetInterrupt() {
|
|
572
|
-
controller = new AbortController();
|
|
573
|
-
interrupted = false;
|
|
574
|
-
}
|
|
575
|
-
|
|
576
|
-
// src/tools/session-context.ts
|
|
577
|
-
import { AsyncLocalStorage } from "async_hooks";
|
|
578
|
-
var als = new AsyncLocalStorage();
|
|
579
|
-
var DEFAULT_SESSION_KEY = "__default__";
|
|
580
|
-
function getCurrentSessionKey() {
|
|
581
|
-
const ctx = als.getStore();
|
|
582
|
-
if (!ctx || !ctx.sessionKey) return DEFAULT_SESSION_KEY;
|
|
583
|
-
return ctx.sessionKey;
|
|
584
|
-
}
|
|
585
|
-
function runWithSessionKey(sessionKey, fn) {
|
|
586
|
-
const key = sessionKey && sessionKey.length > 0 ? sessionKey : DEFAULT_SESSION_KEY;
|
|
587
|
-
return als.run({ sessionKey: key }, fn);
|
|
588
|
-
}
|
|
589
|
-
|
|
590
426
|
// src/tools/builtin/bash.ts
|
|
591
|
-
var IS_WINDOWS3 =
|
|
427
|
+
var IS_WINDOWS3 = platform2() === "win32";
|
|
592
428
|
var SHELL2 = IS_WINDOWS3 ? "powershell.exe" : process.env["SHELL"] ?? "/bin/bash";
|
|
593
429
|
var cwdBySession = /* @__PURE__ */ new Map();
|
|
594
430
|
function getCwd() {
|
|
@@ -618,7 +454,8 @@ Important rules:
|
|
|
618
454
|
4. To delete directories, use Remove-Item -Recurse (the system will automatically optimize to a more reliable method).
|
|
619
455
|
5. IMPORTANT: On Windows, "curl" is an alias for Invoke-WebRequest and does NOT support curl flags like -s, -X, -H. Use Invoke-RestMethod instead for HTTP requests. Example: Invoke-RestMethod -Uri "http://localhost:3000/api/health" -Method Get
|
|
620
456
|
6. NEVER run long-running / never-exiting commands through bash \u2014 they block until timeout. This includes starting an Android emulator (emulator -avd X), streaming logs (adb logcat), and dev servers (node server.js, npm run dev, metro / react-native start). Use the 'task_create' tool to run these in the background, then poll status with short bash calls (e.g. adb shell getprop sys.boot_completed).
|
|
621
|
-
7. CRITICAL \u2014 this is PowerShell, NOT bash: \\ is NOT an escape character. Writing \\" inside a double-quoted string CLOSES the string and silently splits your command into fragments \u2014 it often still exits 0 with no output, so it looks like it worked when it did not. To escape a quote use \`" or "". For anything with nested quoting (especially ssh + psql -c "SELECT ..."), do NOT try to escape your way out: write the payload to a file with the 'write_file' tool, 'scp' it to the host, and run it with a single level of quoting (e.g. ssh host "psql -d db -f /tmp/x.sql")
|
|
457
|
+
7. CRITICAL \u2014 this is PowerShell, NOT bash: \\ is NOT an escape character. Writing \\" inside a double-quoted string CLOSES the string and silently splits your command into fragments \u2014 it often still exits 0 with no output, so it looks like it worked when it did not. To escape a quote use \`" or "". For anything with nested quoting (especially ssh + psql -c "SELECT ..."), do NOT try to escape your way out: write the payload to a file with the 'write_file' tool, 'scp' it to the host, and run it with a single level of quoting (e.g. ssh host "psql -d db -f /tmp/x.sql").
|
|
458
|
+
8. CRITICAL \u2014 running a program by its full path: a quoted string at the START of a statement is parsed as a string EXPRESSION, not a command. "C:\\path\\python.exe" -c "print(1)" is a SYNTAX ERROR. Put the call operator '&' in front: & "C:\\path\\python.exe" -c "print(1)" \u2014 or $py = "C:\\path\\python.exe"; & $py -c "print(1)". This applies to any quoted path used as a command, anywhere a statement starts (including after ';').` : `Execute commands in ${SHELL2}.
|
|
622
459
|
Important rules:
|
|
623
460
|
1. Each bash call runs in an independent subprocess; cd commands do not persist. To run in a specific directory, use the cwd parameter, or combine commands: e.g. "cd mydir && ls" or "mkdir -p mydir && touch mydir/file.txt".
|
|
624
461
|
2. If a command fails (returns an error or non-zero exit code), stop immediately, report the error to the user, and do not retry the same or similar commands.
|
|
@@ -873,6 +710,116 @@ function detectPowerShellBackslashQuote(command) {
|
|
|
873
710
|
}
|
|
874
711
|
return false;
|
|
875
712
|
}
|
|
713
|
+
var PS_OPERATOR_WORDS = /* @__PURE__ */ new Set([
|
|
714
|
+
"eq",
|
|
715
|
+
"ne",
|
|
716
|
+
"gt",
|
|
717
|
+
"ge",
|
|
718
|
+
"lt",
|
|
719
|
+
"le",
|
|
720
|
+
"ceq",
|
|
721
|
+
"cne",
|
|
722
|
+
"cgt",
|
|
723
|
+
"cge",
|
|
724
|
+
"clt",
|
|
725
|
+
"cle",
|
|
726
|
+
"ieq",
|
|
727
|
+
"ine",
|
|
728
|
+
"igt",
|
|
729
|
+
"ige",
|
|
730
|
+
"ilt",
|
|
731
|
+
"ile",
|
|
732
|
+
"like",
|
|
733
|
+
"notlike",
|
|
734
|
+
"match",
|
|
735
|
+
"notmatch",
|
|
736
|
+
"clike",
|
|
737
|
+
"cnotlike",
|
|
738
|
+
"cmatch",
|
|
739
|
+
"cnotmatch",
|
|
740
|
+
"ilike",
|
|
741
|
+
"inotlike",
|
|
742
|
+
"imatch",
|
|
743
|
+
"inotmatch",
|
|
744
|
+
"contains",
|
|
745
|
+
"notcontains",
|
|
746
|
+
"ccontains",
|
|
747
|
+
"cnotcontains",
|
|
748
|
+
"icontains",
|
|
749
|
+
"inotcontains",
|
|
750
|
+
"in",
|
|
751
|
+
"notin",
|
|
752
|
+
"cin",
|
|
753
|
+
"cnotin",
|
|
754
|
+
"iin",
|
|
755
|
+
"inotin",
|
|
756
|
+
"is",
|
|
757
|
+
"isnot",
|
|
758
|
+
"as",
|
|
759
|
+
"and",
|
|
760
|
+
"or",
|
|
761
|
+
"not",
|
|
762
|
+
"xor",
|
|
763
|
+
"band",
|
|
764
|
+
"bor",
|
|
765
|
+
"bnot",
|
|
766
|
+
"bxor",
|
|
767
|
+
"shl",
|
|
768
|
+
"shr",
|
|
769
|
+
"join",
|
|
770
|
+
"split",
|
|
771
|
+
"replace",
|
|
772
|
+
"csplit",
|
|
773
|
+
"creplace",
|
|
774
|
+
"isplit",
|
|
775
|
+
"ireplace",
|
|
776
|
+
"f"
|
|
777
|
+
]);
|
|
778
|
+
function detectMissingCallOperator(command) {
|
|
779
|
+
if (!IS_WINDOWS3) return false;
|
|
780
|
+
let atStatementStart = true;
|
|
781
|
+
let afterCallOperator = false;
|
|
782
|
+
for (let i = 0; i < command.length; i++) {
|
|
783
|
+
const ch = command[i];
|
|
784
|
+
if (ch === " " || ch === " ") continue;
|
|
785
|
+
if (ch === ";" || ch === "\n" || ch === "{" || ch === "(" || ch === "|" || ch === "&") {
|
|
786
|
+
if (ch === "&") {
|
|
787
|
+
const next = command.slice(i + 1).replace(/^[ \t]+/, "");
|
|
788
|
+
afterCallOperator = !next.startsWith("&");
|
|
789
|
+
} else {
|
|
790
|
+
afterCallOperator = false;
|
|
791
|
+
}
|
|
792
|
+
atStatementStart = true;
|
|
793
|
+
continue;
|
|
794
|
+
}
|
|
795
|
+
if (ch === "." && atStatementStart) {
|
|
796
|
+
afterCallOperator = true;
|
|
797
|
+
continue;
|
|
798
|
+
}
|
|
799
|
+
if ((ch === '"' || ch === "'") && atStatementStart && !afterCallOperator) {
|
|
800
|
+
const quote = ch;
|
|
801
|
+
let j = i + 1;
|
|
802
|
+
while (j < command.length && command[j] !== quote) j++;
|
|
803
|
+
if (j >= command.length) return false;
|
|
804
|
+
const rest = command.slice(j + 1);
|
|
805
|
+
const m = /^[ \t]+(\S+)/.exec(rest);
|
|
806
|
+
if (m) {
|
|
807
|
+
const token = m[1];
|
|
808
|
+
if (/^[-]/.test(token)) {
|
|
809
|
+
const word = token.slice(1).toLowerCase();
|
|
810
|
+
if (PS_OPERATOR_WORDS.has(word)) return false;
|
|
811
|
+
return true;
|
|
812
|
+
}
|
|
813
|
+
if (/^[+*%=<>|;)}\]]/.test(token)) return false;
|
|
814
|
+
return true;
|
|
815
|
+
}
|
|
816
|
+
return false;
|
|
817
|
+
}
|
|
818
|
+
atStatementStart = false;
|
|
819
|
+
afterCallOperator = false;
|
|
820
|
+
}
|
|
821
|
+
return false;
|
|
822
|
+
}
|
|
876
823
|
var BACKSLASH_QUOTE_SILENT_WARNING = `
|
|
877
824
|
|
|
878
825
|
\u26A0 WARNING: this command contains \\" inside a double-quoted string, but PowerShell does NOT treat \\ as an escape \u2014 that quote CLOSED the string and the command was split into fragments. It exited 0 with no output because the outer program (e.g. ssh) still ran, but it very likely did NOT run what you intended. Do NOT assume this succeeded.
|
|
@@ -953,6 +900,11 @@ function buildErrorHint(command, stderr) {
|
|
|
953
900
|
`Hint: PowerShell does NOT treat \\ as an escape character \u2014 the \\" in this command closed the double-quoted string and split the command into fragments. Escape quotes with \`" or "" instead. Better: avoid nested quoting entirely \u2014 write the payload to a file with 'write_file', 'scp' it over, and run it with one level of quoting (e.g. ssh host "psql -d <db> -f /tmp/x.sql").`
|
|
954
901
|
);
|
|
955
902
|
}
|
|
903
|
+
if (detectMissingCallOperator(command) && /(ParserError|UnexpectedToken|Unexpected token|意外的标记|运算符后面提供|必须在)/i.test(stderr)) {
|
|
904
|
+
hints.push(
|
|
905
|
+
`Hint: in PowerShell a quoted string at the START of a statement is parsed as a string EXPRESSION, not as a command \u2014 so "C:\\path\\python.exe" -c "..." is a syntax error, not a quoting problem. Do NOT retry by removing the quotes, dropping the argument, or re-quoting it differently; all of those fail the same way. Fix: put the call operator '&' in front \u2014 \`& "C:\\path\\python.exe" -c "..."\` \u2014 or assign the path to a variable first (\`$py = "C:\\path\\python.exe"; & $py -c "..."\`).`
|
|
906
|
+
);
|
|
907
|
+
}
|
|
956
908
|
return hints.length > 0 ? hints.map((h) => `\u{1F4A1} ${h}`).join("\n\n") : null;
|
|
957
909
|
}
|
|
958
910
|
function detectBlockingCommand(command) {
|
|
@@ -1055,7 +1007,7 @@ function pushBashUndoEntries(beforeSnapshot, parsedTargetsBefore, cwd) {
|
|
|
1055
1007
|
function runShell(command, opts) {
|
|
1056
1008
|
return new Promise((resolvePromise) => {
|
|
1057
1009
|
const shellArgs = IS_WINDOWS3 ? ["-NoProfile", "-NonInteractive", "-Command", WIN_UTF8_PREAMBLE + command] : ["-c", command];
|
|
1058
|
-
const child =
|
|
1010
|
+
const child = spawn2(SHELL2, shellArgs, {
|
|
1059
1011
|
cwd: opts.cwd,
|
|
1060
1012
|
env: opts.env,
|
|
1061
1013
|
windowsHide: true,
|
|
@@ -1134,16 +1086,16 @@ function updateCwdFromCommand(command, baseCwd) {
|
|
|
1134
1086
|
}
|
|
1135
1087
|
|
|
1136
1088
|
// src/tools/builtin/read-file.ts
|
|
1137
|
-
import { readFileSync
|
|
1089
|
+
import { readFileSync, existsSync as existsSync3, statSync as statSync2, readdirSync as readdirSync2 } from "fs";
|
|
1138
1090
|
import { execFileSync } from "child_process";
|
|
1139
1091
|
import { extname, resolve as resolve2, basename, sep, dirname } from "path";
|
|
1140
1092
|
import { homedir } from "os";
|
|
1141
1093
|
|
|
1142
1094
|
// src/tools/builtin/notebook-utils.ts
|
|
1143
|
-
function cellSourceToString(
|
|
1144
|
-
if (!
|
|
1145
|
-
if (Array.isArray(
|
|
1146
|
-
return
|
|
1095
|
+
function cellSourceToString(source2) {
|
|
1096
|
+
if (!source2) return "";
|
|
1097
|
+
if (Array.isArray(source2)) return source2.join("");
|
|
1098
|
+
return source2;
|
|
1147
1099
|
}
|
|
1148
1100
|
function stringToCellSource(content) {
|
|
1149
1101
|
if (content.length === 0) return [];
|
|
@@ -1198,16 +1150,16 @@ function renderNotebookAsText(nb, filePath) {
|
|
|
1198
1150
|
nb.cells.forEach((cell, idx) => {
|
|
1199
1151
|
const cellNum = idx + 1;
|
|
1200
1152
|
const type = cell.cell_type;
|
|
1201
|
-
const
|
|
1153
|
+
const source2 = cellSourceToString(cell.source);
|
|
1202
1154
|
if (type === "markdown") {
|
|
1203
1155
|
lines.push(`## Cell ${cellNum} [markdown]`);
|
|
1204
|
-
lines.push(
|
|
1156
|
+
lines.push(source2);
|
|
1205
1157
|
lines.push("");
|
|
1206
1158
|
} else if (type === "code") {
|
|
1207
1159
|
const execLabel = cell.execution_count != null ? ` (execution #${cell.execution_count})` : "";
|
|
1208
1160
|
lines.push(`## Cell ${cellNum} [code${execLabel}]`);
|
|
1209
1161
|
lines.push("```" + language);
|
|
1210
|
-
lines.push(
|
|
1162
|
+
lines.push(source2);
|
|
1211
1163
|
lines.push("```");
|
|
1212
1164
|
const outputText = extractOutputText(cell.outputs);
|
|
1213
1165
|
if (outputText) {
|
|
@@ -1219,7 +1171,7 @@ function renderNotebookAsText(nb, filePath) {
|
|
|
1219
1171
|
lines.push("");
|
|
1220
1172
|
} else if (type === "raw") {
|
|
1221
1173
|
lines.push(`## Cell ${cellNum} [raw]`);
|
|
1222
|
-
lines.push(
|
|
1174
|
+
lines.push(source2);
|
|
1223
1175
|
lines.push("");
|
|
1224
1176
|
}
|
|
1225
1177
|
});
|
|
@@ -1244,6 +1196,7 @@ function createCell(cellType, content) {
|
|
|
1244
1196
|
// src/tools/builtin/read-file.ts
|
|
1245
1197
|
var MAX_FILE_BYTES = 10 * 1024 * 1024;
|
|
1246
1198
|
function getHardRefusal(normalizedPath) {
|
|
1199
|
+
if (isCredentialVault(normalizedPath)) return "[Refused] Credential vault files cannot be read by tools.";
|
|
1247
1200
|
const home2 = homedir();
|
|
1248
1201
|
const p = normalizedPath.toLowerCase();
|
|
1249
1202
|
const base = basename(normalizedPath).toLowerCase();
|
|
@@ -1418,7 +1371,7 @@ var readFileTool = {
|
|
|
1418
1371
|
const offsetLine = typeof rawOffset === "number" && Number.isFinite(rawOffset) && rawOffset > 0 ? Math.floor(rawOffset) : void 0;
|
|
1419
1372
|
const limitLines = typeof rawLimit === "number" && Number.isFinite(rawLimit) && rawLimit > 0 ? Math.floor(rawLimit) : void 0;
|
|
1420
1373
|
if (!filePath) throw new ToolError("read_file", "path is required");
|
|
1421
|
-
const normalizedPath =
|
|
1374
|
+
const normalizedPath = canonicalPath(filePath);
|
|
1422
1375
|
const refusal = getHardRefusal(normalizedPath);
|
|
1423
1376
|
if (refusal) return refusal;
|
|
1424
1377
|
if (!existsSync3(normalizedPath)) {
|
|
@@ -1485,7 +1438,7 @@ Suggestion: read existing text versions (.md / .txt) in the project, or install
|
|
|
1485
1438
|
}
|
|
1486
1439
|
if (ext === ".ipynb") {
|
|
1487
1440
|
try {
|
|
1488
|
-
const raw =
|
|
1441
|
+
const raw = readFileSync(normalizedPath, "utf-8");
|
|
1489
1442
|
const nb = parseNotebook(raw);
|
|
1490
1443
|
return renderNotebookAsText(nb, filePath);
|
|
1491
1444
|
} catch (err) {
|
|
@@ -1503,8 +1456,8 @@ Do NOT claim you inspected this image \u2014 you have not seen it.`;
|
|
|
1503
1456
|
return `[Binary file: ${filePath} (${ext})]
|
|
1504
1457
|
This is a binary file and cannot be read as text. If there is a text version (.md / .txt) in the project, please read that instead.`;
|
|
1505
1458
|
}
|
|
1506
|
-
const { readFile
|
|
1507
|
-
const buf = size > 1048576 ? await
|
|
1459
|
+
const { readFile } = await import("fs/promises");
|
|
1460
|
+
const buf = size > 1048576 ? await readFile(normalizedPath) : readFileSync(normalizedPath);
|
|
1508
1461
|
if (encoding === "base64") {
|
|
1509
1462
|
return `[File: ${filePath} | base64]
|
|
1510
1463
|
|
|
@@ -1542,12 +1495,12 @@ ${content}`;
|
|
|
1542
1495
|
};
|
|
1543
1496
|
|
|
1544
1497
|
// src/tools/builtin/write-file.ts
|
|
1545
|
-
import { appendFileSync, mkdirSync, readFileSync as
|
|
1498
|
+
import { appendFileSync, mkdirSync, readFileSync as readFileSync3, statSync as statSync3 } from "fs";
|
|
1546
1499
|
import { dirname as dirname3 } from "path";
|
|
1547
1500
|
|
|
1548
1501
|
// src/tools/executor.ts
|
|
1549
1502
|
import chalk2 from "chalk";
|
|
1550
|
-
import { existsSync as
|
|
1503
|
+
import { existsSync as existsSync5, readFileSync as readFileSync2 } from "fs";
|
|
1551
1504
|
import { tmpdir } from "os";
|
|
1552
1505
|
|
|
1553
1506
|
// src/core/readline-internal.ts
|
|
@@ -1890,7 +1843,8 @@ var READ_ONLY_TOOLS = /* @__PURE__ */ new Set([
|
|
|
1890
1843
|
"recall_memory"
|
|
1891
1844
|
]);
|
|
1892
1845
|
function normalizePathForPermission(value) {
|
|
1893
|
-
|
|
1846
|
+
const normalized = value.replace(/\\/g, "/").replace(/\/+$/, "");
|
|
1847
|
+
return process.platform === "win32" ? normalized.toLowerCase() : normalized;
|
|
1894
1848
|
}
|
|
1895
1849
|
function canonicalizeWithNearestParent(value) {
|
|
1896
1850
|
const missing = [];
|
|
@@ -1978,7 +1932,7 @@ function profileHardDecision(profileName, toolName, args, dangerLevel, ctx) {
|
|
|
1978
1932
|
return { action: "deny", reason: "file write target is outside workspace/temp roots", profileName, hardBoundary: true };
|
|
1979
1933
|
}
|
|
1980
1934
|
}
|
|
1981
|
-
if (toolName === "bash" || toolName === "run_interactive" || toolName === "task_create") {
|
|
1935
|
+
if (toolName === "bash" || toolName === "run_interactive" || toolName === "task_create" || toolName === "run_tests") {
|
|
1982
1936
|
return {
|
|
1983
1937
|
action: "confirm",
|
|
1984
1938
|
reason: "shell/process writes cannot be proven to stay inside workspace/temp roots",
|
|
@@ -2080,9 +2034,9 @@ function checkNetworkPolicy(toolName, args, policy) {
|
|
|
2080
2034
|
if (!policy?.enabled) return null;
|
|
2081
2035
|
let action = networkToolAction(toolName, policy);
|
|
2082
2036
|
let reason = "network access requires confirmation";
|
|
2083
|
-
if (
|
|
2037
|
+
if (["bash", "run_tests", "run_interactive", "task_create"].includes(toolName)) {
|
|
2084
2038
|
const command = String(args["command"] ?? "");
|
|
2085
|
-
if (!isShellNetworkCommand(command)) return null;
|
|
2039
|
+
if (toolName === "bash" && !isShellNetworkCommand(command) && dangerIsReadOnly(command)) return null;
|
|
2086
2040
|
action = policy.tools?.shell ?? policy.defaultAction ?? "confirm";
|
|
2087
2041
|
reason = "shell command may access the network";
|
|
2088
2042
|
}
|
|
@@ -2118,6 +2072,9 @@ function checkNetworkPolicy(toolName, args, policy) {
|
|
|
2118
2072
|
if (!permissionAction) return null;
|
|
2119
2073
|
return { action: permissionAction, reason, profileName: "networkPolicy" };
|
|
2120
2074
|
}
|
|
2075
|
+
function dangerIsReadOnly(command) {
|
|
2076
|
+
return /^(?:pwd|echo [\w .-]+|git status(?: --short)?)$/.test(command.trim());
|
|
2077
|
+
}
|
|
2121
2078
|
|
|
2122
2079
|
// src/tools/truncate.ts
|
|
2123
2080
|
var DEFAULT_MAX_TOOL_OUTPUT_CHARS = 12e3;
|
|
@@ -2227,8 +2184,7 @@ function truncateOutput(content, toolName, maxChars) {
|
|
|
2227
2184
|
}
|
|
2228
2185
|
|
|
2229
2186
|
// src/tools/action-classifier.ts
|
|
2230
|
-
import {
|
|
2231
|
-
import { isAbsolute as isAbsolute2, join, resolve as resolve4 } from "path";
|
|
2187
|
+
import { isAbsolute as isAbsolute2, resolve as resolve4 } from "path";
|
|
2232
2188
|
var RECENT_DENIED_LIMIT = 50;
|
|
2233
2189
|
var recentlyDenied = [];
|
|
2234
2190
|
function recordRecentlyDeniedAutoAction(call, classification) {
|
|
@@ -2248,7 +2204,8 @@ function clearRecentlyDeniedAutoActions() {
|
|
|
2248
2204
|
recentlyDenied.splice(0, recentlyDenied.length);
|
|
2249
2205
|
}
|
|
2250
2206
|
function normalizePath(value) {
|
|
2251
|
-
|
|
2207
|
+
const normalized = canonicalPath(value).replace(/\\/g, "/").replace(/\/+$/, "");
|
|
2208
|
+
return process.platform === "win32" ? normalized.toLowerCase() : normalized;
|
|
2252
2209
|
}
|
|
2253
2210
|
function isInside(root, target) {
|
|
2254
2211
|
const absoluteRoot = resolve4(root);
|
|
@@ -2261,56 +2218,6 @@ function getPathArg(call) {
|
|
|
2261
2218
|
const raw = call.arguments.path ?? call.arguments.filePath ?? call.arguments.outputPath ?? call.arguments.file;
|
|
2262
2219
|
return typeof raw === "string" && raw.trim() ? raw.trim() : null;
|
|
2263
2220
|
}
|
|
2264
|
-
function splitShellWords(command) {
|
|
2265
|
-
const out = [];
|
|
2266
|
-
const re = /"([^"]*)"|'([^']*)'|([^\s]+)/g;
|
|
2267
|
-
let m;
|
|
2268
|
-
while (m = re.exec(command)) out.push(m[1] ?? m[2] ?? m[3] ?? "");
|
|
2269
|
-
return out.filter(Boolean);
|
|
2270
|
-
}
|
|
2271
|
-
function readPackageManifest(root) {
|
|
2272
|
-
const p = join(root, "package.json");
|
|
2273
|
-
if (!existsSync5(p)) return null;
|
|
2274
|
-
try {
|
|
2275
|
-
return JSON.parse(readFileSync3(p, "utf-8"));
|
|
2276
|
-
} catch {
|
|
2277
|
-
return null;
|
|
2278
|
-
}
|
|
2279
|
-
}
|
|
2280
|
-
function manifestHasDependency(root, name) {
|
|
2281
|
-
const manifest = readPackageManifest(root);
|
|
2282
|
-
if (!manifest) return false;
|
|
2283
|
-
for (const key of ["dependencies", "devDependencies", "peerDependencies", "optionalDependencies"]) {
|
|
2284
|
-
const deps = manifest[key];
|
|
2285
|
-
if (deps && typeof deps === "object" && Object.prototype.hasOwnProperty.call(deps, name)) return true;
|
|
2286
|
-
}
|
|
2287
|
-
return false;
|
|
2288
|
-
}
|
|
2289
|
-
function lockfileMentions(root, name) {
|
|
2290
|
-
for (const file of ["package-lock.json", "pnpm-lock.yaml", "yarn.lock"]) {
|
|
2291
|
-
const p = join(root, file);
|
|
2292
|
-
if (!existsSync5(p)) continue;
|
|
2293
|
-
try {
|
|
2294
|
-
if (readFileSync3(p, "utf-8").includes(name)) return true;
|
|
2295
|
-
} catch {
|
|
2296
|
-
}
|
|
2297
|
-
}
|
|
2298
|
-
return false;
|
|
2299
|
-
}
|
|
2300
|
-
function getInstallPackages(command) {
|
|
2301
|
-
const words = splitShellWords(command);
|
|
2302
|
-
const cmd = words[0]?.toLowerCase();
|
|
2303
|
-
const sub = words[1]?.toLowerCase();
|
|
2304
|
-
if (!cmd || !sub) return [];
|
|
2305
|
-
const isInstall = cmd === "npm" && (sub === "install" || sub === "i" || sub === "add") || (cmd === "pnpm" || cmd === "yarn") && (sub === "add" || sub === "install");
|
|
2306
|
-
if (!isInstall) return [];
|
|
2307
|
-
return words.slice(2).filter((w) => !w.startsWith("-")).filter((w) => w !== "." && w !== "./").map((w) => w.replace(/^@([^/]+)\/([^@]+)(?:@.+)?$/, "@$1/$2").replace(/^([^@]+)@.+$/, "$1"));
|
|
2308
|
-
}
|
|
2309
|
-
function isPlainGitPush(command) {
|
|
2310
|
-
if (!/^\s*git\s+push\b/i.test(command)) return false;
|
|
2311
|
-
if (/--force|-f\b|\+[^\s]+/i.test(command)) return false;
|
|
2312
|
-
return !/[;&|`$<>\n\r]/.test(command);
|
|
2313
|
-
}
|
|
2314
2221
|
function hasSecretExfiltrationShape(command) {
|
|
2315
2222
|
return /(?:\.env|config\.json|id_rsa|id_ed25519|AWS_SECRET|AICLI_API_KEY|OPENAI_API_KEY|ANTHROPIC_API_KEY)/i.test(command) && /\b(curl|wget|scp|rsync|nc|netcat|Invoke-WebRequest|Invoke-RestMethod)\b/i.test(command);
|
|
2316
2223
|
}
|
|
@@ -2332,7 +2239,7 @@ var RuleBasedActionClassifier = class {
|
|
|
2332
2239
|
}
|
|
2333
2240
|
return { decision: "confirm", reason: "destructive actions require confirmation", ruleId: "auto.destructive-confirm", confidence: "high" };
|
|
2334
2241
|
}
|
|
2335
|
-
classifyShell(call, dangerLevel,
|
|
2242
|
+
classifyShell(call, dangerLevel, _ctx) {
|
|
2336
2243
|
const command = String(call.arguments.command ?? "");
|
|
2337
2244
|
if (/\bcurl\b[^\n|;&]*\|\s*(?:sudo\s+)?(?:bash|sh|zsh)\b/i.test(command)) {
|
|
2338
2245
|
return { decision: "deny", reason: "curl pipe shell execution is blocked in auto mode", ruleId: "deny.curl-pipe-shell", confidence: "high" };
|
|
@@ -2358,18 +2265,7 @@ var RuleBasedActionClassifier = class {
|
|
|
2358
2265
|
if (/\b(npx|pnpm\s+dlx|yarn\s+dlx)\s+(claude|codex|aider|cursor-agent|gemini)\b/i.test(command)) {
|
|
2359
2266
|
return { decision: "confirm", reason: "third-party agent loop requires confirmation", ruleId: "confirm.third-party-agent", confidence: "high" };
|
|
2360
2267
|
}
|
|
2361
|
-
if (
|
|
2362
|
-
return { decision: "auto-approve", reason: "plain git push without force", ruleId: "auto.git-push", confidence: "high" };
|
|
2363
|
-
}
|
|
2364
|
-
const pkgs = getInstallPackages(command);
|
|
2365
|
-
if (pkgs.length > 0) {
|
|
2366
|
-
const allDeclared = pkgs.every((p) => manifestHasDependency(ctx.workspaceRoot, p) || lockfileMentions(ctx.workspaceRoot, p));
|
|
2367
|
-
return allDeclared ? { decision: "auto-approve", reason: "installing dependencies already declared in manifest/lockfile", ruleId: "auto.declared-dependency-install", confidence: "medium" } : { decision: "confirm", reason: "dependency install includes undeclared packages", ruleId: "confirm.undeclared-dependency-install", confidence: "medium" };
|
|
2368
|
-
}
|
|
2369
|
-
if (/\b(curl|wget|Invoke-WebRequest|Invoke-RestMethod)\b/i.test(command) && !/[>|;&`$]/.test(command)) {
|
|
2370
|
-
return { decision: "auto-approve", reason: "read-only HTTP shell command", ruleId: "auto.readonly-http-shell", confidence: "medium" };
|
|
2371
|
-
}
|
|
2372
|
-
if (dangerLevel === "safe") {
|
|
2268
|
+
if (dangerLevel === "safe" && getDangerLevel(call.name, call.arguments) === "safe") {
|
|
2373
2269
|
return { decision: "auto-approve", reason: "safe shell action", ruleId: "auto.safe-shell", confidence: "medium" };
|
|
2374
2270
|
}
|
|
2375
2271
|
return { decision: "confirm", reason: "shell action is not proven low-risk", ruleId: "confirm.shell-default", confidence: "medium" };
|
|
@@ -2838,10 +2734,10 @@ var ToolExecutor = class {
|
|
|
2838
2734
|
if (call.name === "write_file") {
|
|
2839
2735
|
const { path: filePath, content: chunk, append } = resolveWriteFileArgs(call.arguments);
|
|
2840
2736
|
if (!filePath) return;
|
|
2841
|
-
if (
|
|
2737
|
+
if (existsSync5(filePath)) {
|
|
2842
2738
|
let oldContent;
|
|
2843
2739
|
try {
|
|
2844
|
-
oldContent =
|
|
2740
|
+
oldContent = readFileSync2(filePath, "utf-8");
|
|
2845
2741
|
} catch {
|
|
2846
2742
|
return;
|
|
2847
2743
|
}
|
|
@@ -2865,7 +2761,7 @@ var ToolExecutor = class {
|
|
|
2865
2761
|
}
|
|
2866
2762
|
} else if (call.name === "edit_file") {
|
|
2867
2763
|
const filePath = String(call.arguments["path"] ?? "");
|
|
2868
|
-
if (!filePath || !
|
|
2764
|
+
if (!filePath || !existsSync5(filePath)) return;
|
|
2869
2765
|
const oldStr = call.arguments["old_str"];
|
|
2870
2766
|
const newStr = call.arguments["new_str"];
|
|
2871
2767
|
if (oldStr !== void 0) {
|
|
@@ -2892,7 +2788,7 @@ var ToolExecutor = class {
|
|
|
2892
2788
|
const to = Number(call.arguments["delete_to_line"] ?? from);
|
|
2893
2789
|
let fileContent;
|
|
2894
2790
|
try {
|
|
2895
|
-
fileContent =
|
|
2791
|
+
fileContent = readFileSync2(filePath, "utf-8");
|
|
2896
2792
|
} catch {
|
|
2897
2793
|
return;
|
|
2898
2794
|
}
|
|
@@ -2988,7 +2884,12 @@ import { resolve as resolve5, sep as sep2, basename as basename3 } from "path";
|
|
|
2988
2884
|
import { homedir as homedir2 } from "os";
|
|
2989
2885
|
var home = homedir2();
|
|
2990
2886
|
function norm(p) {
|
|
2991
|
-
|
|
2887
|
+
let abs;
|
|
2888
|
+
try {
|
|
2889
|
+
abs = canonicalPath(p);
|
|
2890
|
+
} catch {
|
|
2891
|
+
abs = resolve5(p);
|
|
2892
|
+
}
|
|
2992
2893
|
return process.platform === "win32" ? abs.toLowerCase() : abs;
|
|
2993
2894
|
}
|
|
2994
2895
|
function homeRel(p) {
|
|
@@ -3002,6 +2903,7 @@ function homeRel(p) {
|
|
|
3002
2903
|
}
|
|
3003
2904
|
function classifyWritePath(path3) {
|
|
3004
2905
|
if (!path3) return { sensitive: false };
|
|
2906
|
+
if (isCredentialVault(path3)) return { sensitive: true, reason: "credential vault" };
|
|
3005
2907
|
const abs = norm(path3);
|
|
3006
2908
|
const base = basename3(abs);
|
|
3007
2909
|
const rel = homeRel(path3);
|
|
@@ -3084,7 +2986,7 @@ function countTextLines(text) {
|
|
|
3084
2986
|
|
|
3085
2987
|
// src/tools/builtin/write-file.ts
|
|
3086
2988
|
function countFileLines(filePath) {
|
|
3087
|
-
return countTextLines(
|
|
2989
|
+
return countTextLines(readFileSync3(filePath, "utf-8"));
|
|
3088
2990
|
}
|
|
3089
2991
|
var writeFileTool = {
|
|
3090
2992
|
definition: {
|
|
@@ -3148,7 +3050,7 @@ Do NOT split a long document into many write_file(append=true) calls. That patte
|
|
|
3148
3050
|
const mode = appendMode ? "appended" : "written";
|
|
3149
3051
|
void (async () => {
|
|
3150
3052
|
try {
|
|
3151
|
-
const { updateFile } = await import("./indexer-
|
|
3053
|
+
const { updateFile } = await import("./indexer-GKIWSSRP.js");
|
|
3152
3054
|
await updateFile(process.cwd(), filePath);
|
|
3153
3055
|
} catch {
|
|
3154
3056
|
}
|
|
@@ -3167,7 +3069,7 @@ Do NOT split a long document into many write_file(append=true) calls. That patte
|
|
|
3167
3069
|
};
|
|
3168
3070
|
|
|
3169
3071
|
// src/tools/builtin/edit-file.ts
|
|
3170
|
-
import { readFileSync as
|
|
3072
|
+
import { readFileSync as readFileSync4, existsSync as existsSync6 } from "fs";
|
|
3171
3073
|
|
|
3172
3074
|
// src/tools/builtin/patch-apply.ts
|
|
3173
3075
|
function parseUnifiedDiff(patch) {
|
|
@@ -3535,7 +3437,7 @@ Note: Path can be absolute or relative to cwd.`,
|
|
|
3535
3437
|
const filePath = String(args["path"] ?? args["file"] ?? "");
|
|
3536
3438
|
const encoding = args["encoding"] ?? "utf-8";
|
|
3537
3439
|
if (!filePath) throw new ToolError("edit_file", "path is required");
|
|
3538
|
-
if (!
|
|
3440
|
+
if (!existsSync6(filePath)) throw new ToolError("edit_file", `File not found: ${filePath}`);
|
|
3539
3441
|
const verdict = classifyWritePath(filePath);
|
|
3540
3442
|
if (verdict.sensitive && subAgentGuard.active) {
|
|
3541
3443
|
throw new ToolError(
|
|
@@ -3543,7 +3445,7 @@ Note: Path can be absolute or relative to cwd.`,
|
|
|
3543
3445
|
`Refused: sub-agents cannot edit sensitive paths \u2014 ${verdict.reason}. If this is genuinely needed, ask the parent agent to perform the edit so the user can approve it.`
|
|
3544
3446
|
);
|
|
3545
3447
|
}
|
|
3546
|
-
const original =
|
|
3448
|
+
const original = readFileSync4(filePath, encoding);
|
|
3547
3449
|
if (args["patch"] !== void 0) {
|
|
3548
3450
|
const patchText = String(args["patch"] ?? "");
|
|
3549
3451
|
const stopOnError = args["stop_on_error"] !== false;
|
|
@@ -3720,8 +3622,8 @@ function truncatePreview(str, maxLen = 80) {
|
|
|
3720
3622
|
}
|
|
3721
3623
|
|
|
3722
3624
|
// src/tools/builtin/list-dir.ts
|
|
3723
|
-
import { readdirSync as readdirSync3, statSync as statSync4, existsSync as
|
|
3724
|
-
import { join
|
|
3625
|
+
import { readdirSync as readdirSync3, statSync as statSync4, existsSync as existsSync7 } from "fs";
|
|
3626
|
+
import { join, basename as basename4 } from "path";
|
|
3725
3627
|
var listDirTool = {
|
|
3726
3628
|
definition: {
|
|
3727
3629
|
name: "list_dir",
|
|
@@ -3743,7 +3645,7 @@ var listDirTool = {
|
|
|
3743
3645
|
async execute(args) {
|
|
3744
3646
|
const dirPath = String(args["path"] ?? process.cwd());
|
|
3745
3647
|
const recursive = Boolean(args["recursive"] ?? false);
|
|
3746
|
-
if (!
|
|
3648
|
+
if (!existsSync7(dirPath)) {
|
|
3747
3649
|
const targetName = basename4(dirPath).toLowerCase();
|
|
3748
3650
|
const cwd = process.cwd();
|
|
3749
3651
|
const suggestions = [];
|
|
@@ -3801,11 +3703,11 @@ function listRecursive(basePath, indent, recursive, lines) {
|
|
|
3801
3703
|
if (entry.isDirectory()) {
|
|
3802
3704
|
lines.push(`${indent}\u{1F4C1} ${entry.name}/`);
|
|
3803
3705
|
if (recursive) {
|
|
3804
|
-
listRecursive(
|
|
3706
|
+
listRecursive(join(basePath, entry.name), indent + " ", true, lines);
|
|
3805
3707
|
}
|
|
3806
3708
|
} else {
|
|
3807
3709
|
try {
|
|
3808
|
-
const stat = statSync4(
|
|
3710
|
+
const stat = statSync4(join(basePath, entry.name));
|
|
3809
3711
|
const size = formatSize(stat.size);
|
|
3810
3712
|
lines.push(`${indent}\u{1F4C4} ${entry.name} (${size})`);
|
|
3811
3713
|
} catch {
|
|
@@ -3820,10 +3722,77 @@ function formatSize(bytes) {
|
|
|
3820
3722
|
return `${(bytes / 1024 / 1024).toFixed(1)}MB`;
|
|
3821
3723
|
}
|
|
3822
3724
|
|
|
3725
|
+
// src/tools/regex-search.ts
|
|
3726
|
+
import { Worker } from "worker_threads";
|
|
3727
|
+
var source = String.raw`
|
|
3728
|
+
const { parentPort, workerData: d } = require('node:worker_threads');
|
|
3729
|
+
const fs = require('node:fs');
|
|
3730
|
+
let re;
|
|
3731
|
+
try { re = new RegExp(d.pattern, d.ignoreCase ? 'i' : ''); }
|
|
3732
|
+
catch { re = new RegExp(d.pattern.replace(/[.*+?^$()|[\]\\]/g, '\\$&'), d.ignoreCase ? 'i' : ''); }
|
|
3733
|
+
const results = [];
|
|
3734
|
+
for (const f of d.files) {
|
|
3735
|
+
if (results.length >= d.maxResults) break;
|
|
3736
|
+
let fd;
|
|
3737
|
+
try {
|
|
3738
|
+
fd = fs.openSync(f.fullPath, 'r');
|
|
3739
|
+
const buf = Buffer.alloc(1000001);
|
|
3740
|
+
let size = 0;
|
|
3741
|
+
while (size < buf.length) { const n = fs.readSync(fd, buf, size, buf.length - size, null); if (!n) break; size += n; }
|
|
3742
|
+
if (size > 1000000 || buf.subarray(0, Math.min(size,512)).includes(0)) continue;
|
|
3743
|
+
const lines = buf.subarray(0,size).toString('utf8').split('\n');
|
|
3744
|
+
for (let i=0; i<lines.length && results.length<d.maxResults; i++) {
|
|
3745
|
+
if (!re.test(lines[i])) continue;
|
|
3746
|
+
const match = { file:f.relPath.replace(/\\/g,'/'), lineNumber:i+1, lineText:lines[i].slice(0,10000) };
|
|
3747
|
+
if (d.context) {
|
|
3748
|
+
match.contextBefore=[]; match.contextAfter=[];
|
|
3749
|
+
for(let j=Math.max(0,i-d.context);j<i;j++) match.contextBefore.push([j+1,lines[j].slice(0,10000)]);
|
|
3750
|
+
for(let j=i+1;j<=Math.min(lines.length-1,i+d.context);j++) match.contextAfter.push([j+1,lines[j].slice(0,10000)]);
|
|
3751
|
+
}
|
|
3752
|
+
results.push(match);
|
|
3753
|
+
}
|
|
3754
|
+
} catch {} finally { if (fd !== undefined) fs.closeSync(fd); }
|
|
3755
|
+
}
|
|
3756
|
+
parentPort.postMessage(results);
|
|
3757
|
+
`;
|
|
3758
|
+
var active = 0;
|
|
3759
|
+
async function searchInWorker(files, pattern, ignoreCase, context, maxResults) {
|
|
3760
|
+
const signal = currentAbortSignal();
|
|
3761
|
+
if (signal.aborted) throw new Error("Search cancelled");
|
|
3762
|
+
if (active >= 4) throw new Error("Search capacity reached; wait for another search to finish");
|
|
3763
|
+
active++;
|
|
3764
|
+
let worker;
|
|
3765
|
+
try {
|
|
3766
|
+
return await new Promise((resolve7, reject) => {
|
|
3767
|
+
worker = new Worker(source, { eval: true, workerData: { files, pattern, ignoreCase, context, maxResults }, resourceLimits: { maxOldGenerationSizeMb: 64 } });
|
|
3768
|
+
let settled = false;
|
|
3769
|
+
const finish = (error, results) => {
|
|
3770
|
+
if (settled) return;
|
|
3771
|
+
settled = true;
|
|
3772
|
+
clearTimeout(timer);
|
|
3773
|
+
signal.removeEventListener("abort", abort);
|
|
3774
|
+
if (error) reject(error);
|
|
3775
|
+
else resolve7(results ?? []);
|
|
3776
|
+
};
|
|
3777
|
+
const timer = setTimeout(() => finish(new Error("Search exceeded 2 second CPU/time budget; simplify the pattern or narrow the path")), 2e3);
|
|
3778
|
+
const abort = () => finish(new Error("Search cancelled"));
|
|
3779
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
3780
|
+
if (signal.aborted) abort();
|
|
3781
|
+
worker.once("message", (results) => finish(void 0, results));
|
|
3782
|
+
worker.once("error", (error) => finish(error));
|
|
3783
|
+
worker.once("exit", (code) => {
|
|
3784
|
+
if (!settled) finish(new Error(`Search worker exited (${code})`));
|
|
3785
|
+
});
|
|
3786
|
+
});
|
|
3787
|
+
} finally {
|
|
3788
|
+
if (worker) await worker.terminate();
|
|
3789
|
+
active--;
|
|
3790
|
+
}
|
|
3791
|
+
}
|
|
3792
|
+
|
|
3823
3793
|
// src/tools/builtin/grep-files.ts
|
|
3824
|
-
import { readdirSync as readdirSync4,
|
|
3825
|
-
import {
|
|
3826
|
-
import { join as join3, relative } from "path";
|
|
3794
|
+
import { readdirSync as readdirSync4, statSync as statSync5, existsSync as existsSync8 } from "fs";
|
|
3795
|
+
import { join as join2, relative } from "path";
|
|
3827
3796
|
var grepFilesTool = {
|
|
3828
3797
|
definition: {
|
|
3829
3798
|
name: "grep_files",
|
|
@@ -3871,43 +3840,24 @@ Supports regex. Automatically skips node_modules, dist, .git directories.`,
|
|
|
3871
3840
|
const rootPath = String(args["path"] ?? process.cwd());
|
|
3872
3841
|
const filePattern = args["file_pattern"] ? String(args["file_pattern"]) : void 0;
|
|
3873
3842
|
const ignoreCase = Boolean(args["ignore_case"] ?? false);
|
|
3874
|
-
const contextLines = Math.max(0, Number(args["context_lines"]
|
|
3875
|
-
const maxResults = Math.max(1, Number(args["max_results"]
|
|
3843
|
+
const contextLines = Math.min(10, Math.max(0, Number(args["context_lines"]) || 0));
|
|
3844
|
+
const maxResults = Math.min(200, Math.max(1, Number(args["max_results"]) || 50));
|
|
3876
3845
|
if (!pattern) throw new ToolError("grep_files", "pattern is required");
|
|
3877
|
-
if (!
|
|
3846
|
+
if (!existsSync8(rootPath)) throw new ToolError("grep_files", `Path not found: ${rootPath}`);
|
|
3878
3847
|
const MAX_PATTERN_LENGTH = 1e3;
|
|
3879
|
-
if (pattern.length > MAX_PATTERN_LENGTH) {
|
|
3848
|
+
if (pattern.length > MAX_PATTERN_LENGTH || (filePattern?.length ?? 0) > MAX_PATTERN_LENGTH) {
|
|
3880
3849
|
throw new ToolError("grep_files", `Pattern too long (${pattern.length} chars, max ${MAX_PATTERN_LENGTH}). Use a shorter pattern.`);
|
|
3881
3850
|
}
|
|
3882
|
-
|
|
3883
|
-
|
|
3884
|
-
|
|
3885
|
-
|
|
3886
|
-
|
|
3887
|
-
|
|
3888
|
-
|
|
3889
|
-
const results = [];
|
|
3890
|
-
const stat = statSync5(rootPath);
|
|
3891
|
-
if (stat.isFile()) {
|
|
3892
|
-
searchInFile(rootPath, rootPath, regex, contextLines, maxResults, results);
|
|
3893
|
-
} else {
|
|
3894
|
-
const filePaths = collectFilePaths(rootPath, filePattern, maxResults);
|
|
3895
|
-
const BATCH_SIZE = 16;
|
|
3896
|
-
for (let i = 0; i < filePaths.length && results.length < maxResults; i += BATCH_SIZE) {
|
|
3897
|
-
const batch = filePaths.slice(i, i + BATCH_SIZE);
|
|
3898
|
-
const batchResults = await Promise.all(
|
|
3899
|
-
batch.map(
|
|
3900
|
-
({ fullPath, relPath }) => searchInFileAsync(fullPath, relPath, regex, contextLines, maxResults - results.length)
|
|
3901
|
-
)
|
|
3902
|
-
);
|
|
3903
|
-
for (const br of batchResults) {
|
|
3904
|
-
for (const r of br) {
|
|
3905
|
-
if (results.length >= maxResults) break;
|
|
3906
|
-
results.push(r);
|
|
3907
|
-
}
|
|
3908
|
-
}
|
|
3851
|
+
assertReadablePath(rootPath);
|
|
3852
|
+
const paths = statSync5(rootPath).isFile() ? [{ fullPath: rootPath, relPath: rootPath }] : collectFilePaths(rootPath, filePattern, maxResults);
|
|
3853
|
+
const files = paths.filter((f) => {
|
|
3854
|
+
try {
|
|
3855
|
+
return !isCredentialVault(f.fullPath);
|
|
3856
|
+
} catch {
|
|
3857
|
+
return false;
|
|
3909
3858
|
}
|
|
3910
|
-
}
|
|
3859
|
+
});
|
|
3860
|
+
const results = await searchInWorker(files, pattern, ignoreCase, contextLines, maxResults);
|
|
3911
3861
|
if (results.length === 0) {
|
|
3912
3862
|
return `No matches found for pattern: ${pattern}
|
|
3913
3863
|
Searched in: ${rootPath}${filePattern ? `
|
|
@@ -3947,8 +3897,23 @@ File filter: ${filePattern}` : ""}`;
|
|
|
3947
3897
|
var SKIP_DIRS = /* @__PURE__ */ new Set(["node_modules", ".git", "dist", "dist-cjs", "release", ".next", "__pycache__", ".cache", "coverage", ".nyc_output"]);
|
|
3948
3898
|
var BINARY_EXTS = /* @__PURE__ */ new Set([".png", ".jpg", ".jpeg", ".gif", ".webp", ".svg", ".ico", ".woff", ".woff2", ".ttf", ".eot", ".otf", ".mp3", ".mp4", ".wav", ".zip", ".tar", ".gz", ".rar", ".7z", ".exe", ".dll", ".so", ".dylib", ".pdf", ".doc", ".docx", ".xls", ".xlsx", ".ppt", ".pptx"]);
|
|
3949
3899
|
function matchesFilePattern(filename, pattern) {
|
|
3950
|
-
|
|
3951
|
-
|
|
3900
|
+
filename = filename.toLowerCase();
|
|
3901
|
+
pattern = pattern.toLowerCase();
|
|
3902
|
+
let i = 0, j = 0, star = -1, restart = 0;
|
|
3903
|
+
while (i < filename.length) {
|
|
3904
|
+
if (pattern[j] === filename[i]) {
|
|
3905
|
+
i++;
|
|
3906
|
+
j++;
|
|
3907
|
+
} else if (pattern[j] === "*") {
|
|
3908
|
+
star = j++;
|
|
3909
|
+
restart = i;
|
|
3910
|
+
} else if (star >= 0) {
|
|
3911
|
+
j = star + 1;
|
|
3912
|
+
i = ++restart;
|
|
3913
|
+
} else return false;
|
|
3914
|
+
}
|
|
3915
|
+
while (pattern[j] === "*") j++;
|
|
3916
|
+
return j === pattern.length;
|
|
3952
3917
|
}
|
|
3953
3918
|
function isBinary(filename) {
|
|
3954
3919
|
const ext = filename.slice(filename.lastIndexOf(".")).toLowerCase();
|
|
@@ -3956,8 +3921,9 @@ function isBinary(filename) {
|
|
|
3956
3921
|
}
|
|
3957
3922
|
function collectFilePaths(rootPath, filePattern, maxFiles) {
|
|
3958
3923
|
const paths = [];
|
|
3959
|
-
|
|
3960
|
-
|
|
3924
|
+
let visited = 0;
|
|
3925
|
+
function walk(dirPath, depth = 0) {
|
|
3926
|
+
if (paths.length >= maxFiles || depth > 20 || visited >= 2e4) return;
|
|
3961
3927
|
let entries;
|
|
3962
3928
|
try {
|
|
3963
3929
|
entries = readdirSync4(dirPath, { withFileTypes: true });
|
|
@@ -3965,14 +3931,14 @@ function collectFilePaths(rootPath, filePattern, maxFiles) {
|
|
|
3965
3931
|
return;
|
|
3966
3932
|
}
|
|
3967
3933
|
for (const entry of entries) {
|
|
3968
|
-
if (paths.length >= maxFiles) return;
|
|
3934
|
+
if (paths.length >= maxFiles || ++visited > 2e4) return;
|
|
3969
3935
|
if (entry.isDirectory()) {
|
|
3970
3936
|
if (SKIP_DIRS.has(entry.name) || entry.name.startsWith(".")) continue;
|
|
3971
|
-
walk(
|
|
3937
|
+
walk(join2(dirPath, entry.name), depth + 1);
|
|
3972
3938
|
} else if (entry.isFile()) {
|
|
3973
3939
|
if (isBinary(entry.name)) continue;
|
|
3974
3940
|
if (filePattern && !matchesFilePattern(entry.name, filePattern)) continue;
|
|
3975
|
-
const fullPath =
|
|
3941
|
+
const fullPath = join2(dirPath, entry.name);
|
|
3976
3942
|
try {
|
|
3977
3943
|
if (statSync5(fullPath).size > 1e6) continue;
|
|
3978
3944
|
} catch {
|
|
@@ -3985,81 +3951,10 @@ function collectFilePaths(rootPath, filePattern, maxFiles) {
|
|
|
3985
3951
|
walk(rootPath);
|
|
3986
3952
|
return paths;
|
|
3987
3953
|
}
|
|
3988
|
-
async function searchInFileAsync(fullPath, displayPath, regex, contextLines, maxResults) {
|
|
3989
|
-
let content;
|
|
3990
|
-
try {
|
|
3991
|
-
content = await readFile(fullPath, "utf-8");
|
|
3992
|
-
} catch {
|
|
3993
|
-
return [];
|
|
3994
|
-
}
|
|
3995
|
-
const results = [];
|
|
3996
|
-
const lines = content.split("\n");
|
|
3997
|
-
regex.lastIndex = 0;
|
|
3998
|
-
for (let i = 0; i < lines.length && results.length < maxResults; i++) {
|
|
3999
|
-
regex.lastIndex = 0;
|
|
4000
|
-
if (regex.test(lines[i])) {
|
|
4001
|
-
const match = {
|
|
4002
|
-
file: displayPath.replace(/\\/g, "/"),
|
|
4003
|
-
lineNumber: i + 1,
|
|
4004
|
-
lineText: lines[i]
|
|
4005
|
-
};
|
|
4006
|
-
if (contextLines > 0) {
|
|
4007
|
-
match.contextBefore = [];
|
|
4008
|
-
for (let j = Math.max(0, i - contextLines); j < i; j++) {
|
|
4009
|
-
match.contextBefore.push([j + 1, lines[j]]);
|
|
4010
|
-
}
|
|
4011
|
-
match.contextAfter = [];
|
|
4012
|
-
for (let j = i + 1; j <= Math.min(lines.length - 1, i + contextLines); j++) {
|
|
4013
|
-
match.contextAfter.push([j + 1, lines[j]]);
|
|
4014
|
-
}
|
|
4015
|
-
}
|
|
4016
|
-
results.push(match);
|
|
4017
|
-
}
|
|
4018
|
-
}
|
|
4019
|
-
return results;
|
|
4020
|
-
}
|
|
4021
|
-
function searchInFile(fullPath, displayPath, regex, contextLines, maxResults, results) {
|
|
4022
|
-
try {
|
|
4023
|
-
const stat = statSync5(fullPath);
|
|
4024
|
-
if (stat.size > 1e6) return;
|
|
4025
|
-
} catch {
|
|
4026
|
-
return;
|
|
4027
|
-
}
|
|
4028
|
-
let content;
|
|
4029
|
-
try {
|
|
4030
|
-
content = readFileSync7(fullPath, "utf-8");
|
|
4031
|
-
} catch {
|
|
4032
|
-
return;
|
|
4033
|
-
}
|
|
4034
|
-
const lines = content.split("\n");
|
|
4035
|
-
regex.lastIndex = 0;
|
|
4036
|
-
for (let i = 0; i < lines.length; i++) {
|
|
4037
|
-
if (results.length >= maxResults) return;
|
|
4038
|
-
regex.lastIndex = 0;
|
|
4039
|
-
if (regex.test(lines[i])) {
|
|
4040
|
-
const result = {
|
|
4041
|
-
file: displayPath,
|
|
4042
|
-
lineNumber: i + 1,
|
|
4043
|
-
lineText: lines[i].trimEnd()
|
|
4044
|
-
};
|
|
4045
|
-
if (contextLines > 0) {
|
|
4046
|
-
result.contextBefore = [];
|
|
4047
|
-
result.contextAfter = [];
|
|
4048
|
-
for (let c = Math.max(0, i - contextLines); c < i; c++) {
|
|
4049
|
-
result.contextBefore.push([c + 1, lines[c].trimEnd()]);
|
|
4050
|
-
}
|
|
4051
|
-
for (let c = i + 1; c <= Math.min(lines.length - 1, i + contextLines); c++) {
|
|
4052
|
-
result.contextAfter.push([c + 1, lines[c].trimEnd()]);
|
|
4053
|
-
}
|
|
4054
|
-
}
|
|
4055
|
-
results.push(result);
|
|
4056
|
-
}
|
|
4057
|
-
}
|
|
4058
|
-
}
|
|
4059
3954
|
|
|
4060
3955
|
// src/tools/builtin/glob-files.ts
|
|
4061
|
-
import { readdirSync as readdirSync5, statSync as statSync6, existsSync as
|
|
4062
|
-
import { join as
|
|
3956
|
+
import { readdirSync as readdirSync5, statSync as statSync6, existsSync as existsSync9 } from "fs";
|
|
3957
|
+
import { join as join3, relative as relative2, basename as basename5 } from "path";
|
|
4063
3958
|
var globFilesTool = {
|
|
4064
3959
|
definition: {
|
|
4065
3960
|
name: "glob_files",
|
|
@@ -4092,7 +3987,7 @@ Results sorted by most recent modification time. Automatically skips node_module
|
|
|
4092
3987
|
const rootPath = String(args["path"] ?? process.cwd());
|
|
4093
3988
|
const maxResults = Math.max(1, Number(args["max_results"] ?? 100));
|
|
4094
3989
|
if (!pattern) throw new ToolError("glob_files", "pattern is required");
|
|
4095
|
-
if (!
|
|
3990
|
+
if (!existsSync9(rootPath)) throw new ToolError("glob_files", `Path not found: ${rootPath}`);
|
|
4096
3991
|
const regex = globToRegex(pattern);
|
|
4097
3992
|
const matches = [];
|
|
4098
3993
|
collectMatchingFiles(rootPath, rootPath, regex, matches, maxResults);
|
|
@@ -4169,7 +4064,7 @@ function collectMatchingFiles(dirPath, rootPath, regex, results, maxResults) {
|
|
|
4169
4064
|
}
|
|
4170
4065
|
for (const entry of entries) {
|
|
4171
4066
|
if (results.length >= maxResults) break;
|
|
4172
|
-
const fullPath =
|
|
4067
|
+
const fullPath = join3(dirPath, entry.name);
|
|
4173
4068
|
if (entry.isDirectory()) {
|
|
4174
4069
|
if (SKIP_DIRS2.has(entry.name) || entry.name.startsWith(".")) continue;
|
|
4175
4070
|
collectMatchingFiles(fullPath, rootPath, regex, results, maxResults);
|
|
@@ -4188,9 +4083,9 @@ function collectMatchingFiles(dirPath, rootPath, regex, results, maxResults) {
|
|
|
4188
4083
|
}
|
|
4189
4084
|
|
|
4190
4085
|
// src/tools/builtin/run-interactive.ts
|
|
4191
|
-
import { spawn as
|
|
4192
|
-
import { platform as
|
|
4193
|
-
var IS_WINDOWS4 =
|
|
4086
|
+
import { spawn as spawn3 } from "child_process";
|
|
4087
|
+
import { platform as platform3 } from "os";
|
|
4088
|
+
var IS_WINDOWS4 = platform3() === "win32";
|
|
4194
4089
|
var runInteractiveTool = {
|
|
4195
4090
|
definition: {
|
|
4196
4091
|
name: "run_interactive",
|
|
@@ -4252,7 +4147,7 @@ var runInteractiveTool = {
|
|
|
4252
4147
|
};
|
|
4253
4148
|
const prefixWarnings = [argsTypeWarning, stdinTypeWarning].filter(Boolean).join("");
|
|
4254
4149
|
return new Promise((resolve7) => {
|
|
4255
|
-
const child =
|
|
4150
|
+
const child = spawn3(executable, cmdArgs.map(String), {
|
|
4256
4151
|
cwd: process.cwd(),
|
|
4257
4152
|
env,
|
|
4258
4153
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -4522,8 +4417,8 @@ var webFetchTool = {
|
|
|
4522
4417
|
if (e.message.startsWith("Blocked:")) throw e;
|
|
4523
4418
|
throw new NetworkError(`Invalid URL: "${url}"`);
|
|
4524
4419
|
}
|
|
4525
|
-
const
|
|
4526
|
-
const timeoutId = setTimeout(() =>
|
|
4420
|
+
const controller = new AbortController();
|
|
4421
|
+
const timeoutId = setTimeout(() => controller.abort(), 2e4);
|
|
4527
4422
|
let rawHtml;
|
|
4528
4423
|
let finalUrl;
|
|
4529
4424
|
let contentType;
|
|
@@ -4544,7 +4439,7 @@ var webFetchTool = {
|
|
|
4544
4439
|
}
|
|
4545
4440
|
await resolveAndCheck(parsedHop.hostname);
|
|
4546
4441
|
const r = await fetch(currentUrl, {
|
|
4547
|
-
signal:
|
|
4442
|
+
signal: controller.signal,
|
|
4548
4443
|
headers: FETCH_HEADERS,
|
|
4549
4444
|
redirect: "manual",
|
|
4550
4445
|
// 手动控制重定向
|
|
@@ -4907,8 +4802,8 @@ function parseGoogleResults(html, maxResults) {
|
|
|
4907
4802
|
return results;
|
|
4908
4803
|
}
|
|
4909
4804
|
async function fetchHtml(url) {
|
|
4910
|
-
const
|
|
4911
|
-
const timer = setTimeout(() =>
|
|
4805
|
+
const controller = new AbortController();
|
|
4806
|
+
const timer = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
|
|
4912
4807
|
try {
|
|
4913
4808
|
const res = await fetch(url, {
|
|
4914
4809
|
method: "GET",
|
|
@@ -4917,7 +4812,7 @@ async function fetchHtml(url) {
|
|
|
4917
4812
|
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9",
|
|
4918
4813
|
"Accept-Language": "en-US,en;q=0.9,zh-CN;q=0.8"
|
|
4919
4814
|
},
|
|
4920
|
-
signal:
|
|
4815
|
+
signal: controller.signal
|
|
4921
4816
|
});
|
|
4922
4817
|
if (!res.ok) {
|
|
4923
4818
|
throw new NetworkError(`HTTP ${res.status} ${res.statusText}`, res.status);
|
|
@@ -5011,10 +4906,8 @@ Any of these triggers means use save_last_response, NOT write_file:
|
|
|
5011
4906
|
};
|
|
5012
4907
|
|
|
5013
4908
|
// src/tools/builtin/save-memory.ts
|
|
5014
|
-
import { join as join5 } from "path";
|
|
5015
|
-
import { homedir as homedir3 } from "os";
|
|
5016
4909
|
function getConfigDir() {
|
|
5017
|
-
return
|
|
4910
|
+
return getExecutionConfigDir();
|
|
5018
4911
|
}
|
|
5019
4912
|
function parseSensitivity(value) {
|
|
5020
4913
|
return value === "low" || value === "medium" || value === "high" ? value : void 0;
|
|
@@ -5278,15 +5171,15 @@ var googleSearchTool = {
|
|
|
5278
5171
|
url.searchParams.set("cx", cx);
|
|
5279
5172
|
url.searchParams.set("q", query);
|
|
5280
5173
|
url.searchParams.set("num", String(numResults));
|
|
5281
|
-
const
|
|
5282
|
-
const timeout = setTimeout(() =>
|
|
5174
|
+
const controller = new AbortController();
|
|
5175
|
+
const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS2);
|
|
5283
5176
|
try {
|
|
5284
5177
|
const response = await fetch(url.toString(), {
|
|
5285
5178
|
method: "GET",
|
|
5286
5179
|
headers: {
|
|
5287
5180
|
"Accept": "application/json"
|
|
5288
5181
|
},
|
|
5289
|
-
signal:
|
|
5182
|
+
signal: controller.signal
|
|
5290
5183
|
});
|
|
5291
5184
|
if (!response.ok) {
|
|
5292
5185
|
const errorBody = await response.text().catch(() => "");
|
|
@@ -5363,9 +5256,9 @@ function formatResults2(query, data, _requested) {
|
|
|
5363
5256
|
}
|
|
5364
5257
|
|
|
5365
5258
|
// src/agents/agent-config.ts
|
|
5366
|
-
import { existsSync as
|
|
5367
|
-
import { join as
|
|
5368
|
-
import { homedir as
|
|
5259
|
+
import { existsSync as existsSync10, readdirSync as readdirSync6, readFileSync as readFileSync5 } from "fs";
|
|
5260
|
+
import { join as join4 } from "path";
|
|
5261
|
+
import { homedir as homedir3 } from "os";
|
|
5369
5262
|
var READ_ONLY_TOOLS2 = /* @__PURE__ */ new Set([
|
|
5370
5263
|
"read_file",
|
|
5371
5264
|
"list_dir",
|
|
@@ -5436,13 +5329,13 @@ function normalizeName(name) {
|
|
|
5436
5329
|
function isStringArray(value) {
|
|
5437
5330
|
return Array.isArray(value) && value.every((item) => typeof item === "string");
|
|
5438
5331
|
}
|
|
5439
|
-
function parseAgentConfig(raw,
|
|
5332
|
+
function parseAgentConfig(raw, source2, path3) {
|
|
5440
5333
|
if (!raw || typeof raw !== "object") return null;
|
|
5441
5334
|
const obj = raw;
|
|
5442
5335
|
if (typeof obj.name !== "string" || obj.name.trim() === "") return null;
|
|
5443
5336
|
const cfg = {
|
|
5444
5337
|
name: obj.name.trim(),
|
|
5445
|
-
source,
|
|
5338
|
+
source: source2,
|
|
5446
5339
|
path: path3
|
|
5447
5340
|
};
|
|
5448
5341
|
if (typeof obj.description === "string") cfg.description = obj.description;
|
|
@@ -5458,15 +5351,15 @@ function parseAgentConfig(raw, source, path3) {
|
|
|
5458
5351
|
if (typeof obj.contextPolicy === "string") cfg.contextPolicy = obj.contextPolicy;
|
|
5459
5352
|
return cfg;
|
|
5460
5353
|
}
|
|
5461
|
-
function loadAgentDir(dir,
|
|
5462
|
-
if (!
|
|
5354
|
+
function loadAgentDir(dir, source2) {
|
|
5355
|
+
if (!existsSync10(dir)) return [];
|
|
5463
5356
|
const out = [];
|
|
5464
5357
|
for (const file of readdirSync6(dir)) {
|
|
5465
5358
|
if (!file.endsWith(".json")) continue;
|
|
5466
|
-
const path3 =
|
|
5359
|
+
const path3 = join4(dir, file);
|
|
5467
5360
|
try {
|
|
5468
|
-
const parsed = JSON.parse(
|
|
5469
|
-
const cfg = parseAgentConfig(parsed,
|
|
5361
|
+
const parsed = JSON.parse(readFileSync5(path3, "utf-8"));
|
|
5362
|
+
const cfg = parseAgentConfig(parsed, source2, path3);
|
|
5470
5363
|
if (cfg) out.push(cfg);
|
|
5471
5364
|
} catch {
|
|
5472
5365
|
}
|
|
@@ -5475,8 +5368,8 @@ function loadAgentDir(dir, source) {
|
|
|
5475
5368
|
}
|
|
5476
5369
|
function getAgentDirs(configDir, cwd = process.cwd()) {
|
|
5477
5370
|
return {
|
|
5478
|
-
user:
|
|
5479
|
-
project:
|
|
5371
|
+
user: join4(configDir ?? join4(homedir3(), CONFIG_DIR_NAME), "agents"),
|
|
5372
|
+
project: join4(cwd, CONFIG_DIR_NAME, "agents")
|
|
5480
5373
|
};
|
|
5481
5374
|
}
|
|
5482
5375
|
function listAgentConfigs(configDir, cwd = process.cwd()) {
|
|
@@ -5979,9 +5872,8 @@ var spawnAgentTool = {
|
|
|
5979
5872
|
};
|
|
5980
5873
|
|
|
5981
5874
|
// src/tools/builtin/task-manager.ts
|
|
5982
|
-
import { spawn as
|
|
5875
|
+
import { spawn as spawn4 } from "child_process";
|
|
5983
5876
|
import { randomUUID } from "crypto";
|
|
5984
|
-
import { platform as platform5 } from "os";
|
|
5985
5877
|
var MAX_OUTPUT_CHARS = 1e4;
|
|
5986
5878
|
var MAX_OUTPUT_BYTES = 64 * 1024;
|
|
5987
5879
|
var MAX_TASKS = 20;
|
|
@@ -6000,14 +5892,16 @@ function decodeField(chunks) {
|
|
|
6000
5892
|
}
|
|
6001
5893
|
function appendOutput(task, field, chunk) {
|
|
6002
5894
|
const chunks = field === "stdout" ? task._stdoutChunks : task._stderrChunks;
|
|
6003
|
-
chunks.push(chunk);
|
|
5895
|
+
chunks.push(chunk.length > MAX_OUTPUT_BYTES ? chunk.subarray(-MAX_OUTPUT_BYTES) : chunk);
|
|
6004
5896
|
capChunks(chunks);
|
|
6005
5897
|
task[field] = decodeField(chunks);
|
|
6006
5898
|
}
|
|
6007
5899
|
function createTask(command, description) {
|
|
5900
|
+
if ([...tasks.values()].filter((t) => t._active && t.owner === getExecutionConfigDir()).length >= 10) throw new Error("Background task limit reached (10 running per user).");
|
|
5901
|
+
if ([...tasks.values()].filter((t) => t._active).length >= MAX_TASKS) throw new Error("Background task limit reached (20 running). Stop a task before creating another.");
|
|
6008
5902
|
if (tasks.size >= MAX_TASKS) {
|
|
6009
5903
|
for (const [id2, t] of tasks) {
|
|
6010
|
-
if (t.
|
|
5904
|
+
if (!t._active) {
|
|
6011
5905
|
tasks.delete(id2);
|
|
6012
5906
|
break;
|
|
6013
5907
|
}
|
|
@@ -6015,16 +5909,19 @@ function createTask(command, description) {
|
|
|
6015
5909
|
}
|
|
6016
5910
|
const id = randomUUID().slice(0, 8);
|
|
6017
5911
|
const { shell, args } = shellInvocation(command);
|
|
6018
|
-
const proc =
|
|
5912
|
+
const proc = spawn4(shell, args, {
|
|
6019
5913
|
stdio: ["ignore", "pipe", "pipe"],
|
|
6020
|
-
detached:
|
|
5914
|
+
detached: process.platform !== "win32",
|
|
5915
|
+
windowsHide: true,
|
|
6021
5916
|
env: { ...process.env, PYTHONUTF8: "1", PYTHONIOENCODING: "utf-8" }
|
|
6022
5917
|
});
|
|
6023
5918
|
const task = {
|
|
6024
5919
|
id,
|
|
5920
|
+
owner: getExecutionConfigDir(),
|
|
6025
5921
|
command,
|
|
6026
5922
|
description,
|
|
6027
5923
|
process: proc,
|
|
5924
|
+
_active: true,
|
|
6028
5925
|
startTime: Date.now(),
|
|
6029
5926
|
endTime: null,
|
|
6030
5927
|
status: "running",
|
|
@@ -6037,11 +5934,13 @@ function createTask(command, description) {
|
|
|
6037
5934
|
proc.stdout?.on("data", (chunk) => appendOutput(task, "stdout", chunk));
|
|
6038
5935
|
proc.stderr?.on("data", (chunk) => appendOutput(task, "stderr", chunk));
|
|
6039
5936
|
proc.on("close", (code) => {
|
|
5937
|
+
task._active = false;
|
|
6040
5938
|
task.endTime = Date.now();
|
|
6041
5939
|
task.exitCode = code;
|
|
6042
|
-
task.status = code === 0 ? "completed" : "failed";
|
|
5940
|
+
if (task.status !== "stopped") task.status = code === 0 ? "completed" : "failed";
|
|
6043
5941
|
});
|
|
6044
5942
|
proc.on("error", (err) => {
|
|
5943
|
+
task._active = false;
|
|
6045
5944
|
task.endTime = Date.now();
|
|
6046
5945
|
task.status = "failed";
|
|
6047
5946
|
task.stderr += `
|
|
@@ -6051,43 +5950,20 @@ Process error: ${err.message}`;
|
|
|
6051
5950
|
return task;
|
|
6052
5951
|
}
|
|
6053
5952
|
function listTasks() {
|
|
6054
|
-
return [...tasks.values()].map(
|
|
6055
|
-
({ process: _p, _stdoutChunks: _o, _stderrChunks: _e, ...rest }) => rest
|
|
5953
|
+
return [...tasks.values()].filter((t) => t.owner === getExecutionConfigDir()).map(
|
|
5954
|
+
({ process: _p, _stdoutChunks: _o, _stderrChunks: _e, _active: _a, ...rest }) => rest
|
|
6056
5955
|
);
|
|
6057
5956
|
}
|
|
6058
5957
|
function getTaskOutput(id) {
|
|
6059
5958
|
const task = tasks.get(id);
|
|
6060
|
-
if (!task) return null;
|
|
5959
|
+
if (!task || task.owner !== getExecutionConfigDir()) return null;
|
|
6061
5960
|
return { stdout: task.stdout, stderr: task.stderr, status: task.status };
|
|
6062
5961
|
}
|
|
6063
5962
|
function stopTask(id) {
|
|
6064
5963
|
const task = tasks.get(id);
|
|
6065
|
-
if (!task || task.status !== "running") return false;
|
|
5964
|
+
if (!task || task.owner !== getExecutionConfigDir() || task.status !== "running") return false;
|
|
6066
5965
|
try {
|
|
6067
|
-
|
|
6068
|
-
if (platform5() === "win32" && proc.pid) {
|
|
6069
|
-
try {
|
|
6070
|
-
spawn5("taskkill", ["/PID", String(proc.pid), "/T", "/F"], {
|
|
6071
|
-
windowsHide: true,
|
|
6072
|
-
stdio: "ignore"
|
|
6073
|
-
});
|
|
6074
|
-
} catch {
|
|
6075
|
-
}
|
|
6076
|
-
try {
|
|
6077
|
-
proc.kill();
|
|
6078
|
-
} catch {
|
|
6079
|
-
}
|
|
6080
|
-
} else {
|
|
6081
|
-
proc.kill("SIGTERM");
|
|
6082
|
-
setTimeout(() => {
|
|
6083
|
-
if (proc.exitCode === null && proc.signalCode === null) {
|
|
6084
|
-
try {
|
|
6085
|
-
proc.kill("SIGKILL");
|
|
6086
|
-
} catch {
|
|
6087
|
-
}
|
|
6088
|
-
}
|
|
6089
|
-
}, 3e3).unref();
|
|
6090
|
-
}
|
|
5966
|
+
killProcessTree(task.process, true);
|
|
6091
5967
|
task.status = "stopped";
|
|
6092
5968
|
task.endTime = Date.now();
|
|
6093
5969
|
return true;
|
|
@@ -6195,14 +6071,14 @@ var taskStopTool = {
|
|
|
6195
6071
|
|
|
6196
6072
|
// src/tools/builtin/git-tools.ts
|
|
6197
6073
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
6198
|
-
import { existsSync as
|
|
6199
|
-
import { join as
|
|
6074
|
+
import { existsSync as existsSync11 } from "fs";
|
|
6075
|
+
import { join as join5 } from "path";
|
|
6200
6076
|
function assertGitRepo(cwd) {
|
|
6201
6077
|
let dir = cwd;
|
|
6202
6078
|
const root = dir.split(/[\\/]/)[0] + (dir.includes("\\") ? "\\" : "/");
|
|
6203
6079
|
while (dir && dir !== root) {
|
|
6204
|
-
if (
|
|
6205
|
-
const parent =
|
|
6080
|
+
if (existsSync11(join5(dir, ".git"))) return;
|
|
6081
|
+
const parent = join5(dir, "..");
|
|
6206
6082
|
if (parent === dir) break;
|
|
6207
6083
|
dir = parent;
|
|
6208
6084
|
}
|
|
@@ -6467,7 +6343,7 @@ ${commitOutput.trim()}`;
|
|
|
6467
6343
|
};
|
|
6468
6344
|
|
|
6469
6345
|
// src/tools/builtin/notebook-edit.ts
|
|
6470
|
-
import { readFileSync as
|
|
6346
|
+
import { readFileSync as readFileSync6, existsSync as existsSync12 } from "fs";
|
|
6471
6347
|
import { writeFile } from "fs/promises";
|
|
6472
6348
|
import { resolve as resolve6, extname as extname2 } from "path";
|
|
6473
6349
|
var notebookEditTool = {
|
|
@@ -6522,10 +6398,10 @@ var notebookEditTool = {
|
|
|
6522
6398
|
if (extname2(absPath).toLowerCase() !== ".ipynb") {
|
|
6523
6399
|
throw new ToolError("notebook_edit", "path must point to a .ipynb file");
|
|
6524
6400
|
}
|
|
6525
|
-
if (!
|
|
6401
|
+
if (!existsSync12(absPath)) {
|
|
6526
6402
|
throw new ToolError("notebook_edit", `Notebook not found: ${filePath}`);
|
|
6527
6403
|
}
|
|
6528
|
-
const raw =
|
|
6404
|
+
const raw = readFileSync6(absPath, "utf-8");
|
|
6529
6405
|
const nb = parseNotebook(raw);
|
|
6530
6406
|
const cellIdx0 = cellIndexRaw - 1;
|
|
6531
6407
|
undoStack.push(absPath, `notebook_edit (${action}): ${filePath}`);
|
|
@@ -6851,8 +6727,6 @@ ${lines.join("\n")}`;
|
|
|
6851
6727
|
};
|
|
6852
6728
|
|
|
6853
6729
|
// src/tools/builtin/recall-memory.ts
|
|
6854
|
-
import { join as join8 } from "path";
|
|
6855
|
-
import { homedir as homedir5 } from "os";
|
|
6856
6730
|
function formatHit(h, i) {
|
|
6857
6731
|
const ts = h.chunk.timestamp.slice(0, 16).replace("T", " ");
|
|
6858
6732
|
const title = h.chunk.sessionTitle ? ` \xB7 ${h.chunk.sessionTitle}` : "";
|
|
@@ -6902,7 +6776,7 @@ var recallMemoryTool = {
|
|
|
6902
6776
|
const excludeCurrent = Boolean(args["excludeCurrentSession"]);
|
|
6903
6777
|
const currentId = args["currentSessionId"] ? String(args["currentSessionId"]) : void 0;
|
|
6904
6778
|
const minScore = args["minScore"] !== void 0 ? Number(args["minScore"]) : 0.25;
|
|
6905
|
-
const configDir =
|
|
6779
|
+
const configDir = getExecutionConfigDir();
|
|
6906
6780
|
let persistentSection = "";
|
|
6907
6781
|
try {
|
|
6908
6782
|
const persistentHits = searchPersistentMemories(configDir, query, process.cwd(), 3);
|
|
@@ -6956,8 +6830,8 @@ function estimateToolDefinitionTokens(def) {
|
|
|
6956
6830
|
|
|
6957
6831
|
// src/tools/registry.ts
|
|
6958
6832
|
import { pathToFileURL } from "url";
|
|
6959
|
-
import { existsSync as
|
|
6960
|
-
import { join as
|
|
6833
|
+
import { existsSync as existsSync13, mkdirSync as mkdirSync3, readdirSync as readdirSync7 } from "fs";
|
|
6834
|
+
import { join as join6 } from "path";
|
|
6961
6835
|
var ToolRegistry = class {
|
|
6962
6836
|
tools = /* @__PURE__ */ new Map();
|
|
6963
6837
|
pluginToolNames = /* @__PURE__ */ new Set();
|
|
@@ -7118,7 +6992,7 @@ var ToolRegistry = class {
|
|
|
7118
6992
|
* Returns the number of successfully loaded plugins.
|
|
7119
6993
|
*/
|
|
7120
6994
|
async loadPlugins(pluginsDir, allowPlugins = false) {
|
|
7121
|
-
if (!
|
|
6995
|
+
if (!existsSync13(pluginsDir)) {
|
|
7122
6996
|
try {
|
|
7123
6997
|
mkdirSync3(pluginsDir, { recursive: true });
|
|
7124
6998
|
} catch {
|
|
@@ -7144,12 +7018,12 @@ var ToolRegistry = class {
|
|
|
7144
7018
|
process.stderr.write(
|
|
7145
7019
|
`
|
|
7146
7020
|
[plugins] \u26A0 Loading ${files.length} plugin(s) with FULL system privileges:
|
|
7147
|
-
` + files.map((f) => ` + ${
|
|
7021
|
+
` + files.map((f) => ` + ${join6(pluginsDir, f)}`).join("\n") + "\n\n"
|
|
7148
7022
|
);
|
|
7149
7023
|
let loaded = 0;
|
|
7150
7024
|
for (const file of files) {
|
|
7151
7025
|
try {
|
|
7152
|
-
const fileUrl = pathToFileURL(
|
|
7026
|
+
const fileUrl = pathToFileURL(join6(pluginsDir, file)).href;
|
|
7153
7027
|
const mod = await import(fileUrl);
|
|
7154
7028
|
const tool = mod.tool ?? mod.default?.tool ?? mod.default;
|
|
7155
7029
|
if (!tool || typeof tool.execute !== "function" || !tool.definition?.name) {
|
|
@@ -7190,10 +7064,6 @@ export {
|
|
|
7190
7064
|
requestAgentStop,
|
|
7191
7065
|
setPreferredAgentName,
|
|
7192
7066
|
getPreferredAgentName,
|
|
7193
|
-
isInterrupted,
|
|
7194
|
-
requestInterrupt,
|
|
7195
|
-
resetInterrupt,
|
|
7196
|
-
runWithSessionKey,
|
|
7197
7067
|
clearBashSession,
|
|
7198
7068
|
rlInternal,
|
|
7199
7069
|
groupCallsByPhase,
|