@linghun/tools 0.1.2 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -12,51 +12,55 @@ import {
12
12
  } from "./chunk-MMJX44V6.js";
13
13
  import {
14
14
  toolPrompts
15
- } from "./chunk-DWXJBDPQ.js";
15
+ } from "./chunk-ZGLZZKO6.js";
16
16
  import {
17
17
  toolUserFacingNames
18
- } from "./chunk-2R75FXZG.js";
18
+ } from "./chunk-6BBM4UX3.js";
19
+ import "./chunk-4EZHIDZ6.js";
19
20
  import "./chunk-I3Z4OKMS.js";
20
21
  import "./chunk-PS2ZOFZ6.js";
21
22
  import "./chunk-NPKLGVDQ.js";
22
23
  import "./chunk-LGZOG7IQ.js";
23
24
  import "./chunk-ZYAEUABC.js";
24
25
  import "./chunk-SWK6XRZQ.js";
25
- import "./chunk-4EZHIDZ6.js";
26
26
  import "./chunk-YR4HWOR2.js";
27
27
  import "./chunk-VAFW24RN.js";
28
+ import "./chunk-TZ6QK52L.js";
29
+ import "./chunk-K3YDFDLP.js";
28
30
  import "./chunk-GI5XI4HN.js";
29
31
  import "./chunk-WEWXKRKL.js";
30
32
  import "./chunk-A6FUAM64.js";
31
33
  import "./chunk-YCO6C7KF.js";
32
- import "./chunk-MFHY7PBM.js";
33
- import "./chunk-SZFZPQGL.js";
34
+ import "./chunk-4567YAXM.js";
34
35
  import "./chunk-XZWSO5CM.js";
36
+ import "./chunk-SZFZPQGL.js";
35
37
  import "./chunk-AZCQL24J.js";
36
38
  import "./chunk-HUIZOB4A.js";
37
39
  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
- import "./chunk-HNIVNZIS.js";
44
- import "./chunk-DN3KJZRV.js";
45
- import "./chunk-2U4BZXAI.js";
46
- import "./chunk-ITBZHMZQ.js";
47
40
  import "./chunk-WLQ7DGMG.js";
48
41
  import "./chunk-SQP6SJRP.js";
49
42
  import "./chunk-BRG64AEC.js";
50
- import "./chunk-VZWMPUSS.js";
51
- import "./chunk-E75PYM7V.js";
52
- import "./chunk-GAWMEMXB.js";
43
+ import "./chunk-HNIVNZIS.js";
53
44
  import "./chunk-7ZOADACP.js";
54
45
  import "./chunk-H5OOD2OS.js";
55
46
  import "./chunk-IQP3XE32.js";
47
+ import "./chunk-DN3KJZRV.js";
48
+ import "./chunk-2U4BZXAI.js";
49
+ import "./chunk-ITBZHMZQ.js";
50
+ import "./chunk-3NZ3IOPM.js";
51
+ import "./chunk-IOC3BSPT.js";
52
+ import "./chunk-3RP665TH.js";
53
+ import "./chunk-2DIXRINW.js";
54
+ import {
55
+ interpretCommandResult
56
+ } from "./chunk-FI4YPDFE.js";
57
+ import "./chunk-PKMIDA5A.js";
58
+ import "./chunk-GAWMEMXB.js";
56
59
 
57
60
  // src/index.ts
58
61
  import { spawn } from "child_process";
59
62
  import { createHash, randomUUID } from "crypto";
63
+ import { closeSync, createWriteStream, openSync } from "fs";
60
64
  import { mkdir, readFile, readdir, stat, writeFile } from "fs/promises";
61
65
  import { basename, dirname, isAbsolute, join, relative, resolve } from "path";
62
66
  var toolRegistryStatus = "ready";
@@ -84,7 +88,8 @@ function createToolContext(workspaceRoot = process.cwd(), options = {}) {
84
88
  todos: [],
85
89
  readSnapshots: {},
86
90
  sourcePackCandidates: options.sourcePackCandidates,
87
- patchSummaries: {}
91
+ patchSummaries: {},
92
+ recentDiagnostics: []
88
93
  };
89
94
  }
