@wrongstack/tools 0.273.0 → 0.274.0
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 +461 -161
- package/dist/builtin.js.map +1 -1
- package/dist/codebase-index/index.js +24 -16
- package/dist/codebase-index/index.js.map +1 -1
- package/dist/codebase-index/worker.js +24 -16
- package/dist/codebase-index/worker.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/glob.js +61 -11
- package/dist/glob.js.map +1 -1
- package/dist/grep.js +92 -39
- package/dist/grep.js.map +1 -1
- package/dist/index.d.ts +111 -2
- package/dist/index.js +532 -191
- 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 +461 -161
- 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/bash.js
CHANGED
|
@@ -2,6 +2,7 @@ import { spawn } from 'node:child_process';
|
|
|
2
2
|
import * as os2 from 'node:os';
|
|
3
3
|
import * as Core from '@wrongstack/core';
|
|
4
4
|
import { buildChildEnv, expectDefined, wstackGlobalRoot } from '@wrongstack/core';
|
|
5
|
+
import * as fs2 from 'node:fs';
|
|
5
6
|
import { mkdirSync, createWriteStream } from 'node:fs';
|
|
6
7
|
import * as fs from 'node:fs/promises';
|
|
7
8
|
import * as path from 'node:path';
|
|
@@ -1274,6 +1275,145 @@ async function checkAndBlockKillCommand(command) {
|
|
|
1274
1275
|
}
|
|
1275
1276
|
return { blocked: false };
|
|
1276
1277
|
}
|
|
1278
|
+
function pickShell(platform3, command, env) {
|
|
1279
|
+
const override = env.get("WRONGSTACK_SHELL")?.trim().toLowerCase();
|
|
1280
|
+
if (override === "cmd" || override === "cmd.exe") return "cmd";
|
|
1281
|
+
if (override === "powershell" || override === "powershell.exe") return "powershell";
|
|
1282
|
+
if (override === "pwsh" || override === "pwsh.exe") return "pwsh";
|
|
1283
|
+
if (looksLikePowerShell(command)) return "pwsh";
|
|
1284
|
+
return "cmd";
|
|
1285
|
+
}
|
|
1286
|
+
function looksLikePowerShell(command) {
|
|
1287
|
+
if (!command) return false;
|
|
1288
|
+
const trimmed = command.trimStart();
|
|
1289
|
+
if (/\.ps1\b/i.test(trimmed)) return true;
|
|
1290
|
+
if (/^\s*#requires\s/i.test(trimmed)) return true;
|
|
1291
|
+
if (/^\s*param\s*\(/i.test(trimmed)) return true;
|
|
1292
|
+
if (/\$[\w:{]/i.test(trimmed)) return true;
|
|
1293
|
+
if (/\$\(/.test(trimmed)) return true;
|
|
1294
|
+
if (/@\s*['"]/.test(trimmed)) return true;
|
|
1295
|
+
if (/&\s+\$/.test(trimmed)) return true;
|
|
1296
|
+
if (/(^|\s)@\s*\(/.test(trimmed)) return true;
|
|
1297
|
+
if (/(^|\s)@\{/.test(trimmed)) return true;
|
|
1298
|
+
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)) {
|
|
1299
|
+
return true;
|
|
1300
|
+
}
|
|
1301
|
+
if (PS_VERB_RE.test(trimmed)) return true;
|
|
1302
|
+
if (/(?:^|[\s;&|])(gci|gi|gp|gcm|gps|sl|rm|cat|cp|mv)\b/i.test(trimmed)) {
|
|
1303
|
+
return true;
|
|
1304
|
+
}
|
|
1305
|
+
if (looksLikePowerShellExtended(command)) return true;
|
|
1306
|
+
return false;
|
|
1307
|
+
}
|
|
1308
|
+
function looksLikePowerShellExtended(command) {
|
|
1309
|
+
if (!command) return false;
|
|
1310
|
+
const trimmed = command.trimStart();
|
|
1311
|
+
if (/(?:^|\s)[-/](?:WhatIf|Confirm|ErrorAction)(?::[^\s]+|\s|=|$)/i.test(trimmed)) {
|
|
1312
|
+
return true;
|
|
1313
|
+
}
|
|
1314
|
+
if (/(?:^|[\s;&|])(Where-Object|ForEach-Object|Select-Object|Sort-Object|Group-Object|Measure-Object|Compare-Object|Tee-Object)(?:\s|$)/i.test(trimmed)) {
|
|
1315
|
+
return true;
|
|
1316
|
+
}
|
|
1317
|
+
if (/\bWrite-(?:Host|Output|Error|Warning|Verbose|Debug|Information)(?:\s|$)/i.test(trimmed)) {
|
|
1318
|
+
return true;
|
|
1319
|
+
}
|
|
1320
|
+
if (/HK(?:LM|CU|CR|U|CC|DD|PD):\\/i.test(trimmed)) return true;
|
|
1321
|
+
if (/\[(?:string|int|bool|xml|double|float|decimal|char|byte|long|System\.)/i.test(trimmed)) {
|
|
1322
|
+
return true;
|
|
1323
|
+
}
|
|
1324
|
+
if (/^\s*<#|#>\s*$/m.test(trimmed)) return true;
|
|
1325
|
+
if (/(?:^|\s)[-/\/](?:AsPlainText|PipelineVariable|pv|FilterHashtable|OutVariable|ov)(?:\s|=|$)/i.test(trimmed)) {
|
|
1326
|
+
return true;
|
|
1327
|
+
}
|
|
1328
|
+
return false;
|
|
1329
|
+
}
|
|
1330
|
+
function wrapPowerShellScript(command) {
|
|
1331
|
+
const bootstrap = "[Console]::OutputEncoding = [System.Text.Encoding]::UTF8;$ConfirmPreference='None';$WhatIfPreference=$false";
|
|
1332
|
+
return "\uFEFF" + bootstrap + "\ntry {\n" + command + "\n} finally { exit $LASTEXITCODE }";
|
|
1333
|
+
}
|
|
1334
|
+
var PS_VERB_RE = new RegExp(
|
|
1335
|
+
// Boundaries: start-of-string, whitespace, `;`, `&`, `|`, `(`, `{`, `,`.
|
|
1336
|
+
"(?:^|[\\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\\-\\;\\&\\|\\(\\)\\{\\},])",
|
|
1337
|
+
"i"
|
|
1338
|
+
);
|
|
1339
|
+
function shellArgs(shell) {
|
|
1340
|
+
if (shell === "powershell" || shell === "pwsh") {
|
|
1341
|
+
return ["-NoLogo", "-NoProfile", "-NonInteractive", "-Command", "-"];
|
|
1342
|
+
}
|
|
1343
|
+
return ["/c"];
|
|
1344
|
+
}
|
|
1345
|
+
function diagnoseBashism(command, shell) {
|
|
1346
|
+
if (!command) return void 0;
|
|
1347
|
+
const isCmd = shell === "cmd";
|
|
1348
|
+
const hints = [];
|
|
1349
|
+
const add = (h) => {
|
|
1350
|
+
if (!hints.includes(h)) hints.push(h);
|
|
1351
|
+
};
|
|
1352
|
+
if (/\/dev\/null/.test(command)) {
|
|
1353
|
+
add(
|
|
1354
|
+
isCmd ? "use `nul` instead of `/dev/null` (e.g. `2>nul`)" : "use `$null` instead of `/dev/null` (e.g. `2>$null`)"
|
|
1355
|
+
);
|
|
1356
|
+
}
|
|
1357
|
+
if (/(^|[;&|]\s*)export\s+[A-Za-z_]\w*=/.test(command)) {
|
|
1358
|
+
add(
|
|
1359
|
+
isCmd ? "set env vars with `set NAME=value`, not `export`" : "set env vars with `$env:NAME = 'value'`, not `export`"
|
|
1360
|
+
);
|
|
1361
|
+
}
|
|
1362
|
+
if (/<<-?\s*['"]?[A-Za-z_]\w*/.test(command)) {
|
|
1363
|
+
add(
|
|
1364
|
+
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)"
|
|
1365
|
+
);
|
|
1366
|
+
}
|
|
1367
|
+
if (shell === "powershell" && /(&&|\|\|)/.test(command)) {
|
|
1368
|
+
add("Windows PowerShell 5.1 has no `&&`/`||` \u2014 separate commands with `;` (check `$LASTEXITCODE`)");
|
|
1369
|
+
}
|
|
1370
|
+
if (/\brm\s+-[A-Za-z]*[rf]/.test(command)) {
|
|
1371
|
+
add(
|
|
1372
|
+
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"
|
|
1373
|
+
);
|
|
1374
|
+
}
|
|
1375
|
+
if (/\bwhich\s+\S/.test(command)) {
|
|
1376
|
+
add(isCmd ? "use `where <cmd>` instead of `which`" : "use `Get-Command <cmd>` instead of `which`");
|
|
1377
|
+
}
|
|
1378
|
+
if (hints.length === 0) return void 0;
|
|
1379
|
+
const label = isCmd ? "cmd.exe" : shell === "pwsh" ? "PowerShell 7" : "Windows PowerShell";
|
|
1380
|
+
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.`;
|
|
1381
|
+
}
|
|
1382
|
+
function resolveWin32Command(cmd) {
|
|
1383
|
+
if (process.platform !== "win32") return cmd;
|
|
1384
|
+
if (cmd.includes("/") || cmd.includes("\\") || path.extname(cmd.replace(/\//g, "\\"))) {
|
|
1385
|
+
return cmd;
|
|
1386
|
+
}
|
|
1387
|
+
const pathext = (process.env["PATHEXT"] ?? ".COM;.EXE;.BAT;.CMD;.VBS;.JS;.WS;.MSC").toLowerCase().split(";");
|
|
1388
|
+
const pathDirs = (process.env["PATH"] ?? "").split(path.delimiter);
|
|
1389
|
+
for (const dir of pathDirs) {
|
|
1390
|
+
const base = path.join(dir, cmd);
|
|
1391
|
+
for (const ext of pathext) {
|
|
1392
|
+
const full = `${base}${ext}`;
|
|
1393
|
+
try {
|
|
1394
|
+
fs2.accessSync(full, fs2.constants.X_OK);
|
|
1395
|
+
return full;
|
|
1396
|
+
} catch {
|
|
1397
|
+
}
|
|
1398
|
+
}
|
|
1399
|
+
}
|
|
1400
|
+
return cmd;
|
|
1401
|
+
}
|
|
1402
|
+
function resolvePowerShell(cmd) {
|
|
1403
|
+
if (process.platform !== "win32") return cmd;
|
|
1404
|
+
const lower = cmd.toLowerCase();
|
|
1405
|
+
if (lower !== "pwsh" && lower !== "powershell" && lower !== "pwsh.exe" && lower !== "powershell.exe") {
|
|
1406
|
+
return resolveWin32Command(cmd);
|
|
1407
|
+
}
|
|
1408
|
+
const primary = lower.startsWith("pwsh") ? "pwsh.exe" : "powershell.exe";
|
|
1409
|
+
const fallback = lower.startsWith("pwsh") ? "powershell.exe" : "pwsh.exe";
|
|
1410
|
+
const resolved = resolveWin32Command(primary);
|
|
1411
|
+
if (resolved !== primary) {
|
|
1412
|
+
const fb = resolveWin32Command(fallback);
|
|
1413
|
+
return fb === fallback ? cmd : fb;
|
|
1414
|
+
}
|
|
1415
|
+
return resolved;
|
|
1416
|
+
}
|
|
1277
1417
|
|
|
1278
1418
|
// src/bash.ts
|
|
1279
1419
|
var MAX_OUTPUT = 32768;
|
|
@@ -1370,18 +1510,36 @@ var bashTool = {
|
|
|
1370
1510
|
}
|
|
1371
1511
|
const timeoutMs = Math.max(1, Math.min(input.timeout_ms ?? DEFAULT_TIMEOUT_MS, 6e5));
|
|
1372
1512
|
const isWin = os2.platform() === "win32";
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1513
|
+
let plan;
|
|
1514
|
+
let winShellKind;
|
|
1515
|
+
if (isWin) {
|
|
1516
|
+
const shell2 = pickShell("win32", input.command, {
|
|
1517
|
+
get: (k) => process.env[k]
|
|
1518
|
+
});
|
|
1519
|
+
winShellKind = shell2;
|
|
1520
|
+
const bin = shell2 === "powershell" ? resolvePowerShell("powershell.exe") : shell2 === "pwsh" ? resolvePowerShell("pwsh.exe") : process.env["COMSPEC"] ?? "cmd.exe";
|
|
1521
|
+
plan = {
|
|
1522
|
+
bin,
|
|
1523
|
+
argv: shellArgs(shell2),
|
|
1524
|
+
useStdin: shell2 === "powershell" || shell2 === "pwsh",
|
|
1525
|
+
stdinBody: shell2 === "powershell" || shell2 === "pwsh" ? wrapPowerShellScript(input.command) : void 0
|
|
1526
|
+
};
|
|
1527
|
+
} else {
|
|
1528
|
+
const explicit = process.env["WRONGSTACK_SHELL"];
|
|
1529
|
+
let bin;
|
|
1530
|
+
if (explicit) bin = explicit;
|
|
1531
|
+
else {
|
|
1532
|
+
const fromEnv = process.env["SHELL"];
|
|
1533
|
+
if (fromEnv) {
|
|
1534
|
+
const name = fromEnv.split("/").pop() ?? "";
|
|
1535
|
+
if (["bash", "zsh", "sh", "dash", "fish"].includes(name)) bin = fromEnv;
|
|
1536
|
+
else bin = "/bin/bash";
|
|
1537
|
+
} else bin = "/bin/bash";
|
|
1381
1538
|
}
|
|
1382
|
-
|
|
1383
|
-
}
|
|
1384
|
-
const
|
|
1539
|
+
plan = { bin, argv: ["-c"], useStdin: false, stdinBody: void 0 };
|
|
1540
|
+
}
|
|
1541
|
+
const shell = plan.bin;
|
|
1542
|
+
const args = plan.useStdin ? [...plan.argv] : [...plan.argv, input.command];
|
|
1385
1543
|
const env = buildChildEnv(ctx.session?.id);
|
|
1386
1544
|
const detached = !isWin;
|
|
1387
1545
|
const startedAt = Date.now();
|
|
@@ -1391,7 +1549,9 @@ var bashTool = {
|
|
|
1391
1549
|
const child2 = spawn(shell, args, {
|
|
1392
1550
|
cwd: ctx.projectRoot,
|
|
1393
1551
|
env,
|
|
1394
|
-
|
|
1552
|
+
// PowerShell takes the script on stdin (no argv quoting); cmd.exe
|
|
1553
|
+
// and POSIX shells ignore stdin when given the command inline.
|
|
1554
|
+
stdio: [plan.useStdin ? "pipe" : "ignore", "pipe", "pipe"],
|
|
1395
1555
|
// win32: CreateProcess IGNORES CREATE_NO_WINDOW (windowsHide) when
|
|
1396
1556
|
// DETACHED_PROCESS (detached: true) is set, so the console-less
|
|
1397
1557
|
// cmd.exe's grandchildren (node, dev servers) each allocate a fresh
|
|
@@ -1402,6 +1562,13 @@ var bashTool = {
|
|
|
1402
1562
|
detached: !isWin,
|
|
1403
1563
|
windowsHide: true
|
|
1404
1564
|
});
|
|
1565
|
+
if (plan.useStdin) {
|
|
1566
|
+
try {
|
|
1567
|
+
child2.stdin?.write(plan.stdinBody ?? input.command);
|
|
1568
|
+
child2.stdin?.end();
|
|
1569
|
+
} catch {
|
|
1570
|
+
}
|
|
1571
|
+
}
|
|
1405
1572
|
const pid2 = child2.pid;
|
|
1406
1573
|
if (typeof pid2 === "number") {
|
|
1407
1574
|
registry.register({
|
|
@@ -1456,11 +1623,20 @@ var bashTool = {
|
|
|
1456
1623
|
const child = spawn(shell, args, {
|
|
1457
1624
|
cwd: ctx.projectRoot,
|
|
1458
1625
|
env,
|
|
1459
|
-
|
|
1626
|
+
// PowerShell takes the script on stdin (no argv quoting); cmd.exe
|
|
1627
|
+
// and POSIX shells ignore stdin when given the command inline.
|
|
1628
|
+
stdio: [plan.useStdin ? "pipe" : "ignore", "pipe", "pipe"],
|
|
1460
1629
|
detached,
|
|
1461
1630
|
windowsHide: true,
|
|
1462
1631
|
...isWin ? {} : { signal: opts.signal }
|
|
1463
1632
|
});
|
|
1633
|
+
if (plan.useStdin) {
|
|
1634
|
+
try {
|
|
1635
|
+
child.stdin?.write(plan.stdinBody ?? input.command);
|
|
1636
|
+
child.stdin?.end();
|
|
1637
|
+
} catch {
|
|
1638
|
+
}
|
|
1639
|
+
}
|
|
1464
1640
|
const pid = child.pid;
|
|
1465
1641
|
if (typeof pid === "number") {
|
|
1466
1642
|
registry.register({
|
|
@@ -1611,10 +1787,13 @@ var bashTool = {
|
|
|
1611
1787
|
yield { type: "partial_output", text: remainder };
|
|
1612
1788
|
}
|
|
1613
1789
|
const spooled = spool.finalize();
|
|
1790
|
+
const hint = !timedOut && typeof c.code === "number" && c.code !== 0 && winShellKind ? diagnoseBashism(input.command, winShellKind) : void 0;
|
|
1614
1791
|
yield {
|
|
1615
1792
|
type: "final",
|
|
1616
1793
|
output: {
|
|
1617
|
-
output: normalizeCommandOutput(buf) + (spooled ? spoolNote(spooled) : "")
|
|
1794
|
+
output: normalizeCommandOutput(buf) + (spooled ? spoolNote(spooled) : "") + (hint ? `
|
|
1795
|
+
|
|
1796
|
+
${hint}` : ""),
|
|
1618
1797
|
exit_code: c.code,
|
|
1619
1798
|
timed_out: timedOut
|
|
1620
1799
|
}
|