@linghun/tools 0.1.2 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/dist/chunk-236QJATI.js +678 -0
  2. package/dist/{chunk-VZWMPUSS.js → chunk-2DIXRINW.js} +1 -1
  3. package/dist/{chunk-DWXJBDPQ.js → chunk-F7UOI3YR.js} +16 -16
  4. package/dist/chunk-FI4YPDFE.js +38 -0
  5. package/dist/{chunk-2R75FXZG.js → chunk-J7O24FJB.js} +16 -16
  6. package/dist/chunk-PKMIDA5A.js +6 -0
  7. package/dist/chunk-SPAD4DZS.js +319 -0
  8. package/dist/chunk-XG6HNLY6.js +166 -0
  9. package/dist/index.d.ts +81 -1
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/index.js +984 -26
  12. package/dist/tools/Bash/BashTool.d.ts +1 -1
  13. package/dist/tools/Bash/BashTool.js +2 -2
  14. package/dist/tools/Bash/artifact-checker.d.ts +33 -0
  15. package/dist/tools/Bash/artifact-checker.d.ts.map +1 -0
  16. package/dist/tools/Bash/artifact-checker.js +6 -0
  17. package/dist/tools/Bash/binary-helper.d.ts +7 -0
  18. package/dist/tools/Bash/binary-helper.d.ts.map +1 -0
  19. package/dist/tools/Bash/binary-helper.js +6 -0
  20. package/dist/tools/Bash/command-semantics.d.ts +11 -0
  21. package/dist/tools/Bash/command-semantics.d.ts.map +1 -0
  22. package/dist/tools/Bash/command-semantics.js +6 -0
  23. package/dist/tools/Bash/prompt.d.ts +1 -1
  24. package/dist/tools/Bash/prompt.d.ts.map +1 -1
  25. package/dist/tools/Bash/prompt.js +1 -1
  26. package/dist/tools/Bash/service-kernel.d.ts +75 -0
  27. package/dist/tools/Bash/service-kernel.d.ts.map +1 -0
  28. package/dist/tools/Bash/service-kernel.js +8 -0
  29. package/dist/tools/Glob/GlobTool.js +2 -2
  30. package/dist/tools/Todo/TodoTool.js +2 -2
  31. package/dist/tools/prompts.js +18 -18
  32. package/dist/tools/ui.js +18 -18
  33. package/package.json +4 -1
  34. package/dist/chunk-E75PYM7V.js +0 -6
  35. package/dist/{chunk-MFHY7PBM.js → chunk-4567YAXM.js} +3 -3
  36. package/dist/{chunk-DN3KJZRV.js → chunk-Z3H6SZBS.js} +3 -3
package/dist/index.js CHANGED
@@ -7,56 +7,70 @@ import {
7
7
  bingSearch,
8
8
  formatSearchOutput
9
9
  } from "./chunk-EPIB4FSE.js";
10
+ import {
11
+ runBashServiceLifecycleAction,
12
+ startBashService
13
+ } from "./chunk-236QJATI.js";
10
14
  import {
11
15
  createTool
12
16
  } from "./chunk-MMJX44V6.js";
13
17
  import {
14
18
  toolPrompts
15
- } from "./chunk-DWXJBDPQ.js";
19
+ } from "./chunk-F7UOI3YR.js";
16
20
  import {
17
21
  toolUserFacingNames
18
- } from "./chunk-2R75FXZG.js";
22
+ } from "./chunk-J7O24FJB.js";
23
+ import "./chunk-4EZHIDZ6.js";
19
24
  import "./chunk-I3Z4OKMS.js";
20
25
  import "./chunk-PS2ZOFZ6.js";
21
26
  import "./chunk-NPKLGVDQ.js";
27
+ import "./chunk-VJDLOFCF.js";
28
+ import "./chunk-3RP665TH.js";
29
+ import "./chunk-IOC3BSPT.js";
22
30
  import "./chunk-LGZOG7IQ.js";
23
31
  import "./chunk-ZYAEUABC.js";
24
32
  import "./chunk-SWK6XRZQ.js";
25
- import "./chunk-4EZHIDZ6.js";
26
33
  import "./chunk-YR4HWOR2.js";
27
34
  import "./chunk-VAFW24RN.js";
35
+ import "./chunk-TZ6QK52L.js";
36
+ import "./chunk-K3YDFDLP.js";
28
37
  import "./chunk-GI5XI4HN.js";
29
38
  import "./chunk-WEWXKRKL.js";
30
39
  import "./chunk-A6FUAM64.js";
31
40
  import "./chunk-YCO6C7KF.js";
32
- import "./chunk-MFHY7PBM.js";
33
- import "./chunk-SZFZPQGL.js";
41
+ import "./chunk-4567YAXM.js";
34
42
  import "./chunk-XZWSO5CM.js";
43
+ import "./chunk-SZFZPQGL.js";
44
+ import "./chunk-WLQ7DGMG.js";
45
+ import "./chunk-SQP6SJRP.js";
46
+ import "./chunk-BRG64AEC.js";
35
47
  import "./chunk-AZCQL24J.js";
36
48
  import "./chunk-HUIZOB4A.js";
37
49
  import "./chunk-MK3YP2VR.js";
38
- import "./chunk-VJDLOFCF.js";
39
- import "./chunk-3RP665TH.js";
40
- import "./chunk-IOC3BSPT.js";
41
- import "./chunk-TZ6QK52L.js";
42
- import "./chunk-K3YDFDLP.js";
43
50
  import "./chunk-HNIVNZIS.js";
44
- import "./chunk-DN3KJZRV.js";
45
- import "./chunk-2U4BZXAI.js";
46
- import "./chunk-ITBZHMZQ.js";
47
- import "./chunk-WLQ7DGMG.js";
48
- import "./chunk-SQP6SJRP.js";
49
- import "./chunk-BRG64AEC.js";
50
- import "./chunk-VZWMPUSS.js";
51
- import "./chunk-E75PYM7V.js";
52
- import "./chunk-GAWMEMXB.js";
53
51
  import "./chunk-7ZOADACP.js";
54
52
  import "./chunk-H5OOD2OS.js";
55
53
  import "./chunk-IQP3XE32.js";
54
+ import "./chunk-Z3H6SZBS.js";
55
+ import "./chunk-ITBZHMZQ.js";
56
+ import "./chunk-2U4BZXAI.js";
57
+ import {
58
+ checkArtifact
59
+ } from "./chunk-SPAD4DZS.js";
60
+ import "./chunk-2DIXRINW.js";
61
+ import "./chunk-PKMIDA5A.js";
62
+ import "./chunk-GAWMEMXB.js";
63
+ import {
64
+ inspectBinaryFile
65
+ } from "./chunk-XG6HNLY6.js";
66
+ import {
67
+ interpretCommandResult
68
+ } from "./chunk-FI4YPDFE.js";
56
69
 
57
70
  // src/index.ts
58
71
  import { spawn } from "child_process";
59
72
  import { createHash, randomUUID } from "crypto";
73
+ import { closeSync, createWriteStream, openSync } from "fs";
60
74
  import { mkdir, readFile, readdir, stat, writeFile } from "fs/promises";
61
75
  import { basename, dirname, isAbsolute, join, relative, resolve } from "path";
62
76
  var toolRegistryStatus = "ready";
