@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.
- package/dist/chunk-236QJATI.js +678 -0
- package/dist/{chunk-VZWMPUSS.js → chunk-2DIXRINW.js} +1 -1
- package/dist/{chunk-DWXJBDPQ.js → chunk-F7UOI3YR.js} +16 -16
- package/dist/chunk-FI4YPDFE.js +38 -0
- package/dist/{chunk-2R75FXZG.js → chunk-J7O24FJB.js} +16 -16
- package/dist/chunk-PKMIDA5A.js +6 -0
- package/dist/chunk-SPAD4DZS.js +319 -0
- package/dist/chunk-XG6HNLY6.js +166 -0
- package/dist/index.d.ts +81 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +984 -26
- package/dist/tools/Bash/BashTool.d.ts +1 -1
- package/dist/tools/Bash/BashTool.js +2 -2
- package/dist/tools/Bash/artifact-checker.d.ts +33 -0
- package/dist/tools/Bash/artifact-checker.d.ts.map +1 -0
- package/dist/tools/Bash/artifact-checker.js +6 -0
- package/dist/tools/Bash/binary-helper.d.ts +7 -0
- package/dist/tools/Bash/binary-helper.d.ts.map +1 -0
- package/dist/tools/Bash/binary-helper.js +6 -0
- package/dist/tools/Bash/command-semantics.d.ts +11 -0
- package/dist/tools/Bash/command-semantics.d.ts.map +1 -0
- package/dist/tools/Bash/command-semantics.js +6 -0
- package/dist/tools/Bash/prompt.d.ts +1 -1
- package/dist/tools/Bash/prompt.d.ts.map +1 -1
- package/dist/tools/Bash/prompt.js +1 -1
- package/dist/tools/Bash/service-kernel.d.ts +75 -0
- package/dist/tools/Bash/service-kernel.d.ts.map +1 -0
- package/dist/tools/Bash/service-kernel.js +8 -0
- package/dist/tools/Glob/GlobTool.js +2 -2
- package/dist/tools/Todo/TodoTool.js +2 -2
- package/dist/tools/prompts.js +18 -18
- package/dist/tools/ui.js +18 -18
- package/package.json +4 -1
- package/dist/chunk-E75PYM7V.js +0 -6
- package/dist/{chunk-MFHY7PBM.js → chunk-4567YAXM.js} +3 -3
- 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-
|
|
19
|
+
} from "./chunk-F7UOI3YR.js";
|
|
16
20
|
import {
|
|
17
21
|
toolUserFacingNames
|
|
18
|
-
} from "./chunk-
|
|
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-
|
|
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
|
|
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
|
|
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
|
-
|
|
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:
|
|
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
|
};
|