@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/pack.js
CHANGED
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
import { spawn, execFileSync } from 'node:child_process';
|
|
2
2
|
import * as Core from '@wrongstack/core';
|
|
3
|
-
import { buildChildEnv, detectNewlineStyle, normalizeToLf, toStyle, atomicWrite, unifiedDiff, isPrivateIPv4, isPrivateIPv6, assessCommitSafety, compileGlob, expectDefined, recordPackageAction, detectPackageEcosystem, mutatePlan, clearPlan, getPlanTemplate, addPlanItem, deriveTodosFromPlanItem, removePlanItem, setPlanItemStatus, mutateTasks, formatTaskList, formatPlan, toErrorMessage as toErrorMessage$
|
|
3
|
+
import { buildChildEnv, detectNewlineStyle, normalizeToLf, toStyle, atomicWrite, unifiedDiff, isPrivateIPv4, isPrivateIPv6, assessCommitSafety, compileGlob, expectDefined, recordPackageAction, detectPackageEcosystem, mutatePlan, clearPlan, getPlanTemplate, addPlanItem, deriveTodosFromPlanItem, removePlanItem, setPlanItemStatus, mutateTasks, formatTaskList, formatPlan, toErrorMessage as toErrorMessage$2, computeTaskItemProgress, loadPlan, savePlan, loadTasks, saveTasks, wstackGlobalRoot, resolveWstackPaths, truncate } from '@wrongstack/core';
|
|
4
4
|
import * as fs from 'node:fs';
|
|
5
5
|
import { statSync, mkdirSync, createWriteStream } from 'node:fs';
|
|
6
6
|
import * as fs2 from 'node:fs/promises';
|
|
7
7
|
import * as path3 from 'node:path';
|
|
8
8
|
import { resolve, sep, dirname, join } from 'node:path';
|
|
9
9
|
import * as os2 from 'node:os';
|
|
10
|
-
import { toErrorMessage as toErrorMessage$
|
|
10
|
+
import { toErrorMessage as toErrorMessage$3 } from '@wrongstack/core/utils';
|
|
11
11
|
import { createRequire } from 'node:module';
|
|
12
12
|
import { fileURLToPath } from 'node:url';
|
|
13
13
|
import { Worker } from 'node:worker_threads';
|
|
14
14
|
import * as ts from 'typescript';
|
|
15
|
+
import { toErrorMessage as toErrorMessage$1 } from '@wrongstack/core/utils/error';
|
|
15
16
|
import * as dns from 'node:dns/promises';
|
|
16
17
|
import * as net from 'node:net';
|
|
17
18
|
import { Agent } from 'undici';
|
|
@@ -660,6 +661,21 @@ function resolveWin32Command(cmd) {
|
|
|
660
661
|
}
|
|
661
662
|
return cmd;
|
|
662
663
|
}
|
|
664
|
+
function resolvePowerShell(cmd) {
|
|
665
|
+
if (process.platform !== "win32") return cmd;
|
|
666
|
+
const lower = cmd.toLowerCase();
|
|
667
|
+
if (lower !== "pwsh" && lower !== "powershell" && lower !== "pwsh.exe" && lower !== "powershell.exe") {
|
|
668
|
+
return resolveWin32Command(cmd);
|
|
669
|
+
}
|
|
670
|
+
const primary = lower.startsWith("pwsh") ? "pwsh.exe" : "powershell.exe";
|
|
671
|
+
const fallback = lower.startsWith("pwsh") ? "powershell.exe" : "pwsh.exe";
|
|
672
|
+
const resolved = resolveWin32Command(primary);
|
|
673
|
+
if (resolved !== primary) {
|
|
674
|
+
const fb = resolveWin32Command(fallback);
|
|
675
|
+
return fb === fallback ? cmd : fb;
|
|
676
|
+
}
|
|
677
|
+
return resolved;
|
|
678
|
+
}
|
|
663
679
|
var WIN32_SHELL_META = /[&|<>\r\n\0]/;
|
|
664
680
|
function assertSafeWin32ShellArgs(args) {
|
|
665
681
|
for (const a of args) {
|
|
@@ -1659,6 +1675,110 @@ async function checkAndBlockKillCommand(command) {
|
|
|
1659
1675
|
}
|
|
1660
1676
|
return { blocked: false };
|
|
1661
1677
|
}
|
|
1678
|
+
function pickShell(platform3, command, env) {
|
|
1679
|
+
const override = env.get("WRONGSTACK_SHELL")?.trim().toLowerCase();
|
|
1680
|
+
if (override === "cmd" || override === "cmd.exe") return "cmd";
|
|
1681
|
+
if (override === "powershell" || override === "powershell.exe") return "powershell";
|
|
1682
|
+
if (override === "pwsh" || override === "pwsh.exe") return "pwsh";
|
|
1683
|
+
if (looksLikePowerShell(command)) return "pwsh";
|
|
1684
|
+
return "cmd";
|
|
1685
|
+
}
|
|
1686
|
+
function looksLikePowerShell(command) {
|
|
1687
|
+
if (!command) return false;
|
|
1688
|
+
const trimmed = command.trimStart();
|
|
1689
|
+
if (/\.ps1\b/i.test(trimmed)) return true;
|
|
1690
|
+
if (/^\s*#requires\s/i.test(trimmed)) return true;
|
|
1691
|
+
if (/^\s*param\s*\(/i.test(trimmed)) return true;
|
|
1692
|
+
if (/\$[\w:{]/i.test(trimmed)) return true;
|
|
1693
|
+
if (/\$\(/.test(trimmed)) return true;
|
|
1694
|
+
if (/@\s*['"]/.test(trimmed)) return true;
|
|
1695
|
+
if (/&\s+\$/.test(trimmed)) return true;
|
|
1696
|
+
if (/(^|\s)@\s*\(/.test(trimmed)) return true;
|
|
1697
|
+
if (/(^|\s)@\{/.test(trimmed)) return true;
|
|
1698
|
+
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)) {
|
|
1699
|
+
return true;
|
|
1700
|
+
}
|
|
1701
|
+
if (PS_VERB_RE.test(trimmed)) return true;
|
|
1702
|
+
if (/(?:^|[\s;&|])(gci|gi|gp|gcm|gps|sl|rm|cat|cp|mv)\b/i.test(trimmed)) {
|
|
1703
|
+
return true;
|
|
1704
|
+
}
|
|
1705
|
+
if (looksLikePowerShellExtended(command)) return true;
|
|
1706
|
+
return false;
|
|
1707
|
+
}
|
|
1708
|
+
function looksLikePowerShellExtended(command) {
|
|
1709
|
+
if (!command) return false;
|
|
1710
|
+
const trimmed = command.trimStart();
|
|
1711
|
+
if (/(?:^|\s)[-/](?:WhatIf|Confirm|ErrorAction)(?::[^\s]+|\s|=|$)/i.test(trimmed)) {
|
|
1712
|
+
return true;
|
|
1713
|
+
}
|
|
1714
|
+
if (/(?:^|[\s;&|])(Where-Object|ForEach-Object|Select-Object|Sort-Object|Group-Object|Measure-Object|Compare-Object|Tee-Object)(?:\s|$)/i.test(trimmed)) {
|
|
1715
|
+
return true;
|
|
1716
|
+
}
|
|
1717
|
+
if (/\bWrite-(?:Host|Output|Error|Warning|Verbose|Debug|Information)(?:\s|$)/i.test(trimmed)) {
|
|
1718
|
+
return true;
|
|
1719
|
+
}
|
|
1720
|
+
if (/HK(?:LM|CU|CR|U|CC|DD|PD):\\/i.test(trimmed)) return true;
|
|
1721
|
+
if (/\[(?:string|int|bool|xml|double|float|decimal|char|byte|long|System\.)/i.test(trimmed)) {
|
|
1722
|
+
return true;
|
|
1723
|
+
}
|
|
1724
|
+
if (/^\s*<#|#>\s*$/m.test(trimmed)) return true;
|
|
1725
|
+
if (/(?:^|\s)[-/\/](?:AsPlainText|PipelineVariable|pv|FilterHashtable|OutVariable|ov)(?:\s|=|$)/i.test(trimmed)) {
|
|
1726
|
+
return true;
|
|
1727
|
+
}
|
|
1728
|
+
return false;
|
|
1729
|
+
}
|
|
1730
|
+
function wrapPowerShellScript(command) {
|
|
1731
|
+
const bootstrap = "[Console]::OutputEncoding = [System.Text.Encoding]::UTF8;$ConfirmPreference='None';$WhatIfPreference=$false";
|
|
1732
|
+
return "\uFEFF" + bootstrap + "\ntry {\n" + command + "\n} finally { exit $LASTEXITCODE }";
|
|
1733
|
+
}
|
|
1734
|
+
var PS_VERB_RE = new RegExp(
|
|
1735
|
+
// Boundaries: start-of-string, whitespace, `;`, `&`, `|`, `(`, `{`, `,`.
|
|
1736
|
+
"(?:^|[\\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\\-\\;\\&\\|\\(\\)\\{\\},])",
|
|
1737
|
+
"i"
|
|
1738
|
+
);
|
|
1739
|
+
function shellArgs(shell) {
|
|
1740
|
+
if (shell === "powershell" || shell === "pwsh") {
|
|
1741
|
+
return ["-NoLogo", "-NoProfile", "-NonInteractive", "-Command", "-"];
|
|
1742
|
+
}
|
|
1743
|
+
return ["/c"];
|
|
1744
|
+
}
|
|
1745
|
+
function diagnoseBashism(command, shell) {
|
|
1746
|
+
if (!command) return void 0;
|
|
1747
|
+
const isCmd = shell === "cmd";
|
|
1748
|
+
const hints = [];
|
|
1749
|
+
const add = (h) => {
|
|
1750
|
+
if (!hints.includes(h)) hints.push(h);
|
|
1751
|
+
};
|
|
1752
|
+
if (/\/dev\/null/.test(command)) {
|
|
1753
|
+
add(
|
|
1754
|
+
isCmd ? "use `nul` instead of `/dev/null` (e.g. `2>nul`)" : "use `$null` instead of `/dev/null` (e.g. `2>$null`)"
|
|
1755
|
+
);
|
|
1756
|
+
}
|
|
1757
|
+
if (/(^|[;&|]\s*)export\s+[A-Za-z_]\w*=/.test(command)) {
|
|
1758
|
+
add(
|
|
1759
|
+
isCmd ? "set env vars with `set NAME=value`, not `export`" : "set env vars with `$env:NAME = 'value'`, not `export`"
|
|
1760
|
+
);
|
|
1761
|
+
}
|
|
1762
|
+
if (/<<-?\s*['"]?[A-Za-z_]\w*/.test(command)) {
|
|
1763
|
+
add(
|
|
1764
|
+
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)"
|
|
1765
|
+
);
|
|
1766
|
+
}
|
|
1767
|
+
if (shell === "powershell" && /(&&|\|\|)/.test(command)) {
|
|
1768
|
+
add("Windows PowerShell 5.1 has no `&&`/`||` \u2014 separate commands with `;` (check `$LASTEXITCODE`)");
|
|
1769
|
+
}
|
|
1770
|
+
if (/\brm\s+-[A-Za-z]*[rf]/.test(command)) {
|
|
1771
|
+
add(
|
|
1772
|
+
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"
|
|
1773
|
+
);
|
|
1774
|
+
}
|
|
1775
|
+
if (/\bwhich\s+\S/.test(command)) {
|
|
1776
|
+
add(isCmd ? "use `where <cmd>` instead of `which`" : "use `Get-Command <cmd>` instead of `which`");
|
|
1777
|
+
}
|
|
1778
|
+
if (hints.length === 0) return void 0;
|
|
1779
|
+
const label = isCmd ? "cmd.exe" : shell === "pwsh" ? "PowerShell 7" : "Windows PowerShell";
|
|
1780
|
+
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.`;
|
|
1781
|
+
}
|
|
1662
1782
|
|
|
1663
1783
|
// src/bash.ts
|
|
1664
1784
|
var MAX_OUTPUT = 32768;
|
|
@@ -1755,18 +1875,36 @@ var bashTool = {
|
|
|
1755
1875
|
}
|
|
1756
1876
|
const timeoutMs = Math.max(1, Math.min(input.timeout_ms ?? DEFAULT_TIMEOUT_MS, 6e5));
|
|
1757
1877
|
const isWin3 = os2.platform() === "win32";
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1878
|
+
let plan;
|
|
1879
|
+
let winShellKind;
|
|
1880
|
+
if (isWin3) {
|
|
1881
|
+
const shell2 = pickShell("win32", input.command, {
|
|
1882
|
+
get: (k) => process.env[k]
|
|
1883
|
+
});
|
|
1884
|
+
winShellKind = shell2;
|
|
1885
|
+
const bin = shell2 === "powershell" ? resolvePowerShell("powershell.exe") : shell2 === "pwsh" ? resolvePowerShell("pwsh.exe") : process.env["COMSPEC"] ?? "cmd.exe";
|
|
1886
|
+
plan = {
|
|
1887
|
+
bin,
|
|
1888
|
+
argv: shellArgs(shell2),
|
|
1889
|
+
useStdin: shell2 === "powershell" || shell2 === "pwsh",
|
|
1890
|
+
stdinBody: shell2 === "powershell" || shell2 === "pwsh" ? wrapPowerShellScript(input.command) : void 0
|
|
1891
|
+
};
|
|
1892
|
+
} else {
|
|
1893
|
+
const explicit = process.env["WRONGSTACK_SHELL"];
|
|
1894
|
+
let bin;
|
|
1895
|
+
if (explicit) bin = explicit;
|
|
1896
|
+
else {
|
|
1897
|
+
const fromEnv = process.env["SHELL"];
|
|
1898
|
+
if (fromEnv) {
|
|
1899
|
+
const name = fromEnv.split("/").pop() ?? "";
|
|
1900
|
+
if (["bash", "zsh", "sh", "dash", "fish"].includes(name)) bin = fromEnv;
|
|
1901
|
+
else bin = "/bin/bash";
|
|
1902
|
+
} else bin = "/bin/bash";
|
|
1903
|
+
}
|
|
1904
|
+
plan = { bin, argv: ["-c"], useStdin: false, stdinBody: void 0 };
|
|
1905
|
+
}
|
|
1906
|
+
const shell = plan.bin;
|
|
1907
|
+
const args = plan.useStdin ? [...plan.argv] : [...plan.argv, input.command];
|
|
1770
1908
|
const env = buildChildEnv(ctx.session?.id);
|
|
1771
1909
|
const detached = !isWin3;
|
|
1772
1910
|
const startedAt = Date.now();
|
|
@@ -1776,7 +1914,9 @@ var bashTool = {
|
|
|
1776
1914
|
const child2 = spawn(shell, args, {
|
|
1777
1915
|
cwd: ctx.projectRoot,
|
|
1778
1916
|
env,
|
|
1779
|
-
|
|
1917
|
+
// PowerShell takes the script on stdin (no argv quoting); cmd.exe
|
|
1918
|
+
// and POSIX shells ignore stdin when given the command inline.
|
|
1919
|
+
stdio: [plan.useStdin ? "pipe" : "ignore", "pipe", "pipe"],
|
|
1780
1920
|
// win32: CreateProcess IGNORES CREATE_NO_WINDOW (windowsHide) when
|
|
1781
1921
|
// DETACHED_PROCESS (detached: true) is set, so the console-less
|
|
1782
1922
|
// cmd.exe's grandchildren (node, dev servers) each allocate a fresh
|
|
@@ -1787,6 +1927,13 @@ var bashTool = {
|
|
|
1787
1927
|
detached: !isWin3,
|
|
1788
1928
|
windowsHide: true
|
|
1789
1929
|
});
|
|
1930
|
+
if (plan.useStdin) {
|
|
1931
|
+
try {
|
|
1932
|
+
child2.stdin?.write(plan.stdinBody ?? input.command);
|
|
1933
|
+
child2.stdin?.end();
|
|
1934
|
+
} catch {
|
|
1935
|
+
}
|
|
1936
|
+
}
|
|
1790
1937
|
const pid2 = child2.pid;
|
|
1791
1938
|
if (typeof pid2 === "number") {
|
|
1792
1939
|
registry.register({
|
|
@@ -1841,11 +1988,20 @@ var bashTool = {
|
|
|
1841
1988
|
const child = spawn(shell, args, {
|
|
1842
1989
|
cwd: ctx.projectRoot,
|
|
1843
1990
|
env,
|
|
1844
|
-
|
|
1991
|
+
// PowerShell takes the script on stdin (no argv quoting); cmd.exe
|
|
1992
|
+
// and POSIX shells ignore stdin when given the command inline.
|
|
1993
|
+
stdio: [plan.useStdin ? "pipe" : "ignore", "pipe", "pipe"],
|
|
1845
1994
|
detached,
|
|
1846
1995
|
windowsHide: true,
|
|
1847
1996
|
...isWin3 ? {} : { signal: opts.signal }
|
|
1848
1997
|
});
|
|
1998
|
+
if (plan.useStdin) {
|
|
1999
|
+
try {
|
|
2000
|
+
child.stdin?.write(plan.stdinBody ?? input.command);
|
|
2001
|
+
child.stdin?.end();
|
|
2002
|
+
} catch {
|
|
2003
|
+
}
|
|
2004
|
+
}
|
|
1849
2005
|
const pid = child.pid;
|
|
1850
2006
|
if (typeof pid === "number") {
|
|
1851
2007
|
registry.register({
|
|
@@ -1996,10 +2152,13 @@ var bashTool = {
|
|
|
1996
2152
|
yield { type: "partial_output", text: remainder };
|
|
1997
2153
|
}
|
|
1998
2154
|
const spooled = spool.finalize();
|
|
2155
|
+
const hint = !timedOut && typeof c.code === "number" && c.code !== 0 && winShellKind ? diagnoseBashism(input.command, winShellKind) : void 0;
|
|
1999
2156
|
yield {
|
|
2000
2157
|
type: "final",
|
|
2001
2158
|
output: {
|
|
2002
|
-
output: normalizeCommandOutput(buf) + (spooled ? spoolNote(spooled) : "")
|
|
2159
|
+
output: normalizeCommandOutput(buf) + (spooled ? spoolNote(spooled) : "") + (hint ? `
|
|
2160
|
+
|
|
2161
|
+
${hint}` : ""),
|
|
2003
2162
|
exit_code: c.code,
|
|
2004
2163
|
timed_out: timedOut
|
|
2005
2164
|
}
|
|
@@ -2368,7 +2527,7 @@ function loadDatabaseSync() {
|
|
|
2368
2527
|
DatabaseSyncCtor = req("node:sqlite").DatabaseSync;
|
|
2369
2528
|
} catch (err) {
|
|
2370
2529
|
throw new Error(
|
|
2371
|
-
`The codebase index needs Node's built-in SQLite (node:sqlite), available since Node 22.5. This runtime doesn't provide it: ${toErrorMessage$
|
|
2530
|
+
`The codebase index needs Node's built-in SQLite (node:sqlite), available since Node 22.5. This runtime doesn't provide it: ${toErrorMessage$3(err)}`
|
|
2372
2531
|
);
|
|
2373
2532
|
}
|
|
2374
2533
|
return DatabaseSyncCtor;
|
|
@@ -5385,49 +5544,71 @@ function findSimilarity(haystack, needle) {
|
|
|
5385
5544
|
return line;
|
|
5386
5545
|
}
|
|
5387
5546
|
var isWin2 = process.platform === "win32";
|
|
5388
|
-
var
|
|
5389
|
-
|
|
5390
|
-
|
|
5391
|
-
|
|
5392
|
-
|
|
5393
|
-
|
|
5394
|
-
|
|
5395
|
-
|
|
5396
|
-
|
|
5397
|
-
|
|
5398
|
-
|
|
5399
|
-
|
|
5400
|
-
|
|
5401
|
-
|
|
5402
|
-
|
|
5403
|
-
|
|
5404
|
-
|
|
5405
|
-
|
|
5406
|
-
|
|
5407
|
-
|
|
5408
|
-
|
|
5409
|
-
|
|
5410
|
-
|
|
5411
|
-
|
|
5412
|
-
|
|
5413
|
-
|
|
5414
|
-
|
|
5415
|
-
|
|
5416
|
-
|
|
5417
|
-
|
|
5418
|
-
|
|
5419
|
-
|
|
5420
|
-
|
|
5421
|
-
|
|
5422
|
-
|
|
5423
|
-
|
|
5424
|
-
|
|
5425
|
-
|
|
5426
|
-
|
|
5427
|
-
|
|
5428
|
-
|
|
5429
|
-
|
|
5430
|
-
|
|
5547
|
+
var DEFAULT_ALLOWED_COMMANDS = /* @__PURE__ */ new Set([
|
|
5548
|
+
// JS / TS toolchain
|
|
5549
|
+
"node",
|
|
5550
|
+
"npm",
|
|
5551
|
+
"pnpm",
|
|
5552
|
+
"yarn",
|
|
5553
|
+
"npx",
|
|
5554
|
+
"bun",
|
|
5555
|
+
"deno",
|
|
5556
|
+
"tsc",
|
|
5557
|
+
"vitest",
|
|
5558
|
+
"jest",
|
|
5559
|
+
"biome",
|
|
5560
|
+
"eslint",
|
|
5561
|
+
"prettier",
|
|
5562
|
+
// version control
|
|
5563
|
+
"git",
|
|
5564
|
+
// Rust
|
|
5565
|
+
"cargo",
|
|
5566
|
+
"rustc",
|
|
5567
|
+
// Go
|
|
5568
|
+
"go",
|
|
5569
|
+
// Python
|
|
5570
|
+
"python",
|
|
5571
|
+
"python3",
|
|
5572
|
+
"pip",
|
|
5573
|
+
"pip3",
|
|
5574
|
+
// Ruby
|
|
5575
|
+
"ruby",
|
|
5576
|
+
"gem",
|
|
5577
|
+
"bundle",
|
|
5578
|
+
// JVM
|
|
5579
|
+
"java",
|
|
5580
|
+
"javac",
|
|
5581
|
+
"mvn",
|
|
5582
|
+
"gradle",
|
|
5583
|
+
"gradlew",
|
|
5584
|
+
// .NET
|
|
5585
|
+
"dotnet",
|
|
5586
|
+
// C / C++ / native build
|
|
5587
|
+
"make",
|
|
5588
|
+
"cmake",
|
|
5589
|
+
// containers / orchestration (read-only subcommands; see BLOCKED_ARG_PATTERNS)
|
|
5590
|
+
"docker",
|
|
5591
|
+
"kubectl",
|
|
5592
|
+
// common POSIX file/text utilities
|
|
5593
|
+
"ls",
|
|
5594
|
+
"cat",
|
|
5595
|
+
"head",
|
|
5596
|
+
"tail",
|
|
5597
|
+
"wc",
|
|
5598
|
+
"grep",
|
|
5599
|
+
"find",
|
|
5600
|
+
"echo",
|
|
5601
|
+
"mkdir",
|
|
5602
|
+
"cp",
|
|
5603
|
+
"mv",
|
|
5604
|
+
"rm",
|
|
5605
|
+
"touch"
|
|
5606
|
+
]);
|
|
5607
|
+
var allowedCommands = new Set(DEFAULT_ALLOWED_COMMANDS);
|
|
5608
|
+
var normalizeCmd = (c) => c.trim();
|
|
5609
|
+
function isExecCommandAllowed(cmd) {
|
|
5610
|
+
return allowedCommands.has(normalizeCmd(cmd));
|
|
5611
|
+
}
|
|
5431
5612
|
var MAX_ARGS = 20;
|
|
5432
5613
|
var MAX_OUTPUT2 = 2e5;
|
|
5433
5614
|
var DEFAULT_TIMEOUT_MS2 = 3e4;
|
|
@@ -5489,7 +5670,7 @@ var execTool = {
|
|
|
5489
5670
|
name: "exec",
|
|
5490
5671
|
category: "Shell",
|
|
5491
5672
|
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.",
|
|
5492
|
-
usageHint: "PREFERRED SHELL TOOL for most cases.\n\nUse this instead of `bash` whenever possible.\n- `command` must be
|
|
5673
|
+
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.",
|
|
5493
5674
|
permission: "confirm",
|
|
5494
5675
|
mutating: true,
|
|
5495
5676
|
riskTier: "standard",
|
|
@@ -5543,12 +5724,12 @@ var execTool = {
|
|
|
5543
5724
|
truncated: false,
|
|
5544
5725
|
allowed: false
|
|
5545
5726
|
};
|
|
5546
|
-
if (!(cmd
|
|
5727
|
+
if (!isExecCommandAllowed(cmd)) {
|
|
5547
5728
|
return {
|
|
5548
5729
|
command: cmd,
|
|
5549
5730
|
args: input.args ?? [],
|
|
5550
5731
|
stdout: "",
|
|
5551
|
-
stderr: `Command "${cmd}" not in allowlist.
|
|
5732
|
+
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.`,
|
|
5552
5733
|
exitCode: 1,
|
|
5553
5734
|
truncated: false,
|
|
5554
5735
|
allowed: false
|
|
@@ -5591,19 +5772,58 @@ function runCommand(cmd, args, cwd, timeout, signal, sessionId) {
|
|
|
5591
5772
|
let stdout = "";
|
|
5592
5773
|
let stderr = "";
|
|
5593
5774
|
let killed = false;
|
|
5775
|
+
const resolvedOnce = { value: false };
|
|
5776
|
+
const finish = (result) => {
|
|
5777
|
+
if (resolvedOnce.value) return;
|
|
5778
|
+
resolvedOnce.value = true;
|
|
5779
|
+
resolve6(result);
|
|
5780
|
+
};
|
|
5594
5781
|
const startedAt = Date.now();
|
|
5595
5782
|
const spool = createOutputSpool({ tool: `exec-${cmd}`, thresholdBytes: MAX_OUTPUT2 });
|
|
5596
5783
|
const resolved = resolveWin32Command(cmd);
|
|
5597
5784
|
const needsShell = isWin2 && (resolved.endsWith(".cmd") || resolved.endsWith(".bat"));
|
|
5598
5785
|
const spawnCmd = needsShell ? cmd : resolved;
|
|
5599
5786
|
if (needsShell) assertSafeWin32ShellArgs(args);
|
|
5600
|
-
|
|
5601
|
-
|
|
5602
|
-
|
|
5603
|
-
|
|
5604
|
-
|
|
5605
|
-
|
|
5606
|
-
|
|
5787
|
+
let child;
|
|
5788
|
+
try {
|
|
5789
|
+
child = spawn(spawnCmd, args, {
|
|
5790
|
+
cwd,
|
|
5791
|
+
env: buildChildEnv(sessionId),
|
|
5792
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
5793
|
+
windowsHide: true,
|
|
5794
|
+
...isWin2 ? {} : { signal },
|
|
5795
|
+
...needsShell ? { shell: true, windowsVerbatimArguments: true } : {}
|
|
5796
|
+
});
|
|
5797
|
+
} catch (err) {
|
|
5798
|
+
spool.finalize();
|
|
5799
|
+
finish({
|
|
5800
|
+
command: cmd,
|
|
5801
|
+
args,
|
|
5802
|
+
stdout: "",
|
|
5803
|
+
stderr: `spawn failed: ${toErrorMessage$1(err)}`,
|
|
5804
|
+
exitCode: 1,
|
|
5805
|
+
truncated: false,
|
|
5806
|
+
allowed: true
|
|
5807
|
+
});
|
|
5808
|
+
return;
|
|
5809
|
+
}
|
|
5810
|
+
child.on("error", (err) => {
|
|
5811
|
+
const isAbort = err && err.code === "ABORT_ERR";
|
|
5812
|
+
const stderrText = isAbort ? `Aborted: ${err.message}` : err.message;
|
|
5813
|
+
clearTimeout(timer);
|
|
5814
|
+
if (isWin2) signal.removeEventListener("abort", onAbort);
|
|
5815
|
+
if (typeof pid === "number") registry.unregister(pid);
|
|
5816
|
+
registry.afterCall(Date.now() - startedAt, true);
|
|
5817
|
+
spool.finalize();
|
|
5818
|
+
finish({
|
|
5819
|
+
command: cmd,
|
|
5820
|
+
args,
|
|
5821
|
+
stdout: normalizeCommandOutput(stdout),
|
|
5822
|
+
stderr: stderrText,
|
|
5823
|
+
exitCode: isAbort ? 124 : 1,
|
|
5824
|
+
truncated: Buffer.byteLength(stdout, "utf8") > COMMAND_OUTPUT_MAX_BYTES,
|
|
5825
|
+
allowed: true
|
|
5826
|
+
});
|
|
5607
5827
|
});
|
|
5608
5828
|
const registry = getProcessRegistry();
|
|
5609
5829
|
const pid = child.pid;
|
|
@@ -5643,7 +5863,7 @@ function runCommand(cmd, args, cwd, timeout, signal, sessionId) {
|
|
|
5643
5863
|
const exitCode = killed ? 124 : code ?? 1;
|
|
5644
5864
|
registry.afterCall(durationMs, exitCode !== 0);
|
|
5645
5865
|
const spooled = spool.finalize();
|
|
5646
|
-
|
|
5866
|
+
finish({
|
|
5647
5867
|
command: cmd,
|
|
5648
5868
|
args,
|
|
5649
5869
|
stdout: normalizeCommandOutput(stdout) + (spooled ? spoolNote(spooled) : ""),
|
|
@@ -5653,22 +5873,6 @@ function runCommand(cmd, args, cwd, timeout, signal, sessionId) {
|
|
|
5653
5873
|
allowed: true
|
|
5654
5874
|
});
|
|
5655
5875
|
});
|
|
5656
|
-
child.on("error", (err) => {
|
|
5657
|
-
clearTimeout(timer);
|
|
5658
|
-
if (isWin2) signal.removeEventListener("abort", onAbort);
|
|
5659
|
-
if (typeof pid === "number") registry.unregister(pid);
|
|
5660
|
-
registry.afterCall(Date.now() - startedAt, true);
|
|
5661
|
-
spool.finalize();
|
|
5662
|
-
resolve6({
|
|
5663
|
-
command: cmd,
|
|
5664
|
-
args,
|
|
5665
|
-
stdout: normalizeCommandOutput(stdout),
|
|
5666
|
-
stderr: err.message,
|
|
5667
|
-
exitCode: 1,
|
|
5668
|
-
truncated: Buffer.byteLength(stdout, "utf8") > COMMAND_OUTPUT_MAX_BYTES,
|
|
5669
|
-
allowed: true
|
|
5670
|
-
});
|
|
5671
|
-
});
|
|
5672
5876
|
});
|
|
5673
5877
|
}
|
|
5674
5878
|
var TD = new TurndownService({
|
|
@@ -7868,7 +8072,7 @@ var readTool = {
|
|
|
7868
8072
|
} catch (err) {
|
|
7869
8073
|
const code = err.code;
|
|
7870
8074
|
if (code === "ENOENT") throw new Error(`read: file not found "${input.path}"`);
|
|
7871
|
-
throw new Error(`read: failed to stat "${input.path}": ${toErrorMessage$
|
|
8075
|
+
throw new Error(`read: failed to stat "${input.path}": ${toErrorMessage$2(err)}`);
|
|
7872
8076
|
}
|
|
7873
8077
|
if (!stat11.isFile()) throw new Error(`read: "${input.path}" is not a regular file`);
|
|
7874
8078
|
if (stat11.size > MAX_BYTES2) {
|
|
@@ -8464,7 +8668,7 @@ async function duckduckgoSearch(query2, num, signal) {
|
|
|
8464
8668
|
truncated: results.length >= num
|
|
8465
8669
|
};
|
|
8466
8670
|
} catch (err) {
|
|
8467
|
-
console.log(JSON.stringify({ level: "debug", event: "search_failed", query: query2, error: toErrorMessage$
|
|
8671
|
+
console.log(JSON.stringify({ level: "debug", event: "search_failed", query: query2, error: toErrorMessage$3(err) }));
|
|
8468
8672
|
return {
|
|
8469
8673
|
query: query2,
|
|
8470
8674
|
results: [{ title: "Search unavailable", url: "", snippet: "Could not reach DuckDuckGo" }],
|
|
@@ -8628,7 +8832,7 @@ var setWorkingDirTool = {
|
|
|
8628
8832
|
} catch (err) {
|
|
8629
8833
|
return {
|
|
8630
8834
|
current: ctx.workingDir,
|
|
8631
|
-
error: toErrorMessage$
|
|
8835
|
+
error: toErrorMessage$3(err)
|
|
8632
8836
|
};
|
|
8633
8837
|
}
|
|
8634
8838
|
try {
|