@wrongstack/tools 0.273.0 → 0.273.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/audit.js.map +1 -1
- package/dist/bash.js +193 -14
- package/dist/bash.js.map +1 -1
- package/dist/builtin.js +295 -91
- package/dist/builtin.js.map +1 -1
- package/dist/exec.d.ts +20 -1
- package/dist/exec.js +132 -71
- package/dist/exec.js.map +1 -1
- package/dist/format.js.map +1 -1
- package/dist/index.d.ts +111 -2
- package/dist/index.js +366 -121
- package/dist/index.js.map +1 -1
- package/dist/install.js.map +1 -1
- package/dist/lint.js.map +1 -1
- package/dist/outdated.js.map +1 -1
- package/dist/pack.js +295 -91
- package/dist/pack.js.map +1 -1
- package/dist/test.js.map +1 -1
- package/dist/typecheck.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -7,11 +7,12 @@ import { spawn, execFileSync } from 'node:child_process';
|
|
|
7
7
|
import * as os2 from 'node:os';
|
|
8
8
|
import * as fs8 from 'node:fs';
|
|
9
9
|
import { statSync, mkdirSync, createWriteStream } from 'node:fs';
|
|
10
|
+
import { toErrorMessage as toErrorMessage$1 } from '@wrongstack/core/utils/error';
|
|
10
11
|
import * as dns from 'node:dns/promises';
|
|
11
12
|
import * as net from 'node:net';
|
|
12
13
|
import { Agent } from 'undici';
|
|
13
14
|
import TurndownService from 'turndown';
|
|
14
|
-
import { toErrorMessage as toErrorMessage$
|
|
15
|
+
import { toErrorMessage as toErrorMessage$2 } from '@wrongstack/core/utils';
|
|
15
16
|
import { randomUUID } from 'node:crypto';
|
|
16
17
|
import { createRequire } from 'node:module';
|
|
17
18
|
import { fileURLToPath } from 'node:url';
|
|
@@ -2379,6 +2380,155 @@ async function checkAndBlockKillCommand(command) {
|
|
|
2379
2380
|
}
|
|
2380
2381
|
return { blocked: false };
|
|
2381
2382
|
}
|
|
2383
|
+
function pickShell(platform4, command, env) {
|
|
2384
|
+
const override = env.get("WRONGSTACK_SHELL")?.trim().toLowerCase();
|
|
2385
|
+
if (override === "cmd" || override === "cmd.exe") return "cmd";
|
|
2386
|
+
if (override === "powershell" || override === "powershell.exe") return "powershell";
|
|
2387
|
+
if (override === "pwsh" || override === "pwsh.exe") return "pwsh";
|
|
2388
|
+
if (looksLikePowerShell(command)) return "pwsh";
|
|
2389
|
+
return "cmd";
|
|
2390
|
+
}
|
|
2391
|
+
function looksLikePowerShell(command) {
|
|
2392
|
+
if (!command) return false;
|
|
2393
|
+
const trimmed = command.trimStart();
|
|
2394
|
+
if (/\.ps1\b/i.test(trimmed)) return true;
|
|
2395
|
+
if (/^\s*#requires\s/i.test(trimmed)) return true;
|
|
2396
|
+
if (/^\s*param\s*\(/i.test(trimmed)) return true;
|
|
2397
|
+
if (/\$[\w:{]/i.test(trimmed)) return true;
|
|
2398
|
+
if (/\$\(/.test(trimmed)) return true;
|
|
2399
|
+
if (/@\s*['"]/.test(trimmed)) return true;
|
|
2400
|
+
if (/&\s+\$/.test(trimmed)) return true;
|
|
2401
|
+
if (/(^|\s)@\s*\(/.test(trimmed)) return true;
|
|
2402
|
+
if (/(^|\s)@\{/.test(trimmed)) return true;
|
|
2403
|
+
if (/(?:^|[\s\[\(\{,;])(?:-eq|-ne|-lt|-gt|-le|-ge|-like|-notlike|-match|-notmatch|-contains|-notcontains|-in|-notin|-and|-or|-not|-band|-bor|-bxor|-replace|-isplit|-csplit|-osplit|-join|-is|-as|-f)(?:$|[\s\]\)\},;])/i.test(trimmed)) {
|
|
2404
|
+
return true;
|
|
2405
|
+
}
|
|
2406
|
+
if (PS_VERB_RE.test(trimmed)) return true;
|
|
2407
|
+
if (/(?:^|[\s;&|])(gci|gi|gp|gcm|gps|sl|rm|cat|cp|mv)\b/i.test(trimmed)) {
|
|
2408
|
+
return true;
|
|
2409
|
+
}
|
|
2410
|
+
if (looksLikePowerShellExtended(command)) return true;
|
|
2411
|
+
return false;
|
|
2412
|
+
}
|
|
2413
|
+
function looksLikePowerShellExtended(command) {
|
|
2414
|
+
if (!command) return false;
|
|
2415
|
+
const trimmed = command.trimStart();
|
|
2416
|
+
if (/(?:^|\s)[-/](?:WhatIf|Confirm|ErrorAction)(?::[^\s]+|\s|=|$)/i.test(trimmed)) {
|
|
2417
|
+
return true;
|
|
2418
|
+
}
|
|
2419
|
+
if (/(?:^|[\s;&|])(Where-Object|ForEach-Object|Select-Object|Sort-Object|Group-Object|Measure-Object|Compare-Object|Tee-Object)(?:\s|$)/i.test(trimmed)) {
|
|
2420
|
+
return true;
|
|
2421
|
+
}
|
|
2422
|
+
if (/\bWrite-(?:Host|Output|Error|Warning|Verbose|Debug|Information)(?:\s|$)/i.test(trimmed)) {
|
|
2423
|
+
return true;
|
|
2424
|
+
}
|
|
2425
|
+
if (/HK(?:LM|CU|CR|U|CC|DD|PD):\\/i.test(trimmed)) return true;
|
|
2426
|
+
if (/\[(?:string|int|bool|xml|double|float|decimal|char|byte|long|System\.)/i.test(trimmed)) {
|
|
2427
|
+
return true;
|
|
2428
|
+
}
|
|
2429
|
+
if (/^\s*<#|#>\s*$/m.test(trimmed)) return true;
|
|
2430
|
+
if (/(?:^|\s)[-/\/](?:AsPlainText|PipelineVariable|pv|FilterHashtable|OutVariable|ov)(?:\s|=|$)/i.test(trimmed)) {
|
|
2431
|
+
return true;
|
|
2432
|
+
}
|
|
2433
|
+
return false;
|
|
2434
|
+
}
|
|
2435
|
+
function wrapPowerShellScript(command) {
|
|
2436
|
+
const bootstrap = "[Console]::OutputEncoding = [System.Text.Encoding]::UTF8;$ConfirmPreference='None';$WhatIfPreference=$false";
|
|
2437
|
+
return "\uFEFF" + bootstrap + "\ntry {\n" + command + "\n} finally { exit $LASTEXITCODE }";
|
|
2438
|
+
}
|
|
2439
|
+
var PS_VERB_RE = new RegExp(
|
|
2440
|
+
// Boundaries: start-of-string, whitespace, `;`, `&`, `|`, `(`, `{`, `,`.
|
|
2441
|
+
"(?:^|[\\s;&|\\(\\{,])(?:Get|Set|New|Remove|Add|Clear|Copy|Move|Rename|Test|Update|Write|Read|Push|Pop|Invoke|Start|Stop|Wait|Out|Format|Group|Measure|Compare|Resolve|ConvertTo|ConvertFrom|Convert|Import|Export|Select|Where|ForEach|Sort|Tee|Split|Join|Limit|Skip|Step|Trace|Debug|Register|Unregister|Enable|Disable|Restart|Suspend|Resume|Save|Open|Close|Lock|Unlock|Mount|Dismount|Enter|Exit|Use|Show|Hide|Find|Search|Watch|Initialize|Optimize|Compress|Expand|Merge|Checkpoint|Undo|Redo|Approve|Deny|Block|Grant|Revoke|Assert|Confirm|Receive|Send|Connect|Disconnect|Reset|Backup|Restore|Publish|Unpublish|Install|Uninstall|Build|Rebuild|Deploy|Submit|Process|Complete|Approve|Revoke|Pay|Refund|Decline|Receive|Send)-[A-Za-z][A-Za-z0-9]+(?:[\\-\\+][A-Za-z][A-Za-z0-9]+)*(?:$|[\\s\\-\\;\\&\\|\\(\\)\\{\\},])",
|
|
2442
|
+
"i"
|
|
2443
|
+
);
|
|
2444
|
+
function shellArgs(shell) {
|
|
2445
|
+
if (shell === "powershell" || shell === "pwsh") {
|
|
2446
|
+
return ["-NoLogo", "-NoProfile", "-NonInteractive", "-Command", "-"];
|
|
2447
|
+
}
|
|
2448
|
+
return ["/c"];
|
|
2449
|
+
}
|
|
2450
|
+
function diagnoseBashism(command, shell) {
|
|
2451
|
+
if (!command) return void 0;
|
|
2452
|
+
const isCmd = shell === "cmd";
|
|
2453
|
+
const hints = [];
|
|
2454
|
+
const add = (h) => {
|
|
2455
|
+
if (!hints.includes(h)) hints.push(h);
|
|
2456
|
+
};
|
|
2457
|
+
if (/\/dev\/null/.test(command)) {
|
|
2458
|
+
add(
|
|
2459
|
+
isCmd ? "use `nul` instead of `/dev/null` (e.g. `2>nul`)" : "use `$null` instead of `/dev/null` (e.g. `2>$null`)"
|
|
2460
|
+
);
|
|
2461
|
+
}
|
|
2462
|
+
if (/(^|[;&|]\s*)export\s+[A-Za-z_]\w*=/.test(command)) {
|
|
2463
|
+
add(
|
|
2464
|
+
isCmd ? "set env vars with `set NAME=value`, not `export`" : "set env vars with `$env:NAME = 'value'`, not `export`"
|
|
2465
|
+
);
|
|
2466
|
+
}
|
|
2467
|
+
if (/<<-?\s*['"]?[A-Za-z_]\w*/.test(command)) {
|
|
2468
|
+
add(
|
|
2469
|
+
isCmd ? "cmd has no heredocs \u2014 write the content to a file or use multiple `echo` lines" : "PowerShell has no heredocs \u2014 use a single-quoted here-string `@'\u2026'@` (closing `'@` at column 0)"
|
|
2470
|
+
);
|
|
2471
|
+
}
|
|
2472
|
+
if (shell === "powershell" && /(&&|\|\|)/.test(command)) {
|
|
2473
|
+
add("Windows PowerShell 5.1 has no `&&`/`||` \u2014 separate commands with `;` (check `$LASTEXITCODE`)");
|
|
2474
|
+
}
|
|
2475
|
+
if (/\brm\s+-[A-Za-z]*[rf]/.test(command)) {
|
|
2476
|
+
add(
|
|
2477
|
+
isCmd ? "`rm` is not a cmd builtin \u2014 use `del` (files) or `rmdir /s /q` (dirs)" : "use `Remove-Item -Recurse -Force` \u2014 the `rm -rf` bash flags don't exist in PowerShell"
|
|
2478
|
+
);
|
|
2479
|
+
}
|
|
2480
|
+
if (/\bwhich\s+\S/.test(command)) {
|
|
2481
|
+
add(isCmd ? "use `where <cmd>` instead of `which`" : "use `Get-Command <cmd>` instead of `which`");
|
|
2482
|
+
}
|
|
2483
|
+
if (hints.length === 0) return void 0;
|
|
2484
|
+
const label = isCmd ? "cmd.exe" : shell === "pwsh" ? "PowerShell 7" : "Windows PowerShell";
|
|
2485
|
+
return `[wrongstack] This command failed and contains bash/POSIX syntax that ${label} does not accept \u2014 ${hints.join("; ")}. Rewrite it in ${isCmd ? "cmd" : "PowerShell"} syntax and retry.`;
|
|
2486
|
+
}
|
|
2487
|
+
function resolveWin32Command(cmd) {
|
|
2488
|
+
if (process.platform !== "win32") return cmd;
|
|
2489
|
+
if (cmd.includes("/") || cmd.includes("\\") || path.extname(cmd.replace(/\//g, "\\"))) {
|
|
2490
|
+
return cmd;
|
|
2491
|
+
}
|
|
2492
|
+
const pathext = (process.env["PATHEXT"] ?? ".COM;.EXE;.BAT;.CMD;.VBS;.JS;.WS;.MSC").toLowerCase().split(";");
|
|
2493
|
+
const pathDirs = (process.env["PATH"] ?? "").split(path.delimiter);
|
|
2494
|
+
for (const dir of pathDirs) {
|
|
2495
|
+
const base = path.join(dir, cmd);
|
|
2496
|
+
for (const ext of pathext) {
|
|
2497
|
+
const full = `${base}${ext}`;
|
|
2498
|
+
try {
|
|
2499
|
+
fs8.accessSync(full, fs8.constants.X_OK);
|
|
2500
|
+
return full;
|
|
2501
|
+
} catch {
|
|
2502
|
+
}
|
|
2503
|
+
}
|
|
2504
|
+
}
|
|
2505
|
+
return cmd;
|
|
2506
|
+
}
|
|
2507
|
+
function resolvePowerShell(cmd) {
|
|
2508
|
+
if (process.platform !== "win32") return cmd;
|
|
2509
|
+
const lower = cmd.toLowerCase();
|
|
2510
|
+
if (lower !== "pwsh" && lower !== "powershell" && lower !== "pwsh.exe" && lower !== "powershell.exe") {
|
|
2511
|
+
return resolveWin32Command(cmd);
|
|
2512
|
+
}
|
|
2513
|
+
const primary = lower.startsWith("pwsh") ? "pwsh.exe" : "powershell.exe";
|
|
2514
|
+
const fallback = lower.startsWith("pwsh") ? "powershell.exe" : "pwsh.exe";
|
|
2515
|
+
const resolved = resolveWin32Command(primary);
|
|
2516
|
+
if (resolved !== primary) {
|
|
2517
|
+
const fb = resolveWin32Command(fallback);
|
|
2518
|
+
return fb === fallback ? cmd : fb;
|
|
2519
|
+
}
|
|
2520
|
+
return resolved;
|
|
2521
|
+
}
|
|
2522
|
+
var WIN32_SHELL_META = /[&|<>\r\n\0]/;
|
|
2523
|
+
function assertSafeWin32ShellArgs(args) {
|
|
2524
|
+
for (const a of args) {
|
|
2525
|
+
if (typeof a === "string" && WIN32_SHELL_META.test(a)) {
|
|
2526
|
+
throw new Error(
|
|
2527
|
+
"win32 shell spawn: argument contains a shell metacharacter (one of & | < > or a newline) that could enable command injection through the .cmd/.bat wrapper \u2014 refusing to run. Offending argument: " + JSON.stringify(a)
|
|
2528
|
+
);
|
|
2529
|
+
}
|
|
2530
|
+
}
|
|
2531
|
+
}
|
|
2382
2532
|
|
|
2383
2533
|
// src/bash.ts
|
|
2384
2534
|
var MAX_OUTPUT = 32768;
|
|
@@ -2475,18 +2625,36 @@ var bashTool = {
|
|
|
2475
2625
|
}
|
|
2476
2626
|
const timeoutMs = Math.max(1, Math.min(input.timeout_ms ?? DEFAULT_TIMEOUT_MS, 6e5));
|
|
2477
2627
|
const isWin3 = os2.platform() === "win32";
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2628
|
+
let plan;
|
|
2629
|
+
let winShellKind;
|
|
2630
|
+
if (isWin3) {
|
|
2631
|
+
const shell2 = pickShell("win32", input.command, {
|
|
2632
|
+
get: (k) => process.env[k]
|
|
2633
|
+
});
|
|
2634
|
+
winShellKind = shell2;
|
|
2635
|
+
const bin = shell2 === "powershell" ? resolvePowerShell("powershell.exe") : shell2 === "pwsh" ? resolvePowerShell("pwsh.exe") : process.env["COMSPEC"] ?? "cmd.exe";
|
|
2636
|
+
plan = {
|
|
2637
|
+
bin,
|
|
2638
|
+
argv: shellArgs(shell2),
|
|
2639
|
+
useStdin: shell2 === "powershell" || shell2 === "pwsh",
|
|
2640
|
+
stdinBody: shell2 === "powershell" || shell2 === "pwsh" ? wrapPowerShellScript(input.command) : void 0
|
|
2641
|
+
};
|
|
2642
|
+
} else {
|
|
2643
|
+
const explicit = process.env["WRONGSTACK_SHELL"];
|
|
2644
|
+
let bin;
|
|
2645
|
+
if (explicit) bin = explicit;
|
|
2646
|
+
else {
|
|
2647
|
+
const fromEnv = process.env["SHELL"];
|
|
2648
|
+
if (fromEnv) {
|
|
2649
|
+
const name = fromEnv.split("/").pop() ?? "";
|
|
2650
|
+
if (["bash", "zsh", "sh", "dash", "fish"].includes(name)) bin = fromEnv;
|
|
2651
|
+
else bin = "/bin/bash";
|
|
2652
|
+
} else bin = "/bin/bash";
|
|
2653
|
+
}
|
|
2654
|
+
plan = { bin, argv: ["-c"], useStdin: false, stdinBody: void 0 };
|
|
2655
|
+
}
|
|
2656
|
+
const shell = plan.bin;
|
|
2657
|
+
const args = plan.useStdin ? [...plan.argv] : [...plan.argv, input.command];
|
|
2490
2658
|
const env = buildChildEnv(ctx.session?.id);
|
|
2491
2659
|
const detached = !isWin3;
|
|
2492
2660
|
const startedAt = Date.now();
|
|
@@ -2496,7 +2664,9 @@ var bashTool = {
|
|
|
2496
2664
|
const child2 = spawn(shell, args, {
|
|
2497
2665
|
cwd: ctx.projectRoot,
|
|
2498
2666
|
env,
|
|
2499
|
-
|
|
2667
|
+
// PowerShell takes the script on stdin (no argv quoting); cmd.exe
|
|
2668
|
+
// and POSIX shells ignore stdin when given the command inline.
|
|
2669
|
+
stdio: [plan.useStdin ? "pipe" : "ignore", "pipe", "pipe"],
|
|
2500
2670
|
// win32: CreateProcess IGNORES CREATE_NO_WINDOW (windowsHide) when
|
|
2501
2671
|
// DETACHED_PROCESS (detached: true) is set, so the console-less
|
|
2502
2672
|
// cmd.exe's grandchildren (node, dev servers) each allocate a fresh
|
|
@@ -2507,6 +2677,13 @@ var bashTool = {
|
|
|
2507
2677
|
detached: !isWin3,
|
|
2508
2678
|
windowsHide: true
|
|
2509
2679
|
});
|
|
2680
|
+
if (plan.useStdin) {
|
|
2681
|
+
try {
|
|
2682
|
+
child2.stdin?.write(plan.stdinBody ?? input.command);
|
|
2683
|
+
child2.stdin?.end();
|
|
2684
|
+
} catch {
|
|
2685
|
+
}
|
|
2686
|
+
}
|
|
2510
2687
|
const pid2 = child2.pid;
|
|
2511
2688
|
if (typeof pid2 === "number") {
|
|
2512
2689
|
registry.register({
|
|
@@ -2561,11 +2738,20 @@ var bashTool = {
|
|
|
2561
2738
|
const child = spawn(shell, args, {
|
|
2562
2739
|
cwd: ctx.projectRoot,
|
|
2563
2740
|
env,
|
|
2564
|
-
|
|
2741
|
+
// PowerShell takes the script on stdin (no argv quoting); cmd.exe
|
|
2742
|
+
// and POSIX shells ignore stdin when given the command inline.
|
|
2743
|
+
stdio: [plan.useStdin ? "pipe" : "ignore", "pipe", "pipe"],
|
|
2565
2744
|
detached,
|
|
2566
2745
|
windowsHide: true,
|
|
2567
2746
|
...isWin3 ? {} : { signal: opts.signal }
|
|
2568
2747
|
});
|
|
2748
|
+
if (plan.useStdin) {
|
|
2749
|
+
try {
|
|
2750
|
+
child.stdin?.write(plan.stdinBody ?? input.command);
|
|
2751
|
+
child.stdin?.end();
|
|
2752
|
+
} catch {
|
|
2753
|
+
}
|
|
2754
|
+
}
|
|
2569
2755
|
const pid = child.pid;
|
|
2570
2756
|
if (typeof pid === "number") {
|
|
2571
2757
|
registry.register({
|
|
@@ -2716,10 +2902,13 @@ var bashTool = {
|
|
|
2716
2902
|
yield { type: "partial_output", text: remainder };
|
|
2717
2903
|
}
|
|
2718
2904
|
const spooled = spool.finalize();
|
|
2905
|
+
const hint = !timedOut && typeof c.code === "number" && c.code !== 0 && winShellKind ? diagnoseBashism(input.command, winShellKind) : void 0;
|
|
2719
2906
|
yield {
|
|
2720
2907
|
type: "final",
|
|
2721
2908
|
output: {
|
|
2722
|
-
output: normalizeCommandOutput(buf) + (spooled ? spoolNote(spooled) : "")
|
|
2909
|
+
output: normalizeCommandOutput(buf) + (spooled ? spoolNote(spooled) : "") + (hint ? `
|
|
2910
|
+
|
|
2911
|
+
${hint}` : ""),
|
|
2723
2912
|
exit_code: c.code,
|
|
2724
2913
|
timed_out: timedOut
|
|
2725
2914
|
}
|
|
@@ -2747,82 +2936,115 @@ var bashTool = {
|
|
|
2747
2936
|
}
|
|
2748
2937
|
}
|
|
2749
2938
|
};
|
|
2750
|
-
function resolveWin32Command(cmd) {
|
|
2751
|
-
if (process.platform !== "win32") return cmd;
|
|
2752
|
-
if (cmd.includes("/") || cmd.includes("\\") || path.extname(cmd.replace(/\//g, "\\"))) {
|
|
2753
|
-
return cmd;
|
|
2754
|
-
}
|
|
2755
|
-
const pathext = (process.env["PATHEXT"] ?? ".COM;.EXE;.BAT;.CMD;.VBS;.JS;.WS;.MSC").toLowerCase().split(";");
|
|
2756
|
-
const pathDirs = (process.env["PATH"] ?? "").split(path.delimiter);
|
|
2757
|
-
for (const dir of pathDirs) {
|
|
2758
|
-
const base = path.join(dir, cmd);
|
|
2759
|
-
for (const ext of pathext) {
|
|
2760
|
-
const full = `${base}${ext}`;
|
|
2761
|
-
try {
|
|
2762
|
-
fs8.accessSync(full, fs8.constants.X_OK);
|
|
2763
|
-
return full;
|
|
2764
|
-
} catch {
|
|
2765
|
-
}
|
|
2766
|
-
}
|
|
2767
|
-
}
|
|
2768
|
-
return cmd;
|
|
2769
|
-
}
|
|
2770
|
-
var WIN32_SHELL_META = /[&|<>\r\n\0]/;
|
|
2771
|
-
function assertSafeWin32ShellArgs(args) {
|
|
2772
|
-
for (const a of args) {
|
|
2773
|
-
if (typeof a === "string" && WIN32_SHELL_META.test(a)) {
|
|
2774
|
-
throw new Error(
|
|
2775
|
-
"win32 shell spawn: argument contains a shell metacharacter (one of & | < > or a newline) that could enable command injection through the .cmd/.bat wrapper \u2014 refusing to run. Offending argument: " + JSON.stringify(a)
|
|
2776
|
-
);
|
|
2777
|
-
}
|
|
2778
|
-
}
|
|
2779
|
-
}
|
|
2780
2939
|
|
|
2781
|
-
// src/
|
|
2940
|
+
// src/_session-shell.ts
|
|
2941
|
+
function normalizeShell(value) {
|
|
2942
|
+
const v = value?.trim().toLowerCase();
|
|
2943
|
+
if (v === "cmd" || v === "cmd.exe") return "cmd";
|
|
2944
|
+
if (v === "powershell" || v === "powershell.exe") return "powershell";
|
|
2945
|
+
if (v === "pwsh" || v === "pwsh.exe") return "pwsh";
|
|
2946
|
+
return void 0;
|
|
2947
|
+
}
|
|
2948
|
+
function resolveSessionShell(platform4, env, deps = {}) {
|
|
2949
|
+
if (platform4 !== "win32") return void 0;
|
|
2950
|
+
const override = normalizeShell(env.get("WRONGSTACK_SHELL"));
|
|
2951
|
+
if (override) return override;
|
|
2952
|
+
const hasBinary = deps.hasBinary ?? ((bin) => resolveWin32Command(bin) !== bin);
|
|
2953
|
+
if (hasBinary("pwsh.exe")) return "pwsh";
|
|
2954
|
+
if (hasBinary("powershell.exe")) return "powershell";
|
|
2955
|
+
return "cmd";
|
|
2956
|
+
}
|
|
2957
|
+
function ensureSessionShell(opts = {}) {
|
|
2958
|
+
const env = opts.env ?? process.env;
|
|
2959
|
+
const platform4 = opts.platform ?? process.platform;
|
|
2960
|
+
if (platform4 !== "win32") return void 0;
|
|
2961
|
+
const existing = normalizeShell(env["WRONGSTACK_SHELL"]);
|
|
2962
|
+
if (existing) return existing;
|
|
2963
|
+
const chosen = resolveSessionShell(platform4, { get: (k) => env[k] }, { hasBinary: opts.hasBinary }) ?? "cmd";
|
|
2964
|
+
env["WRONGSTACK_SHELL"] = chosen;
|
|
2965
|
+
return chosen;
|
|
2966
|
+
}
|
|
2782
2967
|
var isWin = process.platform === "win32";
|
|
2783
|
-
var
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
|
|
2797
|
-
|
|
2798
|
-
|
|
2799
|
-
|
|
2800
|
-
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
|
|
2822
|
-
|
|
2823
|
-
|
|
2824
|
-
|
|
2825
|
-
|
|
2968
|
+
var DEFAULT_ALLOWED_COMMANDS = /* @__PURE__ */ new Set([
|
|
2969
|
+
// JS / TS toolchain
|
|
2970
|
+
"node",
|
|
2971
|
+
"npm",
|
|
2972
|
+
"pnpm",
|
|
2973
|
+
"yarn",
|
|
2974
|
+
"npx",
|
|
2975
|
+
"bun",
|
|
2976
|
+
"deno",
|
|
2977
|
+
"tsc",
|
|
2978
|
+
"vitest",
|
|
2979
|
+
"jest",
|
|
2980
|
+
"biome",
|
|
2981
|
+
"eslint",
|
|
2982
|
+
"prettier",
|
|
2983
|
+
// version control
|
|
2984
|
+
"git",
|
|
2985
|
+
// Rust
|
|
2986
|
+
"cargo",
|
|
2987
|
+
"rustc",
|
|
2988
|
+
// Go
|
|
2989
|
+
"go",
|
|
2990
|
+
// Python
|
|
2991
|
+
"python",
|
|
2992
|
+
"python3",
|
|
2993
|
+
"pip",
|
|
2994
|
+
"pip3",
|
|
2995
|
+
// Ruby
|
|
2996
|
+
"ruby",
|
|
2997
|
+
"gem",
|
|
2998
|
+
"bundle",
|
|
2999
|
+
// JVM
|
|
3000
|
+
"java",
|
|
3001
|
+
"javac",
|
|
3002
|
+
"mvn",
|
|
3003
|
+
"gradle",
|
|
3004
|
+
"gradlew",
|
|
3005
|
+
// .NET
|
|
3006
|
+
"dotnet",
|
|
3007
|
+
// C / C++ / native build
|
|
3008
|
+
"make",
|
|
3009
|
+
"cmake",
|
|
3010
|
+
// containers / orchestration (read-only subcommands; see BLOCKED_ARG_PATTERNS)
|
|
3011
|
+
"docker",
|
|
3012
|
+
"kubectl",
|
|
3013
|
+
// common POSIX file/text utilities
|
|
3014
|
+
"ls",
|
|
3015
|
+
"cat",
|
|
3016
|
+
"head",
|
|
3017
|
+
"tail",
|
|
3018
|
+
"wc",
|
|
3019
|
+
"grep",
|
|
3020
|
+
"find",
|
|
3021
|
+
"echo",
|
|
3022
|
+
"mkdir",
|
|
3023
|
+
"cp",
|
|
3024
|
+
"mv",
|
|
3025
|
+
"rm",
|
|
3026
|
+
"touch"
|
|
3027
|
+
]);
|
|
3028
|
+
var allowedCommands = new Set(DEFAULT_ALLOWED_COMMANDS);
|
|
3029
|
+
var normalizeCmd = (c) => c.trim();
|
|
3030
|
+
function configureExecPolicy(opts = {}) {
|
|
3031
|
+
const next = new Set(DEFAULT_ALLOWED_COMMANDS);
|
|
3032
|
+
for (const c of opts.allow ?? []) {
|
|
3033
|
+
const n = normalizeCmd(c);
|
|
3034
|
+
if (n) next.add(n);
|
|
3035
|
+
}
|
|
3036
|
+
for (const c of opts.deny ?? []) next.delete(normalizeCmd(c));
|
|
3037
|
+
allowedCommands = next;
|
|
3038
|
+
}
|
|
3039
|
+
function resetExecPolicy() {
|
|
3040
|
+
allowedCommands = new Set(DEFAULT_ALLOWED_COMMANDS);
|
|
3041
|
+
}
|
|
3042
|
+
function isExecCommandAllowed(cmd) {
|
|
3043
|
+
return allowedCommands.has(normalizeCmd(cmd));
|
|
3044
|
+
}
|
|
3045
|
+
function getExecAllowlist() {
|
|
3046
|
+
return [...allowedCommands].sort();
|
|
3047
|
+
}
|
|
2826
3048
|
var MAX_ARGS = 20;
|
|
2827
3049
|
var MAX_OUTPUT2 = 2e5;
|
|
2828
3050
|
var DEFAULT_TIMEOUT_MS2 = 3e4;
|
|
@@ -2884,7 +3106,7 @@ var execTool = {
|
|
|
2884
3106
|
name: "exec",
|
|
2885
3107
|
category: "Shell",
|
|
2886
3108
|
description: "Execute a **whitelisted, restricted set of commands** with strict argument validation. This is the **preferred and safer** alternative to the `bash` tool for running development tools (node, npm, pnpm, tsc, git, tests, linters, etc.). It prevents arbitrary command injection and limits what the model can do.",
|
|
2887
|
-
usageHint: "PREFERRED SHELL TOOL for most cases.\n\nUse this instead of `bash` whenever possible.\n- `command` must be
|
|
3109
|
+
usageHint: "PREFERRED SHELL TOOL for most cases.\n\nUse this instead of `bash` whenever possible.\n- `command` must be in the allowlist. Defaults cover JS (node/npm/pnpm/yarn/bun/deno/tsc/vitest/eslint/biome), Go (`go build`/`go test`), Rust (cargo), Python (python/pip), Ruby (gem/bundle), JVM (java/mvn/gradle), .NET (dotnet), native (make/cmake), and git. Users can extend it via `tools.exec.allow` in config.\n- Arguments are passed as a clean array (no shell interpretation).\n- `cwd` is validated to stay inside the project.\n- If a command is not allowlisted, the error explains how to add it; for one-off arbitrary commands, fall back to `bash` (with strong justification).\nThis tool significantly reduces the risk compared to full shell access.",
|
|
2888
3110
|
permission: "confirm",
|
|
2889
3111
|
mutating: true,
|
|
2890
3112
|
riskTier: "standard",
|
|
@@ -2938,12 +3160,12 @@ var execTool = {
|
|
|
2938
3160
|
truncated: false,
|
|
2939
3161
|
allowed: false
|
|
2940
3162
|
};
|
|
2941
|
-
if (!(cmd
|
|
3163
|
+
if (!isExecCommandAllowed(cmd)) {
|
|
2942
3164
|
return {
|
|
2943
3165
|
command: cmd,
|
|
2944
3166
|
args: input.args ?? [],
|
|
2945
3167
|
stdout: "",
|
|
2946
|
-
stderr: `Command "${cmd}" not in allowlist.
|
|
3168
|
+
stderr: `Command "${cmd}" not in allowlist. Add it to your ~/.wrongstack/config.json under "tools": { "exec": { "allow": ["${cmd}"] } }, or use the bash tool for one-off arbitrary commands.`,
|
|
2947
3169
|
exitCode: 1,
|
|
2948
3170
|
truncated: false,
|
|
2949
3171
|
allowed: false
|
|
@@ -2986,19 +3208,58 @@ function runCommand(cmd, args, cwd, timeout, signal, sessionId) {
|
|
|
2986
3208
|
let stdout = "";
|
|
2987
3209
|
let stderr = "";
|
|
2988
3210
|
let killed = false;
|
|
3211
|
+
const resolvedOnce = { value: false };
|
|
3212
|
+
const finish = (result) => {
|
|
3213
|
+
if (resolvedOnce.value) return;
|
|
3214
|
+
resolvedOnce.value = true;
|
|
3215
|
+
resolve6(result);
|
|
3216
|
+
};
|
|
2989
3217
|
const startedAt = Date.now();
|
|
2990
3218
|
const spool = createOutputSpool({ tool: `exec-${cmd}`, thresholdBytes: MAX_OUTPUT2 });
|
|
2991
3219
|
const resolved = resolveWin32Command(cmd);
|
|
2992
3220
|
const needsShell = isWin && (resolved.endsWith(".cmd") || resolved.endsWith(".bat"));
|
|
2993
3221
|
const spawnCmd = needsShell ? cmd : resolved;
|
|
2994
3222
|
if (needsShell) assertSafeWin32ShellArgs(args);
|
|
2995
|
-
|
|
2996
|
-
|
|
2997
|
-
|
|
2998
|
-
|
|
2999
|
-
|
|
3000
|
-
|
|
3001
|
-
|
|
3223
|
+
let child;
|
|
3224
|
+
try {
|
|
3225
|
+
child = spawn(spawnCmd, args, {
|
|
3226
|
+
cwd,
|
|
3227
|
+
env: buildChildEnv(sessionId),
|
|
3228
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
3229
|
+
windowsHide: true,
|
|
3230
|
+
...isWin ? {} : { signal },
|
|
3231
|
+
...needsShell ? { shell: true, windowsVerbatimArguments: true } : {}
|
|
3232
|
+
});
|
|
3233
|
+
} catch (err) {
|
|
3234
|
+
spool.finalize();
|
|
3235
|
+
finish({
|
|
3236
|
+
command: cmd,
|
|
3237
|
+
args,
|
|
3238
|
+
stdout: "",
|
|
3239
|
+
stderr: `spawn failed: ${toErrorMessage$1(err)}`,
|
|
3240
|
+
exitCode: 1,
|
|
3241
|
+
truncated: false,
|
|
3242
|
+
allowed: true
|
|
3243
|
+
});
|
|
3244
|
+
return;
|
|
3245
|
+
}
|
|
3246
|
+
child.on("error", (err) => {
|
|
3247
|
+
const isAbort = err && err.code === "ABORT_ERR";
|
|
3248
|
+
const stderrText = isAbort ? `Aborted: ${err.message}` : err.message;
|
|
3249
|
+
clearTimeout(timer);
|
|
3250
|
+
if (isWin) signal.removeEventListener("abort", onAbort);
|
|
3251
|
+
if (typeof pid === "number") registry.unregister(pid);
|
|
3252
|
+
registry.afterCall(Date.now() - startedAt, true);
|
|
3253
|
+
spool.finalize();
|
|
3254
|
+
finish({
|
|
3255
|
+
command: cmd,
|
|
3256
|
+
args,
|
|
3257
|
+
stdout: normalizeCommandOutput(stdout),
|
|
3258
|
+
stderr: stderrText,
|
|
3259
|
+
exitCode: isAbort ? 124 : 1,
|
|
3260
|
+
truncated: Buffer.byteLength(stdout, "utf8") > COMMAND_OUTPUT_MAX_BYTES,
|
|
3261
|
+
allowed: true
|
|
3262
|
+
});
|
|
3002
3263
|
});
|
|
3003
3264
|
const registry = getProcessRegistry();
|
|
3004
3265
|
const pid = child.pid;
|
|
@@ -3038,7 +3299,7 @@ function runCommand(cmd, args, cwd, timeout, signal, sessionId) {
|
|
|
3038
3299
|
const exitCode = killed ? 124 : code ?? 1;
|
|
3039
3300
|
registry.afterCall(durationMs, exitCode !== 0);
|
|
3040
3301
|
const spooled = spool.finalize();
|
|
3041
|
-
|
|
3302
|
+
finish({
|
|
3042
3303
|
command: cmd,
|
|
3043
3304
|
args,
|
|
3044
3305
|
stdout: normalizeCommandOutput(stdout) + (spooled ? spoolNote(spooled) : ""),
|
|
@@ -3048,22 +3309,6 @@ function runCommand(cmd, args, cwd, timeout, signal, sessionId) {
|
|
|
3048
3309
|
allowed: true
|
|
3049
3310
|
});
|
|
3050
3311
|
});
|
|
3051
|
-
child.on("error", (err) => {
|
|
3052
|
-
clearTimeout(timer);
|
|
3053
|
-
if (isWin) signal.removeEventListener("abort", onAbort);
|
|
3054
|
-
if (typeof pid === "number") registry.unregister(pid);
|
|
3055
|
-
registry.afterCall(Date.now() - startedAt, true);
|
|
3056
|
-
spool.finalize();
|
|
3057
|
-
resolve6({
|
|
3058
|
-
command: cmd,
|
|
3059
|
-
args,
|
|
3060
|
-
stdout: normalizeCommandOutput(stdout),
|
|
3061
|
-
stderr: err.message,
|
|
3062
|
-
exitCode: 1,
|
|
3063
|
-
truncated: Buffer.byteLength(stdout, "utf8") > COMMAND_OUTPUT_MAX_BYTES,
|
|
3064
|
-
allowed: true
|
|
3065
|
-
});
|
|
3066
|
-
});
|
|
3067
3312
|
});
|
|
3068
3313
|
}
|
|
3069
3314
|
var TD = new TurndownService({
|
|
@@ -3428,7 +3673,7 @@ async function duckduckgoSearch(query2, num, signal) {
|
|
|
3428
3673
|
truncated: results.length >= num
|
|
3429
3674
|
};
|
|
3430
3675
|
} catch (err) {
|
|
3431
|
-
console.log(JSON.stringify({ level: "debug", event: "search_failed", query: query2, error: toErrorMessage$
|
|
3676
|
+
console.log(JSON.stringify({ level: "debug", event: "search_failed", query: query2, error: toErrorMessage$2(err) }));
|
|
3432
3677
|
return {
|
|
3433
3678
|
query: query2,
|
|
3434
3679
|
results: [{ title: "Search unavailable", url: "", snippet: "Could not reach DuckDuckGo" }],
|
|
@@ -7667,7 +7912,7 @@ function loadDatabaseSync() {
|
|
|
7667
7912
|
DatabaseSyncCtor = req("node:sqlite").DatabaseSync;
|
|
7668
7913
|
} catch (err) {
|
|
7669
7914
|
throw new Error(
|
|
7670
|
-
`The codebase index needs Node's built-in SQLite (node:sqlite), available since Node 22.5. This runtime doesn't provide it: ${toErrorMessage$
|
|
7915
|
+
`The codebase index needs Node's built-in SQLite (node:sqlite), available since Node 22.5. This runtime doesn't provide it: ${toErrorMessage$2(err)}`
|
|
7671
7916
|
);
|
|
7672
7917
|
}
|
|
7673
7918
|
return DatabaseSyncCtor;
|
|
@@ -10358,7 +10603,7 @@ var setWorkingDirTool = {
|
|
|
10358
10603
|
} catch (err) {
|
|
10359
10604
|
return {
|
|
10360
10605
|
current: ctx.workingDir,
|
|
10361
|
-
error: toErrorMessage$
|
|
10606
|
+
error: toErrorMessage$2(err)
|
|
10362
10607
|
};
|
|
10363
10608
|
}
|
|
10364
10609
|
try {
|
|
@@ -11004,6 +11249,6 @@ var TOOL_ICON_CONFIG = {
|
|
|
11004
11249
|
};
|
|
11005
11250
|
var FALLBACK_ICON = "fallback";
|
|
11006
11251
|
|
|
11007
|
-
export { CircuitBreaker, CircuitOpenError, FALLBACK_ICON, IndexCircuitBreaker, IndexTimeoutError, OPTIONAL_TOOLS, TIER1_TOOLS, TIER2_TOOLS, TIER3_TOOLS, TOOL_ICON_CONFIG, TOOL_ICON_MAP, _resetProcessRegistry, auditTool, bashTool, batchToolUseTool, builtinTools, builtinToolsPack, cancelPendingReindexes, codebaseIndexStats, codebaseIndexTool, codebaseSearchTool, codebaseStatsTool, createGlobalPsSlashCommand, createModeTool, diffTool, documentTool, editTool, enqueueReindex, execTool, fetchTool, forgetTool, formatGlobalStatus, formatInstanceList, formatInstanceSummary, formatTool, getIndexState, getInstanceCount, getPersistentProcessRegistry, getProcessGuardian, getProcessRegistry, getToolIcon, gitTool, globTool, grepTool, indexCircuitBreaker, installTool, isIndexReady, isIndexableFile, isIndexing, jsonTool, lintTool, listInstances, logsTool, onIndexStateChange, outdatedTool, patchTool, planTool, readTool, relatedMemoryTool, rememberTool, replaceTool, resetIndexCircuitBreaker, resetPersistentProcessRegistry, runStartupIndex, scaffoldTool, searchCodebaseIndex, searchMemoryTool, searchTool, shutdownCodebaseIndexHost, startProcessGuardian, stopProcessGuardian, testTool, todoTool, toolHelpTool, toolSearchTool, toolUseTool, treeTool, typecheckTool, writeTool };
|
|
11252
|
+
export { CircuitBreaker, CircuitOpenError, FALLBACK_ICON, IndexCircuitBreaker, IndexTimeoutError, OPTIONAL_TOOLS, TIER1_TOOLS, TIER2_TOOLS, TIER3_TOOLS, TOOL_ICON_CONFIG, TOOL_ICON_MAP, _resetProcessRegistry, auditTool, bashTool, batchToolUseTool, builtinTools, builtinToolsPack, cancelPendingReindexes, codebaseIndexStats, codebaseIndexTool, codebaseSearchTool, codebaseStatsTool, configureExecPolicy, createGlobalPsSlashCommand, createModeTool, diffTool, documentTool, editTool, enqueueReindex, ensureSessionShell, execTool, fetchTool, forgetTool, formatGlobalStatus, formatInstanceList, formatInstanceSummary, formatTool, getExecAllowlist, getIndexState, getInstanceCount, getPersistentProcessRegistry, getProcessGuardian, getProcessRegistry, getToolIcon, gitTool, globTool, grepTool, indexCircuitBreaker, installTool, isExecCommandAllowed, isIndexReady, isIndexableFile, isIndexing, jsonTool, lintTool, listInstances, logsTool, normalizeShell, onIndexStateChange, outdatedTool, patchTool, planTool, readTool, relatedMemoryTool, rememberTool, replaceTool, resetExecPolicy, resetIndexCircuitBreaker, resetPersistentProcessRegistry, resolveSessionShell, runStartupIndex, scaffoldTool, searchCodebaseIndex, searchMemoryTool, searchTool, shutdownCodebaseIndexHost, startProcessGuardian, stopProcessGuardian, testTool, todoTool, toolHelpTool, toolSearchTool, toolUseTool, treeTool, typecheckTool, writeTool };
|
|
11008
11253
|
//# sourceMappingURL=index.js.map
|
|
11009
11254
|
//# sourceMappingURL=index.js.map
|