@@ -84,7 +98,9 @@ function createToolContext(workspaceRoot = process.cwd(), options = {}) {
84
98
  todos: [],
85
99
  readSnapshots: {},
86
100
  sourcePackCandidates: options.sourcePackCandidates,
87
- patchSummaries: {}
101
+ patchSummaries: {},
102
+ recentDiagnostics: [],
103
+ services: []
88
104
  };
89
105
  }
90
106
  async function runTool(name, input, context) {
@@ -292,7 +308,7 @@ var toolDefinitions = {
292
308
  call: bashTool,
293
309
  prompt: () => toolPrompts.Bash,
294
310
  userFacingName: () => toolUserFacingNames.Bash,
295
- getToolUseSummary: (input) => `Bash ${input.command}`,
311
+ getToolUseSummary: (input) => input.artifact ? `Bash artifact ${input.artifact.path}` : input.binary ? `Bash binary ${input.binary.path}` : `Bash ${input.command}`,
296
312
  getActivityDescription: () => "Running command"
297
313
  }),
298
314
  Todo: defineTool({
@@ -571,11 +587,121 @@ function validateGlobInput(input) {
571
587
  }
572
588
  function validateBashInput(input) {
573
589
  const record = validateRecord(input, "Bash");
590
+ const command = readOptionalString(record, "command", "Bash");
591
+ const binary = validateOptionalBashBinaryInspect(record.binary);
592
+ const artifact = validateOptionalBashArtifactCheck(record.artifact);
593
+ const service = validateOptionalBashServiceInput(record.service);
594
+ const serviceAction = isBashServiceLifecycleAction(service);
595
+ if (command === void 0 && binary === void 0 && artifact === void 0 && !serviceAction) {
596
+ throw new Error("Bash.command\u3001Bash.binary\u3001Bash.artifact \u6216 Bash.service action \u5FC5\u987B\u63D0\u4F9B\u4E00\u4E2A\u3002");
597
+ }
598
+ const modeCount = [
599
+ command !== void 0,
600
+ binary !== void 0,
601
+ artifact !== void 0,
602
+ serviceAction
603
+ ].filter(Boolean).length;
604
+ if (modeCount > 1 || (binary !== void 0 || artifact !== void 0) && service !== void 0) {
605
+ throw new Error("Bash.command\u3001Bash.binary\u3001Bash.artifact \u548C Bash.service \u4E0D\u80FD\u6DF7\u7528\u3002");
606
+ }
607
+ if (service !== void 0 && !serviceAction && command === void 0) {
608
+ throw new Error("Bash.service readiness \u5FC5\u987B\u548C Bash.command \u4E00\u8D77\u4F7F\u7528\u3002");
609
+ }
610
+ const runInBackground = record.runInBackground === true || record.run_in_background === true;
574
611
  return {
575
- command: readString(record, "command", "Bash"),
576
- timeoutMs: readOptionalPositiveInteger(record, "timeoutMs", "Bash")
612
+ command,
613
+ timeoutMs: readOptionalPositiveInteger(record, "timeoutMs", "Bash"),
614
+ service,
615
+ binary,
616
+ artifact,
617
+ ...runInBackground ? { runInBackground: true } : {}
577
618
  };
578
619
  }
620
+ function validateOptionalBashBinaryInspect(input) {
621
+ if (input === void 0) return void 0;
622
+ if (isEmptyOptionalObject(input)) return void 0;
623
+ const record = validateRecord(input, "Bash");
624
+ return {
625
+ path: readString(record, "path", "Bash"),
626
+ previewBytes: readOptionalPositiveInteger(record, "previewBytes", "Bash")
627
+ };
628
+ }
629
+ function validateOptionalBashArtifactCheck(input) {
630
+ if (input === void 0) return void 0;
631
+ if (isEmptyOptionalObject(input)) return void 0;
632
+ const record = validateRecord(input, "Bash");
633
+ return {
634
+ path: readString(record, "path", "Bash"),
635
+ expectHeader: readOptionalString(record, "expectHeader", "Bash"),
636
+ expectMagic: readOptionalString(record, "expectMagic", "Bash"),
637
+ json: readOptionalBoolean(record, "json", "Bash"),
638
+ executable: readOptionalBoolean(record, "executable", "Bash"),
639
+ protectPaths: readOptionalStringArray(record, "protectPaths", "Bash"),
640
+ text: validateOptionalBashArtifactTextCheck(record.text),
641
+ preserve: validateOptionalBashArtifactPreserveCheck(record.preserve)
642
+ };
643
+ }
644
+ function validateOptionalBashServiceInput(input) {
645
+ if (input === void 0) return void 0;
646
+ if (isEmptyOptionalObject(input)) return void 0;
647
+ const record = validateRecord(input, "Bash");
648
+ const action = readOptionalString(record, "action", "Bash");
649
+ if (action !== void 0) {
650
+ if (action === "fetch") {
651
+ const url = readString(record, "url", "Bash");
652
+ validateHttpUrl(url);
653
+ return {
654
+ action,
655
+ url,
656
+ expectStatus: readOptionalPositiveInteger(record, "expectStatus", "Bash"),
657
+ bodyContains: readOptionalStringOrStringArray(record, "bodyContains", "Bash"),
658
+ timeoutMs: readOptionalPositiveInteger(record, "timeoutMs", "Bash"),
659
+ retry: readOptionalPositiveInteger(record, "retry", "Bash"),
660
+ intervalMs: readOptionalPositiveInteger(record, "intervalMs", "Bash")
661
+ };
662
+ }
663
+ if (action === "status" || action === "probe" || action === "logs" || action === "stop") {
664
+ return {
665
+ action,
666
+ serviceId: readString(record, "serviceId", "Bash"),
667
+ ...action === "logs" ? { tailBytes: readOptionalPositiveInteger(record, "tailBytes", "Bash") } : {}
668
+ };
669
+ }
670
+ throw new Error("Bash.service.action \u5FC5\u987B\u662F status/probe/logs/stop/fetch\u3002");
671
+ }
672
+ const type = readString(record, "type", "Bash");
673
+ const timeoutMs = readOptionalPositiveInteger(record, "timeoutMs", "Bash");
674
+ const intervalMs = readOptionalPositiveInteger(record, "intervalMs", "Bash");
675
+ if (type === "tcp") {
676
+ const rawPort = record.port;
677
+ if (!Number.isInteger(rawPort) || rawPort <= 0) {
678
+ throw new Error("Bash.service.port \u5FC5\u987B\u662F\u6B63\u6574\u6570\u3002");
679
+ }
680
+ const port = rawPort;
681
+ if (port > 65535) {
682
+ throw new Error("Bash.service.port \u5FC5\u987B\u5728 1-65535 \u8303\u56F4\u5185\u3002");
683
+ }
684
+ return {
685
+ type,
686
+ port,
687
+ host: readOptionalString(record, "host", "Bash"),
688
+ timeoutMs,
689
+ intervalMs
690
+ };
691
+ }
692
+ if (type === "http") {
693
+ const url = readString(record, "url", "Bash");
694
+ validateHttpUrl(url);
695
+ return { type, url, timeoutMs, intervalMs };
696
+ }
697
+ throw new Error("Bash.service.type \u5FC5\u987B\u662F tcp \u6216 http\u3002");
698
+ }
699
+ function isEmptyOptionalObject(input) {
700
+ return input === null || typeof input === "object" && !Array.isArray(input) && Object.keys(input).length === 0;
701
+ }
702
+ function isBashServiceLifecycleAction(input) {
703
+ return Boolean(input && "action" in input);
704
+ }
579
705
  function validateTodoInput(input) {
580
706
  const record = validateRecord(input, "Todo");
581
707
  const action = readString(record, "action", "Todo");
@@ -633,6 +759,53 @@ function readOptionalStringArray(record, key, toolName) {
633
759
  }
634
760
  return value;
635
761
  }
762
+ function readOptionalBoolean(record, key, toolName) {
763
+ const value = record[key];
764
+ if (value === void 0) return void 0;
765
+ if (typeof value !== "boolean") {
766
+ throw new Error(`${toolName}.${key} \u5FC5\u987B\u662F boolean \u6216 undefined\u3002`);
767
+ }
768
+ return value;
769
+ }
770
+ function readOptionalStringOrStringArray(record, key, toolName) {
771
+ const value = record[key];
772
+ if (value === void 0) return void 0;
773
+ if (typeof value === "string") return value;
774
+ if (Array.isArray(value) && value.every((item) => typeof item === "string")) return value;
775
+ throw new Error(`${toolName}.${key} \u5FC5\u987B\u662F\u5B57\u7B26\u4E32\u3001\u5B57\u7B26\u4E32\u6570\u7EC4\u6216 undefined\u3002`);
776
+ }
777
+ function validateOptionalBashArtifactTextCheck(input) {
778
+ if (input === void 0) return void 0;
779
+ const record = validateRecord(input, "Bash");
780
+ return {
781
+ exact: readOptionalString(record, "exact", "Bash"),
782
+ contains: readOptionalStringOrStringArray(record, "contains", "Bash"),
783
+ lineSet: readOptionalStringArray(record, "lineSet", "Bash")
784
+ };
785
+ }
786
+ function validateOptionalBashArtifactPreserveCheck(input) {
787
+ if (input === void 0) return void 0;
788
+ const record = validateRecord(input, "Bash");
789
+ const mode = readString(record, "mode", "Bash");
790
+ if (mode !== "rawPreserve" && mode !== "compareNormalizedHtml") {
791
+ throw new Error("Bash.artifact.preserve.mode \u5FC5\u987B\u662F rawPreserve \u6216 compareNormalizedHtml\u3002");
792
+ }
793
+ return {
794
+ mode,
795
+ expectedPath: readOptionalString(record, "expectedPath", "Bash"),
796
+ expectedText: readOptionalString(record, "expectedText", "Bash")
797
+ };
798
+ }
799
+ function validateHttpUrl(url) {
800
+ try {
801
+ const parsed = new URL(url);
802
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
803
+ throw new Error("protocol");
804
+ }
805
+ } catch {
806
+ throw new Error("Bash.service.url \u5FC5\u987B\u662F http/https URL\u3002");
807
+ }
808
+ }
636
809
  async function readTool(input, context) {
637
810
  const filePath = resolveWorkspacePath(context.workspaceRoot, input.path);
638
811
  const content = await readFile(filePath, "utf8");
@@ -1009,11 +1182,70 @@ function sanitizeSecrets(text) {
1009
1182
  return sanitized;
1010
1183
  }
1011
1184
  async function bashTool(input, context) {
1185
+ if (input.artifact) {
1186
+ return checkArtifact({
1187
+ input: input.artifact,
1188
+ target: resolveArtifactPath(context, input.artifact.path),
1189
+ protectPaths: (input.artifact.protectPaths ?? []).map((path) => resolveArtifactPath(context, path)),
1190
+ context
1191
+ });
1192
+ }
1193
+ if (input.binary) {
1194
+ return inspectBinaryFile(input.binary, resolveWorkspacePath(context.workspaceRoot, input.binary.path));
1195
+ }
1196
+ if (!input.command) {
1197
+ if (isBashServiceLifecycleAction(input.service)) {
1198
+ return runBashServiceLifecycleAction(input.service, context);
1199
+ }
1200
+ throw new Error("Bash.command\u3001Bash.binary \u6216 Bash.service action \u5FC5\u987B\u63D0\u4F9B\u4E00\u4E2A\u3002");
1201
+ }
1012
1202
  const logRoot = context.logRoot ?? join(context.workspaceRoot, ".linghun", "logs", "tools");
1013
1203
  await mkdir(logRoot, { recursive: true });
1014
1204
  const fullOutputPath = join(logRoot, `bash-${Date.now()}-${randomUUID()}.log`);
1015
1205
  const timeoutMs = input.timeoutMs ?? BASH_TIMEOUT_MS;
1016
1206
  const adapted = adaptShellCommand(input.command);
1207
+ if (input.service && !isBashServiceLifecycleAction(input.service)) {
1208
+ const readiness = withDefaultServiceReadiness(input.service, context);
1209
+ return startBashService({
1210
+ command: adapted.command,
1211
+ logCommand: adapted.logCommand ?? adapted.command,
1212
+ cwd: context.workspaceRoot,
1213
+ fullOutputPath,
1214
+ readiness,
1215
+ context,
1216
+ abortSignal: context.abortSignal,
1217
+ onProgress: (stream, text) => void context.onProgress?.({ toolName: "Bash", stream, text }),
1218
+ trackChildProcess: context.trackChildProcess,
1219
+ sanitizeText: sanitizeSecrets
1220
+ });
1221
+ }
1222
+ if (input.runInBackground === true) {
1223
+ const taskId = randomUUID();
1224
+ void runBackgroundBash({
1225
+ taskId,
1226
+ command: adapted.command,
1227
+ originalCommand: input.command,
1228
+ cwd: context.workspaceRoot,
1229
+ timeoutMs,
1230
+ fullOutputPath,
1231
+ adapter: adapted.adapter,
1232
+ logCommand: adapted.logCommand,
1233
+ retainAfterReturn: isHeadlessBenchContext(context),
1234
+ abortSignal: context.abortSignal,
1235
+ trackChildProcess: context.trackChildProcess,
1236
+ onComplete: context.onBackgroundBashComplete
1237
+ });
1238
+ return {
1239
+ text: `\u547D\u4EE4\u5DF2\u5728\u540E\u53F0\u542F\u52A8\u3002
1240
+ taskId: ${taskId}
1241
+ outputPath: ${fullOutputPath}`,
1242
+ data: {
1243
+ backgroundTaskId: taskId,
1244
+ outputPath: fullOutputPath,
1245
+ command: sanitizeSecrets(input.command)
1246
+ }
1247
+ };
1248
+ }
1017
1249
  const result = await runShell(
1018
1250
  adapted.command,
1019
1251
  context.workspaceRoot,
@@ -1027,23 +1259,35 @@ async function bashTool(input, context) {
1027
1259
  `original command ${summarizeOriginalShellCommand(input.command)}`
1028
1260
  ];
1029
1261
  const commandForLog = adapted.logCommand ?? adapted.command;
1030
- const fullText = [
1262
+ const sanitizedShellOutput = sanitizeSecrets(result.output);
1263
+ const cmdInterpretation = interpretCommandResult(input.command, result.exitCode);
1264
+ const rawFullText = [
1031
1265
  `$ ${sanitizeSecrets(commandForLog)}`,
1032
1266
  ...adapterLines,
1033
1267
  `exit code ${result.exitCode}`,
1034
1268
  `outcome ${result.outcome}`,
1269
+ ...cmdInterpretation.message ? [`returnCodeInterpretation: ${cmdInterpretation.message}`] : [],
1035
1270
  "",
1036
- sanitizeSecrets(result.output)
1271
+ sanitizedShellOutput
1037
1272
  ].join("\n");
1273
+ const diagnostics = createBashOutcomeDiagnostics(result.outcome);
1274
+ const fullText = rawFullText;
1038
1275
  await writeFile(fullOutputPath, fullText, "utf8");
1039
1276
  const truncated = fullText.length > BASH_PREVIEW_LIMIT;
1040
1277
  const preview = truncated ? `${fullText.slice(0, BASH_PREVIEW_LIMIT)}
1041
1278
  ...\uFF08\u8F93\u51FA\u5DF2\u622A\u65AD\uFF0C\u5B8C\u6574\u65E5\u5FD7\u89C1 fullOutputPath\uFF09` : fullText;
1042
1279
  const details = createBashDetails(fullOutputPath, fullText);
1280
+ const data = adapted.adapter === "native" ? { exitCode: result.exitCode, outcome: result.outcome } : { exitCode: result.exitCode, outcome: result.outcome, adapter: adapted.adapter };
1281
+ const outputData = {
1282
+ ...data,
1283
+ ...cmdInterpretation.message ? { returnCodeInterpretation: cmdInterpretation.message } : {},
1284
+ ...cmdInterpretation.isError === false && result.exitCode !== 0 ? { isError: false } : {},
1285
+ ...diagnostics.length > 0 ? { diagnostics } : {}
1286
+ };
1043
1287
  return {
1044
1288
  text: preview,
1045
1289
  details,
1046
- data: adapted.adapter === "native" ? { exitCode: result.exitCode, outcome: result.outcome } : { exitCode: result.exitCode, outcome: result.outcome, adapter: adapted.adapter },
1290
+ data: outputData,
1047
1291
  truncated,
1048
1292
  fullOutputPath
1049
1293
  };
@@ -1057,6 +1301,513 @@ function createBashDetails(fullOutputPath, fullText) {
1057
1301
  ...tailLines(fullText, 80)
1058
1302
  ].join("\n");
1059
1303
  }
1304
+ var BINARY_HINT_EXTENSIONS = [
1305
+ ".bin",
1306
+ ".elf",
1307
+ ".o",
1308
+ ".so",
1309
+ ".a",
1310
+ ".exe",
1311
+ ".dll",
1312
+ ".7z",
1313
+ ".zip",
1314
+ ".gz",
1315
+ ".png",
1316
+ ".jpg",
1317
+ ".jpeg",
1318
+ ".pdf",
1319
+ ".sqlite",
1320
+ ".db"
1321
+ ];
1322
+ var ARTIFACT_HINT_EXTENSIONS = [
1323
+ ".txt",
1324
+ ".json",
1325
+ ".out",
1326
+ ".log",
1327
+ ".py",
1328
+ ".html",
1329
+ ".htm",
1330
+ ".csv",
1331
+ ".yaml",
1332
+ ".yml",
1333
+ ".toml",
1334
+ ".md",
1335
+ ".sh",
1336
+ ".bin",
1337
+ ".elf",
1338
+ ".zip",
1339
+ ".7z",
1340
+ ".png",
1341
+ ".pdf",
1342
+ ".sqlite",
1343
+ ".db"
1344
+ ];
1345
+ function parseBashCommandIntent(command, _context) {
1346
+ const tokens = tokenizeShellLike(command);
1347
+ const segments = splitCommandSegments(tokens);
1348
+ const commandNames = [];
1349
+ const parsedSegments = [];
1350
+ const binaryCandidates = /* @__PURE__ */ new Set();
1351
+ const artifactCandidates = /* @__PURE__ */ new Set();
1352
+ const serviceCandidates = [];
1353
+ for (const segment of segments) {
1354
+ const parsed = parseCommandSegment(segment.tokens);
1355
+ if (!parsed) continue;
1356
+ commandNames.push(parsed.commandName);
1357
+ parsedSegments.push({
1358
+ commandName: parsed.commandName,
1359
+ argv: parsed.argv,
1360
+ background: segment.background || parsed.background,
1361
+ redirections: collectSegmentRedirections(parsed.argv)
1362
+ });
1363
+ collectSegmentBinaryCandidates(parsed, binaryCandidates);
1364
+ collectSegmentArtifactCandidates(parsed, artifactCandidates);
1365
+ serviceCandidates.push(...collectSegmentServiceCandidates(parsed));
1366
+ }
1367
+ return {
1368
+ binaryCandidates: [...binaryCandidates],
1369
+ serviceCandidates: dedupeServiceCandidates(serviceCandidates),
1370
+ artifactCandidates: [...artifactCandidates],
1371
+ backgroundLikely: isBackgroundLikely(segments, serviceCandidates),
1372
+ commandNames: unique(commandNames.filter(Boolean)),
1373
+ segments: parsedSegments,
1374
+ hasShellControl: segments.length !== 1 || tokens.some((token) => isCommandSeparator(token))
1375
+ };
1376
+ }
1377
+ var BINARY_COMMAND_NAMES = /* @__PURE__ */ new Set(["file", "xxd", "readelf", "objdump", "hexdump", "strings", "7z", "7za", "unzip", "tar"]);
1378
+ var SHELL_RESERVED_COMMAND_NAMES = /* @__PURE__ */ new Set([
1379
+ "if",
1380
+ "then",
1381
+ "else",
1382
+ "elif",
1383
+ "fi",
1384
+ "for",
1385
+ "while",
1386
+ "until",
1387
+ "do",
1388
+ "done",
1389
+ "case",
1390
+ "esac",
1391
+ "in",
1392
+ "function",
1393
+ "coproc"
1394
+ ]);
1395
+ var SHELL_SYNTAX_COMMAND_NAMES = /* @__PURE__ */ new Set([
1396
+ "[",
1397
+ "[[",
1398
+ "test",
1399
+ "{",
1400
+ "}",
1401
+ "(",
1402
+ ")",
1403
+ "!",
1404
+ ":",
1405
+ "true",
1406
+ "false"
1407
+ ]);
1408
+ var ARTIFACT_PRODUCING_COMMAND_NAMES = /* @__PURE__ */ new Set(["touch", "tee"]);
1409
+ var ARTIFACT_PRODUCING_TOKENS = /* @__PURE__ */ new Set(["write", "wrote", "written", "create", "created", "generate", "generated", "build", "built", "output"]);
1410
+ function canSafelyAliasPythonCommand(intent) {
1411
+ const segment = intent.segments[0];
1412
+ return intent.segments.length === 1 && !intent.hasShellControl && segment?.commandName === "python" && !segment.background && segment.redirections.length === 0;
1413
+ }
1414
+ function tokenizeShellLike(command) {
1415
+ const tokens = [];
1416
+ let current = "";
1417
+ let quote;
1418
+ let escaped = false;
1419
+ const push = () => {
1420
+ if (current.length > 0) tokens.push(current);
1421
+ current = "";
1422
+ };
1423
+ for (let index = 0; index < command.length; index += 1) {
1424
+ const char = command[index] ?? "";
1425
+ if (escaped) {
1426
+ current += char;
1427
+ escaped = false;
1428
+ continue;
1429
+ }
1430
+ if (char === "\\") {
1431
+ escaped = true;
1432
+ continue;
1433
+ }
1434
+ if (quote) {
1435
+ if (char === quote) quote = void 0;
1436
+ else current += char;
1437
+ continue;
1438
+ }
1439
+ if (char === "'" || char === '"') {
1440
+ quote = char;
1441
+ continue;
1442
+ }
1443
+ if (/\s/u.test(char)) {
1444
+ push();
1445
+ continue;
1446
+ }
1447
+ if (char === ">" && /^\d+$/u.test(current)) {
1448
+ const prefix = current;
1449
+ current = "";
1450
+ let redirect = `${prefix}>`;
1451
+ if (command[index + 1] === ">") {
1452
+ redirect += ">";
1453
+ index += 1;
1454
+ }
1455
+ if (command[index + 1] === "&") {
1456
+ redirect += "&";
1457
+ index += 1;
1458
+ while (index + 1 < command.length && /[A-Za-z0-9_/-]/u.test(command[index + 1] ?? "")) {
1459
+ redirect += command[index + 1];
1460
+ index += 1;
1461
+ }
1462
+ }
1463
+ tokens.push(redirect);
1464
+ continue;
1465
+ }
1466
+ if (char === "(" && current.length === 0) {
1467
+ push();
1468
+ tokens.push(char);
1469
+ continue;
1470
+ }
1471
+ if ((char === "{" || char === "}") && current.length === 0) {
1472
+ push();
1473
+ tokens.push(char);
1474
+ continue;
1475
+ }
1476
+ if (char === ")" && !current.includes("$(")) {
1477
+ push();
1478
+ tokens.push(char);
1479
+ continue;
1480
+ }
1481
+ if (char === "|" || char === ";" || char === "&" || char === ">") {
1482
+ push();
1483
+ const next = command[index + 1];
1484
+ if (char === "&" && next === "&" || char === ">" && next === ">") {
1485
+ tokens.push(`${char}${next}`);
1486
+ index += 1;
1487
+ } else {
1488
+ tokens.push(char);
1489
+ }
1490
+ continue;
1491
+ }
1492
+ current += char;
1493
+ }
1494
+ push();
1495
+ return tokens;
1496
+ }
1497
+ function isCommandSeparator(token) {
1498
+ return token === "|" || token === "&&" || token === ";" || token === "&";
1499
+ }
1500
+ function splitCommandSegments(tokens) {
1501
+ const segments = [];
1502
+ let current = [];
1503
+ for (const token of tokens) {
1504
+ if (!isCommandSeparator(token)) {
1505
+ current.push(token);
1506
+ continue;
1507
+ }
1508
+ if (current.length > 0) {
1509
+ segments.push({ tokens: current, background: token === "&" });
1510
+ current = [];
1511
+ }
1512
+ }
1513
+ if (current.length > 0) segments.push({ tokens: current, background: false });
1514
+ return segments;
1515
+ }
1516
+ function parseCommandSegment(tokens) {
1517
+ const env = /* @__PURE__ */ new Map();
1518
+ let index = 0;
1519
+ while (index < tokens.length && isShellGroupBoundaryToken(tokens[index] ?? "")) {
1520
+ index += 1;
1521
+ }
1522
+ while (index < tokens.length && isEnvironmentAssignment(tokens[index] ?? "")) {
1523
+ const [name, ...rest] = (tokens[index] ?? "").split("=");
1524
+ if (name) env.set(name.toUpperCase(), rest.join("="));
1525
+ index += 1;
1526
+ }
1527
+ if (index >= tokens.length) return void 0;
1528
+ let commandToken = tokens[index] ?? "";
1529
+ if (commandToken === "nohup" || commandToken === "setsid" || commandToken === "time") {
1530
+ index += 1;
1531
+ commandToken = tokens[index] ?? "";
1532
+ }
1533
+ if (isShellReservedCommandToken(commandToken)) return void 0;
1534
+ if (isShellSyntaxCommandToken(commandToken)) return void 0;
1535
+ if (!commandToken) return void 0;
1536
+ const commandName = stripExecutableToken(commandToken);
1537
+ if (!commandName || isShellReservedCommandToken(commandName) || isShellSyntaxCommandToken(commandName)) {
1538
+ return void 0;
1539
+ }
1540
+ return {
1541
+ commandName,
1542
+ argv: tokens.slice(index + 1).filter((token) => !isShellGroupBoundaryToken(token)),
1543
+ env,
1544
+ background: tokens.includes("disown")
1545
+ };
1546
+ }
1547
+ function collectSegmentBinaryCandidates(segment, target) {
1548
+ if (BINARY_COMMAND_NAMES.has(segment.commandName)) {
1549
+ for (const token of segment.argv) {
1550
+ if (!isOptionToken(token)) addPathCandidate(target, token);
1551
+ }
1552
+ }
1553
+ for (const token of segment.argv) {
1554
+ if (isBinarySuffix(token)) addPathCandidate(target, token);
1555
+ }
1556
+ }
1557
+ function collectSegmentArtifactCandidates(segment, target) {
1558
+ for (let index = 0; index < segment.argv.length; index += 1) {
1559
+ const token = segment.argv[index] ?? "";
1560
+ if (token === ">" || token === ">>") {
1561
+ addPathCandidate(target, segment.argv[index + 1] ?? "");
1562
+ index += 1;
1563
+ continue;
1564
+ }
1565
+ if (isOutputFlag(token)) {
1566
+ addPathCandidate(target, segment.argv[index + 1] ?? "");
1567
+ index += 1;
1568
+ continue;
1569
+ }
1570
+ if (isArtifactSuffix(token) && isArtifactProducingCommand(segment.commandName, segment.argv)) {
1571
+ addPathCandidate(target, token);
1572
+ }
1573
+ }
1574
+ }
1575
+ function collectSegmentRedirections(argv) {
1576
+ const redirections = [];
1577
+ for (let index = 0; index < argv.length; index += 1) {
1578
+ const token = argv[index] ?? "";
1579
+ if (token !== ">" && token !== ">>") continue;
1580
+ const target = argv[index + 1] ?? "";
1581
+ if (target) redirections.push({ operator: token, target });
1582
+ index += 1;
1583
+ }
1584
+ return redirections;
1585
+ }
1586
+ function collectSegmentServiceCandidates(segment) {
1587
+ const candidates = [];
1588
+ const envPort = parsePort(segment.env.get("PORT"));
1589
+ const explicit = findExplicitServiceTarget(segment.argv, envPort);
1590
+ if (isPythonHttpServer(segment)) {
1591
+ const port = findFirstPortToken(segment.argv) ?? envPort ?? 8e3;
1592
+ candidates.push(createServiceIntent("http-server", port, "high", "python -m http.server"));
1593
+ return candidates;
1594
+ }
1595
+ if (segment.commandName === "uvicorn") {
1596
+ if (explicit) candidates.push(createServiceIntent("framework-server", explicit.port, "high", explicit.evidence, explicit.host));
1597
+ return candidates;
1598
+ }
1599
+ if (segment.commandName === "flask" && segment.argv[0]?.toLowerCase() === "run") {
1600
+ if (explicit) candidates.push(createServiceIntent("framework-server", explicit.port, "high", explicit.evidence, explicit.host));
1601
+ return candidates;
1602
+ }
1603
+ if (segment.commandName === "gunicorn") {
1604
+ const bind = findGunicornBindTarget(segment.argv);
1605
+ if (bind) candidates.push(createServiceIntent("framework-server", bind.port, "high", bind.evidence, bind.host));
1606
+ return candidates;
1607
+ }
1608
+ if (isPackageScriptCommand(segment)) {
1609
+ if (explicit) candidates.push(createServiceIntent("package-script", explicit.port, "medium", explicit.evidence, explicit.host));
1610
+ return candidates;
1611
+ }
1612
+ if (isJsRuntimeCommand(segment.commandName)) {
1613
+ if (explicit) candidates.push(createServiceIntent("explicit-port", explicit.port, "medium", explicit.evidence, explicit.host));
1614
+ return candidates;
1615
+ }
1616
+ if (explicit && isKnownServiceCommand(segment.commandName)) {
1617
+ candidates.push(createServiceIntent("explicit-port", explicit.port, "medium", explicit.evidence, explicit.host));
1618
+ }
1619
+ return candidates;
1620
+ }
1621
+ function createServiceIntent(kind, port, confidence, evidence, host = "127.0.0.1") {
1622
+ return { kind, host: normalizeServiceHost(host), port, confidence, evidence };
1623
+ }
1624
+ function isPythonHttpServer(segment) {
1625
+ return (segment.commandName === "python" || segment.commandName === "python3") && hasAdjacentTokens(segment.argv, "-m", "http.server");
1626
+ }
1627
+ function findExplicitServiceTarget(argv, envPort) {
1628
+ let host = "127.0.0.1";
1629
+ if (envPort) return { host, port: envPort, evidence: "PORT=" };
1630
+ for (let index = 0; index < argv.length; index += 1) {
1631
+ const token = argv[index] ?? "";
1632
+ if (token === "--host") host = normalizeServiceHost(argv[index + 1] ?? host);
1633
+ if (token === "--port" || token === "-p") {
1634
+ const port = parsePort(argv[index + 1]);
1635
+ if (port) return { host, port, evidence: token };
1636
+ }
1637
+ const inlinePort = parseInlineOptionPort(token);
1638
+ if (inlinePort) return { host, port: inlinePort, evidence: token.split("=")[0] ?? token };
1639
+ const target = parseHostPortToken(token);
1640
+ if (target?.port) return { host: target.host ?? host, port: target.port, evidence: "host:port" };
1641
+ }
1642
+ return void 0;
1643
+ }
1644
+ function findGunicornBindTarget(argv) {
1645
+ for (let index = 0; index < argv.length; index += 1) {
1646
+ const token = argv[index] ?? "";
1647
+ if (token !== "-b" && token !== "--bind") continue;
1648
+ const target = parseHostPortToken(argv[index + 1] ?? "");
1649
+ if (target?.port) return { host: target.host ?? "127.0.0.1", port: target.port, evidence: token };
1650
+ }
1651
+ return void 0;
1652
+ }
1653
+ function findFirstPortToken(argv) {
1654
+ for (const token of argv) {
1655
+ const port = parsePort(token);
1656
+ if (port) return port;
1657
+ }
1658
+ return void 0;
1659
+ }
1660
+ function parseInlineOptionPort(token) {
1661
+ if (!token.startsWith("--port=") && !token.startsWith("-p=")) return void 0;
1662
+ return parsePort(token.slice(token.indexOf("=") + 1));
1663
+ }
1664
+ function parsePort(value) {
1665
+ if (!value || !/^\d{1,5}$/u.test(value)) return void 0;
1666
+ return normalizePort(Number(value));
1667
+ }
1668
+ function isPackageScriptCommand(segment) {
1669
+ if (segment.commandName !== "npm" && segment.commandName !== "pnpm" && segment.commandName !== "yarn") return false;
1670
+ const command = segment.argv[0]?.toLowerCase();
1671
+ const script = segment.argv[1]?.toLowerCase();
1672
+ return command === "start" || command === "dev" || command === "test-server" || command === "run" && (script === "start" || script === "dev" || script === "test-server");
1673
+ }
1674
+ function isJsRuntimeCommand(commandName) {
1675
+ return commandName === "node" || commandName === "deno" || commandName === "bun";
1676
+ }
1677
+ function isKnownServiceCommand(commandName) {
1678
+ return commandName === "grpc";
1679
+ }
1680
+ function normalizeServiceHost(host) {
1681
+ return "127.0.0.1";
1682
+ }
1683
+ function isBackgroundLikely(segments, serviceCandidates) {
1684
+ return segments.some(
1685
+ (segment) => segment.background || segment.tokens[0] === "nohup" || segment.tokens[0] === "setsid" || segment.tokens.includes("disown")
1686
+ ) || serviceCandidates.some(
1687
+ (candidate) => candidate.kind === "http-server" || candidate.kind === "framework-server" || candidate.kind === "package-script"
1688
+ );
1689
+ }
1690
+ function stripExecutableToken(token) {
1691
+ const normalized = token.replace(/^\(+/u, "").replace(/\)+$/u, "");
1692
+ if (normalized.includes("/") || normalized.includes("\\")) {
1693
+ return normalized.replace(/\.(?:exe|cmd|bat)$/iu, "");
1694
+ }
1695
+ return basename(normalized).replace(/\.(?:exe|cmd|bat)$/iu, "").toLowerCase();
1696
+ }
1697
+ function addPathCandidate(target, token) {
1698
+ if (!isStaticPathCandidate(token)) return;
1699
+ target.add(token.replaceAll("\\", "/"));
1700
+ }
1701
+ function isOptionToken(token) {
1702
+ return token.startsWith("-") && !/^-?\d+$/u.test(token);
1703
+ }
1704
+ function isOutputFlag(token) {
1705
+ return token === "-o" || token === "--output" || token === "--out" || token === "--dest" || token === "--file";
1706
+ }
1707
+ function isEnvironmentAssignment(token) {
1708
+ return /^[A-Za-z_][A-Za-z0-9_]*=.*$/u.test(token);
1709
+ }
1710
+ function isShellReservedCommandToken(token) {
1711
+ return SHELL_RESERVED_COMMAND_NAMES.has(token.toLowerCase());
1712
+ }
1713
+ function isShellSyntaxCommandToken(token) {
1714
+ return SHELL_SYNTAX_COMMAND_NAMES.has(token.toLowerCase());
1715
+ }
1716
+ function isShellGroupBoundaryToken(token) {
1717
+ return token === "(" || token === ")" || token === "{" || token === "}";
1718
+ }
1719
+ function isStaticPathCandidate(token) {
1720
+ if (!token || isOptionToken(token) || token.includes("://")) return false;
1721
+ if (token.includes("$") || token.includes("`")) return false;
1722
+ if (/^(?:\d+)?>{1,2}(?:&\d+)?$/u.test(token)) return false;
1723
+ if (token === "|" || token === ";" || token === "&" || token === "<") return false;
1724
+ if (isShellGroupBoundaryToken(token) || isShellSyntaxCommandToken(token)) return false;
1725
+ if (/^\d+$/u.test(token)) return false;
1726
+ if (token === "/dev/null" || token === "/dev/stdout" || token === "/dev/stderr") return false;
1727
+ return true;
1728
+ }
1729
+ function isBinarySuffix(token) {
1730
+ return BINARY_HINT_EXTENSIONS.some((extension) => token.toLowerCase().endsWith(extension));
1731
+ }
1732
+ function isArtifactSuffix(token) {
1733
+ return ARTIFACT_HINT_EXTENSIONS.some((extension) => token.toLowerCase().endsWith(extension));
1734
+ }
1735
+ function isArtifactProducingCommand(commandName, tokens) {
1736
+ if (!commandName) return false;
1737
+ if (ARTIFACT_PRODUCING_COMMAND_NAMES.has(commandName)) return true;
1738
+ return tokens.some((token) => ARTIFACT_PRODUCING_TOKENS.has(token.toLowerCase()));
1739
+ }
1740
+ function parseHostPortToken(token) {
1741
+ const url = tryParseLocalUrl(token);
1742
+ if (url) return url;
1743
+ const separator = token.lastIndexOf(":");
1744
+ if (separator <= 0) return void 0;
1745
+ const host = token.slice(0, separator).replace(/^\[(.*)\]$/u, "$1");
1746
+ const port = normalizePort(Number(token.slice(separator + 1)));
1747
+ if (!port || !isLocalHost(host)) return void 0;
1748
+ return normalizeHostPort(host, port);
1749
+ }
1750
+ function tryParseLocalUrl(token) {
1751
+ try {
1752
+ const url = new URL(token);
1753
+ if (url.protocol !== "http:" && url.protocol !== "https:") return void 0;
1754
+ if (!isLocalHost(url.hostname)) return void 0;
1755
+ const port = normalizePort(Number(url.port));
1756
+ if (!port) return void 0;
1757
+ return normalizeHostPort(url.hostname, port);
1758
+ } catch {
1759
+ return void 0;
1760
+ }
1761
+ }
1762
+ function isLocalHost(host) {
1763
+ return host === "localhost" || host === "127.0.0.1" || host === "0.0.0.0" || host === "::1";
1764
+ }
1765
+ function hasAdjacentTokens(tokens, first, second) {
1766
+ return tokens.some(
1767
+ (token, index) => token.toLowerCase() === first && tokens[index + 1]?.toLowerCase() === second
1768
+ );
1769
+ }
1770
+ function normalizePort(value) {
1771
+ return Number.isInteger(value) && value > 0 && value <= 65535 ? value : void 0;
1772
+ }
1773
+ function dedupeServiceCandidates(candidates) {
1774
+ const seen = /* @__PURE__ */ new Set();
1775
+ return candidates.filter((candidate) => {
1776
+ const key = `${candidate.host}:${candidate.port}:${candidate.kind}`;
1777
+ if (seen.has(key)) return false;
1778
+ seen.add(key);
1779
+ return true;
1780
+ });
1781
+ }
1782
+ function normalizeHostPort(host, port) {
1783
+ if (!Number.isInteger(port) || port <= 0 || port > 65535) return void 0;
1784
+ return { host: host === "0.0.0.0" || host === "localhost" ? "127.0.0.1" : host.replace(/^\[(.*)\]$/u, "$1"), port };
1785
+ }
1786
+ function isHeadlessBenchContext(context) {
1787
+ const record = context;
1788
+ return record.headlessBench?.enabled === true || record.isHeadlessBench === true;
1789
+ }
1790
+ function withDefaultServiceReadiness(readiness, context) {
1791
+ if (readiness.timeoutMs !== void 0) return readiness;
1792
+ const hasReadinessRisk = (context.recentDiagnostics ?? []).some(
1793
+ (diagnostic) => diagnostic.type === "service_readiness" || diagnostic.type === "timeout"
1794
+ );
1795
+ const timeoutMs = hasReadinessRisk ? 15e3 : isHeadlessBenchContext(context) ? 1e4 : 5e3;
1796
+ return { ...readiness, timeoutMs };
1797
+ }
1798
+ function createBashOutcomeDiagnostics(outcome) {
1799
+ if (outcome === "timeout") {
1800
+ return [
1801
+ {
1802
+ type: "timeout",
1803
+ severity: "recoverable",
1804
+ evidence: "Bash outcome timeout",
1805
+ suggestion: "Run the smallest focused check first and avoid repeating long blind commands."
1806
+ }
1807
+ ];
1808
+ }
1809
+ return [];
1810
+ }
1060
1811
  function tailLines(text, limit) {
1061
1812
  const lines = text.split(/\r?\n/u);
1062
1813
  return lines.slice(Math.max(0, lines.length - limit));
@@ -1431,6 +2182,14 @@ function resolveWorkspacePath(workspaceRoot, inputPath) {
1431
2182
  function relativePath(workspaceRoot, filePath) {
1432
2183
  return relative(workspaceRoot, filePath).replaceAll("\\", "/") || ".";
1433
2184
  }
2185
+ function resolveArtifactPath(context, inputPath) {
2186
+ const absolute = resolveWorkspacePath(context.workspaceRoot, inputPath);
2187
+ return {
2188
+ input: inputPath,
2189
+ absolute,
2190
+ relative: relativePath(context.workspaceRoot, absolute)
2191
+ };
2192
+ }
1434
2193
  function ensureUnique(content, oldText) {
1435
2194
  if (!oldText) {
1436
2195
  throw new Error("oldText \u4E0D\u80FD\u4E3A\u7A7A\u3002\u5EFA\u8BAE\uFF1A\u63D0\u4F9B\u8DB3\u591F\u4E0A\u4E0B\u6587\u7684\u552F\u4E00\u7247\u6BB5\u3002");
@@ -2063,6 +2822,198 @@ function runShell(command, cwd, timeoutMs, signal, onProgress, trackChildProcess
2063
2822
  });
2064
2823
  });
2065
2824
  }
2825
+ var STALL_THRESHOLD_MS = 45e3;
2826
+ var PROMPT_PATTERNS = [
2827
+ /\(y\/n\)\s*$/i,
2828
+ /\[y\/n\]\s*$/i,
2829
+ /\(yes\/no\)\s*$/i,
2830
+ /Press Enter/i,
2831
+ /Continue\?/i,
2832
+ /Overwrite\?/i,
2833
+ /password[:\s]*$/i,
2834
+ /passphrase[:\s]*$/i,
2835
+ /\?\s*$/
2836
+ ];
2837
+ function looksLikePrompt(tail) {
2838
+ const lastLine = tail.split(/\r?\n/).filter(Boolean).pop() ?? "";
2839
+ return PROMPT_PATTERNS.some((re) => re.test(lastLine));
2840
+ }
2841
+ async function runBackgroundBash(opts) {
2842
+ const {
2843
+ taskId,
2844
+ command,
2845
+ originalCommand,
2846
+ cwd,
2847
+ timeoutMs,
2848
+ fullOutputPath,
2849
+ adapter,
2850
+ logCommand,
2851
+ retainAfterReturn,
2852
+ abortSignal,
2853
+ onProgress,
2854
+ trackChildProcess,
2855
+ onComplete
2856
+ } = opts;
2857
+ await mkdir(dirname(fullOutputPath), { recursive: true }).catch(() => {
2858
+ });
2859
+ const fileStream = createWriteStream(fullOutputPath, { encoding: "utf8" });
2860
+ const commandForLog = logCommand ?? command;
2861
+ const header = [
2862
+ `$ ${sanitizeSecrets(commandForLog)}`,
2863
+ ...adapter !== "native" ? [`adapter ${adapter}`, `original command ${summarizeOriginalShellCommand(originalCommand)}`] : [],
2864
+ ""
2865
+ ].join("\n");
2866
+ fileStream.write(header);
2867
+ const detached = process.platform !== "win32";
2868
+ if (retainAfterReturn) {
2869
+ fileStream.write("\n[background] retained process started\n");
2870
+ await new Promise((resolveEnd) => fileStream.end(resolveEnd));
2871
+ const outFd = openSync(fullOutputPath, "a");
2872
+ const errFd = openSync(fullOutputPath, "a");
2873
+ const child2 = spawn(command, {
2874
+ cwd,
2875
+ shell: true,
2876
+ windowsHide: true,
2877
+ detached,
2878
+ stdio: ["ignore", outFd, errFd]
2879
+ });
2880
+ closeSync(outFd);
2881
+ closeSync(errFd);
2882
+ child2.unref();
2883
+ trackChildProcess?.(child2, {
2884
+ detached,
2885
+ cwd,
2886
+ label: `BashBg:${command.slice(0, 80)}`,
2887
+ retainAfterExit: true
2888
+ });
2889
+ onComplete?.({
2890
+ taskId,
2891
+ exitCode: 0,
2892
+ outcome: "completed",
2893
+ outputPath: fullOutputPath,
2894
+ command: sanitizeSecrets(originalCommand)
2895
+ });
2896
+ return;
2897
+ }
2898
+ const child = spawn(command, { cwd, shell: true, windowsHide: true, detached });
2899
+ trackChildProcess?.(child, {
2900
+ detached,
2901
+ cwd,
2902
+ label: `BashBg:${command.slice(0, 80)}`,
2903
+ retainAfterExit: detached
2904
+ });
2905
+ let tailBuffer = "";
2906
+ let lastOutputTime = Date.now();
2907
+ let stallTimer;
2908
+ let settled = false;
2909
+ let outcome = "completed";
2910
+ const resetStallTimer = () => {
2911
+ lastOutputTime = Date.now();
2912
+ if (stallTimer) clearTimeout(stallTimer);
2913
+ stallTimer = setTimeout(checkStall, STALL_THRESHOLD_MS);
2914
+ };
2915
+ const checkStall = () => {
2916
+ if (Date.now() - lastOutputTime >= STALL_THRESHOLD_MS && looksLikePrompt(tailBuffer)) {
2917
+ const msg = "\n[stall watchdog] \u68C0\u6D4B\u5230\u4EA4\u4E92\u5F0F\u63D0\u793A\uFF0C\u53EF\u80FD\u9700\u8981\u7528\u6237\u8F93\u5165\u3002\n";
2918
+ fileStream.write(msg);
2919
+ onProgress?.("system", msg);
2920
+ }
2921
+ };
2922
+ const finish = (exitCode) => {
2923
+ if (settled) return;
2924
+ settled = true;
2925
+ if (stallTimer) clearTimeout(stallTimer);
2926
+ clearTimeout(timer);
2927
+ abortSignal?.removeEventListener("abort", onAbort);
2928
+ const footer = `
2929
+ exit code ${exitCode}
2930
+ outcome ${outcome}
2931
+ `;
2932
+ fileStream.write(footer);
2933
+ fileStream.end();
2934
+ onComplete?.({
2935
+ taskId,
2936
+ exitCode,
2937
+ outcome,
2938
+ outputPath: fullOutputPath,
2939
+ command: sanitizeSecrets(originalCommand)
2940
+ });
2941
+ };
2942
+ const requestStop = async (force) => {
2943
+ if (process.platform === "win32" && child.pid) {
2944
+ await stopWindowsProcessTree(child.pid, cwd);
2945
+ return;
2946
+ }
2947
+ const sig = force ? "SIGKILL" : "SIGTERM";
2948
+ if (detached && child.pid) {
2949
+ try {
2950
+ process.kill(-child.pid, sig);
2951
+ } catch {
2952
+ child.kill(sig);
2953
+ }
2954
+ } else {
2955
+ child.kill(sig);
2956
+ }
2957
+ };
2958
+ const FORCE_KILL_WAIT_MS = 3e3;
2959
+ const waitForCloseOrForceKill = () => {
2960
+ const forceTimer = setTimeout(() => {
2961
+ void requestStop(true);
2962
+ setTimeout(() => finish(1), 500);
2963
+ }, FORCE_KILL_WAIT_MS);
2964
+ child.once("close", () => {
2965
+ clearTimeout(forceTimer);
2966
+ });
2967
+ };
2968
+ const onAbort = () => {
2969
+ if (settled) return;
2970
+ outcome = "cancelled";
2971
+ const msg = "\n[cancelled] \u5DE5\u5177\u8C03\u7528\u5DF2\u53D6\u6D88\uFF0C\u6B63\u5728\u7EC8\u6B62\u5B50\u8FDB\u7A0B\u3002\n";
2972
+ fileStream.write(msg);
2973
+ onProgress?.("system", msg);
2974
+ void requestStop(false);
2975
+ waitForCloseOrForceKill();
2976
+ };
2977
+ const timer = setTimeout(() => {
2978
+ if (settled) return;
2979
+ outcome = "timeout";
2980
+ const msg = `
2981
+ [timeout] \u547D\u4EE4\u8D85\u65F6\uFF1A\u8D85\u8FC7 ${timeoutMs}ms\uFF0C\u5DF2\u7EC8\u6B62\u3002
2982
+ `;
2983
+ fileStream.write(msg);
2984
+ onProgress?.("system", msg);
2985
+ void requestStop(false);
2986
+ waitForCloseOrForceKill();
2987
+ }, timeoutMs);
2988
+ if (abortSignal?.aborted) {
2989
+ onAbort();
2990
+ return;
2991
+ }
2992
+ abortSignal?.addEventListener("abort", onAbort, { once: true });
2993
+ child.stdout.on("data", (chunk) => {
2994
+ const text = decodeShellChunk(chunk);
2995
+ fileStream.write(text);
2996
+ tailBuffer = (tailBuffer + text).slice(-2e3);
2997
+ resetStallTimer();
2998
+ onProgress?.("stdout", text);
2999
+ });
3000
+ child.stderr.on("data", (chunk) => {
3001
+ const text = decodeShellChunk(chunk);
3002
+ fileStream.write(text);
3003
+ tailBuffer = (tailBuffer + text).slice(-2e3);
3004
+ resetStallTimer();
3005
+ onProgress?.("stderr", text);
3006
+ });
3007
+ child.on("close", (code) => {
3008
+ finish(code ?? 1);
3009
+ });
3010
+ child.on("error", (err) => {
3011
+ fileStream.write(`
3012
+ [error] ${err.message}
3013
+ `);
3014
+ finish(1);
3015
+ });
3016
+ }
2066
3017
  function decodeShellChunk(chunk) {
2067
3018
  const utf8 = chunk.toString("utf8");
2068
3019
  if (process.platform !== "win32") return utf8;
@@ -2190,14 +3141,21 @@ function unique(items) {
2190
3141
  }
2191
3142
  var __testGlobToRegExp = globToRegExp;
2192
3143
  var __testDecodeShellChunk = decodeShellChunk;
3144
+ var __testParseBashCommandIntent = parseBashCommandIntent;
3145
+ var __testCanSafelyAliasPythonCommand = canSafelyAliasPythonCommand;
3146
+ var __testRunBackgroundBash = runBackgroundBash;
2193
3147
  export {
3148
+ __testCanSafelyAliasPythonCommand,
2194
3149
  __testDecodeShellChunk,
2195
3150
  __testGlobToRegExp,
3151
+ __testParseBashCommandIntent,
3152
+ __testRunBackgroundBash,
2196
3153
  adaptShellCommand,
2197
3154
  adaptShellCommandForPlatform,
2198
3155
  builtInTools,
2199
3156
  createTool,
2200
3157
  createToolContext,
3158
+ interpretCommandResult,
2201
3159
  runTool,
2202
3160
  toolRegistryStatus
2203
3161
  };