@cydm/pie 1.0.23 → 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 +51 -5
- package/dist/builtin/extensions/ask-user/index.js +3 -3
- package/dist/builtin/extensions/ask-user/package.json +16 -0
- 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/package.json +11 -0
- package/dist/builtin/extensions/kimi-attachments/package.json +11 -0
- package/dist/builtin/extensions/plan-mode/index.js +3 -3
- package/dist/builtin/extensions/plan-mode/package.json +11 -0
- package/dist/builtin/extensions/subagent/index.js +4 -4
- package/dist/builtin/extensions/subagent/package.json +14 -0
- package/dist/builtin/extensions/todo/index.js +3 -3
- package/dist/builtin/extensions/todo/package.json +11 -0
- package/dist/builtin/skills/browser-tools/README.md +1 -1
- package/dist/builtin/skills/browser-tools/SKILL.md +3 -3
- package/dist/builtin/skills/browser-tools/playwright-cli.js +22 -1
- package/dist/chunks/{chunk-PUZP2Q3F.js → chunk-4B42ZU4B.js} +1 -1
- package/dist/chunks/{chunk-7ABSJC2D.js → chunk-64XJIKVK.js} +371 -120
- package/dist/chunks/{chunk-4WU5VAX4.js → chunk-BEWVRLCP.js} +387 -230
- package/dist/chunks/{chunk-AFTBFF6J.js → chunk-O4DPFYYP.js} +3425 -416
- package/dist/chunks/{src-UCXJ4C6P.js → src-27UD2ZBZ.js} +2 -2
- package/dist/chunks/{test-stream-UKULOPZB.js → test-stream-ZNGAUUVH.js} +6 -2
- package/dist/cli.js +3574 -2050
- package/dist/{chunks/typescript-GSKWJIO4.js → code-intel-worker.cjs} +2507 -2031
- package/models.schema.json +38 -13
- package/package.json +11 -7
|
@@ -2,16 +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
8
|
interpretShellExit,
|
|
10
9
|
requestInteraction
|
|
11
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-O4DPFYYP.js";
|
|
12
11
|
import {
|
|
13
12
|
Type
|
|
14
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-64XJIKVK.js";
|
|
15
14
|
|
|
16
15
|
// src/config.ts
|
|
17
16
|
import { existsSync, mkdirSync, readFileSync, renameSync } from "fs";
|
|
@@ -75,6 +74,9 @@ function getCustomThemesDir() {
|
|
|
75
74
|
function getSettingsPath() {
|
|
76
75
|
return join(getConfigDir(), "settings.json");
|
|
77
76
|
}
|
|
77
|
+
function getProjectSettingsPath(cwd) {
|
|
78
|
+
return join(cwd, CONFIG_DIR_NAME, "settings.json");
|
|
79
|
+
}
|
|
78
80
|
function getSessionsDir() {
|
|
79
81
|
return join(getConfigDir(), "sessions");
|
|
80
82
|
}
|
|
@@ -125,14 +127,54 @@ function migrateConfigFromAgentDir() {
|
|
|
125
127
|
|
|
126
128
|
// src/capabilities/bash/index.ts
|
|
127
129
|
import { spawn } from "child_process";
|
|
128
|
-
import { createWriteStream, existsSync as existsSync2, statSync, writeFileSync } from "fs";
|
|
130
|
+
import { closeSync, createWriteStream, existsSync as existsSync2, openSync, statSync, unlinkSync, writeFileSync } from "fs";
|
|
129
131
|
import { tmpdir } from "os";
|
|
130
|
-
import { join as join2 } from "path";
|
|
131
|
-
import { randomBytes } from "crypto";
|
|
132
|
+
import { join as join2, resolve as resolve2 } from "path";
|
|
133
|
+
import { createHash, randomBytes } from "crypto";
|
|
132
134
|
|
|
133
135
|
// src/capabilities/bash/truncate.ts
|
|
134
|
-
var DEFAULT_MAX_LINES =
|
|
135
|
-
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
|
+
}
|
|
136
178
|
function formatSize(bytes) {
|
|
137
179
|
if (bytes < 1024) {
|
|
138
180
|
return `${bytes}B`;
|
|
@@ -217,7 +259,7 @@ var bashSchema = Type.Object({
|
|
|
217
259
|
command: Type.String({ description: "The bash command to execute" }),
|
|
218
260
|
timeout: Type.Optional(
|
|
219
261
|
Type.Number({
|
|
220
|
-
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."
|
|
221
263
|
})
|
|
222
264
|
),
|
|
223
265
|
cwd: Type.Optional(Type.String({ description: "Working directory for the command" })),
|
|
@@ -229,6 +271,23 @@ var bashSchema = Type.Object({
|
|
|
229
271
|
], { description: "Shell to use. Defaults to bash on macOS/Linux and PowerShell on Windows. Use this only for platform-specific commands." }))
|
|
230
272
|
});
|
|
231
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
|
+
}
|
|
232
291
|
function validateBashCommand(command) {
|
|
233
292
|
const value = typeof command === "string" ? command : "";
|
|
234
293
|
const trimmed = value.trim();
|
|
@@ -263,7 +322,7 @@ function buildSpawnCommand(shell, command) {
|
|
|
263
322
|
if (shell === "cmd") {
|
|
264
323
|
return { file: "cmd.exe", args: ["/d", "/s", "/c", command], shell: false };
|
|
265
324
|
}
|
|
266
|
-
return { file:
|
|
325
|
+
return { file: process.platform === "win32" ? "bash.exe" : "bash", args: ["-c", command], shell: false };
|
|
267
326
|
}
|
|
268
327
|
async function requestHighRiskConfirmation(request) {
|
|
269
328
|
const response = await requestInteraction({
|
|
@@ -296,24 +355,101 @@ function validateWorkingDirectory(cwd) {
|
|
|
296
355
|
}
|
|
297
356
|
function normalizeWorkingDirectory(cwd, defaultCwd) {
|
|
298
357
|
const requestedCwd = typeof cwd === "string" && cwd.trim().length > 0 ? cwd.trim() : void 0;
|
|
299
|
-
return requestedCwd ?? defaultCwd ?? process.cwd();
|
|
358
|
+
return resolve2(requestedCwd ?? defaultCwd ?? process.cwd());
|
|
300
359
|
}
|
|
301
360
|
function getTempFilePath() {
|
|
302
361
|
const id = randomBytes(8).toString("hex");
|
|
303
362
|
return join2(tmpdir(), `pie-bash-${id}.log`);
|
|
304
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);
|
|
305
422
|
function finishTempFileStream(stream) {
|
|
306
|
-
if (!stream) return Promise.resolve();
|
|
423
|
+
if (!stream || stream.closed) return Promise.resolve();
|
|
307
424
|
return new Promise((resolve3, reject) => {
|
|
308
|
-
|
|
425
|
+
let done = false;
|
|
426
|
+
const cleanup = () => {
|
|
427
|
+
stream.off("close", onClose);
|
|
309
428
|
stream.off("error", onError);
|
|
310
|
-
reject(error);
|
|
311
429
|
};
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
430
|
+
const onClose = () => {
|
|
431
|
+
if (done) return;
|
|
432
|
+
done = true;
|
|
433
|
+
cleanup();
|
|
315
434
|
resolve3();
|
|
316
|
-
}
|
|
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);
|
|
443
|
+
stream.once("error", onError);
|
|
444
|
+
try {
|
|
445
|
+
if (!stream.writableEnded) {
|
|
446
|
+
stream.end();
|
|
447
|
+
} else if (stream.closed) {
|
|
448
|
+
onClose();
|
|
449
|
+
}
|
|
450
|
+
} catch (error) {
|
|
451
|
+
onError(error);
|
|
452
|
+
}
|
|
317
453
|
});
|
|
318
454
|
}
|
|
319
455
|
function buildBashRecoveryHint(command, output) {
|
|
@@ -346,10 +482,22 @@ function killProcessTree(pid) {
|
|
|
346
482
|
}
|
|
347
483
|
}
|
|
348
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
|
+
};
|
|
349
497
|
return {
|
|
350
498
|
name: "bash",
|
|
351
499
|
label: "bash",
|
|
352
|
-
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.`,
|
|
353
501
|
parameters: bashSchema,
|
|
354
502
|
execute: async (_toolCallId, { command, timeout, cwd, shell }, signal, onUpdate) => {
|
|
355
503
|
validateBashCommand(command);
|
|
@@ -357,7 +505,10 @@ function createBashTool(defaultCwd, options) {
|
|
|
357
505
|
const effectiveCommand = buildEffectiveCommand(command, options);
|
|
358
506
|
const workingDir = normalizeWorkingDirectory(cwd, defaultCwd);
|
|
359
507
|
validateWorkingDirectory(workingDir);
|
|
360
|
-
const risk = classifyShellCommand(effectiveCommand, resolvedShell
|
|
508
|
+
const risk = classifyShellCommand(effectiveCommand, resolvedShell, {
|
|
509
|
+
cwd: workingDir,
|
|
510
|
+
sandboxRoot: defaultCwd ?? workingDir
|
|
511
|
+
});
|
|
361
512
|
if (risk.destructive && !options?.isYoloMode?.()) {
|
|
362
513
|
const approved = await (options?.confirm ?? requestHighRiskConfirmation)({
|
|
363
514
|
command: effectiveCommand,
|
|
@@ -388,7 +539,8 @@ function createBashTool(defaultCwd, options) {
|
|
|
388
539
|
};
|
|
389
540
|
}
|
|
390
541
|
}
|
|
391
|
-
const
|
|
542
|
+
const timeoutPolicy = resolveTimeoutPolicy(timeout, risk.commandClass);
|
|
543
|
+
const timeoutMs = timeoutPolicy.effectiveSeconds * 1e3;
|
|
392
544
|
return new Promise((resolve3, reject) => {
|
|
393
545
|
if (signal?.aborted) {
|
|
394
546
|
reject(new Error("Operation aborted"));
|
|
@@ -396,13 +548,25 @@ function createBashTool(defaultCwd, options) {
|
|
|
396
548
|
}
|
|
397
549
|
let tempFilePath;
|
|
398
550
|
let tempFileStream;
|
|
551
|
+
let settled = false;
|
|
399
552
|
let totalBytes = 0;
|
|
400
553
|
const chunks = [];
|
|
401
554
|
let chunksBytes = 0;
|
|
402
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
|
+
};
|
|
403
567
|
let timedOut = false;
|
|
404
568
|
const spawnCommand = buildSpawnCommand(resolvedShell, effectiveCommand);
|
|
405
|
-
const child = spawn(spawnCommand.file, spawnCommand.args, {
|
|
569
|
+
const child = (options?.spawnProcess ?? spawn)(spawnCommand.file, spawnCommand.args, {
|
|
406
570
|
shell: spawnCommand.shell,
|
|
407
571
|
cwd: workingDir,
|
|
408
572
|
env: process.env,
|
|
@@ -414,19 +578,33 @@ function createBashTool(defaultCwd, options) {
|
|
|
414
578
|
killProcessTree(child.pid);
|
|
415
579
|
}
|
|
416
580
|
}, timeoutMs);
|
|
417
|
-
const
|
|
581
|
+
const failExecution = (error) => {
|
|
582
|
+
if (settled) return;
|
|
583
|
+
settled = true;
|
|
418
584
|
clearTimeout(timeoutId);
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
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"));
|
|
423
592
|
};
|
|
424
593
|
signal?.addEventListener("abort", onAbort, { once: true });
|
|
425
594
|
child.stdout?.on("data", (data) => {
|
|
595
|
+
if (settled) return;
|
|
426
596
|
totalBytes += data.length;
|
|
597
|
+
captureHead(data);
|
|
427
598
|
if (totalBytes > DEFAULT_MAX_BYTES && !tempFilePath) {
|
|
428
|
-
|
|
429
|
-
|
|
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
|
+
}
|
|
430
608
|
for (const chunk of chunks) {
|
|
431
609
|
tempFileStream.write(chunk);
|
|
432
610
|
}
|
|
@@ -443,7 +621,7 @@ function createBashTool(defaultCwd, options) {
|
|
|
443
621
|
if (onUpdate) {
|
|
444
622
|
const fullBuffer = Buffer.concat(chunks);
|
|
445
623
|
const fullText = fullBuffer.toString("utf-8");
|
|
446
|
-
const truncation =
|
|
624
|
+
const truncation = truncateHeadTail(fullText);
|
|
447
625
|
onUpdate({
|
|
448
626
|
content: [{ type: "text", text: truncation.content || "" }],
|
|
449
627
|
details: {
|
|
@@ -460,6 +638,7 @@ function createBashTool(defaultCwd, options) {
|
|
|
460
638
|
shell: resolvedShell,
|
|
461
639
|
commandClass: risk.commandClass,
|
|
462
640
|
riskWarnings: risk.riskWarnings,
|
|
641
|
+
timeoutPolicy,
|
|
463
642
|
permissionDecision: {
|
|
464
643
|
allowed: true,
|
|
465
644
|
requiresPermission: risk.requiresPermission
|
|
@@ -469,10 +648,19 @@ function createBashTool(defaultCwd, options) {
|
|
|
469
648
|
}
|
|
470
649
|
});
|
|
471
650
|
child.stderr?.on("data", (data) => {
|
|
651
|
+
if (settled) return;
|
|
472
652
|
totalBytes += data.length;
|
|
653
|
+
captureHead(data);
|
|
473
654
|
if (totalBytes > DEFAULT_MAX_BYTES && !tempFilePath) {
|
|
474
|
-
|
|
475
|
-
|
|
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
|
+
}
|
|
476
664
|
for (const chunk of chunks) {
|
|
477
665
|
tempFileStream.write(chunk);
|
|
478
666
|
}
|
|
@@ -491,13 +679,32 @@ function createBashTool(defaultCwd, options) {
|
|
|
491
679
|
try {
|
|
492
680
|
clearTimeout(timeoutId);
|
|
493
681
|
signal?.removeEventListener("abort", onAbort);
|
|
682
|
+
if (settled) return;
|
|
494
683
|
const fullBuffer = Buffer.concat(chunks);
|
|
495
684
|
let fullOutput = fullBuffer.toString("utf-8");
|
|
496
|
-
|
|
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
|
+
}
|
|
497
697
|
let outputText = truncation.content || "(no output)";
|
|
498
698
|
if (truncation.truncated && !tempFilePath) {
|
|
499
|
-
|
|
500
|
-
|
|
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
|
+
}
|
|
501
708
|
}
|
|
502
709
|
await finishTempFileStream(tempFileStream);
|
|
503
710
|
const details = {
|
|
@@ -507,6 +714,7 @@ function createBashTool(defaultCwd, options) {
|
|
|
507
714
|
shell: resolvedShell,
|
|
508
715
|
commandClass: risk.commandClass,
|
|
509
716
|
riskWarnings: risk.riskWarnings,
|
|
717
|
+
timeoutPolicy,
|
|
510
718
|
permissionDecision: {
|
|
511
719
|
allowed: true,
|
|
512
720
|
requiresPermission: risk.requiresPermission
|
|
@@ -525,29 +733,26 @@ function createBashTool(defaultCwd, options) {
|
|
|
525
733
|
outputLines: truncation.outputLines
|
|
526
734
|
};
|
|
527
735
|
details.fullOutputPath = tempFilePath;
|
|
528
|
-
const startLine = truncation.totalLines - truncation.outputLines + 1;
|
|
529
|
-
const endLine = truncation.totalLines;
|
|
530
736
|
if (truncation.lastLinePartial) {
|
|
531
|
-
const lastLineSize = formatSize(
|
|
532
|
-
Buffer.byteLength(fullOutput.split("\n").pop() || "", "utf-8")
|
|
533
|
-
);
|
|
534
737
|
outputText += `
|
|
535
738
|
|
|
536
|
-
[
|
|
537
|
-
} else if (truncation.truncatedBy === "lines") {
|
|
538
|
-
outputText += `
|
|
539
|
-
|
|
540
|
-
[Showing lines ${startLine}-${endLine} of ${truncation.totalLines}. Full output: ${tempFilePath}]`;
|
|
739
|
+
[Head/tail preview limited to ${formatSize(truncation.outputBytes)}. Full output: ${tempFilePath}]`;
|
|
541
740
|
} else {
|
|
542
741
|
outputText += `
|
|
543
742
|
|
|
544
|
-
[Showing
|
|
743
|
+
[Showing a bounded head/tail preview of ${truncation.totalLines} lines (${formatSize(DEFAULT_MAX_BYTES)} limit). Full output: ${tempFilePath}]`;
|
|
545
744
|
}
|
|
546
745
|
}
|
|
547
746
|
if (timedOut) {
|
|
548
747
|
outputText += `
|
|
549
748
|
|
|
550
|
-
[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}.]`;
|
|
551
756
|
}
|
|
552
757
|
const recoveryHint = buildBashRecoveryHint(command, outputText);
|
|
553
758
|
if (recoveryHint) {
|
|
@@ -555,19 +760,25 @@ function createBashTool(defaultCwd, options) {
|
|
|
555
760
|
|
|
556
761
|
[Recovery hint: ${recoveryHint}]`;
|
|
557
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
|
+
}
|
|
558
771
|
const content = [{ type: "text", text: outputText }];
|
|
772
|
+
settled = true;
|
|
559
773
|
resolve3({ content, details, isError: timedOut || semantic.isError });
|
|
560
774
|
} catch (error) {
|
|
775
|
+
discardOutputArtifact(tempFilePath, tempFileStream);
|
|
776
|
+
settled = true;
|
|
561
777
|
reject(error);
|
|
562
778
|
}
|
|
563
779
|
});
|
|
564
780
|
child.on("error", (err) => {
|
|
565
|
-
|
|
566
|
-
signal?.removeEventListener("abort", onAbort);
|
|
567
|
-
if (tempFileStream) {
|
|
568
|
-
tempFileStream.end();
|
|
569
|
-
}
|
|
570
|
-
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.`));
|
|
571
782
|
});
|
|
572
783
|
});
|
|
573
784
|
}
|
|
@@ -577,185 +788,129 @@ function createBashTool(defaultCwd, options) {
|
|
|
577
788
|
// src/capabilities/code-intel.ts
|
|
578
789
|
import * as fs from "node:fs";
|
|
579
790
|
import * as path from "node:path";
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
while (
|
|
590
|
-
const
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
if (
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
if (
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
if (
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
const
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
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();
|
|
807
|
+
}
|
|
808
|
+
}
|
|
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);
|
|
815
|
+
}
|
|
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
|
+
});
|
|
837
|
+
}
|
|
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;
|
|
848
|
+
}
|
|
849
|
+
function runWorker(request, timeoutMs, signal) {
|
|
850
|
+
return new Promise((resolve3, reject) => {
|
|
851
|
+
if (signal?.aborted) {
|
|
852
|
+
reject(new Error("Operation aborted"));
|
|
853
|
+
return;
|
|
637
854
|
}
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
}
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
}
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
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
|
+
});
|
|
896
|
+
}
|
|
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
|
+
}
|
|
687
907
|
}
|
|
688
908
|
function createTypeScriptCodeIntelProvider() {
|
|
689
|
-
const lightweight = createLightweightCodeIntelProvider();
|
|
690
909
|
return {
|
|
691
910
|
async run(input, context) {
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
const project = createProject(ts, context.projectRoot, filePath);
|
|
696
|
-
const files = filePath ? [filePath] : project.fileNames;
|
|
697
|
-
if (input.operation === "diagnostics") {
|
|
698
|
-
const diagnostics = files.flatMap((file) => [
|
|
699
|
-
...project.service.getSyntacticDiagnostics(file),
|
|
700
|
-
...project.service.getSemanticDiagnostics(file)
|
|
701
|
-
]);
|
|
702
|
-
return {
|
|
703
|
-
text: diagnostics.length ? diagnostics.map((diagnostic) => diagnosticText(ts, diagnostic)).join("\n") : "No TypeScript diagnostics found.",
|
|
704
|
-
details: {
|
|
705
|
-
operation: input.operation,
|
|
706
|
-
projectRoot: context.projectRoot,
|
|
707
|
-
path: filePath,
|
|
708
|
-
provider: "typescript_service",
|
|
709
|
-
resultCount: diagnostics.length,
|
|
710
|
-
diagnosticCount: diagnostics.length,
|
|
711
|
-
tsconfigPath: project.configPath
|
|
712
|
-
}
|
|
713
|
-
};
|
|
714
|
-
}
|
|
715
|
-
if (input.operation === "symbols") {
|
|
716
|
-
const query = input.query?.toLowerCase();
|
|
717
|
-
const symbols = files.flatMap((file) => navItems(project.service.getNavigationBarItems(file), file)).filter((line) => !query || line.toLowerCase().includes(query));
|
|
718
|
-
return {
|
|
719
|
-
text: symbols.length ? symbols.join("\n") : "No symbols found.",
|
|
720
|
-
details: { operation: input.operation, projectRoot: context.projectRoot, path: filePath, provider: "typescript_service", resultCount: symbols.length, tsconfigPath: project.configPath }
|
|
721
|
-
};
|
|
722
|
-
}
|
|
723
|
-
if (!filePath) throw new Error(`${input.operation} requires path`);
|
|
724
|
-
const source = project.service.getProgram()?.getSourceFile(filePath);
|
|
725
|
-
if (!source) throw new Error(`File is not part of the TypeScript project: ${input.path}`);
|
|
726
|
-
const offset = offsetOf(ts, source, input.line, input.character);
|
|
727
|
-
if (input.operation === "definition") {
|
|
728
|
-
const defs = project.service.getDefinitionAtPosition(filePath, offset) ?? [];
|
|
729
|
-
return {
|
|
730
|
-
text: defs.length ? defs.map((def) => `${def.name} ${loc(def.fileName, def.textSpan.start)}`).join("\n") : "No definition found.",
|
|
731
|
-
details: { operation: input.operation, projectRoot: context.projectRoot, path: filePath, provider: "typescript_service", resultCount: defs.length, tsconfigPath: project.configPath }
|
|
732
|
-
};
|
|
733
|
-
}
|
|
734
|
-
if (input.operation === "references") {
|
|
735
|
-
const refs = project.service.getReferencesAtPosition(filePath, offset) ?? [];
|
|
736
|
-
return {
|
|
737
|
-
text: refs.length ? refs.map((ref) => loc(ref.fileName, ref.textSpan.start)).join("\n") : "No references found.",
|
|
738
|
-
details: { operation: input.operation, projectRoot: context.projectRoot, path: filePath, provider: "typescript_service", resultCount: refs.length, tsconfigPath: project.configPath }
|
|
739
|
-
};
|
|
740
|
-
}
|
|
741
|
-
const quickInfo = project.service.getQuickInfoAtPosition(filePath, offset);
|
|
742
|
-
const text = quickInfo ? ts.displayPartsToString(quickInfo.displayParts ?? []) + (quickInfo.documentation?.length ? `
|
|
743
|
-
${ts.displayPartsToString(quickInfo.documentation)}` : "") : "No hover information found.";
|
|
744
|
-
return {
|
|
745
|
-
text,
|
|
746
|
-
details: { operation: input.operation, projectRoot: context.projectRoot, path: filePath, provider: "typescript_service", resultCount: quickInfo ? 1 : 0, tsconfigPath: project.configPath }
|
|
747
|
-
};
|
|
748
|
-
} catch (error) {
|
|
749
|
-
const fallback = await lightweight.run(input, context);
|
|
750
|
-
return {
|
|
751
|
-
...fallback,
|
|
752
|
-
details: {
|
|
753
|
-
...fallback.details,
|
|
754
|
-
provider: "lightweight_fallback",
|
|
755
|
-
fallbackReason: error instanceof Error ? error.message : String(error)
|
|
756
|
-
}
|
|
757
|
-
};
|
|
758
|
-
}
|
|
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);
|
|
759
914
|
}
|
|
760
915
|
};
|
|
761
916
|
}
|
|
@@ -833,12 +988,12 @@ function createCliRenderedPageReader() {
|
|
|
833
988
|
try {
|
|
834
989
|
await runPlaywright([`-s=${session}`, "open", "about:blank"]);
|
|
835
990
|
await runPlaywright([`-s=${session}`, "goto", url]);
|
|
836
|
-
const [snapshot, pageInfoRaw, screenshotOutput, consoleOutput,
|
|
991
|
+
const [snapshot, pageInfoRaw, screenshotOutput, consoleOutput, requestsOutput] = await Promise.all([
|
|
837
992
|
runPlaywright([`-s=${session}`, "snapshot"]),
|
|
838
993
|
runPlaywright([`-s=${session}`, "--raw", "eval", "() => JSON.stringify({ title: document.title, href: location.href, text: document.body ? document.body.innerText : '' })"]),
|
|
839
994
|
runPlaywright([`-s=${session}`, "--raw", "screenshot"]).catch((error) => `screenshot_failed: ${error instanceof Error ? error.message : String(error)}`),
|
|
840
995
|
runPlaywright([`-s=${session}`, "--raw", "console"]).catch((error) => `console_failed: ${error instanceof Error ? error.message : String(error)}`),
|
|
841
|
-
runPlaywright([`-s=${session}`, "--raw", "
|
|
996
|
+
runPlaywright([`-s=${session}`, "--raw", "requests"]).catch((error) => `requests_failed: ${error instanceof Error ? error.message : String(error)}`)
|
|
842
997
|
]);
|
|
843
998
|
const pageInfo = parsePageInfo(pageInfoRaw);
|
|
844
999
|
const text = pageInfo.text?.trim() || snapshot.trim();
|
|
@@ -853,7 +1008,7 @@ function createCliRenderedPageReader() {
|
|
|
853
1008
|
citationAnchors: [{ url: pageInfo.finalUrl || url, title: pageInfo.title, startOffset: 0, endOffset: Math.min(120, text.length) }],
|
|
854
1009
|
screenshotPath: parseScreenshotPath(screenshotOutput),
|
|
855
1010
|
consoleMessages: linesOf(consoleOutput),
|
|
856
|
-
networkRequests: linesOf(
|
|
1011
|
+
networkRequests: linesOf(requestsOutput)
|
|
857
1012
|
};
|
|
858
1013
|
} finally {
|
|
859
1014
|
runPlaywright([`-s=${session}`, "close"], 1e4).catch(() => void 0);
|
|
@@ -983,10 +1138,12 @@ export {
|
|
|
983
1138
|
getAgentDir,
|
|
984
1139
|
getCustomThemesDir,
|
|
985
1140
|
getSettingsPath,
|
|
1141
|
+
getProjectSettingsPath,
|
|
986
1142
|
getSessionsDir,
|
|
987
1143
|
getLogsDir,
|
|
988
1144
|
getRuntimeLogPath,
|
|
989
1145
|
migrateConfigFromAgentDir,
|
|
990
1146
|
createCliWebResearchHealthStore,
|
|
1147
|
+
cleanupBashOutputArtifacts,
|
|
991
1148
|
createCliHostCapabilities
|
|
992
1149
|
};
|