@cydm/pie 1.0.24 → 1.0.25
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 +23 -5
- package/dist/builtin/extensions/ask-user/index.js +3 -4
- package/dist/builtin/extensions/ask-user/package.json +16 -0
- package/dist/builtin/extensions/changelog/index.js +65 -65
- package/dist/builtin/extensions/changelog/package.json +11 -0
- package/dist/builtin/extensions/deploy/package.json +11 -0
- package/dist/builtin/extensions/document-attachments/package.json +11 -0
- package/dist/builtin/extensions/files/package.json +14 -0
- package/dist/builtin/extensions/init/index.js +33 -33
- package/dist/builtin/extensions/init/package.json +11 -0
- package/dist/builtin/extensions/kimi-attachments/index.js +1 -1
- package/dist/builtin/extensions/kimi-attachments/package.json +11 -0
- package/dist/builtin/extensions/plan-mode/index.js +5 -34
- package/dist/builtin/extensions/plan-mode/package.json +11 -0
- package/dist/builtin/extensions/subagent/index.js +4 -5
- package/dist/builtin/extensions/subagent/package.json +14 -0
- package/dist/builtin/extensions/todo/index.js +3 -4
- package/dist/builtin/extensions/todo/package.json +11 -0
- package/dist/chunks/{chunk-X44YTB6R.js → chunk-4B42ZU4B.js} +1 -1
- package/dist/chunks/{chunk-HAAYC2N5.js → chunk-64XJIKVK.js} +334 -99
- package/dist/chunks/{chunk-QYYRJ7JW.js → chunk-BEWVRLCP.js} +375 -252
- package/dist/chunks/{chunk-XWNIIJ3X.js → chunk-O4DPFYYP.js} +7511 -6681
- package/dist/chunks/{src-PENSDHBM.js → src-27UD2ZBZ.js} +2 -2
- package/dist/chunks/{test-stream-PRUYMLXQ.js → test-stream-ZNGAUUVH.js} +6 -2
- package/dist/cli.js +2215 -1698
- package/dist/{chunks/typescript-GSKWJIO4.js → code-intel-worker.cjs} +2507 -2031
- package/models.schema.json +38 -13
- package/package.json +10 -6
- package/dist/chunks/chunk-J4P2LP7K.js +0 -3846
- package/dist/chunks/chunk-QMIIW75M.js +0 -95
- package/dist/session-preview-worker.js +0 -159
|
@@ -2,18 +2,15 @@ import { createRequire as __createRequire } from "node:module"; const require =
|
|
|
2
2
|
import {
|
|
3
3
|
classifyShellCommand,
|
|
4
4
|
createCodeIntelTool,
|
|
5
|
-
createLightweightCodeIntelProvider,
|
|
6
5
|
createSharedWebFetchTool,
|
|
7
6
|
createSharedWebResearchTool,
|
|
8
7
|
createSharedWebSearchTool,
|
|
9
|
-
interpretShellExit
|
|
10
|
-
} from "./chunk-XWNIIJ3X.js";
|
|
11
|
-
import {
|
|
8
|
+
interpretShellExit,
|
|
12
9
|
requestInteraction
|
|
13
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-O4DPFYYP.js";
|
|
14
11
|
import {
|
|
15
12
|
Type
|
|
16
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-64XJIKVK.js";
|
|
17
14
|
|
|
18
15
|
// src/config.ts
|
|
19
16
|
import { existsSync, mkdirSync, readFileSync, renameSync } from "fs";
|
|
@@ -130,14 +127,54 @@ function migrateConfigFromAgentDir() {
|
|
|
130
127
|
|
|
131
128
|
// src/capabilities/bash/index.ts
|
|
132
129
|
import { spawn } from "child_process";
|
|
133
|
-
import { createWriteStream, existsSync as existsSync2, statSync, writeFileSync } from "fs";
|
|
134
|
-
import { tmpdir
|
|
130
|
+
import { closeSync, createWriteStream, existsSync as existsSync2, openSync, statSync, unlinkSync, writeFileSync } from "fs";
|
|
131
|
+
import { tmpdir } from "os";
|
|
135
132
|
import { join as join2, resolve as resolve2 } from "path";
|
|
136
|
-
import { randomBytes } from "crypto";
|
|
133
|
+
import { createHash, randomBytes } from "crypto";
|
|
137
134
|
|
|
138
135
|
// src/capabilities/bash/truncate.ts
|
|
139
|
-
var DEFAULT_MAX_LINES =
|
|
140
|
-
var DEFAULT_MAX_BYTES =
|
|
136
|
+
var DEFAULT_MAX_LINES = 400;
|
|
137
|
+
var DEFAULT_MAX_BYTES = 16 * 1024;
|
|
138
|
+
function truncateHeadTail(content, options = {}) {
|
|
139
|
+
const maxLines = options.maxLines ?? DEFAULT_MAX_LINES;
|
|
140
|
+
const maxBytes = options.maxBytes ?? DEFAULT_MAX_BYTES;
|
|
141
|
+
const totalBytes = Buffer.byteLength(content, "utf-8");
|
|
142
|
+
const lines = content.split("\n");
|
|
143
|
+
if (lines.length <= maxLines && totalBytes <= maxBytes) return truncateTail(content, options);
|
|
144
|
+
const headCount = Math.max(1, Math.floor(maxLines / 2));
|
|
145
|
+
const tailCount = Math.max(1, maxLines - headCount);
|
|
146
|
+
let head = lines.slice(0, headCount);
|
|
147
|
+
let tail = lines.slice(-tailCount);
|
|
148
|
+
const omittedNotice = () => `... [truncated: ${Math.max(0, lines.length - head.length - tail.length)} middle lines omitted] ...`;
|
|
149
|
+
let combined = () => [...head, omittedNotice(), ...tail].join("\n");
|
|
150
|
+
while (Buffer.byteLength(combined(), "utf-8") > maxBytes && (head.length > 1 || tail.length > 1)) {
|
|
151
|
+
if (head.length >= tail.length && head.length > 1) head.pop();
|
|
152
|
+
else if (tail.length > 1) tail.shift();
|
|
153
|
+
}
|
|
154
|
+
let output = combined();
|
|
155
|
+
let lastLinePartial = false;
|
|
156
|
+
if (Buffer.byteLength(output, "utf-8") > maxBytes) {
|
|
157
|
+
const marker = "\n... [truncated middle] ...\n";
|
|
158
|
+
const available = Math.max(2, maxBytes - Buffer.byteLength(marker, "utf-8"));
|
|
159
|
+
const bytes = Buffer.from(content, "utf-8");
|
|
160
|
+
const prefix = bytes.subarray(0, Math.floor(available / 2)).toString("utf-8").replace(/\uFFFD+$/g, "");
|
|
161
|
+
const suffix = bytes.subarray(Math.max(0, bytes.length - Math.ceil(available / 2))).toString("utf-8").replace(/^\uFFFD+/g, "");
|
|
162
|
+
output = `${prefix}${marker}${suffix}`;
|
|
163
|
+
lastLinePartial = true;
|
|
164
|
+
}
|
|
165
|
+
return {
|
|
166
|
+
content: output,
|
|
167
|
+
truncated: true,
|
|
168
|
+
truncatedBy: lines.length > maxLines ? "lines" : "bytes",
|
|
169
|
+
totalLines: lines.length,
|
|
170
|
+
totalBytes,
|
|
171
|
+
outputLines: head.length + tail.length,
|
|
172
|
+
outputBytes: Buffer.byteLength(output, "utf-8"),
|
|
173
|
+
lastLinePartial,
|
|
174
|
+
maxLines,
|
|
175
|
+
maxBytes
|
|
176
|
+
};
|
|
177
|
+
}
|
|
141
178
|
function formatSize(bytes) {
|
|
142
179
|
if (bytes < 1024) {
|
|
143
180
|
return `${bytes}B`;
|
|
@@ -222,7 +259,7 @@ var bashSchema = Type.Object({
|
|
|
222
259
|
command: Type.String({ description: "The bash command to execute" }),
|
|
223
260
|
timeout: Type.Optional(
|
|
224
261
|
Type.Number({
|
|
225
|
-
description: "Timeout in seconds. Default: 120s (2 minutes).
|
|
262
|
+
description: "Timeout in seconds. Default: 120s (2 minutes). Longer timeouts are honored for installs, builds, and tests; generic custom scripts and polling commands are capped to protect the turn budget."
|
|
226
263
|
})
|
|
227
264
|
),
|
|
228
265
|
cwd: Type.Optional(Type.String({ description: "Working directory for the command" })),
|
|
@@ -234,6 +271,23 @@ var bashSchema = Type.Object({
|
|
|
234
271
|
], { description: "Shell to use. Defaults to bash on macOS/Linux and PowerShell on Windows. Use this only for platform-specific commands." }))
|
|
235
272
|
});
|
|
236
273
|
var DEFAULT_TIMEOUT_SECONDS = 120;
|
|
274
|
+
var MAX_GENERIC_TIMEOUT_SECONDS = 120;
|
|
275
|
+
function resolveTimeoutPolicy(requestedTimeout, commandClass) {
|
|
276
|
+
const requestedSeconds = requestedTimeout ?? DEFAULT_TIMEOUT_SECONDS;
|
|
277
|
+
if (requestedSeconds > MAX_GENERIC_TIMEOUT_SECONDS && commandClass !== "package_manager" && commandClass !== "test_or_build") {
|
|
278
|
+
return {
|
|
279
|
+
requestedSeconds,
|
|
280
|
+
effectiveSeconds: MAX_GENERIC_TIMEOUT_SECONDS,
|
|
281
|
+
capped: true,
|
|
282
|
+
reason: `long timeouts are capped at ${MAX_GENERIC_TIMEOUT_SECONDS}s for non-build commands`
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
return {
|
|
286
|
+
requestedSeconds,
|
|
287
|
+
effectiveSeconds: requestedSeconds,
|
|
288
|
+
capped: false
|
|
289
|
+
};
|
|
290
|
+
}
|
|
237
291
|
function validateBashCommand(command) {
|
|
238
292
|
const value = typeof command === "string" ? command : "";
|
|
239
293
|
const trimmed = value.trim();
|
|
@@ -307,18 +361,95 @@ function getTempFilePath() {
|
|
|
307
361
|
const id = randomBytes(8).toString("hex");
|
|
308
362
|
return join2(tmpdir(), `pie-bash-${id}.log`);
|
|
309
363
|
}
|
|
364
|
+
var retainedOutputArtifacts = /* @__PURE__ */ new Set();
|
|
365
|
+
function getErrorCode(error) {
|
|
366
|
+
if (!error || typeof error !== "object" || !("code" in error)) return void 0;
|
|
367
|
+
const code = error.code;
|
|
368
|
+
return typeof code === "string" ? code : void 0;
|
|
369
|
+
}
|
|
370
|
+
function createPrivateOutputArtifact() {
|
|
371
|
+
for (let attempt = 0; attempt < 10; attempt++) {
|
|
372
|
+
const path3 = getTempFilePath();
|
|
373
|
+
try {
|
|
374
|
+
const fd = openSync(path3, "wx", 384);
|
|
375
|
+
retainedOutputArtifacts.add(path3);
|
|
376
|
+
try {
|
|
377
|
+
return { path: path3, stream: createWriteStream(path3, { fd, autoClose: true }) };
|
|
378
|
+
} catch (error) {
|
|
379
|
+
closeSync(fd);
|
|
380
|
+
removeOutputArtifact(path3);
|
|
381
|
+
throw error;
|
|
382
|
+
}
|
|
383
|
+
} catch (error) {
|
|
384
|
+
if (getErrorCode(error) !== "EEXIST") throw error;
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
throw new Error("Unable to allocate a private bash output artifact.");
|
|
388
|
+
}
|
|
389
|
+
function removeOutputArtifact(path3) {
|
|
390
|
+
if (!path3) return;
|
|
391
|
+
try {
|
|
392
|
+
unlinkSync(path3);
|
|
393
|
+
} catch (error) {
|
|
394
|
+
if (getErrorCode(error) !== "ENOENT") throw error;
|
|
395
|
+
}
|
|
396
|
+
retainedOutputArtifacts.delete(path3);
|
|
397
|
+
}
|
|
398
|
+
function discardOutputArtifact(path3, stream) {
|
|
399
|
+
if (stream && !stream.closed) {
|
|
400
|
+
stream.once("close", () => {
|
|
401
|
+
try {
|
|
402
|
+
removeOutputArtifact(path3);
|
|
403
|
+
} catch {
|
|
404
|
+
}
|
|
405
|
+
});
|
|
406
|
+
stream.destroy();
|
|
407
|
+
}
|
|
408
|
+
try {
|
|
409
|
+
removeOutputArtifact(path3);
|
|
410
|
+
} catch {
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
function cleanupBashOutputArtifacts() {
|
|
414
|
+
for (const path3 of [...retainedOutputArtifacts]) {
|
|
415
|
+
try {
|
|
416
|
+
removeOutputArtifact(path3);
|
|
417
|
+
} catch {
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
process.once("exit", cleanupBashOutputArtifacts);
|
|
310
422
|
function finishTempFileStream(stream) {
|
|
311
|
-
if (!stream) return Promise.resolve();
|
|
312
|
-
return new Promise((
|
|
313
|
-
|
|
423
|
+
if (!stream || stream.closed) return Promise.resolve();
|
|
424
|
+
return new Promise((resolve3, reject) => {
|
|
425
|
+
let done = false;
|
|
426
|
+
const cleanup = () => {
|
|
427
|
+
stream.off("close", onClose);
|
|
314
428
|
stream.off("error", onError);
|
|
315
|
-
reject(error);
|
|
316
429
|
};
|
|
430
|
+
const onClose = () => {
|
|
431
|
+
if (done) return;
|
|
432
|
+
done = true;
|
|
433
|
+
cleanup();
|
|
434
|
+
resolve3();
|
|
435
|
+
};
|
|
436
|
+
const onError = (error) => {
|
|
437
|
+
if (done) return;
|
|
438
|
+
done = true;
|
|
439
|
+
cleanup();
|
|
440
|
+
reject(error instanceof Error ? error : new Error(String(error)));
|
|
441
|
+
};
|
|
442
|
+
stream.once("close", onClose);
|
|
317
443
|
stream.once("error", onError);
|
|
318
|
-
|
|
319
|
-
stream.
|
|
320
|
-
|
|
321
|
-
|
|
444
|
+
try {
|
|
445
|
+
if (!stream.writableEnded) {
|
|
446
|
+
stream.end();
|
|
447
|
+
} else if (stream.closed) {
|
|
448
|
+
onClose();
|
|
449
|
+
}
|
|
450
|
+
} catch (error) {
|
|
451
|
+
onError(error);
|
|
452
|
+
}
|
|
322
453
|
});
|
|
323
454
|
}
|
|
324
455
|
function buildBashRecoveryHint(command, output) {
|
|
@@ -351,10 +482,22 @@ function killProcessTree(pid) {
|
|
|
351
482
|
}
|
|
352
483
|
}
|
|
353
484
|
function createBashTool(defaultCwd, options) {
|
|
485
|
+
const recentCommands = /* @__PURE__ */ new Map();
|
|
486
|
+
const rememberCommandResult = (key, fingerprint) => {
|
|
487
|
+
const previous = recentCommands.get(key);
|
|
488
|
+
const count = (previous?.count ?? 0) + 1;
|
|
489
|
+
const sameResult = previous?.fingerprint === fingerprint;
|
|
490
|
+
recentCommands.set(key, { count, fingerprint });
|
|
491
|
+
if (recentCommands.size > 100) {
|
|
492
|
+
const oldest = recentCommands.keys().next().value;
|
|
493
|
+
if (oldest) recentCommands.delete(oldest);
|
|
494
|
+
}
|
|
495
|
+
return { count, sameResult };
|
|
496
|
+
};
|
|
354
497
|
return {
|
|
355
498
|
name: "bash",
|
|
356
499
|
label: "bash",
|
|
357
|
-
description: `Execute a shell command in the current working directory. The public tool name remains bash for compatibility; on Windows shell:auto uses PowerShell, otherwise bash. Returns stdout and stderr
|
|
500
|
+
description: `Execute a shell command in the current working directory. The public tool name remains bash for compatibility; on Windows shell:auto uses PowerShell, otherwise bash. Returns stdout and stderr as a bounded head/tail preview of at most ${DEFAULT_MAX_LINES} lines or ${DEFAULT_MAX_BYTES / 1024}KB. If truncated, the result includes a private full-output artifact path. Default timeout: 120s. Long timeouts are reserved for installs, builds, and tests; custom scripts, searches, and polling commands are capped at ${MAX_GENERIC_TIMEOUT_SECONDS}s and should be split into bounded steps with progress output.`,
|
|
358
501
|
parameters: bashSchema,
|
|
359
502
|
execute: async (_toolCallId, { command, timeout, cwd, shell }, signal, onUpdate) => {
|
|
360
503
|
validateBashCommand(command);
|
|
@@ -363,34 +506,9 @@ function createBashTool(defaultCwd, options) {
|
|
|
363
506
|
const workingDir = normalizeWorkingDirectory(cwd, defaultCwd);
|
|
364
507
|
validateWorkingDirectory(workingDir);
|
|
365
508
|
const risk = classifyShellCommand(effectiveCommand, resolvedShell, {
|
|
366
|
-
homeDir: homedir2(),
|
|
367
509
|
cwd: workingDir,
|
|
368
|
-
|
|
369
|
-
pathStyle: process.platform === "win32" ? "windows" : "posix"
|
|
510
|
+
sandboxRoot: defaultCwd ?? workingDir
|
|
370
511
|
});
|
|
371
|
-
if (risk.destructive && risk.catastrophic) {
|
|
372
|
-
const targetExplanation = risk.catastrophicReason === "indeterminate_target" ? "The agent shell tool cannot statically prove every deletion target is scoped" : "The command targets a filesystem root, home directory, or system directory";
|
|
373
|
-
return {
|
|
374
|
-
content: [{
|
|
375
|
-
type: "text",
|
|
376
|
-
text: `Blocked catastrophic shell command (${risk.commandClass}): ${risk.riskWarnings.join("; ")}. ${targetExplanation} and is never executed, even in YOLO mode, by the agent shell tool. Narrow the target to a statically resolvable subdirectory and retry.`
|
|
377
|
-
}],
|
|
378
|
-
details: {
|
|
379
|
-
exitCode: null,
|
|
380
|
-
signal: null,
|
|
381
|
-
timedOut: false,
|
|
382
|
-
shell: resolvedShell,
|
|
383
|
-
commandClass: risk.commandClass,
|
|
384
|
-
riskWarnings: risk.riskWarnings,
|
|
385
|
-
permissionDecision: {
|
|
386
|
-
allowed: false,
|
|
387
|
-
reason: "catastrophic command blocked by shell policy",
|
|
388
|
-
requiresPermission: true
|
|
389
|
-
}
|
|
390
|
-
},
|
|
391
|
-
isError: true
|
|
392
|
-
};
|
|
393
|
-
}
|
|
394
512
|
if (risk.destructive && !options?.isYoloMode?.()) {
|
|
395
513
|
const approved = await (options?.confirm ?? requestHighRiskConfirmation)({
|
|
396
514
|
command: effectiveCommand,
|
|
@@ -421,18 +539,31 @@ function createBashTool(defaultCwd, options) {
|
|
|
421
539
|
};
|
|
422
540
|
}
|
|
423
541
|
}
|
|
424
|
-
const
|
|
425
|
-
|
|
542
|
+
const timeoutPolicy = resolveTimeoutPolicy(timeout, risk.commandClass);
|
|
543
|
+
const timeoutMs = timeoutPolicy.effectiveSeconds * 1e3;
|
|
544
|
+
return new Promise((resolve3, reject) => {
|
|
426
545
|
if (signal?.aborted) {
|
|
427
546
|
reject(new Error("Operation aborted"));
|
|
428
547
|
return;
|
|
429
548
|
}
|
|
430
549
|
let tempFilePath;
|
|
431
550
|
let tempFileStream;
|
|
551
|
+
let settled = false;
|
|
432
552
|
let totalBytes = 0;
|
|
433
553
|
const chunks = [];
|
|
434
554
|
let chunksBytes = 0;
|
|
435
555
|
const maxChunksBytes = DEFAULT_MAX_BYTES * 2;
|
|
556
|
+
const headChunks = [];
|
|
557
|
+
let headBytes = 0;
|
|
558
|
+
let totalNewlines = 0;
|
|
559
|
+
const maxHeadBytes = Math.floor(DEFAULT_MAX_BYTES / 2);
|
|
560
|
+
const captureHead = (data) => {
|
|
561
|
+
totalNewlines += data.reduce((count, byte) => count + (byte === 10 ? 1 : 0), 0);
|
|
562
|
+
if (headBytes >= maxHeadBytes) return;
|
|
563
|
+
const prefix = data.subarray(0, maxHeadBytes - headBytes);
|
|
564
|
+
headChunks.push(prefix);
|
|
565
|
+
headBytes += prefix.length;
|
|
566
|
+
};
|
|
436
567
|
let timedOut = false;
|
|
437
568
|
const spawnCommand = buildSpawnCommand(resolvedShell, effectiveCommand);
|
|
438
569
|
const child = (options?.spawnProcess ?? spawn)(spawnCommand.file, spawnCommand.args, {
|
|
@@ -447,19 +578,33 @@ function createBashTool(defaultCwd, options) {
|
|
|
447
578
|
killProcessTree(child.pid);
|
|
448
579
|
}
|
|
449
580
|
}, timeoutMs);
|
|
450
|
-
const
|
|
581
|
+
const failExecution = (error) => {
|
|
582
|
+
if (settled) return;
|
|
583
|
+
settled = true;
|
|
451
584
|
clearTimeout(timeoutId);
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
reject(
|
|
585
|
+
signal?.removeEventListener("abort", onAbort);
|
|
586
|
+
if (child.pid) killProcessTree(child.pid);
|
|
587
|
+
discardOutputArtifact(tempFilePath, tempFileStream);
|
|
588
|
+
reject(error);
|
|
589
|
+
};
|
|
590
|
+
const onAbort = () => {
|
|
591
|
+
failExecution(new Error("Operation aborted"));
|
|
456
592
|
};
|
|
457
593
|
signal?.addEventListener("abort", onAbort, { once: true });
|
|
458
594
|
child.stdout?.on("data", (data) => {
|
|
595
|
+
if (settled) return;
|
|
459
596
|
totalBytes += data.length;
|
|
597
|
+
captureHead(data);
|
|
460
598
|
if (totalBytes > DEFAULT_MAX_BYTES && !tempFilePath) {
|
|
461
|
-
|
|
462
|
-
|
|
599
|
+
try {
|
|
600
|
+
const artifact = createPrivateOutputArtifact();
|
|
601
|
+
tempFilePath = artifact.path;
|
|
602
|
+
tempFileStream = artifact.stream;
|
|
603
|
+
tempFileStream.once("error", failExecution);
|
|
604
|
+
} catch (error) {
|
|
605
|
+
failExecution(error instanceof Error ? error : new Error(String(error)));
|
|
606
|
+
return;
|
|
607
|
+
}
|
|
463
608
|
for (const chunk of chunks) {
|
|
464
609
|
tempFileStream.write(chunk);
|
|
465
610
|
}
|
|
@@ -476,7 +621,7 @@ function createBashTool(defaultCwd, options) {
|
|
|
476
621
|
if (onUpdate) {
|
|
477
622
|
const fullBuffer = Buffer.concat(chunks);
|
|
478
623
|
const fullText = fullBuffer.toString("utf-8");
|
|
479
|
-
const truncation =
|
|
624
|
+
const truncation = truncateHeadTail(fullText);
|
|
480
625
|
onUpdate({
|
|
481
626
|
content: [{ type: "text", text: truncation.content || "" }],
|
|
482
627
|
details: {
|
|
@@ -493,6 +638,7 @@ function createBashTool(defaultCwd, options) {
|
|
|
493
638
|
shell: resolvedShell,
|
|
494
639
|
commandClass: risk.commandClass,
|
|
495
640
|
riskWarnings: risk.riskWarnings,
|
|
641
|
+
timeoutPolicy,
|
|
496
642
|
permissionDecision: {
|
|
497
643
|
allowed: true,
|
|
498
644
|
requiresPermission: risk.requiresPermission
|
|
@@ -502,10 +648,19 @@ function createBashTool(defaultCwd, options) {
|
|
|
502
648
|
}
|
|
503
649
|
});
|
|
504
650
|
child.stderr?.on("data", (data) => {
|
|
651
|
+
if (settled) return;
|
|
505
652
|
totalBytes += data.length;
|
|
653
|
+
captureHead(data);
|
|
506
654
|
if (totalBytes > DEFAULT_MAX_BYTES && !tempFilePath) {
|
|
507
|
-
|
|
508
|
-
|
|
655
|
+
try {
|
|
656
|
+
const artifact = createPrivateOutputArtifact();
|
|
657
|
+
tempFilePath = artifact.path;
|
|
658
|
+
tempFileStream = artifact.stream;
|
|
659
|
+
tempFileStream.once("error", failExecution);
|
|
660
|
+
} catch (error) {
|
|
661
|
+
failExecution(error instanceof Error ? error : new Error(String(error)));
|
|
662
|
+
return;
|
|
663
|
+
}
|
|
509
664
|
for (const chunk of chunks) {
|
|
510
665
|
tempFileStream.write(chunk);
|
|
511
666
|
}
|
|
@@ -524,13 +679,32 @@ function createBashTool(defaultCwd, options) {
|
|
|
524
679
|
try {
|
|
525
680
|
clearTimeout(timeoutId);
|
|
526
681
|
signal?.removeEventListener("abort", onAbort);
|
|
682
|
+
if (settled) return;
|
|
527
683
|
const fullBuffer = Buffer.concat(chunks);
|
|
528
684
|
let fullOutput = fullBuffer.toString("utf-8");
|
|
529
|
-
|
|
685
|
+
if (totalBytes > fullBuffer.length) {
|
|
686
|
+
fullOutput = `${Buffer.concat(headChunks).toString("utf-8")}
|
|
687
|
+
... [stream middle omitted] ...
|
|
688
|
+
${fullOutput}`;
|
|
689
|
+
}
|
|
690
|
+
const truncation = truncateHeadTail(fullOutput);
|
|
691
|
+
if (totalBytes > truncation.totalBytes) {
|
|
692
|
+
truncation.truncated = true;
|
|
693
|
+
truncation.totalBytes = totalBytes;
|
|
694
|
+
truncation.totalLines = totalNewlines + 1;
|
|
695
|
+
truncation.truncatedBy = totalNewlines + 1 > DEFAULT_MAX_LINES ? "lines" : "bytes";
|
|
696
|
+
}
|
|
530
697
|
let outputText = truncation.content || "(no output)";
|
|
531
698
|
if (truncation.truncated && !tempFilePath) {
|
|
532
|
-
|
|
533
|
-
|
|
699
|
+
const path3 = getTempFilePath();
|
|
700
|
+
const fd = openSync(path3, "wx", 384);
|
|
701
|
+
tempFilePath = path3;
|
|
702
|
+
retainedOutputArtifacts.add(path3);
|
|
703
|
+
try {
|
|
704
|
+
writeFileSync(fd, fullOutput, "utf-8");
|
|
705
|
+
} finally {
|
|
706
|
+
closeSync(fd);
|
|
707
|
+
}
|
|
534
708
|
}
|
|
535
709
|
await finishTempFileStream(tempFileStream);
|
|
536
710
|
const details = {
|
|
@@ -540,6 +714,7 @@ function createBashTool(defaultCwd, options) {
|
|
|
540
714
|
shell: resolvedShell,
|
|
541
715
|
commandClass: risk.commandClass,
|
|
542
716
|
riskWarnings: risk.riskWarnings,
|
|
717
|
+
timeoutPolicy,
|
|
543
718
|
permissionDecision: {
|
|
544
719
|
allowed: true,
|
|
545
720
|
requiresPermission: risk.requiresPermission
|
|
@@ -558,29 +733,26 @@ function createBashTool(defaultCwd, options) {
|
|
|
558
733
|
outputLines: truncation.outputLines
|
|
559
734
|
};
|
|
560
735
|
details.fullOutputPath = tempFilePath;
|
|
561
|
-
const startLine = truncation.totalLines - truncation.outputLines + 1;
|
|
562
|
-
const endLine = truncation.totalLines;
|
|
563
736
|
if (truncation.lastLinePartial) {
|
|
564
|
-
const lastLineSize = formatSize(
|
|
565
|
-
Buffer.byteLength(fullOutput.split("\n").pop() || "", "utf-8")
|
|
566
|
-
);
|
|
567
|
-
outputText += `
|
|
568
|
-
|
|
569
|
-
[Showing last ${formatSize(truncation.outputBytes)} of line ${endLine} (line is ${lastLineSize}). Full output: ${tempFilePath}]`;
|
|
570
|
-
} else if (truncation.truncatedBy === "lines") {
|
|
571
737
|
outputText += `
|
|
572
738
|
|
|
573
|
-
[
|
|
739
|
+
[Head/tail preview limited to ${formatSize(truncation.outputBytes)}. Full output: ${tempFilePath}]`;
|
|
574
740
|
} else {
|
|
575
741
|
outputText += `
|
|
576
742
|
|
|
577
|
-
[Showing
|
|
743
|
+
[Showing a bounded head/tail preview of ${truncation.totalLines} lines (${formatSize(DEFAULT_MAX_BYTES)} limit). Full output: ${tempFilePath}]`;
|
|
578
744
|
}
|
|
579
745
|
}
|
|
580
746
|
if (timedOut) {
|
|
581
747
|
outputText += `
|
|
582
748
|
|
|
583
|
-
[Command timed out after ${
|
|
749
|
+
[Command timed out after ${timeoutPolicy.effectiveSeconds} seconds]`;
|
|
750
|
+
outputText += "\n[Timeout hint: split custom searches or service polls into smaller bounded steps with progress output, inspect partial results/logs, then make a decision. Avoid repeating long waits. Reserve long timeouts for installs, builds, and tests.]";
|
|
751
|
+
}
|
|
752
|
+
if (timeoutPolicy.capped) {
|
|
753
|
+
outputText += `
|
|
754
|
+
|
|
755
|
+
[Timeout policy: requested ${timeoutPolicy.requestedSeconds} seconds, capped to ${timeoutPolicy.effectiveSeconds} seconds because ${timeoutPolicy.reason}.]`;
|
|
584
756
|
}
|
|
585
757
|
const recoveryHint = buildBashRecoveryHint(command, outputText);
|
|
586
758
|
if (recoveryHint) {
|
|
@@ -588,19 +760,25 @@ function createBashTool(defaultCwd, options) {
|
|
|
588
760
|
|
|
589
761
|
[Recovery hint: ${recoveryHint}]`;
|
|
590
762
|
}
|
|
763
|
+
const repeatedCommand = rememberCommandResult(
|
|
764
|
+
`${resolvedShell}\0${workingDir}\0${effectiveCommand}`,
|
|
765
|
+
createHash("sha256").update(JSON.stringify({ code, sig, timedOut, outputText })).digest("hex")
|
|
766
|
+
);
|
|
767
|
+
details.repeatedCommand = repeatedCommand;
|
|
768
|
+
if (repeatedCommand.count > 1 && repeatedCommand.sameResult) {
|
|
769
|
+
outputText += "\n\n[Repeat hint: this exact command already returned the same result in this session. If it proves the task, stop; otherwise change the command or inspect a different file/range.]";
|
|
770
|
+
}
|
|
591
771
|
const content = [{ type: "text", text: outputText }];
|
|
592
|
-
|
|
772
|
+
settled = true;
|
|
773
|
+
resolve3({ content, details, isError: timedOut || semantic.isError });
|
|
593
774
|
} catch (error) {
|
|
775
|
+
discardOutputArtifact(tempFilePath, tempFileStream);
|
|
776
|
+
settled = true;
|
|
594
777
|
reject(error);
|
|
595
778
|
}
|
|
596
779
|
});
|
|
597
780
|
child.on("error", (err) => {
|
|
598
|
-
|
|
599
|
-
signal?.removeEventListener("abort", onAbort);
|
|
600
|
-
if (tempFileStream) {
|
|
601
|
-
tempFileStream.end();
|
|
602
|
-
}
|
|
603
|
-
reject(new Error(`Failed to start bash command in ${workingDir}: ${err.message}. Check that the command is executable and cwd exists.`));
|
|
781
|
+
failExecution(new Error(`Failed to start bash command in ${workingDir}: ${err.message}. Check that the command is executable and cwd exists.`));
|
|
604
782
|
});
|
|
605
783
|
});
|
|
606
784
|
}
|
|
@@ -610,185 +788,129 @@ function createBashTool(defaultCwd, options) {
|
|
|
610
788
|
// src/capabilities/code-intel.ts
|
|
611
789
|
import * as fs from "node:fs";
|
|
612
790
|
import * as path from "node:path";
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
while (
|
|
623
|
-
const
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
if (
|
|
627
|
-
|
|
791
|
+
import { fileURLToPath as fileURLToPath2, pathToFileURL } from "node:url";
|
|
792
|
+
import { Worker } from "node:worker_threads";
|
|
793
|
+
var WORKER_TIMEOUT_MS = 3e4;
|
|
794
|
+
var MAX_ACTIVE_CODE_INTEL_WORKERS = 1;
|
|
795
|
+
var MAX_QUEUED_CODE_INTEL_WORKERS = 16;
|
|
796
|
+
var activeCodeIntelWorkers = 0;
|
|
797
|
+
var codeIntelWorkerWaiters = [];
|
|
798
|
+
function releaseWorkerSlot() {
|
|
799
|
+
activeCodeIntelWorkers = Math.max(0, activeCodeIntelWorkers - 1);
|
|
800
|
+
while (activeCodeIntelWorkers < MAX_ACTIVE_CODE_INTEL_WORKERS && codeIntelWorkerWaiters.length > 0) {
|
|
801
|
+
const waiter = codeIntelWorkerWaiters.shift();
|
|
802
|
+
waiter.signal?.removeEventListener("abort", waiter.onAbort);
|
|
803
|
+
if (waiter.timeout) clearTimeout(waiter.timeout);
|
|
804
|
+
if (waiter.signal?.aborted) continue;
|
|
805
|
+
activeCodeIntelWorkers++;
|
|
806
|
+
waiter.resolve();
|
|
628
807
|
}
|
|
629
|
-
const rootCandidate = path.join(root, "tsconfig.json");
|
|
630
|
-
return fs.existsSync(rootCandidate) ? rootCandidate : void 0;
|
|
631
808
|
}
|
|
632
|
-
function
|
|
633
|
-
|
|
634
|
-
if (
|
|
635
|
-
|
|
809
|
+
function acquireWorkerSlot(deadline, signal) {
|
|
810
|
+
if (signal?.aborted) return Promise.reject(new Error("Operation aborted"));
|
|
811
|
+
if (Date.now() >= deadline) return Promise.reject(new Error(`TypeScript code intelligence exceeded ${WORKER_TIMEOUT_MS}ms including queue wait`));
|
|
812
|
+
if (activeCodeIntelWorkers < MAX_ACTIVE_CODE_INTEL_WORKERS) {
|
|
813
|
+
activeCodeIntelWorkers++;
|
|
814
|
+
return Promise.resolve(releaseWorkerSlot);
|
|
636
815
|
}
|
|
637
|
-
|
|
638
|
-
|
|
816
|
+
if (codeIntelWorkerWaiters.length >= MAX_QUEUED_CODE_INTEL_WORKERS) {
|
|
817
|
+
return Promise.reject(new Error("TypeScript code intelligence queue is full"));
|
|
818
|
+
}
|
|
819
|
+
return new Promise((resolve3, reject) => {
|
|
820
|
+
const waiter = { resolve: () => resolve3(releaseWorkerSlot), reject, signal };
|
|
821
|
+
waiter.onAbort = () => {
|
|
822
|
+
const index = codeIntelWorkerWaiters.indexOf(waiter);
|
|
823
|
+
if (index >= 0) codeIntelWorkerWaiters.splice(index, 1);
|
|
824
|
+
if (waiter.timeout) clearTimeout(waiter.timeout);
|
|
825
|
+
reject(new Error("Operation aborted"));
|
|
826
|
+
};
|
|
827
|
+
waiter.timeout = setTimeout(() => {
|
|
828
|
+
const index = codeIntelWorkerWaiters.indexOf(waiter);
|
|
829
|
+
if (index >= 0) codeIntelWorkerWaiters.splice(index, 1);
|
|
830
|
+
signal?.removeEventListener("abort", waiter.onAbort);
|
|
831
|
+
reject(new Error(`TypeScript code intelligence exceeded ${WORKER_TIMEOUT_MS}ms including queue wait`));
|
|
832
|
+
}, Math.max(1, deadline - Date.now()));
|
|
833
|
+
waiter.timeout.unref?.();
|
|
834
|
+
signal?.addEventListener("abort", waiter.onAbort, { once: true });
|
|
835
|
+
codeIntelWorkerWaiters.push(waiter);
|
|
836
|
+
});
|
|
639
837
|
}
|
|
640
|
-
function
|
|
641
|
-
const
|
|
642
|
-
const
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
if (out.length >= limit) return;
|
|
651
|
-
}
|
|
652
|
-
};
|
|
653
|
-
if (fs.existsSync(root)) visit(root);
|
|
654
|
-
return out;
|
|
838
|
+
function resolveWorkerPath() {
|
|
839
|
+
const moduleDirectory = path.dirname(fileURLToPath2(import.meta.url));
|
|
840
|
+
const candidates = [
|
|
841
|
+
path.join(moduleDirectory, "code-intel-worker.cjs"),
|
|
842
|
+
path.join(moduleDirectory, "..", "code-intel-worker.cjs"),
|
|
843
|
+
path.join(moduleDirectory, "..", "..", "dist", "code-intel-worker.cjs")
|
|
844
|
+
];
|
|
845
|
+
const workerPath = candidates.find((candidate) => fs.existsSync(candidate));
|
|
846
|
+
if (!workerPath) throw new Error("compiled code-intel worker is unavailable; rebuild the CLI");
|
|
847
|
+
return workerPath;
|
|
655
848
|
}
|
|
656
|
-
function
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
const config = configPath ? readConfig(ts, configPath) : {
|
|
662
|
-
fileNames: walkJsTs(projectRoot),
|
|
663
|
-
options: {
|
|
664
|
-
allowJs: true,
|
|
665
|
-
checkJs: true,
|
|
666
|
-
moduleResolution: ts.ModuleResolutionKind.NodeJs,
|
|
667
|
-
target: ts.ScriptTarget.ES2022,
|
|
668
|
-
module: ts.ModuleKind.ESNext,
|
|
669
|
-
jsx: ts.JsxEmit.ReactJSX
|
|
849
|
+
function runWorker(request, timeoutMs, signal) {
|
|
850
|
+
return new Promise((resolve3, reject) => {
|
|
851
|
+
if (signal?.aborted) {
|
|
852
|
+
reject(new Error("Operation aborted"));
|
|
853
|
+
return;
|
|
670
854
|
}
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
}
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
855
|
+
const worker = new Worker(pathToFileURL(resolveWorkerPath()), {
|
|
856
|
+
workerData: request,
|
|
857
|
+
execArgv: [],
|
|
858
|
+
resourceLimits: { maxOldGenerationSizeMb: 128, maxYoungGenerationSizeMb: 32, stackSizeMb: 4 }
|
|
859
|
+
});
|
|
860
|
+
let finishing = false;
|
|
861
|
+
let exited = false;
|
|
862
|
+
const finish = (error, result) => {
|
|
863
|
+
if (finishing) return;
|
|
864
|
+
finishing = true;
|
|
865
|
+
clearTimeout(timeout);
|
|
866
|
+
signal?.removeEventListener("abort", onAbort);
|
|
867
|
+
void (async () => {
|
|
868
|
+
if (!exited) {
|
|
869
|
+
try {
|
|
870
|
+
await worker.terminate();
|
|
871
|
+
} catch {
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
if (error) reject(error);
|
|
875
|
+
else resolve3(result);
|
|
876
|
+
})();
|
|
877
|
+
};
|
|
878
|
+
const onAbort = () => finish(new Error("Operation aborted"));
|
|
879
|
+
const timeout = setTimeout(() => finish(new Error(`TypeScript code intelligence exceeded ${WORKER_TIMEOUT_MS}ms including queue wait`)), Math.max(1, timeoutMs));
|
|
880
|
+
timeout.unref?.();
|
|
881
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
882
|
+
worker.once("message", (message) => {
|
|
883
|
+
if (!message || typeof message !== "object" || typeof message.ok !== "boolean") {
|
|
884
|
+
finish(new Error("code-intel worker returned an invalid response"));
|
|
885
|
+
return;
|
|
886
|
+
}
|
|
887
|
+
if ("error" in message) finish(new Error(message.error));
|
|
888
|
+
else finish(void 0, message.result);
|
|
889
|
+
});
|
|
890
|
+
worker.once("error", (error) => finish(error));
|
|
891
|
+
worker.once("exit", (code) => {
|
|
892
|
+
exited = true;
|
|
893
|
+
if (!finishing) finish(new Error(`code-intel worker exited before responding (${code})`));
|
|
894
|
+
});
|
|
895
|
+
});
|
|
710
896
|
}
|
|
711
|
-
function
|
|
712
|
-
const
|
|
713
|
-
const
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
897
|
+
async function runWorkerBounded(request, signal) {
|
|
898
|
+
const deadline = Date.now() + WORKER_TIMEOUT_MS;
|
|
899
|
+
const release = await acquireWorkerSlot(deadline, signal);
|
|
900
|
+
try {
|
|
901
|
+
const remaining = deadline - Date.now();
|
|
902
|
+
if (remaining <= 0) throw new Error(`TypeScript code intelligence exceeded ${WORKER_TIMEOUT_MS}ms including queue wait`);
|
|
903
|
+
return await runWorker(request, remaining, signal);
|
|
904
|
+
} finally {
|
|
905
|
+
release();
|
|
906
|
+
}
|
|
720
907
|
}
|
|
721
908
|
function createTypeScriptCodeIntelProvider() {
|
|
722
|
-
const lightweight = createLightweightCodeIntelProvider();
|
|
723
909
|
return {
|
|
724
910
|
async run(input, context) {
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
const project = createProject(ts, context.projectRoot, filePath);
|
|
729
|
-
const files = filePath ? [filePath] : project.fileNames;
|
|
730
|
-
if (input.operation === "diagnostics") {
|
|
731
|
-
const diagnostics = files.flatMap((file) => [
|
|
732
|
-
...project.service.getSyntacticDiagnostics(file),
|
|
733
|
-
...project.service.getSemanticDiagnostics(file)
|
|
734
|
-
]);
|
|
735
|
-
return {
|
|
736
|
-
text: diagnostics.length ? diagnostics.map((diagnostic) => diagnosticText(ts, diagnostic)).join("\n") : "No TypeScript diagnostics found.",
|
|
737
|
-
details: {
|
|
738
|
-
operation: input.operation,
|
|
739
|
-
projectRoot: context.projectRoot,
|
|
740
|
-
path: filePath,
|
|
741
|
-
provider: "typescript_service",
|
|
742
|
-
resultCount: diagnostics.length,
|
|
743
|
-
diagnosticCount: diagnostics.length,
|
|
744
|
-
tsconfigPath: project.configPath
|
|
745
|
-
}
|
|
746
|
-
};
|
|
747
|
-
}
|
|
748
|
-
if (input.operation === "symbols") {
|
|
749
|
-
const query = input.query?.toLowerCase();
|
|
750
|
-
const symbols = files.flatMap((file) => navItems(project.service.getNavigationBarItems(file), file)).filter((line) => !query || line.toLowerCase().includes(query));
|
|
751
|
-
return {
|
|
752
|
-
text: symbols.length ? symbols.join("\n") : "No symbols found.",
|
|
753
|
-
details: { operation: input.operation, projectRoot: context.projectRoot, path: filePath, provider: "typescript_service", resultCount: symbols.length, tsconfigPath: project.configPath }
|
|
754
|
-
};
|
|
755
|
-
}
|
|
756
|
-
if (!filePath) throw new Error(`${input.operation} requires path`);
|
|
757
|
-
const source = project.service.getProgram()?.getSourceFile(filePath);
|
|
758
|
-
if (!source) throw new Error(`File is not part of the TypeScript project: ${input.path}`);
|
|
759
|
-
const offset = offsetOf(ts, source, input.line, input.character);
|
|
760
|
-
if (input.operation === "definition") {
|
|
761
|
-
const defs = project.service.getDefinitionAtPosition(filePath, offset) ?? [];
|
|
762
|
-
return {
|
|
763
|
-
text: defs.length ? defs.map((def) => `${def.name} ${loc(def.fileName, def.textSpan.start)}`).join("\n") : "No definition found.",
|
|
764
|
-
details: { operation: input.operation, projectRoot: context.projectRoot, path: filePath, provider: "typescript_service", resultCount: defs.length, tsconfigPath: project.configPath }
|
|
765
|
-
};
|
|
766
|
-
}
|
|
767
|
-
if (input.operation === "references") {
|
|
768
|
-
const refs = project.service.getReferencesAtPosition(filePath, offset) ?? [];
|
|
769
|
-
return {
|
|
770
|
-
text: refs.length ? refs.map((ref) => loc(ref.fileName, ref.textSpan.start)).join("\n") : "No references found.",
|
|
771
|
-
details: { operation: input.operation, projectRoot: context.projectRoot, path: filePath, provider: "typescript_service", resultCount: refs.length, tsconfigPath: project.configPath }
|
|
772
|
-
};
|
|
773
|
-
}
|
|
774
|
-
const quickInfo = project.service.getQuickInfoAtPosition(filePath, offset);
|
|
775
|
-
const text = quickInfo ? ts.displayPartsToString(quickInfo.displayParts ?? []) + (quickInfo.documentation?.length ? `
|
|
776
|
-
${ts.displayPartsToString(quickInfo.documentation)}` : "") : "No hover information found.";
|
|
777
|
-
return {
|
|
778
|
-
text,
|
|
779
|
-
details: { operation: input.operation, projectRoot: context.projectRoot, path: filePath, provider: "typescript_service", resultCount: quickInfo ? 1 : 0, tsconfigPath: project.configPath }
|
|
780
|
-
};
|
|
781
|
-
} catch (error) {
|
|
782
|
-
const fallback = await lightweight.run(input, context);
|
|
783
|
-
return {
|
|
784
|
-
...fallback,
|
|
785
|
-
details: {
|
|
786
|
-
...fallback.details,
|
|
787
|
-
provider: "lightweight_fallback",
|
|
788
|
-
fallbackReason: error instanceof Error ? error.message : String(error)
|
|
789
|
-
}
|
|
790
|
-
};
|
|
791
|
-
}
|
|
911
|
+
const filePath = context.resolvePath(input.path);
|
|
912
|
+
if (filePath && !context.isPathAllowed(filePath)) throw new Error(`Path escapes the project root: ${input.path}`);
|
|
913
|
+
return await runWorkerBounded({ input, projectRoot: context.projectRoot, filePath }, context.signal);
|
|
792
914
|
}
|
|
793
915
|
};
|
|
794
916
|
}
|
|
@@ -807,7 +929,7 @@ function resolvePlaywrightCli() {
|
|
|
807
929
|
return require2.resolve("@playwright/cli/playwright-cli.js");
|
|
808
930
|
}
|
|
809
931
|
function runPlaywright(args, timeoutMs = 6e4) {
|
|
810
|
-
return new Promise((
|
|
932
|
+
return new Promise((resolve3, reject) => {
|
|
811
933
|
const child = spawn2(process.execPath, [resolvePlaywrightCli(), ...args], {
|
|
812
934
|
stdio: ["ignore", "pipe", "pipe"],
|
|
813
935
|
shell: false
|
|
@@ -832,7 +954,7 @@ function runPlaywright(args, timeoutMs = 6e4) {
|
|
|
832
954
|
});
|
|
833
955
|
child.on("close", (code) => {
|
|
834
956
|
clearTimeout(timer);
|
|
835
|
-
if (code === 0)
|
|
957
|
+
if (code === 0) resolve3(stdout.trim());
|
|
836
958
|
else reject(new Error((stderr || stdout || `Playwright CLI exited with ${code}`).trim()));
|
|
837
959
|
});
|
|
838
960
|
});
|
|
@@ -941,7 +1063,7 @@ function createCliWebResearchHealthStore(configDir) {
|
|
|
941
1063
|
|
|
942
1064
|
// src/capabilities/index.ts
|
|
943
1065
|
function createCliHostCapabilities(cwd, options = {}) {
|
|
944
|
-
const bashTool = createBashTool(cwd
|
|
1066
|
+
const bashTool = createBashTool(cwd);
|
|
945
1067
|
const healthStore = createCliWebResearchHealthStore(options.configDir ?? getConfigDir());
|
|
946
1068
|
const webSearchTool = createSharedWebSearchTool({
|
|
947
1069
|
getModel: options.getModel ?? (() => void 0),
|
|
@@ -1022,5 +1144,6 @@ export {
|
|
|
1022
1144
|
getRuntimeLogPath,
|
|
1023
1145
|
migrateConfigFromAgentDir,
|
|
1024
1146
|
createCliWebResearchHealthStore,
|
|
1147
|
+
cleanupBashOutputArtifacts,
|
|
1025
1148
|
createCliHostCapabilities
|
|
1026
1149
|
};
|