90
95
  async function runTool(name, input, context) {
@@ -571,9 +576,16 @@ function validateGlobInput(input) {
571
576
  }
572
577
  function validateBashInput(input) {
573
578
  const record = validateRecord(input, "Bash");
579
+ const command = readOptionalString(record, "command", "Bash");
580
+ if (command === void 0) {
581
+ throw new Error("Bash.command \u5FC5\u987B\u63D0\u4F9B\u3002");
582
+ }
583
+ const runInBackground = record.runInBackground === true || record.run_in_background === true;
574
584
  return {
575
- command: readString(record, "command", "Bash"),
576
- timeoutMs: readOptionalPositiveInteger(record, "timeoutMs", "Bash")
585
+ command,
586
+ description: readOptionalString(record, "description", "Bash"),
587
+ timeoutMs: readOptionalPositiveInteger(record, "timeoutMs", "Bash"),
588
+ ...runInBackground ? { runInBackground: true } : {}
577
589
  };
578
590
  }
579
591
  function validateTodoInput(input) {
@@ -1009,11 +1021,41 @@ function sanitizeSecrets(text) {
1009
1021
  return sanitized;
1010
1022
  }
1011
1023
  async function bashTool(input, context) {
1024
+ if (!input.command) {
1025
+ throw new Error("Bash.command \u5FC5\u987B\u63D0\u4F9B\u3002");
1026
+ }
1012
1027
  const logRoot = context.logRoot ?? join(context.workspaceRoot, ".linghun", "logs", "tools");
1013
1028
  await mkdir(logRoot, { recursive: true });
1014
1029
  const fullOutputPath = join(logRoot, `bash-${Date.now()}-${randomUUID()}.log`);
1015
1030
  const timeoutMs = input.timeoutMs ?? BASH_TIMEOUT_MS;
1016
1031
  const adapted = adaptShellCommand(input.command);
1032
+ if (input.runInBackground === true) {
1033
+ const taskId = randomUUID();
1034
+ void runBackgroundBash({
1035
+ taskId,
1036
+ command: adapted.command,
1037
+ originalCommand: input.command,
1038
+ cwd: context.workspaceRoot,
1039
+ timeoutMs,
1040
+ fullOutputPath,
1041
+ adapter: adapted.adapter,
1042
+ logCommand: adapted.logCommand,
1043
+ retainAfterReturn: isHeadlessBenchContext(context),
1044
+ abortSignal: context.abortSignal,
1045
+ trackChildProcess: context.trackChildProcess,
1046
+ onComplete: context.onBackgroundBashComplete
1047
+ });
1048
+ return {
1049
+ text: `\u547D\u4EE4\u5DF2\u5728\u540E\u53F0\u542F\u52A8\u3002
1050
+ taskId: ${taskId}
1051
+ outputPath: ${fullOutputPath}`,
1052
+ data: {
1053
+ backgroundTaskId: taskId,
1054
+ outputPath: fullOutputPath,
1055
+ command: sanitizeSecrets(input.command)
1056
+ }
1057
+ };
1058
+ }
1017
1059
  const result = await runShell(
1018
1060
  adapted.command,
1019
1061
  context.workspaceRoot,
@@ -1027,23 +1069,35 @@ async function bashTool(input, context) {
1027
1069
  `original command ${summarizeOriginalShellCommand(input.command)}`
1028
1070
  ];
1029
1071
  const commandForLog = adapted.logCommand ?? adapted.command;
1030
- const fullText = [
1072
+ const sanitizedShellOutput = sanitizeSecrets(result.output);
1073
+ const cmdInterpretation = interpretCommandResult(input.command, result.exitCode);
1074
+ const rawFullText = [
1031
1075
  `$ ${sanitizeSecrets(commandForLog)}`,
1032
1076
  ...adapterLines,
1033
1077
  `exit code ${result.exitCode}`,
1034
1078
  `outcome ${result.outcome}`,
1079
+ ...cmdInterpretation.message ? [`returnCodeInterpretation: ${cmdInterpretation.message}`] : [],
1035
1080
  "",
1036
- sanitizeSecrets(result.output)
1081
+ sanitizedShellOutput
1037
1082
  ].join("\n");
1083
+ const diagnostics = createBashOutcomeDiagnostics(result.outcome);
1084
+ const fullText = rawFullText;
1038
1085
  await writeFile(fullOutputPath, fullText, "utf8");
1039
1086
  const truncated = fullText.length > BASH_PREVIEW_LIMIT;
1040
1087
  const preview = truncated ? `${fullText.slice(0, BASH_PREVIEW_LIMIT)}
1041
1088
  ...\uFF08\u8F93\u51FA\u5DF2\u622A\u65AD\uFF0C\u5B8C\u6574\u65E5\u5FD7\u89C1 fullOutputPath\uFF09` : fullText;
1042
1089
  const details = createBashDetails(fullOutputPath, fullText);
1090
+ const data = adapted.adapter === "native" ? { exitCode: result.exitCode, outcome: result.outcome } : { exitCode: result.exitCode, outcome: result.outcome, adapter: adapted.adapter };
1091
+ const outputData = {
1092
+ ...data,
1093
+ ...cmdInterpretation.message ? { returnCodeInterpretation: cmdInterpretation.message } : {},
1094
+ ...cmdInterpretation.isError === false && result.exitCode !== 0 ? { isError: false } : {},
1095
+ ...diagnostics.length > 0 ? { diagnostics } : {}
1096
+ };
1043
1097
  return {
1044
1098
  text: preview,
1045
1099
  details,
1046
- data: adapted.adapter === "native" ? { exitCode: result.exitCode, outcome: result.outcome } : { exitCode: result.exitCode, outcome: result.outcome, adapter: adapted.adapter },
1100
+ data: outputData,
1047
1101
  truncated,
1048
1102
  fullOutputPath
1049
1103
  };
@@ -1057,6 +1111,505 @@ function createBashDetails(fullOutputPath, fullText) {
1057
1111
  ...tailLines(fullText, 80)
1058
1112
  ].join("\n");
1059
1113
  }
1114
+ var BINARY_HINT_EXTENSIONS = [
1115
+ ".bin",
1116
+ ".elf",
1117
+ ".o",
1118
+ ".so",
1119
+ ".a",
1120
+ ".exe",
1121
+ ".dll",
1122
+ ".7z",
1123
+ ".zip",
1124
+ ".gz",
1125
+ ".png",
1126
+ ".jpg",
1127
+ ".jpeg",
1128
+ ".pdf",
1129
+ ".sqlite",
1130
+ ".db"
1131
+ ];
1132
+ var ARTIFACT_HINT_EXTENSIONS = [
1133
+ ".txt",
1134
+ ".json",
1135
+ ".out",
1136
+ ".log",
1137
+ ".py",
1138
+ ".html",
1139
+ ".htm",
1140
+ ".csv",
1141
+ ".yaml",
1142
+ ".yml",
1143
+ ".toml",
1144
+ ".md",
1145
+ ".sh",
1146
+ ".bin",
1147
+ ".elf",
1148
+ ".zip",
1149
+ ".7z",
1150
+ ".png",
1151
+ ".pdf",
1152
+ ".sqlite",
1153
+ ".db"
1154
+ ];
1155
+ function parseBashCommandIntent(command, _context) {
1156
+ const tokens = tokenizeShellLike(command);
1157
+ const segments = splitCommandSegments(tokens);
1158
+ const commandNames = [];
1159
+ const parsedSegments = [];
1160
+ const binaryCandidates = /* @__PURE__ */ new Set();
1161
+ const artifactCandidates = /* @__PURE__ */ new Set();
1162
+ const serviceCandidates = [];
1163
+ for (const segment of segments) {
1164
+ const parsed = parseCommandSegment(segment.tokens);
1165
+ if (!parsed) continue;
1166
+ commandNames.push(parsed.commandName);
1167
+ parsedSegments.push({
1168
+ commandName: parsed.commandName,
1169
+ argv: parsed.argv,
1170
+ background: segment.background || parsed.background,
1171
+ redirections: collectSegmentRedirections(parsed.argv)
1172
+ });
1173
+ collectSegmentBinaryCandidates(parsed, binaryCandidates);
1174
+ collectSegmentArtifactCandidates(parsed, artifactCandidates);
1175
+ serviceCandidates.push(...collectSegmentServiceCandidates(parsed));
1176
+ }
1177
+ return {
1178
+ binaryCandidates: [...binaryCandidates],
1179
+ serviceCandidates: dedupeServiceCandidates(serviceCandidates),
1180
+ artifactCandidates: [...artifactCandidates],
1181
+ backgroundLikely: isBackgroundLikely(segments, serviceCandidates),
1182
+ commandNames: unique(commandNames.filter(Boolean)),
1183
+ segments: parsedSegments,
1184
+ hasShellControl: segments.length !== 1 || tokens.some((token) => isCommandSeparator(token))
1185
+ };
1186
+ }
1187
+ var BINARY_COMMAND_NAMES = /* @__PURE__ */ new Set(["file", "xxd", "readelf", "objdump", "hexdump", "strings", "7z", "7za", "unzip", "tar"]);
1188
+ var SHELL_RESERVED_COMMAND_NAMES = /* @__PURE__ */ new Set([
1189
+ "if",
1190
+ "then",
1191
+ "else",
1192
+ "elif",
1193
+ "fi",
1194
+ "for",
1195
+ "while",
1196
+ "until",
1197
+ "do",
1198
+ "done",
1199
+ "case",
1200
+ "esac",
1201
+ "in",
1202
+ "function",
1203
+ "coproc"
1204
+ ]);
1205
+ var SHELL_SYNTAX_COMMAND_NAMES = /* @__PURE__ */ new Set([
1206
+ "[",
1207
+ "[[",
1208
+ "test",
1209
+ "{",
1210
+ "}",
1211
+ "(",
1212
+ ")",
1213
+ "!",
1214
+ ":",
1215
+ "true",
1216
+ "false"
1217
+ ]);
1218
+ var ARTIFACT_PRODUCING_COMMAND_NAMES = /* @__PURE__ */ new Set(["touch", "tee"]);
1219
+ var ARTIFACT_PRODUCING_TOKENS = /* @__PURE__ */ new Set(["write", "wrote", "written", "create", "created", "generate", "generated", "build", "built", "output"]);
1220
+ function canSafelyAliasPythonCommand(intent) {
1221
+ const segment = intent.segments[0];
1222
+ return intent.segments.length === 1 && !intent.hasShellControl && segment?.commandName === "python" && !segment.background && segment.redirections.length === 0;
1223
+ }
1224
+ function tokenizeShellLike(command) {
1225
+ const tokens = [];
1226
+ let current = "";
1227
+ let quote;
1228
+ let escaped = false;
1229
+ const push = () => {
1230
+ if (current.length > 0) tokens.push(current);
1231
+ current = "";
1232
+ };
1233
+ for (let index = 0; index < command.length; index += 1) {
1234
+ const char = command[index] ?? "";
1235
+ if (escaped) {
1236
+ current += char;
1237
+ escaped = false;
1238
+ continue;
1239
+ }
1240
+ if (char === "\\") {
1241
+ escaped = true;
1242
+ continue;
1243
+ }
1244
+ if (quote) {
1245
+ if (char === quote) quote = void 0;
1246
+ else current += char;
1247
+ continue;
1248
+ }
1249
+ if (char === "'" || char === '"') {
1250
+ quote = char;
1251
+ continue;
1252
+ }
1253
+ if (/\s/u.test(char)) {
1254
+ push();
1255
+ continue;
1256
+ }
1257
+ if (char === ">" && /^\d+$/u.test(current)) {
1258
+ const prefix = current;
1259
+ current = "";
1260
+ let redirect = `${prefix}>`;
1261
+ if (command[index + 1] === ">") {
1262
+ redirect += ">";
1263
+ index += 1;
1264
+ }
1265
+ if (command[index + 1] === "&") {
1266
+ redirect += "&";
1267
+ index += 1;
1268
+ while (index + 1 < command.length && /[A-Za-z0-9_/-]/u.test(command[index + 1] ?? "")) {
1269
+ redirect += command[index + 1];
1270
+ index += 1;
1271
+ }
1272
+ }
1273
+ tokens.push(redirect);
1274
+ continue;
1275
+ }
1276
+ if (char === "(" && current.length === 0) {
1277
+ push();
1278
+ tokens.push(char);
1279
+ continue;
1280
+ }
1281
+ if ((char === "{" || char === "}") && current.length === 0) {
1282
+ push();
1283
+ tokens.push(char);
1284
+ continue;
1285
+ }
1286
+ if (char === ")" && !current.includes("$(")) {
1287
+ push();
1288
+ tokens.push(char);
1289
+ continue;
1290
+ }
1291
+ if (char === "|" || char === ";" || char === "&" || char === ">") {
1292
+ push();
1293
+ const next = command[index + 1];
1294
+ if (char === "&" && next === "&" || char === ">" && next === ">") {
1295
+ tokens.push(`${char}${next}`);
1296
+ index += 1;
1297
+ } else {
1298
+ tokens.push(char);
1299
+ }
1300
+ continue;
1301
+ }
1302
+ current += char;
1303
+ }
1304
+ push();
1305
+ return tokens;
1306
+ }
1307
+ function isCommandSeparator(token) {
1308
+ return token === "|" || token === "&&" || token === ";" || token === "&";
1309
+ }
1310
+ function splitCommandSegments(tokens) {
1311
+ const segments = [];
1312
+ let current = [];
1313
+ for (const token of tokens) {
1314
+ if (!isCommandSeparator(token)) {
1315
+ current.push(token);
1316
+ continue;
1317
+ }
1318
+ if (current.length > 0) {
1319
+ segments.push({ tokens: current, background: token === "&" });
1320
+ current = [];
1321
+ }
1322
+ }
1323
+ if (current.length > 0) segments.push({ tokens: current, background: false });
1324
+ return segments;
1325
+ }
1326
+ function parseCommandSegment(tokens) {
1327
+ const env = /* @__PURE__ */ new Map();
1328
+ let index = 0;
1329
+ while (index < tokens.length && isShellGroupBoundaryToken(tokens[index] ?? "")) {
1330
+ index += 1;
1331
+ }
1332
+ while (index < tokens.length && isEnvironmentAssignment(tokens[index] ?? "")) {
1333
+ const [name, ...rest] = (tokens[index] ?? "").split("=");
1334
+ if (name) env.set(name.toUpperCase(), rest.join("="));
1335
+ index += 1;
1336
+ }
1337
+ if (index >= tokens.length) return void 0;
1338
+ let commandToken = tokens[index] ?? "";
1339
+ if (commandToken === "nohup" || commandToken === "setsid" || commandToken === "time") {
1340
+ index += 1;
1341
+ commandToken = tokens[index] ?? "";
1342
+ }
1343
+ if (isShellReservedCommandToken(commandToken)) return void 0;
1344
+ if (isShellSyntaxCommandToken(commandToken)) return void 0;
1345
+ if (!commandToken) return void 0;
1346
+ const commandName = stripExecutableToken(commandToken);
1347
+ if (!commandName || isShellReservedCommandToken(commandName) || isShellSyntaxCommandToken(commandName)) {
1348
+ return void 0;
1349
+ }
1350
+ return {
1351
+ commandName,
1352
+ argv: tokens.slice(index + 1).filter((token) => !isShellGroupBoundaryToken(token)),
1353
+ env,
1354
+ background: tokens.includes("disown")
1355
+ };
1356
+ }
1357
+ function collectSegmentBinaryCandidates(segment, target) {
1358
+ if (BINARY_COMMAND_NAMES.has(segment.commandName)) {
1359
+ for (const token of segment.argv) {
1360
+ if (!isOptionToken(token)) addPathCandidate(target, token);
1361
+ }
1362
+ }
1363
+ for (const token of segment.argv) {
1364
+ if (isBinarySuffix(token)) addPathCandidate(target, token);
1365
+ }
1366
+ }
1367
+ function collectSegmentArtifactCandidates(segment, target) {
1368
+ for (let index = 0; index < segment.argv.length; index += 1) {
1369
+ const token = segment.argv[index] ?? "";
1370
+ if (token === ">" || token === ">>") {
1371
+ addPathCandidate(target, segment.argv[index + 1] ?? "");
1372
+ index += 1;
1373
+ continue;
1374
+ }
1375
+ if (isOutputFlag(token)) {
1376
+ addPathCandidate(target, segment.argv[index + 1] ?? "");
1377
+ index += 1;
1378
+ continue;
1379
+ }
1380
+ if (isArtifactSuffix(token) && isArtifactProducingCommand(segment.commandName, segment.argv)) {
1381
+ addPathCandidate(target, token);
1382
+ }
1383
+ }
1384
+ }
1385
+ function collectSegmentRedirections(argv) {
1386
+ const redirections = [];
1387
+ for (let index = 0; index < argv.length; index += 1) {
1388
+ const token = argv[index] ?? "";
1389
+ if (token !== ">" && token !== ">>") continue;
1390
+ const target = argv[index + 1] ?? "";
1391
+ if (target) redirections.push({ operator: token, target });
1392
+ index += 1;
1393
+ }
1394
+ return redirections;
1395
+ }
1396
+ function collectSegmentServiceCandidates(segment) {
1397
+ const candidates = [];
1398
+ const envPort = parsePort(segment.env.get("PORT"));
1399
+ const explicit = findExplicitServiceTarget(segment.argv, envPort);
1400
+ if (isPythonHttpServer(segment)) {
1401
+ const port = findFirstPortToken(segment.argv) ?? envPort ?? 8e3;
1402
+ candidates.push(createServiceIntent("http-server", port, "high", "python -m http.server"));
1403
+ return candidates;
1404
+ }
1405
+ if (segment.commandName === "uvicorn") {
1406
+ if (explicit) candidates.push(createServiceIntent("framework-server", explicit.port, "high", explicit.evidence, explicit.host));
1407
+ return candidates;
1408
+ }
1409
+ if (segment.commandName === "flask" && segment.argv[0]?.toLowerCase() === "run") {
1410
+ if (explicit) candidates.push(createServiceIntent("framework-server", explicit.port, "high", explicit.evidence, explicit.host));
1411
+ return candidates;
1412
+ }
1413
+ if (segment.commandName === "gunicorn") {
1414
+ const bind = findGunicornBindTarget(segment.argv);
1415
+ if (bind) candidates.push(createServiceIntent("framework-server", bind.port, "high", bind.evidence, bind.host));
1416
+ return candidates;
1417
+ }
1418
+ if (isPackageScriptCommand(segment)) {
1419
+ if (explicit) candidates.push(createServiceIntent("package-script", explicit.port, "medium", explicit.evidence, explicit.host));
1420
+ return candidates;
1421
+ }
1422
+ if (isJsRuntimeCommand(segment.commandName)) {
1423
+ if (explicit) candidates.push(createServiceIntent("explicit-port", explicit.port, "medium", explicit.evidence, explicit.host));
1424
+ return candidates;
1425
+ }
1426
+ if (explicit && isKnownServiceCommand(segment.commandName)) {
1427
+ candidates.push(createServiceIntent("explicit-port", explicit.port, "medium", explicit.evidence, explicit.host));
1428
+ }
1429
+ return candidates;
1430
+ }
1431
+ function createServiceIntent(kind, port, confidence, evidence, host = "127.0.0.1") {
1432
+ return { kind, host: normalizeServiceHost(host), port, confidence, evidence };
1433
+ }
1434
+ function isPythonHttpServer(segment) {
1435
+ return (segment.commandName === "python" || segment.commandName === "python3") && hasAdjacentTokens(segment.argv, "-m", "http.server");
1436
+ }
1437
+ function findExplicitServiceTarget(argv, envPort) {
1438
+ let host = "127.0.0.1";
1439
+ if (envPort) return { host, port: envPort, evidence: "PORT=" };
1440
+ for (let index = 0; index < argv.length; index += 1) {
1441
+ const token = argv[index] ?? "";
1442
+ if (token === "--host") host = normalizeServiceHost(argv[index + 1] ?? host);
1443
+ if (token === "--port" || token === "-p") {
1444
+ const port = parsePort(argv[index + 1]);
1445
+ if (port) return { host, port, evidence: token };
1446
+ }
1447
+ const inlinePort = parseInlineOptionPort(token);
1448
+ if (inlinePort) return { host, port: inlinePort, evidence: token.split("=")[0] ?? token };
1449
+ const target = parseHostPortToken(token);
1450
+ if (target?.port) return { host: target.host ?? host, port: target.port, evidence: "host:port" };
1451
+ }
1452
+ return void 0;
1453
+ }
1454
+ function findGunicornBindTarget(argv) {
1455
+ for (let index = 0; index < argv.length; index += 1) {
1456
+ const token = argv[index] ?? "";
1457
+ if (token !== "-b" && token !== "--bind") continue;
1458
+ const target = parseHostPortToken(argv[index + 1] ?? "");
1459
+ if (target?.port) return { host: target.host ?? "127.0.0.1", port: target.port, evidence: token };
1460
+ }
1461
+ return void 0;
1462
+ }
1463
+ function findFirstPortToken(argv) {
1464
+ for (const token of argv) {
1465
+ const port = parsePort(token);
1466
+ if (port) return port;
1467
+ }
1468
+ return void 0;
1469
+ }
1470
+ function parseInlineOptionPort(token) {
1471
+ if (!token.startsWith("--port=") && !token.startsWith("-p=")) return void 0;
1472
+ return parsePort(token.slice(token.indexOf("=") + 1));
1473
+ }
1474
+ function parsePort(value) {
1475
+ if (!value || !/^\d{1,5}$/u.test(value)) return void 0;
1476
+ return normalizePort(Number(value));
1477
+ }
1478
+ function isPackageScriptCommand(segment) {
1479
+ if (segment.commandName !== "npm" && segment.commandName !== "pnpm" && segment.commandName !== "yarn") return false;
1480
+ const command = segment.argv[0]?.toLowerCase();
1481
+ const script = segment.argv[1]?.toLowerCase();
1482
+ return command === "start" || command === "dev" || command === "test-server" || command === "run" && (script === "start" || script === "dev" || script === "test-server");
1483
+ }
1484
+ function isJsRuntimeCommand(commandName) {
1485
+ return commandName === "node" || commandName === "deno" || commandName === "bun";
1486
+ }
1487
+ function isKnownServiceCommand(commandName) {
1488
+ return commandName === "grpc";
1489
+ }
1490
+ function normalizeServiceHost(host) {
1491
+ return "127.0.0.1";
1492
+ }
1493
+ function isBackgroundLikely(segments, serviceCandidates) {
1494
+ return segments.some(
1495
+ (segment) => segment.background || segment.tokens[0] === "nohup" || segment.tokens[0] === "setsid" || segment.tokens.includes("disown")
1496
+ ) || serviceCandidates.some(
1497
+ (candidate) => candidate.kind === "http-server" || candidate.kind === "framework-server" || candidate.kind === "package-script"
1498
+ );
1499
+ }
1500
+ function stripExecutableToken(token) {
1501
+ const normalized = token.replace(/^\(+/u, "").replace(/\)+$/u, "");
1502
+ if (normalized.includes("/") || normalized.includes("\\")) {
1503
+ return normalized.replace(/\.(?:exe|cmd|bat)$/iu, "");
1504
+ }
1505
+ return basename(normalized).replace(/\.(?:exe|cmd|bat)$/iu, "").toLowerCase();
1506
+ }
1507
+ function addPathCandidate(target, token) {
1508
+ if (!isStaticPathCandidate(token)) return;
1509
+ target.add(token.replaceAll("\\", "/"));
1510
+ }
1511
+ function isOptionToken(token) {
1512
+ return token.startsWith("-") && !/^-?\d+$/u.test(token);
1513
+ }
1514
+ function isOutputFlag(token) {
1515
+ return token === "-o" || token === "--output" || token === "--out" || token === "--dest" || token === "--file";
1516
+ }
1517
+ function isEnvironmentAssignment(token) {
1518
+ return /^[A-Za-z_][A-Za-z0-9_]*=.*$/u.test(token);
1519
+ }
1520
+ function isShellReservedCommandToken(token) {
1521
+ return SHELL_RESERVED_COMMAND_NAMES.has(token.toLowerCase());
1522
+ }
1523
+ function isShellSyntaxCommandToken(token) {
1524
+ return SHELL_SYNTAX_COMMAND_NAMES.has(token.toLowerCase());
1525
+ }
1526
+ function isShellGroupBoundaryToken(token) {
1527
+ return token === "(" || token === ")" || token === "{" || token === "}";
1528
+ }
1529
+ function isStaticPathCandidate(token) {
1530
+ if (!token || isOptionToken(token) || token.includes("://")) return false;
1531
+ if (token.includes("$") || token.includes("`")) return false;
1532
+ if (/^(?:\d+)?>{1,2}(?:&\d+)?$/u.test(token)) return false;
1533
+ if (token === "|" || token === ";" || token === "&" || token === "<") return false;
1534
+ if (isShellGroupBoundaryToken(token) || isShellSyntaxCommandToken(token)) return false;
1535
+ if (/^\d+$/u.test(token)) return false;
1536
+ if (token === "/dev/null" || token === "/dev/stdout" || token === "/dev/stderr") return false;
1537
+ return true;
1538
+ }
1539
+ function isBinarySuffix(token) {
1540
+ return BINARY_HINT_EXTENSIONS.some((extension) => token.toLowerCase().endsWith(extension));
1541
+ }
1542
+ function isArtifactSuffix(token) {
1543
+ return ARTIFACT_HINT_EXTENSIONS.some((extension) => token.toLowerCase().endsWith(extension));
1544
+ }
1545
+ function isArtifactProducingCommand(commandName, tokens) {
1546
+ if (!commandName) return false;
1547
+ if (ARTIFACT_PRODUCING_COMMAND_NAMES.has(commandName)) return true;
1548
+ return tokens.some((token) => ARTIFACT_PRODUCING_TOKENS.has(token.toLowerCase()));
1549
+ }
1550
+ function parseHostPortToken(token) {
1551
+ const url = tryParseLocalUrl(token);
1552
+ if (url) return url;
1553
+ const separator = token.lastIndexOf(":");
1554
+ if (separator <= 0) return void 0;
1555
+ const host = token.slice(0, separator).replace(/^\[(.*)\]$/u, "$1");
1556
+ const port = normalizePort(Number(token.slice(separator + 1)));
1557
+ if (!port || !isLocalHost(host)) return void 0;
1558
+ return normalizeHostPort(host, port);
1559
+ }
1560
+ function tryParseLocalUrl(token) {
1561
+ try {
1562
+ const url = new URL(token);
1563
+ if (url.protocol !== "http:" && url.protocol !== "https:") return void 0;
1564
+ if (!isLocalHost(url.hostname)) return void 0;
1565
+ const port = normalizePort(Number(url.port));
1566
+ if (!port) return void 0;
1567
+ return normalizeHostPort(url.hostname, port);
1568
+ } catch {
1569
+ return void 0;
1570
+ }
1571
+ }
1572
+ function isLocalHost(host) {
1573
+ return host === "localhost" || host === "127.0.0.1" || host === "0.0.0.0" || host === "::1";
1574
+ }
1575
+ function hasAdjacentTokens(tokens, first, second) {
1576
+ return tokens.some(
1577
+ (token, index) => token.toLowerCase() === first && tokens[index + 1]?.toLowerCase() === second
1578
+ );
1579
+ }
1580
+ function normalizePort(value) {
1581
+ return Number.isInteger(value) && value > 0 && value <= 65535 ? value : void 0;
1582
+ }
1583
+ function dedupeServiceCandidates(candidates) {
1584
+ const seen = /* @__PURE__ */ new Set();
1585
+ return candidates.filter((candidate) => {
1586
+ const key = `${candidate.host}:${candidate.port}:${candidate.kind}`;
1587
+ if (seen.has(key)) return false;
1588
+ seen.add(key);
1589
+ return true;
1590
+ });
1591
+ }
1592
+ function normalizeHostPort(host, port) {
1593
+ if (!Number.isInteger(port) || port <= 0 || port > 65535) return void 0;
1594
+ return { host: host === "0.0.0.0" || host === "localhost" ? "127.0.0.1" : host.replace(/^\[(.*)\]$/u, "$1"), port };
1595
+ }
1596
+ function isHeadlessBenchContext(context) {
1597
+ const record = context;
1598
+ return record.headlessBench?.enabled === true || record.isHeadlessBench === true;
1599
+ }
1600
+ function createBashOutcomeDiagnostics(outcome) {
1601
+ if (outcome === "timeout") {
1602
+ return [
1603
+ {
1604
+ type: "timeout",
1605
+ severity: "recoverable",
1606
+ evidence: "Bash outcome timeout",
1607
+ suggestion: "Run the smallest focused check first and avoid repeating long blind commands."
1608
+ }
1609
+ ];
1610
+ }
1611
+ return [];
1612
+ }
1060
1613
  function tailLines(text, limit) {
1061
1614
  const lines = text.split(/\r?\n/u);
1062
1615
  return lines.slice(Math.max(0, lines.length - limit));
@@ -1073,6 +1626,8 @@ function adaptShellCommandForPlatform(command, platform) {
1073
1626
  "Unsupported multi-line Unix shell syntax on Windows PowerShell; use PowerShell-safe commands or Node one-liners."
1074
1627
  );
1075
1628
  }
1629
+ const nativePowerShell = convertNativePowerShellCommand(command);
1630
+ if (nativePowerShell) return nativePowerShell;
1076
1631
  const converted = convertUnixPipelineForPowerShell(command);
1077
1632
  if (converted) return { command: converted, adapter: "powershell-adapted" };
1078
1633
  const readOnlyCommand = convertUnixReadOnlyCommandForPowerShell(command);
@@ -1085,6 +1640,22 @@ function adaptShellCommandForPlatform(command, platform) {
1085
1640
  if (unsupportedReadOnlyCommand) return unsupportedReadOnlyCommand;
1086
1641
  return { command, adapter: "native" };
1087
1642
  }
1643
+ function convertNativePowerShellCommand(command) {
1644
+ const normalized = command.trim();
1645
+ if (!normalized) return void 0;
1646
+ if (/^(?:powershell(?:\.exe)?|pwsh(?:\.exe)?)\b/iu.test(normalized)) return void 0;
1647
+ if (!/^(?:Get|Set|New|Remove|Select|ForEach|Where|Test|Resolve|Join|Split|Copy|Move|Write|Out|Measure|Compare|Sort|Format)-[A-Za-z]+\b/u.test(normalized)) {
1648
+ return void 0;
1649
+ }
1650
+ return {
1651
+ command: [
1652
+ "powershell.exe -NoProfile -NonInteractive -Command",
1653
+ quoteCmdArg(`$ErrorActionPreference='Stop'; ${normalized}`)
1654
+ ].join(" "),
1655
+ adapter: "powershell-adapted",
1656
+ logCommand: `powershell.exe -NoProfile -NonInteractive -Command <powershell cmdlet>`
1657
+ };
1658
+ }
1088
1659
  function convertNodeHereDocForPowerShell(command) {
1089
1660
  const normalized = command.replace(/\r\n/gu, "\n");
1090
1661
  const match = normalized.match(
@@ -2063,6 +2634,198 @@ function runShell(command, cwd, timeoutMs, signal, onProgress, trackChildProcess
2063
2634
  });
2064
2635
  });
2065
2636
  }
2637
+ var STALL_THRESHOLD_MS = 45e3;
2638
+ var PROMPT_PATTERNS = [
2639
+ /\(y\/n\)\s*$/i,
2640
+ /\[y\/n\]\s*$/i,
2641
+ /\(yes\/no\)\s*$/i,
2642
+ /Press Enter/i,
2643
+ /Continue\?/i,
2644
+ /Overwrite\?/i,
2645
+ /password[:\s]*$/i,
2646
+ /passphrase[:\s]*$/i,
2647
+ /\?\s*$/
2648
+ ];
2649
+ function looksLikePrompt(tail) {
2650
+ const lastLine = tail.split(/\r?\n/).filter(Boolean).pop() ?? "";
2651
+ return PROMPT_PATTERNS.some((re) => re.test(lastLine));
2652
+ }
2653
+ async function runBackgroundBash(opts) {
2654
+ const {
2655
+ taskId,
2656
+ command,
2657
+ originalCommand,
2658
+ cwd,
2659
+ timeoutMs,
2660
+ fullOutputPath,
2661
+ adapter,
2662
+ logCommand,
2663
+ retainAfterReturn,
2664
+ abortSignal,
2665
+ onProgress,
2666
+ trackChildProcess,
2667
+ onComplete
2668
+ } = opts;
2669
+ await mkdir(dirname(fullOutputPath), { recursive: true }).catch(() => {
2670
+ });
2671
+ const fileStream = createWriteStream(fullOutputPath, { encoding: "utf8" });
2672
+ const commandForLog = logCommand ?? command;
2673
+ const header = [
2674
+ `$ ${sanitizeSecrets(commandForLog)}`,
2675
+ ...adapter !== "native" ? [`adapter ${adapter}`, `original command ${summarizeOriginalShellCommand(originalCommand)}`] : [],
2676
+ ""
2677
+ ].join("\n");
2678
+ fileStream.write(header);
2679
+ const detached = process.platform !== "win32";
2680
+ if (retainAfterReturn) {
2681
+ fileStream.write("\n[background] retained process started\n");
2682
+ await new Promise((resolveEnd) => fileStream.end(resolveEnd));
2683
+ const outFd = openSync(fullOutputPath, "a");
2684
+ const errFd = openSync(fullOutputPath, "a");
2685
+ const child2 = spawn(command, {
2686
+ cwd,
2687
+ shell: true,
2688
+ windowsHide: true,
2689
+ detached,
2690
+ stdio: ["ignore", outFd, errFd]
2691
+ });
2692
+ closeSync(outFd);
2693
+ closeSync(errFd);
2694
+ child2.unref();
2695
+ trackChildProcess?.(child2, {
2696
+ detached,
2697
+ cwd,
2698
+ label: `BashBg:${command.slice(0, 80)}`,
2699
+ retainAfterExit: true
2700
+ });
2701
+ onComplete?.({
2702
+ taskId,
2703
+ exitCode: 0,
2704
+ outcome: "completed",
2705
+ outputPath: fullOutputPath,
2706
+ command: sanitizeSecrets(originalCommand)
2707
+ });
2708
+ return;
2709
+ }
2710
+ const child = spawn(command, { cwd, shell: true, windowsHide: true, detached });
2711
+ trackChildProcess?.(child, {
2712
+ detached,
2713
+ cwd,
2714
+ label: `BashBg:${command.slice(0, 80)}`,
2715
+ retainAfterExit: detached
2716
+ });
2717
+ let tailBuffer = "";
2718
+ let lastOutputTime = Date.now();
2719
+ let stallTimer;
2720
+ let settled = false;
2721
+ let outcome = "completed";
2722
+ const resetStallTimer = () => {
2723
+ lastOutputTime = Date.now();
2724
+ if (stallTimer) clearTimeout(stallTimer);
2725
+ stallTimer = setTimeout(checkStall, STALL_THRESHOLD_MS);
2726
+ };
2727
+ const checkStall = () => {
2728
+ if (Date.now() - lastOutputTime >= STALL_THRESHOLD_MS && looksLikePrompt(tailBuffer)) {
2729
+ const msg = "\n[stall watchdog] \u68C0\u6D4B\u5230\u4EA4\u4E92\u5F0F\u63D0\u793A\uFF0C\u53EF\u80FD\u9700\u8981\u7528\u6237\u8F93\u5165\u3002\n";
2730
+ fileStream.write(msg);
2731
+ onProgress?.("system", msg);
2732
+ }
2733
+ };
2734
+ const finish = (exitCode) => {
2735
+ if (settled) return;
2736
+ settled = true;
2737
+ if (stallTimer) clearTimeout(stallTimer);
2738
+ clearTimeout(timer);
2739
+ abortSignal?.removeEventListener("abort", onAbort);
2740
+ const footer = `
2741
+ exit code ${exitCode}
2742
+ outcome ${outcome}
2743
+ `;
2744
+ fileStream.write(footer);
2745
+ fileStream.end();
2746
+ onComplete?.({
2747
+ taskId,
2748
+ exitCode,
2749
+ outcome,
2750
+ outputPath: fullOutputPath,
2751
+ command: sanitizeSecrets(originalCommand)
2752
+ });
2753
+ };
2754
+ const requestStop = async (force) => {
2755
+ if (process.platform === "win32" && child.pid) {
2756
+ await stopWindowsProcessTree(child.pid, cwd);
2757
+ return;
2758
+ }
2759
+ const sig = force ? "SIGKILL" : "SIGTERM";
2760
+ if (detached && child.pid) {
2761
+ try {
2762
+ process.kill(-child.pid, sig);
2763
+ } catch {
2764
+ child.kill(sig);
2765
+ }
2766
+ } else {
2767
+ child.kill(sig);
2768
+ }
2769
+ };
2770
+ const FORCE_KILL_WAIT_MS = 3e3;
2771
+ const waitForCloseOrForceKill = () => {
2772
+ const forceTimer = setTimeout(() => {
2773
+ void requestStop(true);
2774
+ setTimeout(() => finish(1), 500);
2775
+ }, FORCE_KILL_WAIT_MS);
2776
+ child.once("close", () => {
2777
+ clearTimeout(forceTimer);
2778
+ });
2779
+ };
2780
+ const onAbort = () => {
2781
+ if (settled) return;
2782
+ outcome = "cancelled";
2783
+ const msg = "\n[cancelled] \u5DE5\u5177\u8C03\u7528\u5DF2\u53D6\u6D88\uFF0C\u6B63\u5728\u7EC8\u6B62\u5B50\u8FDB\u7A0B\u3002\n";
2784
+ fileStream.write(msg);
2785
+ onProgress?.("system", msg);
2786
+ void requestStop(false);
2787
+ waitForCloseOrForceKill();
2788
+ };
2789
+ const timer = setTimeout(() => {
2790
+ if (settled) return;
2791
+ outcome = "timeout";
2792
+ const msg = `
2793
+ [timeout] \u547D\u4EE4\u8D85\u65F6\uFF1A\u8D85\u8FC7 ${timeoutMs}ms\uFF0C\u5DF2\u7EC8\u6B62\u3002
2794
+ `;
2795
+ fileStream.write(msg);
2796
+ onProgress?.("system", msg);
2797
+ void requestStop(false);
2798
+ waitForCloseOrForceKill();
2799
+ }, timeoutMs);
2800
+ if (abortSignal?.aborted) {
2801
+ onAbort();
2802
+ return;
2803
+ }
2804
+ abortSignal?.addEventListener("abort", onAbort, { once: true });
2805
+ child.stdout.on("data", (chunk) => {
2806
+ const text = decodeShellChunk(chunk);
2807
+ fileStream.write(text);
2808
+ tailBuffer = (tailBuffer + text).slice(-2e3);
2809
+ resetStallTimer();
2810
+ onProgress?.("stdout", text);
2811
+ });
2812
+ child.stderr.on("data", (chunk) => {
2813
+ const text = decodeShellChunk(chunk);
2814
+ fileStream.write(text);
2815
+ tailBuffer = (tailBuffer + text).slice(-2e3);
2816
+ resetStallTimer();
2817
+ onProgress?.("stderr", text);
2818
+ });
2819
+ child.on("close", (code) => {
2820
+ finish(code ?? 1);
2821
+ });
2822
+ child.on("error", (err) => {
2823
+ fileStream.write(`
2824
+ [error] ${err.message}
2825
+ `);
2826
+ finish(1);
2827
+ });
2828
+ }
2066
2829
  function decodeShellChunk(chunk) {
2067
2830
  const utf8 = chunk.toString("utf8");
2068
2831
  if (process.platform !== "win32") return utf8;
@@ -2190,14 +2953,21 @@ function unique(items) {
2190
2953
  }
2191
2954
  var __testGlobToRegExp = globToRegExp;
2192
2955
  var __testDecodeShellChunk = decodeShellChunk;
2956
+ var __testParseBashCommandIntent = parseBashCommandIntent;
2957
+ var __testCanSafelyAliasPythonCommand = canSafelyAliasPythonCommand;
2958
+ var __testRunBackgroundBash = runBackgroundBash;
2193
2959
  export {
2960
+ __testCanSafelyAliasPythonCommand,
2194
2961
  __testDecodeShellChunk,
2195
2962
  __testGlobToRegExp,
2963
+ __testParseBashCommandIntent,
2964
+ __testRunBackgroundBash,
2196
2965
  adaptShellCommand,
2197
2966
  adaptShellCommandForPlatform,
2198
2967
  builtInTools,
2199
2968
  createTool,
2200
2969
  createToolContext,
2970
+ interpretCommandResult,
2201
2971
  runTool,
2202
2972
  toolRegistryStatus
2203
2973
  };