@wrongstack/tools 0.313.0 → 0.316.1
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/_edit-match.d.ts +3 -2
- package/dist/_output-spool.d.ts +4 -3
- package/dist/_shell-pick.d.ts +2 -1
- package/dist/_spawn-stream.d.ts +2 -1
- package/dist/_syntax-check.d.ts +2 -1
- package/dist/audit.js +10 -4055
- package/dist/bash-kill-guard.d.ts +3 -6
- package/dist/bash.js +9 -2308
- package/dist/batch-tool-use.js +2 -187
- package/dist/browser/index.js +25 -1339
- package/dist/browser/network-guard-proxy.d.ts +2 -1
- package/dist/builtin.js +69 -33949
- package/dist/chunk-22B7GE5T.js +260 -0
- package/dist/chunk-2VSZC6QM.js +113 -0
- package/dist/chunk-3PVYQFRQ.js +472 -0
- package/dist/chunk-42BHI7TN.js +337 -0
- package/dist/chunk-4BHUSMNL.js +104 -0
- package/dist/chunk-4I4LWFOO.js +431 -0
- package/dist/chunk-4UESMQDP.js +122 -0
- package/dist/chunk-553IQUMO.js +478 -0
- package/dist/chunk-5H4GSJAI.js +223 -0
- package/dist/chunk-6RNIFLG4.js +305 -0
- package/dist/chunk-7EY5DCRO.js +434 -0
- package/dist/chunk-A6BRWIXN.js +317 -0
- package/dist/chunk-AHHDWNXC.js +200 -0
- package/dist/chunk-BSLVR56S.js +199 -0
- package/dist/chunk-BTSFTGIL.js +7 -0
- package/dist/chunk-D33S4RQG.js +222 -0
- package/dist/chunk-DA4KNE2H.js +4412 -0
- package/dist/chunk-DR7JOQTC.js +520 -0
- package/dist/chunk-EEOFQDT6.js +898 -0
- package/dist/chunk-EJWSCQ6D.js +137 -0
- package/dist/chunk-EMRGN4KM.js +170 -0
- package/dist/chunk-FQZOHLG3.js +89 -0
- package/dist/chunk-GOIZNNOA.js +221 -0
- package/dist/chunk-HFXSNKCV.js +196 -0
- package/dist/chunk-HYX2TZ7O.js +1272 -0
- package/dist/chunk-I64BS3VC.js +15 -0
- package/dist/chunk-IVWV2UM6.js +955 -0
- package/dist/chunk-IXMNTNW5.js +238 -0
- package/dist/chunk-IYK7XGDZ.js +892 -0
- package/dist/chunk-J5SKD6LN.js +235 -0
- package/dist/chunk-JNNVRHMV.js +2111 -0
- package/dist/chunk-JOHXDF3D.js +295 -0
- package/dist/chunk-JRJJX3L5.js +424 -0
- package/dist/chunk-K3WNSHUF.js +1371 -0
- package/dist/chunk-L25CVUVR.js +2830 -0
- package/dist/chunk-LIJQMYYX.js +59 -0
- package/dist/chunk-LWKUTMD5.js +197 -0
- package/dist/chunk-MQX2KXVW.js +54 -0
- package/dist/chunk-MZZN7VCD.js +359 -0
- package/dist/chunk-NBDZMMG2.js +169 -0
- package/dist/chunk-NKJW5GFO.js +366 -0
- package/dist/chunk-NKNOBUBU.js +522 -0
- package/dist/chunk-O4MBV4AX.js +134 -0
- package/dist/chunk-OUBDL3WH.js +3515 -0
- package/dist/chunk-PQKK5JH7.js +101 -0
- package/dist/chunk-PY2IOS7C.js +1000 -0
- package/dist/chunk-Q5JPPCZC.js +12 -0
- package/dist/chunk-QBV725YG.js +128 -0
- package/dist/chunk-QZQSPUBF.js +315 -0
- package/dist/chunk-RGISBZBD.js +231 -0
- package/dist/chunk-RJZQWEMS.js +84 -0
- package/dist/chunk-RXISBFR5.js +315 -0
- package/dist/chunk-S5CCLZL3.js +99 -0
- package/dist/chunk-SDLVH5RZ.js +178 -0
- package/dist/chunk-T2F4S7U4.js +1723 -0
- package/dist/chunk-TEBQ4VPZ.js +163 -0
- package/dist/chunk-U4D5S3VQ.js +21 -0
- package/dist/chunk-U4VAATKP.js +193 -0
- package/dist/chunk-UCYQTZ7F.js +113 -0
- package/dist/chunk-UKU2ZX4C.js +203 -0
- package/dist/chunk-UNSKG6DC.js +159 -0
- package/dist/chunk-V43EQ6VR.js +68 -0
- package/dist/chunk-VB3MJGCV.js +610 -0
- package/dist/chunk-VFKPWSOX.js +154 -0
- package/dist/chunk-VJNHFZ7J.js +190 -0
- package/dist/chunk-VXECAJJ7.js +1091 -0
- package/dist/chunk-WHIIIREL.js +223 -0
- package/dist/chunk-Z6DAKQ3U.js +1 -0
- package/dist/chunk-ZJQ4IGXK.js +474 -0
- package/dist/chunk-ZLTFLC25.js +385 -0
- package/dist/circuit-breaker.js +3 -191
- package/dist/codebase-index/ast-invariant-engine.d.ts +5 -4
- package/dist/codebase-index/binary-frame.d.ts +0 -2
- package/dist/codebase-index/bm25.d.ts +1 -1
- package/dist/codebase-index/circuit-breaker.d.ts +2 -1
- package/dist/codebase-index/codebase-ast-replace-tool.d.ts +3 -2
- package/dist/codebase-index/codebase-impact-analysis-tool.d.ts +4 -3
- package/dist/codebase-index/codebase-invariant-check-tool.d.ts +3 -2
- package/dist/codebase-index/codebase-repo-map-tool.d.ts +3 -2
- package/dist/codebase-index/codebase-skeleton-tool.d.ts +3 -2
- package/dist/codebase-index/codebase-targeted-test-tool.d.ts +3 -2
- package/dist/codebase-index/content-hash.d.ts +0 -2
- package/dist/codebase-index/import-extractor.d.ts +0 -13
- package/dist/codebase-index/index-service.d.ts +1 -1
- package/dist/codebase-index/index.js +84 -13688
- package/dist/codebase-index/languages.d.ts +2 -1
- package/dist/codebase-index/module-roots.d.ts +3 -2
- package/dist/codebase-index/parser-batch.d.ts +0 -5
- package/dist/codebase-index/parser-dispatch.d.ts +2 -1
- package/dist/codebase-index/parser-output.d.ts +4 -3
- package/dist/codebase-index/parser-worker-pool.d.ts +0 -2
- package/dist/codebase-index/parser-worker-script.d.ts +1 -16
- package/dist/codebase-index/parser-worker-script.js +8 -3995
- package/dist/codebase-index/project-server-client-state.d.ts +2 -2
- package/dist/codebase-index/project-server-endpoint.d.ts +0 -15
- package/dist/codebase-index/project-server.js +60 -8548
- package/dist/codebase-index/tree-sitter/visitor.d.ts +2 -1
- package/dist/codebase-index/tree-sitter-parser.d.ts +0 -2
- package/dist/codebase-index/ts-parser.d.ts +1 -1
- package/dist/codebase-index/vector-search.d.ts +2 -1
- package/dist/codebase-index/wal-maintenance.d.ts +3 -2
- package/dist/codebase-index/worker.js +20 -8416
- package/dist/codebase-index/writer-admin.d.ts +0 -1
- package/dist/codebase-index/writer-search.d.ts +0 -9
- package/dist/design.js +2 -299
- package/dist/diff.js +4 -256
- package/dist/document.js +4 -222
- package/dist/e2e.js +5 -476
- package/dist/edit.js +14 -10200
- package/dist/exec.js +16 -3131
- package/dist/fetch.js +8 -398
- package/dist/format.js +10 -3750
- package/dist/generic-parser-VUXDIAOZ.js +13 -0
- package/dist/git.js +4 -380
- package/dist/glob.js +6 -280
- package/dist/go-parser-JXYKJHRC.js +469 -0
- package/dist/grep.js +7 -604
- package/dist/index.js +318 -35387
- package/dist/install.js +10 -4091
- package/dist/json-parser-IBHZ4BZC.js +213 -0
- package/dist/json.js +4 -665
- package/dist/kanban-contract-actions.d.ts +1 -1
- package/dist/kanban-detail-actions.d.ts +2 -1
- package/dist/kanban-presence.d.ts +2 -1
- package/dist/kanban-serializer.d.ts +0 -8
- package/dist/kanban-split-task-handler.d.ts +2 -2
- package/dist/kanban-tool-types.d.ts +2 -1
- package/dist/kanban.js +3 -2096
- package/dist/languages/diagnostics.d.ts +2 -1
- package/dist/languages/index.js +23 -4664
- package/dist/languages/legacy-bridge.d.ts +3 -2
- package/dist/languages/types.d.ts +2 -1
- package/dist/legacy-bridge-7M6QLUQN.js +18 -0
- package/dist/lint.js +10 -3706
- package/dist/logs.js +4 -287
- package/dist/memory.js +6 -215
- package/dist/mode.js +3 -99
- package/dist/outdated.js +4 -5170
- package/dist/pack.js +65 -33897
- package/dist/parser-dispatch-QH4XPGG6.js +14 -0
- package/dist/patch.js +13 -9935
- package/dist/plan.js +5 -3286
- package/dist/process-registry.js +8 -651
- package/dist/ps-slash.js +12 -1439
- package/dist/pwsh.d.ts +0 -1
- package/dist/py-parser-IS4HKYMF.js +16 -0
- package/dist/read.js +13 -9816
- package/dist/replace.js +15 -10014
- package/dist/rs-parser-XHUUO2KZ.js +81 -0
- package/dist/scaffold.js +4 -253
- package/dist/search.js +5 -585
- package/dist/session-kanban-sync.d.ts +0 -6
- package/dist/session-kanban.js +30 -972
- package/dist/skill.js +2 -215
- package/dist/spawn-background.d.ts +1 -1
- package/dist/task.js +5 -3410
- package/dist/test.js +10 -3773
- package/dist/todo.js +4 -2891
- package/dist/tool-help.js +3 -123
- package/dist/tool-search.js +3 -79
- package/dist/tool-tier.js +68 -33984
- package/dist/tool-use.js +2 -93
- package/dist/tree-sitter-parser-AIERW3YI.js +15 -0
- package/dist/tree.js +4 -288
- package/dist/ts-parser-RCTYKSVI.js +232 -0
- package/dist/typecheck.js +10 -3753
- package/dist/win32.js +6 -60
- package/dist/write.js +14 -9882
- package/dist/yaml-parser-4SAXV3PL.js +157 -0
- package/package.json +5 -5
|
@@ -0,0 +1,478 @@
|
|
|
1
|
+
import {
|
|
2
|
+
capSubject,
|
|
3
|
+
compileUserRegex
|
|
4
|
+
} from "./chunk-Q5JPPCZC.js";
|
|
5
|
+
import {
|
|
6
|
+
mapWithConcurrency
|
|
7
|
+
} from "./chunk-U4D5S3VQ.js";
|
|
8
|
+
import {
|
|
9
|
+
loadGitignoreMatcher
|
|
10
|
+
} from "./chunk-LIJQMYYX.js";
|
|
11
|
+
import {
|
|
12
|
+
isBinaryBuffer,
|
|
13
|
+
safeResolveReal
|
|
14
|
+
} from "./chunk-D33S4RQG.js";
|
|
15
|
+
|
|
16
|
+
// src/grep.ts
|
|
17
|
+
import { spawn } from "node:child_process";
|
|
18
|
+
import * as fs from "node:fs/promises";
|
|
19
|
+
import * as path from "node:path";
|
|
20
|
+
import { ToolValidationError } from "@wrongstack/core/types";
|
|
21
|
+
import {
|
|
22
|
+
buildChildEnv,
|
|
23
|
+
compileGlob,
|
|
24
|
+
DEFAULT_WALK_IGNORE_DIRS,
|
|
25
|
+
expectDefined
|
|
26
|
+
} from "@wrongstack/core/utils";
|
|
27
|
+
var DEFAULT_IGNORE = new Set(DEFAULT_WALK_IGNORE_DIRS);
|
|
28
|
+
var NATIVE_SCAN_CONCURRENCY = 32;
|
|
29
|
+
var NATIVE_READ_CHUNK_BYTES = 64 * 1024;
|
|
30
|
+
var NATIVE_MAX_FILE_BYTES = 1e6;
|
|
31
|
+
var RG_MAX_QUEUE_CHUNKS = 128;
|
|
32
|
+
var RG_MAX_QUEUE_CHARS = 8 * 1024 * 1024;
|
|
33
|
+
var grepTool = {
|
|
34
|
+
name: "grep",
|
|
35
|
+
category: "Search",
|
|
36
|
+
description: "Search exact file contents using a regular expression. Use index-backed `codebase-search` first for symbol or concept discovery when it is live. Prefers ripgrep for speed and features when available.",
|
|
37
|
+
usageHint: 'EXACT-TEXT AND REGEX SEARCH:\n\n- When `codebase-search` is live, use it first for symbols and concepts; use `grep` for exact text, regexes, unsupported content, and concrete usage sites.\n- `pattern` is a regular expression.\n- Use `output_mode: "content"` (default) to get matching lines with context.\n- Use `"files_with_matches"` when you only need the list of files.\n- Use `"count"` for quick statistics.\n- `glob` and `path` let you narrow the search scope significantly.\n- Always prefer this over `bash grep` when searching code.',
|
|
38
|
+
selection: {
|
|
39
|
+
doNotUseWhen: "you only need to locate files by name or path pattern.",
|
|
40
|
+
useInstead: ["glob"]
|
|
41
|
+
},
|
|
42
|
+
permission: "auto",
|
|
43
|
+
mutating: false,
|
|
44
|
+
capabilities: ["fs.read"],
|
|
45
|
+
icon: "search",
|
|
46
|
+
maxOutputBytes: 131072,
|
|
47
|
+
timeoutMs: 1e4,
|
|
48
|
+
inputSchema: {
|
|
49
|
+
type: "object",
|
|
50
|
+
properties: {
|
|
51
|
+
pattern: {
|
|
52
|
+
type: "string",
|
|
53
|
+
description: "Regular expression pattern to search for in file contents."
|
|
54
|
+
},
|
|
55
|
+
path: {
|
|
56
|
+
type: "string",
|
|
57
|
+
description: "Limit search to this directory or file (relative to project root)."
|
|
58
|
+
},
|
|
59
|
+
glob: {
|
|
60
|
+
type: "string",
|
|
61
|
+
description: 'Glob filter for which files to include (e.g. "**/*.ts", "src/**").'
|
|
62
|
+
},
|
|
63
|
+
output_mode: {
|
|
64
|
+
type: "string",
|
|
65
|
+
enum: ["content", "files_with_matches", "count"],
|
|
66
|
+
description: "Return style: detailed matches, just file list, or count only."
|
|
67
|
+
},
|
|
68
|
+
context_lines: {
|
|
69
|
+
type: "integer",
|
|
70
|
+
description: "How many lines of surrounding context to include with each match."
|
|
71
|
+
},
|
|
72
|
+
case_insensitive: {
|
|
73
|
+
type: "boolean",
|
|
74
|
+
description: "Ignore case when matching."
|
|
75
|
+
},
|
|
76
|
+
limit: {
|
|
77
|
+
type: "integer",
|
|
78
|
+
description: "Maximum number of matches to return."
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
required: ["pattern"]
|
|
82
|
+
},
|
|
83
|
+
async execute(input, ctx, opts) {
|
|
84
|
+
let final;
|
|
85
|
+
const executeStream = grepTool.executeStream;
|
|
86
|
+
if (!executeStream) throw new Error("grepTool: stream execution unavailable");
|
|
87
|
+
for await (const ev of executeStream(input, ctx, opts)) {
|
|
88
|
+
if (ev.type === "final") final = ev.output;
|
|
89
|
+
}
|
|
90
|
+
if (!final) throw new Error("grep: stream ended without final event");
|
|
91
|
+
return final;
|
|
92
|
+
},
|
|
93
|
+
async *executeStream(input, ctx, opts) {
|
|
94
|
+
if (!input?.pattern) {
|
|
95
|
+
throw new ToolValidationError({
|
|
96
|
+
message: "grep: pattern is required",
|
|
97
|
+
field: "pattern"
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
const base = input.path ? await safeResolveReal(input.path, ctx) : ctx.cwd;
|
|
101
|
+
const mode = input.output_mode ?? "content";
|
|
102
|
+
const limit = Math.max(1, Math.min(input.limit ?? 200, 2e3));
|
|
103
|
+
const validation = compileUserRegex(input.pattern, input.case_insensitive ? "i" : "");
|
|
104
|
+
if (!validation.ok) {
|
|
105
|
+
throw new ToolValidationError({
|
|
106
|
+
message: `grep: ${validation.reason}`,
|
|
107
|
+
field: "pattern"
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
const rgAvailable = await detectRg();
|
|
111
|
+
if (rgAvailable) {
|
|
112
|
+
try {
|
|
113
|
+
yield* runRgStream(input, base, mode, limit, opts.signal);
|
|
114
|
+
return;
|
|
115
|
+
} catch {
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
yield { type: "log", text: "Falling back to native grep\u2026" };
|
|
119
|
+
const out = await runNative(input, base, mode, limit, opts.signal);
|
|
120
|
+
yield { type: "final", output: out };
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
var rgAvailabilityCache;
|
|
124
|
+
function __resetRgDetectionForTests() {
|
|
125
|
+
rgAvailabilityCache = void 0;
|
|
126
|
+
}
|
|
127
|
+
function detectRg() {
|
|
128
|
+
rgAvailabilityCache ??= new Promise((resolve) => {
|
|
129
|
+
try {
|
|
130
|
+
const p = spawn("rg", ["--version"], {
|
|
131
|
+
env: buildChildEnv(),
|
|
132
|
+
stdio: "ignore",
|
|
133
|
+
signal: AbortSignal.timeout(1e4),
|
|
134
|
+
windowsHide: true
|
|
135
|
+
});
|
|
136
|
+
p.on("error", () => resolve(false));
|
|
137
|
+
p.on("close", (code) => resolve(code === 0));
|
|
138
|
+
} catch {
|
|
139
|
+
resolve(false);
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
return rgAvailabilityCache;
|
|
143
|
+
}
|
|
144
|
+
async function* runRgStream(input, base, mode, limit, signal) {
|
|
145
|
+
const args = ["--no-heading"];
|
|
146
|
+
if (input.case_insensitive) args.push("-i");
|
|
147
|
+
if (mode === "files_with_matches") args.push("-l");
|
|
148
|
+
if (mode === "count") args.push("-c");
|
|
149
|
+
if (mode === "content") {
|
|
150
|
+
args.push("-n");
|
|
151
|
+
if (input.context_lines) args.push("-C", String(input.context_lines));
|
|
152
|
+
}
|
|
153
|
+
for (const ignored of DEFAULT_IGNORE) {
|
|
154
|
+
args.push("--glob", `!${ignored}/**`, "--glob", `!**/${ignored}/**`);
|
|
155
|
+
}
|
|
156
|
+
const gitignorePath = path.join(base, ".gitignore");
|
|
157
|
+
if (await fs.access(gitignorePath).then(
|
|
158
|
+
() => true,
|
|
159
|
+
() => false
|
|
160
|
+
)) {
|
|
161
|
+
args.push("--ignore-file", gitignorePath);
|
|
162
|
+
}
|
|
163
|
+
if (input.glob) args.push("--glob", input.glob);
|
|
164
|
+
args.push("--", input.pattern, base);
|
|
165
|
+
const matches = [];
|
|
166
|
+
let buf = "";
|
|
167
|
+
let totalLines = 0;
|
|
168
|
+
let totalCount = 0;
|
|
169
|
+
let batchSinceFlush = 0;
|
|
170
|
+
const FLUSH_AT = 16;
|
|
171
|
+
const MAX_BUF_BYTES = 1e6;
|
|
172
|
+
let bufOverflow = false;
|
|
173
|
+
const child = spawn("rg", args, {
|
|
174
|
+
signal,
|
|
175
|
+
env: buildChildEnv(),
|
|
176
|
+
// rg diagnostics are not part of the tool result. Ignoring stderr avoids
|
|
177
|
+
// an unread pipe filling up and pinning the child when rg reports many
|
|
178
|
+
// filesystem errors.
|
|
179
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
180
|
+
windowsHide: true
|
|
181
|
+
});
|
|
182
|
+
const queue = [];
|
|
183
|
+
let queuedChars = 0;
|
|
184
|
+
let waiter;
|
|
185
|
+
let paused = false;
|
|
186
|
+
const wake = () => {
|
|
187
|
+
if (waiter) {
|
|
188
|
+
const w = waiter;
|
|
189
|
+
waiter = void 0;
|
|
190
|
+
w();
|
|
191
|
+
}
|
|
192
|
+
};
|
|
193
|
+
const pauseIfFlooded = () => {
|
|
194
|
+
if (paused || queue.length < RG_MAX_QUEUE_CHUNKS && queuedChars < RG_MAX_QUEUE_CHARS) return;
|
|
195
|
+
paused = true;
|
|
196
|
+
child.stdout?.pause();
|
|
197
|
+
};
|
|
198
|
+
const resumeIfDrained = () => {
|
|
199
|
+
if (!paused || queue.length >= RG_MAX_QUEUE_CHUNKS || queuedChars >= RG_MAX_QUEUE_CHARS) return;
|
|
200
|
+
paused = false;
|
|
201
|
+
child.stdout?.resume();
|
|
202
|
+
};
|
|
203
|
+
const onStdoutData = (c) => {
|
|
204
|
+
const data = c.toString();
|
|
205
|
+
queue.push({ kind: "out", data });
|
|
206
|
+
queuedChars += data.length;
|
|
207
|
+
wake();
|
|
208
|
+
pauseIfFlooded();
|
|
209
|
+
};
|
|
210
|
+
const onError = (e) => {
|
|
211
|
+
queue.push({ kind: "error", data: e.message });
|
|
212
|
+
wake();
|
|
213
|
+
};
|
|
214
|
+
const onClose = () => {
|
|
215
|
+
queue.push({ kind: "close", data: "" });
|
|
216
|
+
wake();
|
|
217
|
+
};
|
|
218
|
+
child.stdout?.on("data", onStdoutData);
|
|
219
|
+
child.on("error", onError);
|
|
220
|
+
child.on("close", onClose);
|
|
221
|
+
let pendingBatch = [];
|
|
222
|
+
let errored = false;
|
|
223
|
+
let closed = false;
|
|
224
|
+
try {
|
|
225
|
+
for (; ; ) {
|
|
226
|
+
while (queue.length === 0) {
|
|
227
|
+
await new Promise((r) => {
|
|
228
|
+
waiter = r;
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
const c = expectDefined(queue.shift());
|
|
232
|
+
queuedChars = Math.max(0, queuedChars - c.data.length);
|
|
233
|
+
resumeIfDrained();
|
|
234
|
+
if (c.kind === "error") {
|
|
235
|
+
errored = true;
|
|
236
|
+
continue;
|
|
237
|
+
}
|
|
238
|
+
if (c.kind === "close") {
|
|
239
|
+
closed = true;
|
|
240
|
+
break;
|
|
241
|
+
}
|
|
242
|
+
buf += c.data;
|
|
243
|
+
if (buf.length > MAX_BUF_BYTES && !bufOverflow) {
|
|
244
|
+
bufOverflow = true;
|
|
245
|
+
buf = buf.slice(-MAX_BUF_BYTES);
|
|
246
|
+
try {
|
|
247
|
+
child.kill("SIGTERM");
|
|
248
|
+
} catch {
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
const idx = buf.lastIndexOf("\n");
|
|
252
|
+
if (idx === -1) continue;
|
|
253
|
+
const ready = buf.slice(0, idx);
|
|
254
|
+
buf = buf.slice(idx + 1);
|
|
255
|
+
for (const line of ready.split("\n")) {
|
|
256
|
+
if (!line) continue;
|
|
257
|
+
totalLines++;
|
|
258
|
+
if (mode === "count") totalCount += parseRgCountLine(line);
|
|
259
|
+
if (matches.length < limit) {
|
|
260
|
+
matches.push(line);
|
|
261
|
+
pendingBatch.push(line);
|
|
262
|
+
batchSinceFlush++;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
if (batchSinceFlush >= FLUSH_AT) {
|
|
266
|
+
yield {
|
|
267
|
+
type: "partial_output",
|
|
268
|
+
text: pendingBatch.join("\n"),
|
|
269
|
+
data: { matches_so_far: matches.length }
|
|
270
|
+
};
|
|
271
|
+
pendingBatch = [];
|
|
272
|
+
batchSinceFlush = 0;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
if (buf.trim()) {
|
|
276
|
+
for (const line of buf.split("\n")) {
|
|
277
|
+
if (!line) continue;
|
|
278
|
+
totalLines++;
|
|
279
|
+
if (mode === "count") totalCount += parseRgCountLine(line);
|
|
280
|
+
if (matches.length < limit) {
|
|
281
|
+
matches.push(line);
|
|
282
|
+
pendingBatch.push(line);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
if (pendingBatch.length > 0) {
|
|
287
|
+
yield {
|
|
288
|
+
type: "partial_output",
|
|
289
|
+
text: pendingBatch.join("\n"),
|
|
290
|
+
data: { matches_so_far: matches.length }
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
if (errored) throw new Error("rg: spawn error");
|
|
294
|
+
yield {
|
|
295
|
+
type: "final",
|
|
296
|
+
output: {
|
|
297
|
+
matches,
|
|
298
|
+
count: mode === "count" ? totalCount : totalLines,
|
|
299
|
+
truncated: totalLines > limit || bufOverflow,
|
|
300
|
+
used: "rg"
|
|
301
|
+
}
|
|
302
|
+
};
|
|
303
|
+
} finally {
|
|
304
|
+
child.stdout?.off("data", onStdoutData);
|
|
305
|
+
child.off("error", onError);
|
|
306
|
+
child.off("close", onClose);
|
|
307
|
+
child.stdout?.destroy();
|
|
308
|
+
queue.length = 0;
|
|
309
|
+
queuedChars = 0;
|
|
310
|
+
waiter = void 0;
|
|
311
|
+
if (!closed && child.exitCode === null) {
|
|
312
|
+
try {
|
|
313
|
+
child.kill("SIGTERM");
|
|
314
|
+
} catch {
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
function parseRgCountLine(line) {
|
|
320
|
+
const idx = line.lastIndexOf(":");
|
|
321
|
+
if (idx === -1) return 0;
|
|
322
|
+
const n = Number.parseInt(line.slice(idx + 1), 10);
|
|
323
|
+
return Number.isFinite(n) ? n : 0;
|
|
324
|
+
}
|
|
325
|
+
async function runNative(input, base, mode, limit, signal) {
|
|
326
|
+
const flags = input.case_insensitive ? "i" : "";
|
|
327
|
+
const compiled = compileUserRegex(input.pattern, flags);
|
|
328
|
+
if (!compiled.ok) {
|
|
329
|
+
throw new ToolValidationError({
|
|
330
|
+
message: `grep: ${compiled.reason}`,
|
|
331
|
+
field: "pattern"
|
|
332
|
+
});
|
|
333
|
+
}
|
|
334
|
+
const re = compiled.regex;
|
|
335
|
+
const globRe = input.glob ? compileGlob(input.glob) : null;
|
|
336
|
+
const isGitIgnored = await loadGitignoreMatcher(base);
|
|
337
|
+
const matches = [];
|
|
338
|
+
const countOnlyFirstHit = mode === "count" && limit === 1;
|
|
339
|
+
const maxBytes = mode === "content" ? NATIVE_MAX_FILE_BYTES : Math.min(NATIVE_MAX_FILE_BYTES, 256 * 1024);
|
|
340
|
+
let total = 0;
|
|
341
|
+
let stopped = false;
|
|
342
|
+
const scanFile = async (full, name) => {
|
|
343
|
+
if (stopped || signal.aborted) return;
|
|
344
|
+
if (globRe && !globRe.test(name) && !globRe.test(full)) return;
|
|
345
|
+
if (globRe) globRe.lastIndex = 0;
|
|
346
|
+
try {
|
|
347
|
+
const stat2 = await fs.stat(full);
|
|
348
|
+
if (!stat2.isFile() || stat2.size > maxBytes || stopped || signal.aborted) return;
|
|
349
|
+
const file = await fs.open(full, "r");
|
|
350
|
+
try {
|
|
351
|
+
let bytesReadTotal = 0;
|
|
352
|
+
let lineNumber = 0;
|
|
353
|
+
let fileHits = 0;
|
|
354
|
+
let leftover = "";
|
|
355
|
+
let binaryChecked = false;
|
|
356
|
+
const buffer = Buffer.allocUnsafe(Math.min(NATIVE_READ_CHUNK_BYTES, maxBytes));
|
|
357
|
+
while (!stopped && !signal.aborted && bytesReadTotal < maxBytes) {
|
|
358
|
+
const remaining = maxBytes - bytesReadTotal;
|
|
359
|
+
const { bytesRead } = await file.read(
|
|
360
|
+
buffer,
|
|
361
|
+
0,
|
|
362
|
+
Math.min(buffer.length, remaining),
|
|
363
|
+
null
|
|
364
|
+
);
|
|
365
|
+
if (bytesRead === 0) break;
|
|
366
|
+
const chunk = buffer.subarray(0, bytesRead);
|
|
367
|
+
if (!binaryChecked) {
|
|
368
|
+
binaryChecked = true;
|
|
369
|
+
if (isBinaryBuffer(chunk)) return;
|
|
370
|
+
}
|
|
371
|
+
bytesReadTotal += bytesRead;
|
|
372
|
+
const text = leftover + chunk.toString("utf8");
|
|
373
|
+
const lines = text.split(/\r?\n/);
|
|
374
|
+
leftover = lines.pop() ?? "";
|
|
375
|
+
for (const rawLine of lines) {
|
|
376
|
+
if (stopped || signal.aborted) break;
|
|
377
|
+
lineNumber++;
|
|
378
|
+
const ln = capSubject(rawLine);
|
|
379
|
+
re.lastIndex = 0;
|
|
380
|
+
if (!re.test(ln)) continue;
|
|
381
|
+
fileHits++;
|
|
382
|
+
total++;
|
|
383
|
+
if (mode === "content") {
|
|
384
|
+
if (matches.length < limit) matches.push(`${full}:${lineNumber}:${ln}`);
|
|
385
|
+
} else if (mode === "files_with_matches") {
|
|
386
|
+
if (fileHits === 1 && matches.length < limit) matches.push(full);
|
|
387
|
+
break;
|
|
388
|
+
} else if (fileHits === 1 && matches.length < limit) {
|
|
389
|
+
matches.push(`${full}:${countOnlyFirstHit ? 1 : 0}`);
|
|
390
|
+
}
|
|
391
|
+
if (countOnlyFirstHit || mode !== "content" && matches.length >= limit) {
|
|
392
|
+
stopped = true;
|
|
393
|
+
break;
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
if (mode === "files_with_matches" && fileHits > 0) break;
|
|
397
|
+
}
|
|
398
|
+
if (!stopped && !signal.aborted && leftover.length > 0) {
|
|
399
|
+
lineNumber++;
|
|
400
|
+
const ln = capSubject(leftover);
|
|
401
|
+
re.lastIndex = 0;
|
|
402
|
+
if (re.test(ln)) {
|
|
403
|
+
fileHits++;
|
|
404
|
+
total++;
|
|
405
|
+
if (mode === "content") {
|
|
406
|
+
if (matches.length < limit) matches.push(`${full}:${lineNumber}:${ln}`);
|
|
407
|
+
} else if (mode === "files_with_matches") {
|
|
408
|
+
if (matches.length < limit) matches.push(full);
|
|
409
|
+
} else if (matches.length < limit) {
|
|
410
|
+
matches.push(`${full}:${countOnlyFirstHit ? 1 : fileHits}`);
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
if (fileHits > 0) {
|
|
415
|
+
if (mode === "count") {
|
|
416
|
+
const idx = matches.findIndex((entry) => entry.startsWith(`${full}:`));
|
|
417
|
+
if (idx !== -1) matches[idx] = `${full}:${fileHits}`;
|
|
418
|
+
}
|
|
419
|
+
if (mode === "files_with_matches" && matches.length >= limit) stopped = true;
|
|
420
|
+
}
|
|
421
|
+
if (mode === "content" && matches.length >= limit) stopped = true;
|
|
422
|
+
if (mode === "count" && matches.length >= limit && (countOnlyFirstHit || mode !== "count"))
|
|
423
|
+
stopped = true;
|
|
424
|
+
} finally {
|
|
425
|
+
await file.close();
|
|
426
|
+
}
|
|
427
|
+
} catch {
|
|
428
|
+
}
|
|
429
|
+
};
|
|
430
|
+
const walk = async (dir, relPrefix) => {
|
|
431
|
+
if (stopped || signal.aborted) return;
|
|
432
|
+
let entries;
|
|
433
|
+
try {
|
|
434
|
+
entries = await fs.readdir(dir, { withFileTypes: true });
|
|
435
|
+
} catch {
|
|
436
|
+
return;
|
|
437
|
+
}
|
|
438
|
+
const files = [];
|
|
439
|
+
const subdirs = [];
|
|
440
|
+
for (const e of entries) {
|
|
441
|
+
if (stopped) return;
|
|
442
|
+
if (DEFAULT_IGNORE.has(e.name)) continue;
|
|
443
|
+
if (e.isSymbolicLink()) continue;
|
|
444
|
+
const rel = relPrefix ? `${relPrefix}/${e.name}` : e.name;
|
|
445
|
+
const full = path.join(dir, e.name);
|
|
446
|
+
if (e.isDirectory()) {
|
|
447
|
+
if (isGitIgnored(rel, true)) continue;
|
|
448
|
+
subdirs.push({ full, rel });
|
|
449
|
+
} else if (e.isFile()) {
|
|
450
|
+
if (isGitIgnored(rel, false)) continue;
|
|
451
|
+
files.push({ full, name: e.name });
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
await mapWithConcurrency(
|
|
455
|
+
files,
|
|
456
|
+
NATIVE_SCAN_CONCURRENCY,
|
|
457
|
+
({ full, name }) => scanFile(full, name)
|
|
458
|
+
);
|
|
459
|
+
await mapWithConcurrency(
|
|
460
|
+
subdirs,
|
|
461
|
+
Math.min(16, NATIVE_SCAN_CONCURRENCY),
|
|
462
|
+
({ full, rel }) => walk(full, rel)
|
|
463
|
+
);
|
|
464
|
+
};
|
|
465
|
+
await walk(base, "");
|
|
466
|
+
return {
|
|
467
|
+
matches,
|
|
468
|
+
count: total,
|
|
469
|
+
truncated: stopped,
|
|
470
|
+
used: "native"
|
|
471
|
+
};
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
export {
|
|
475
|
+
grepTool,
|
|
476
|
+
__resetRgDetectionForTests
|
|
477
|
+
};
|
|
478
|
+
//# sourceMappingURL=chunk-553IQUMO.js.map
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import {
|
|
2
|
+
tryLegacyPackageOperation
|
|
3
|
+
} from "./chunk-PQKK5JH7.js";
|
|
4
|
+
import {
|
|
5
|
+
spawnStream
|
|
6
|
+
} from "./chunk-22B7GE5T.js";
|
|
7
|
+
import {
|
|
8
|
+
detectPackageManager,
|
|
9
|
+
normalizeCommandOutput,
|
|
10
|
+
safeResolveReal
|
|
11
|
+
} from "./chunk-D33S4RQG.js";
|
|
12
|
+
|
|
13
|
+
// src/install.ts
|
|
14
|
+
import { join } from "node:path";
|
|
15
|
+
import {
|
|
16
|
+
detectEcosystem as detectPackageEcosystem,
|
|
17
|
+
recordPackageAction
|
|
18
|
+
} from "@wrongstack/core/coordination";
|
|
19
|
+
var installTool = {
|
|
20
|
+
name: "install",
|
|
21
|
+
category: "Package Management",
|
|
22
|
+
description: "Install, update or manage packages using the detected package manager (pnpm/npm/yarn). Strongly preferred over raw shell commands for dependency management because it is structured and safer.",
|
|
23
|
+
usageHint: "ALWAYS USE THIS INSTEAD OF BASH FOR PACKAGE WORK:\n\n- Empty `packages` \u2192 normal `install` (respects lockfile).\n- Provide names \u2192 adds/updates specific packages.\n- `dry_run: true` for safe preview.\n- Set `save` appropriately.\nThis tool has proper capability declaration and is heavily recommended in the security posture of the project.",
|
|
24
|
+
permission: "confirm",
|
|
25
|
+
// WS-046: gives permission decisions something to key on.
|
|
26
|
+
// What gets installed is the subject — installing a package runs its
|
|
27
|
+
// lifecycle scripts, so `express` and `evil-pkg` must not be interchangeable.
|
|
28
|
+
subjectKey: "packages",
|
|
29
|
+
mutating: true,
|
|
30
|
+
riskTier: "standard",
|
|
31
|
+
icon: "package",
|
|
32
|
+
timeoutMs: 12e4,
|
|
33
|
+
capabilities: ["package.install", "shell.restricted"],
|
|
34
|
+
inputSchema: {
|
|
35
|
+
type: "object",
|
|
36
|
+
properties: {
|
|
37
|
+
packages: {
|
|
38
|
+
type: "string",
|
|
39
|
+
description: "Package(s) to install: single name, comma-separated list, or empty for all deps"
|
|
40
|
+
},
|
|
41
|
+
save: {
|
|
42
|
+
type: "string",
|
|
43
|
+
enum: ["dependency", "dev", "optional"],
|
|
44
|
+
description: 'Where to save the package(s): "dependency", "devDependencies", or "optionalDependencies".'
|
|
45
|
+
},
|
|
46
|
+
cwd: {
|
|
47
|
+
type: "string",
|
|
48
|
+
description: "Working directory for the install command (must stay inside project)."
|
|
49
|
+
},
|
|
50
|
+
dry_run: {
|
|
51
|
+
type: "boolean",
|
|
52
|
+
description: "If true, show what would be installed without actually modifying package.json or node_modules."
|
|
53
|
+
},
|
|
54
|
+
global: {
|
|
55
|
+
type: "boolean",
|
|
56
|
+
description: "Whether to perform a global install (use with caution)."
|
|
57
|
+
},
|
|
58
|
+
lifecycleScripts: {
|
|
59
|
+
type: "boolean",
|
|
60
|
+
description: "Opt in to running package lifecycle scripts (preinstall / install / postinstall / prepare / \u2026). Default: false \u2014 installs pass --ignore-scripts so a malicious package cannot execute arbitrary code at install time. Set true to opt back in to the legacy npm/pnpm/yarn default."
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
async execute(input, ctx, opts) {
|
|
65
|
+
let final;
|
|
66
|
+
const executeStream = installTool.executeStream;
|
|
67
|
+
if (!executeStream) throw new Error("installTool: stream execution unavailable");
|
|
68
|
+
for await (const ev of executeStream(input, ctx, opts)) {
|
|
69
|
+
if (ev.type === "final") final = ev.output;
|
|
70
|
+
}
|
|
71
|
+
if (!final) throw new Error("install: stream ended without final event");
|
|
72
|
+
return final;
|
|
73
|
+
},
|
|
74
|
+
async *executeStream(input, ctx, opts) {
|
|
75
|
+
const cwd = input.cwd ? await safeResolveReal(input.cwd, ctx) : ctx.cwd;
|
|
76
|
+
if (!input.global) {
|
|
77
|
+
const pkgList2 = input.packages ? Array.isArray(input.packages) ? input.packages : input.packages.split(",").map((p) => p.trim()) : [];
|
|
78
|
+
const bridge = await tryLegacyPackageOperation(
|
|
79
|
+
"package-install",
|
|
80
|
+
{
|
|
81
|
+
cwd,
|
|
82
|
+
projectRoot: ctx.projectRoot,
|
|
83
|
+
signal: opts.signal
|
|
84
|
+
},
|
|
85
|
+
pkgList2
|
|
86
|
+
);
|
|
87
|
+
if (bridge?.outcome) {
|
|
88
|
+
const outcome = bridge.outcome;
|
|
89
|
+
const run = outcome.run;
|
|
90
|
+
yield {
|
|
91
|
+
type: "final",
|
|
92
|
+
output: {
|
|
93
|
+
exit_code: run?.exitCode ?? 0,
|
|
94
|
+
packages: [],
|
|
95
|
+
output: normalizeCommandOutput(
|
|
96
|
+
run?.output || outcome.manifestsChanged.join(", ") || "ok"
|
|
97
|
+
),
|
|
98
|
+
truncated: run?.truncated ?? false,
|
|
99
|
+
dry_run: input.dry_run ?? false
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
const pkgManager = await detectPackageManager(cwd, ctx.projectRoot);
|
|
106
|
+
yield { type: "log", text: `Resolving with ${pkgManager}\u2026`, data: { phase: "resolve" } };
|
|
107
|
+
const globalFlag = input.global ? ["-g"] : [];
|
|
108
|
+
const ignoreScripts = input.lifecycleScripts !== true;
|
|
109
|
+
const pkgList = input.packages ? (Array.isArray(input.packages) ? input.packages : input.packages.split(",")).map(
|
|
110
|
+
(p) => p.trim()
|
|
111
|
+
) : [];
|
|
112
|
+
const PKG_NAME_RE = /^(?:@[a-z0-9._-]+\/)?[a-z0-9._-]+(?:@[a-z0-9^~><=*.+-]+)?$/i;
|
|
113
|
+
for (const pkg of pkgList) {
|
|
114
|
+
if (!PKG_NAME_RE.test(pkg) || pkg.startsWith("-") || pkg.length > 200) {
|
|
115
|
+
yield {
|
|
116
|
+
type: "final",
|
|
117
|
+
output: {
|
|
118
|
+
packages: pkgList,
|
|
119
|
+
exit_code: 1,
|
|
120
|
+
output: `Invalid package name "${pkg}". Names must match ${PKG_NAME_RE} and not start with "-".`,
|
|
121
|
+
dry_run: Boolean(input.dry_run),
|
|
122
|
+
truncated: false
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
const hasPkgs = pkgList.length > 0;
|
|
129
|
+
const args = [];
|
|
130
|
+
if (input.dry_run) args.push("--dry-run");
|
|
131
|
+
if (ignoreScripts) args.push("--ignore-scripts");
|
|
132
|
+
if (pkgManager === "pnpm") {
|
|
133
|
+
if (hasPkgs) {
|
|
134
|
+
if (input.save === "dev") args.push("-D");
|
|
135
|
+
else if (input.save === "optional") args.push("-O");
|
|
136
|
+
args.push("add", ...globalFlag);
|
|
137
|
+
} else {
|
|
138
|
+
args.push("install", ...globalFlag);
|
|
139
|
+
}
|
|
140
|
+
} else if (pkgManager === "yarn") {
|
|
141
|
+
if (hasPkgs) {
|
|
142
|
+
args.push("add", ...globalFlag);
|
|
143
|
+
if (input.save === "dev") args.push("--dev");
|
|
144
|
+
else if (input.save === "optional") args.push("--optional");
|
|
145
|
+
} else {
|
|
146
|
+
args.push("install", ...globalFlag);
|
|
147
|
+
}
|
|
148
|
+
} else {
|
|
149
|
+
args.push("install", ...globalFlag);
|
|
150
|
+
if (hasPkgs) {
|
|
151
|
+
if (input.save === "dev") args.push("--save-dev");
|
|
152
|
+
else if (input.save === "optional") args.push("--save-optional");
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
if (hasPkgs) args.push(...pkgList);
|
|
156
|
+
yield {
|
|
157
|
+
type: "log",
|
|
158
|
+
text: `Fetching ${pkgList.length || "all"} packages\u2026`,
|
|
159
|
+
data: { phase: "fetch" }
|
|
160
|
+
};
|
|
161
|
+
const result = yield* spawnStream({
|
|
162
|
+
cmd: pkgManager,
|
|
163
|
+
args,
|
|
164
|
+
cwd,
|
|
165
|
+
signal: opts.signal,
|
|
166
|
+
maxBytes: 1e5
|
|
167
|
+
});
|
|
168
|
+
const output = {
|
|
169
|
+
packages: pkgList,
|
|
170
|
+
exit_code: result.exitCode,
|
|
171
|
+
output: normalizeCommandOutput(result.stdout || result.stderr || result.error || ""),
|
|
172
|
+
dry_run: args.includes("--dry-run"),
|
|
173
|
+
truncated: result.truncated
|
|
174
|
+
};
|
|
175
|
+
const isSuccess = result.exitCode === 0 && !output.dry_run && !input.global;
|
|
176
|
+
if (isSuccess && pkgList.length > 0) {
|
|
177
|
+
const trackerOpts = ctx.meta?.["packageTrackerOpts"];
|
|
178
|
+
if (trackerOpts) {
|
|
179
|
+
const manifestPath = resolveManifestPath(cwd, pkgManager);
|
|
180
|
+
for (const pkg of pkgList) {
|
|
181
|
+
try {
|
|
182
|
+
await recordPackageAction(trackerOpts, {
|
|
183
|
+
manifestPath,
|
|
184
|
+
packageName: pkg,
|
|
185
|
+
versionSpec: "latest",
|
|
186
|
+
// exact version resolved by package manager at install time
|
|
187
|
+
ecosystem: detectPackageEcosystem(manifestPath),
|
|
188
|
+
agentId: ctx.agentId,
|
|
189
|
+
agentName: ctx.agentName,
|
|
190
|
+
sessionId: ctx.session.id
|
|
191
|
+
});
|
|
192
|
+
} catch {
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
ctx.recordSideEffect?.({
|
|
198
|
+
toolUseId: `install-${Date.now()}`,
|
|
199
|
+
toolName: "install",
|
|
200
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
201
|
+
input: { packages: pkgList, cwd, dry_run: Boolean(input.dry_run) },
|
|
202
|
+
outcome: output.dry_run ? "dry run" : result.exitCode === 0 ? `installed ${pkgList.length || "all"} packages` : `failed (exit ${result.exitCode})`,
|
|
203
|
+
risk: "package"
|
|
204
|
+
});
|
|
205
|
+
yield { type: "final", output };
|
|
206
|
+
}
|
|
207
|
+
};
|
|
208
|
+
function resolveManifestPath(cwd, pkgManager) {
|
|
209
|
+
switch (pkgManager) {
|
|
210
|
+
case "pnpm":
|
|
211
|
+
case "yarn":
|
|
212
|
+
case "npm":
|
|
213
|
+
return join(cwd, "package.json");
|
|
214
|
+
/* v8 ignore next 2 -- pkgManager is always pnpm/yarn/npm; the default is defensive. */
|
|
215
|
+
default:
|
|
216
|
+
return join(cwd, "package.json");
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
export {
|
|
221
|
+
installTool
|
|
222
|
+
};
|
|
223
|
+
//# sourceMappingURL=chunk-5H4GSJAI.js.map
|