@warlock.js/ai-workspace 5.44.0 → 5.46.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/CHANGELOG.md +13 -0
- package/cjs/index.cjs +101 -35
- package/cjs/index.cjs.map +1 -1
- package/esm/contracts/shell-approval.type.d.mts +11 -1
- package/esm/contracts/shell-approval.type.d.mts.map +1 -1
- package/esm/contracts/tool-io.type.d.mts +10 -0
- package/esm/contracts/tool-io.type.d.mts.map +1 -1
- package/esm/contracts/workspace.contract.d.mts +12 -1
- package/esm/contracts/workspace.contract.d.mts.map +1 -1
- package/esm/index.d.mts +2 -2
- package/esm/ops.d.mts.map +1 -1
- package/esm/ops.mjs +27 -10
- package/esm/ops.mjs.map +1 -1
- package/esm/output/spill.mjs +4 -1
- package/esm/output/spill.mjs.map +1 -1
- package/esm/policy/approval-helpers.mjs +22 -0
- package/esm/policy/approval-helpers.mjs.map +1 -0
- package/esm/policy/powershell-parser.mjs +6 -4
- package/esm/policy/powershell-parser.mjs.map +1 -1
- package/esm/policy/shell-check.d.mts.map +1 -1
- package/esm/policy/shell-check.mjs +4 -4
- package/esm/policy/shell-check.mjs.map +1 -1
- package/esm/policy/shell-interpreter.d.mts +20 -2
- package/esm/policy/shell-interpreter.d.mts.map +1 -1
- package/esm/policy/shell-interpreter.mjs +26 -12
- package/esm/policy/shell-interpreter.mjs.map +1 -1
- package/esm/policy/shell-parser.d.mts +3 -1
- package/esm/policy/shell-parser.d.mts.map +1 -1
- package/esm/policy/shell-parser.mjs +1 -1
- package/esm/policy/shell-parser.mjs.map +1 -1
- package/esm/tools/run-shell.d.mts +1 -1
- package/esm/tools/run-shell.d.mts.map +1 -1
- package/esm/tools/run-shell.mjs +2 -1
- package/esm/tools/run-shell.mjs.map +1 -1
- package/esm/tools/run-tests.d.mts +1 -1
- package/esm/tools/run-tests.mjs +1 -1
- package/esm/tools/run-tests.mjs.map +1 -1
- package/esm/workspace.d.mts.map +1 -1
- package/esm/workspace.mjs +12 -1
- package/esm/workspace.mjs.map +1 -1
- package/llms-full.txt +23 -17
- package/package.json +3 -3
- package/skills/use-a-workspace/SKILL.md +23 -17
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,19 @@ All notable changes to `@warlock.js/ai-workspace` are documented in this file.
|
|
|
4
4
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). `@warlock.js/*` packages are released in lockstep — every package shares the same version number, so a version below may list only the changes that affected this package.
|
|
6
6
|
|
|
7
|
+
## 5.45.0 - 2026-11-05
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- `resolveShell()` reports which PowerShell it found: `ResolvedShell` gains `edition` (`"desktop"` for Windows PowerShell 5.1, `"core"` for PowerShell 7+; exported type `PowerShellEdition`) and `version` (`"5.1"` for Windows PowerShell; for `pwsh` when `ShellHost.powershellVersion` is supplied). Windows PowerShell 5.1 gets the label `Windows PowerShell 5.1 (powershell)` and its own `"powershell5"` dialect, whose parser refuses `&&` and `||` with "Windows PowerShell 5.1 cannot run &&; use ; or run each command in its own call instead.". `run_shell`'s description for that dialect no longer promises chaining.
|
|
12
|
+
- `onAsk` decisions: `remember` accepts one pattern or an array, so a line that asks about several commands can approve them all. `workspace.shell.remembered()` lists the remembered patterns and `workspace.shell.forget(pattern)` removes one. A configured deny still beats them.
|
|
13
|
+
- `onAsk` decisions: `reason?: string` is appended to the `run_shell` refusal the model reads, cut at 500 characters with a `[truncated]` marker.
|
|
14
|
+
- A flat `spilledTo?: string[]` on every `run_shell`, `shell_output`, `read_file`, `grep` and `glob` result that spilled output to disk, and a spilled head-and-tail text now ends with a `[full output saved to …]` line. The `use-a-workspace` skill documents each tool's result shape.
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
|
|
18
|
+
- On Windows, `interpreter: "auto"` now picks `pwsh` (PowerShell 7+), then Git Bash, then Windows PowerShell 5.1, then `cmd.exe`, so a machine with Git for Windows and only PowerShell 5.1 runs `&&` lines under bash instead of failing in PowerShell.
|
|
19
|
+
|
|
7
20
|
## 5.44.0 - 2026-11-05
|
|
8
21
|
|
|
9
22
|
### Added
|
package/cjs/index.cjs
CHANGED
|
@@ -484,8 +484,8 @@ function currentShellHost() {
|
|
|
484
484
|
};
|
|
485
485
|
}
|
|
486
486
|
/** The grammar a concrete interpreter reads on `platform`. */
|
|
487
|
-
function dialectOf(interpreter, platform = process.platform) {
|
|
488
|
-
if (interpreter === "powershell") return "powershell";
|
|
487
|
+
function dialectOf(interpreter, platform = process.platform, edition) {
|
|
488
|
+
if (interpreter === "powershell") return edition === "desktop" ? "powershell5" : "powershell";
|
|
489
489
|
if (interpreter === "cmd") return "cmd";
|
|
490
490
|
return interpreter === "sh" && platform === "win32" ? "cmd" : "posix";
|
|
491
491
|
}
|
|
@@ -512,15 +512,23 @@ function findGitBash(host) {
|
|
|
512
512
|
function powershellNames(host) {
|
|
513
513
|
return host.platform === "win32" ? ["pwsh", "powershell"] : ["pwsh"];
|
|
514
514
|
}
|
|
515
|
-
|
|
516
|
-
|
|
515
|
+
/** The edition of the PowerShell executable `file`: `powershell.exe` is Windows PowerShell, anything else (`pwsh`) is PowerShell 7+. */
|
|
516
|
+
function editionOf(file) {
|
|
517
|
+
return /(^|[\\/])powershell(\.exe)?$/i.test(file) ? "desktop" : "core";
|
|
518
|
+
}
|
|
519
|
+
function powershellLabel(edition) {
|
|
520
|
+
if (edition === "desktop") return "Windows PowerShell 5.1 (powershell)";
|
|
521
|
+
return edition === "core" ? "PowerShell 7 (pwsh)" : "PowerShell";
|
|
517
522
|
}
|
|
518
523
|
/**
|
|
519
524
|
* Resolve a shell policy's `interpreter` (and `interpreterPath`) to the
|
|
520
525
|
* concrete shell that will run lines, and the grammar they are checked with.
|
|
521
526
|
*
|
|
522
|
-
* `"auto"` picks `pwsh
|
|
523
|
-
* `git`), then
|
|
527
|
+
* `"auto"` picks, on Windows, `pwsh` (PowerShell 7+), then Git Bash (found next
|
|
528
|
+
* to `git`), then Windows PowerShell 5.1, then `cmd.exe`; elsewhere `bash`,
|
|
529
|
+
* then `sh`. Windows PowerShell 5.1 gets its own dialect (`"powershell5"`)
|
|
530
|
+
* because it cannot run `&&` / `||`; `edition` and `version` say which one
|
|
531
|
+
* was found. An explicit interpreter whose executable
|
|
524
532
|
* is not on `PATH` keeps its name and has no `file`, so the spawn reports the
|
|
525
533
|
* missing shell instead of quietly running another one.
|
|
526
534
|
*
|
|
@@ -540,10 +548,12 @@ function resolveShell(shell, host = currentShellHost()) {
|
|
|
540
548
|
if (requested === "auto") {
|
|
541
549
|
if (explicit !== void 0) interpreter = /(^|[\\/])(pwsh|powershell)(\.exe)?$/i.test(explicit) ? "powershell" : /(^|[\\/])cmd(\.exe)?$/i.test(explicit) ? "cmd" : /(^|[\\/])bash(\.exe)?$/i.test(explicit) ? "bash" : "sh";
|
|
542
550
|
else if (host.platform === "win32") {
|
|
543
|
-
const
|
|
544
|
-
const gitBash =
|
|
545
|
-
|
|
546
|
-
|
|
551
|
+
const core = findOnPath(["pwsh"], host);
|
|
552
|
+
const gitBash = core === void 0 ? findGitBash(host) : void 0;
|
|
553
|
+
const desktop = core === void 0 && gitBash === void 0 ? findOnPath(["powershell"], host) : void 0;
|
|
554
|
+
const found = core ?? gitBash ?? desktop;
|
|
555
|
+
interpreter = core !== void 0 || desktop !== void 0 ? "powershell" : gitBash !== void 0 ? "bash" : "cmd";
|
|
556
|
+
file = found;
|
|
547
557
|
} else {
|
|
548
558
|
const bash = findOnPath(["bash"], host);
|
|
549
559
|
interpreter = bash !== void 0 ? "bash" : "sh";
|
|
@@ -555,16 +565,20 @@ function resolveShell(shell, host = currentShellHost()) {
|
|
|
555
565
|
else if (interpreter === "bash") file = findOnPath(["bash"], host) ?? (host.platform === "win32" ? findGitBash(host) : void 0);
|
|
556
566
|
else if (interpreter === "cmd" && host.platform === "win32") file = process.env.ComSpec ?? "cmd.exe";
|
|
557
567
|
}
|
|
568
|
+
const edition = interpreter === "powershell" && file !== void 0 ? editionOf(file) : void 0;
|
|
569
|
+
const version = edition === "desktop" ? "5.1" : edition === "core" && file !== void 0 ? host.powershellVersion?.(file) : void 0;
|
|
558
570
|
const labels = {
|
|
559
571
|
none: "none",
|
|
560
572
|
sh: host.platform === "win32" ? "cmd.exe" : "sh",
|
|
561
573
|
bash: "bash",
|
|
562
574
|
cmd: "cmd.exe",
|
|
563
|
-
powershell: powershellLabel(
|
|
575
|
+
powershell: powershellLabel(edition)
|
|
564
576
|
};
|
|
565
577
|
return {
|
|
566
578
|
interpreter,
|
|
567
|
-
dialect: dialectOf(interpreter, host.platform),
|
|
579
|
+
dialect: dialectOf(interpreter, host.platform, edition),
|
|
580
|
+
...edition !== void 0 ? { edition } : {},
|
|
581
|
+
...version !== void 0 ? { version } : {},
|
|
568
582
|
...file !== void 0 ? { file } : {},
|
|
569
583
|
label: labels[interpreter]
|
|
570
584
|
};
|
|
@@ -644,10 +658,11 @@ function unsupported$1(message) {
|
|
|
644
658
|
return new PowerShellRefusal("unsupported-syntax", message);
|
|
645
659
|
}
|
|
646
660
|
var PowerShellParser = class PowerShellParser {
|
|
647
|
-
constructor(src, commands = [], depth = 0) {
|
|
661
|
+
constructor(src, commands = [], depth = 0, chaining = true) {
|
|
648
662
|
this.src = src;
|
|
649
663
|
this.commands = commands;
|
|
650
664
|
this.depth = depth;
|
|
665
|
+
this.chaining = chaining;
|
|
651
666
|
this.pos = 0;
|
|
652
667
|
}
|
|
653
668
|
atEnd() {
|
|
@@ -689,6 +704,7 @@ var PowerShellParser = class PowerShellParser {
|
|
|
689
704
|
}
|
|
690
705
|
readListOperator() {
|
|
691
706
|
const char = this.peek();
|
|
707
|
+
if (!this.chaining && (char === "&" && this.peek(1) === "&" || char === "|" && this.peek(1) === "|")) throw unsupported$1(`Windows PowerShell 5.1 cannot run ${char}${char}; use ; or run each command in its own call instead.`);
|
|
692
708
|
if (char === "&" && this.peek(1) === "&") {
|
|
693
709
|
this.pos += 2;
|
|
694
710
|
return "&&";
|
|
@@ -943,7 +959,7 @@ var PowerShellParser = class PowerShellParser {
|
|
|
943
959
|
}
|
|
944
960
|
if (level !== 0) throw new PowerShellRefusal("unbalanced-quotes", "A `$(` subexpression is never closed.");
|
|
945
961
|
const inner = this.src.slice(open + 1, index);
|
|
946
|
-
new PowerShellParser(inner, this.commands, this.depth + 1).parseProgram([]);
|
|
962
|
+
new PowerShellParser(inner, this.commands, this.depth + 1, this.chaining).parseProgram([]);
|
|
947
963
|
this.pos = index + 1;
|
|
948
964
|
}
|
|
949
965
|
/** Read `$name`, `${name}` or `$env:NAME` at the cursor; a `$(` is refused. */
|
|
@@ -965,9 +981,9 @@ var PowerShellParser = class PowerShellParser {
|
|
|
965
981
|
}
|
|
966
982
|
};
|
|
967
983
|
/** Parse a PowerShell command line for the shell policy. Never throws. */
|
|
968
|
-
function parsePowerShellLine(line) {
|
|
984
|
+
function parsePowerShellLine(line, options) {
|
|
969
985
|
const segments = [];
|
|
970
|
-
const parser = new PowerShellParser(line);
|
|
986
|
+
const parser = new PowerShellParser(line, [], 0, options?.chaining !== false);
|
|
971
987
|
try {
|
|
972
988
|
parser.parseProgram(segments);
|
|
973
989
|
return {
|
|
@@ -1753,7 +1769,7 @@ var CmdParser = class {
|
|
|
1753
1769
|
*/
|
|
1754
1770
|
function parseShellLine(line, dialect) {
|
|
1755
1771
|
const segments = [];
|
|
1756
|
-
if (dialect === "powershell") return parsePowerShellLine(line);
|
|
1772
|
+
if (dialect === "powershell" || dialect === "powershell5") return parsePowerShellLine(line, { chaining: dialect === "powershell" });
|
|
1757
1773
|
try {
|
|
1758
1774
|
if (dialect === "cmd") {
|
|
1759
1775
|
const commands = [];
|
|
@@ -2089,7 +2105,7 @@ function shellDialect() {
|
|
|
2089
2105
|
* stripped). The cmd dialect splits Windows paths too.
|
|
2090
2106
|
*/
|
|
2091
2107
|
function executableBasename(firstToken, dialect = "posix") {
|
|
2092
|
-
return (dialect === "cmd" || dialect === "powershell" ? node_path.default.win32.basename(firstToken) : node_path.default.basename(firstToken)).replace(dialect === "powershell" ? /\.(exe|cmd|bat|com|ps1)$/i : /\.(exe|cmd|bat|com)$/i, "");
|
|
2108
|
+
return (dialect === "cmd" || dialect === "powershell" || dialect === "powershell5" ? node_path.default.win32.basename(firstToken) : node_path.default.basename(firstToken)).replace(dialect === "powershell" || dialect === "powershell5" ? /\.(exe|cmd|bat|com|ps1)$/i : /\.(exe|cmd|bat|com)$/i, "");
|
|
2093
2109
|
}
|
|
2094
2110
|
/** Show a command in a refusal, abbreviated at a word boundary when long. */
|
|
2095
2111
|
function show(source) {
|
|
@@ -2254,7 +2270,7 @@ var LineChecker = class {
|
|
|
2254
2270
|
this.commands.push([...argv]);
|
|
2255
2271
|
const remembered = this.isRemembered(argv);
|
|
2256
2272
|
const approved = rule?.effect === "allow" || remembered || this.explicitlyAllowed(executable);
|
|
2257
|
-
if (OPAQUE_RUNNERS.has(key) || this.shellMode && this.dialect === "posix" && SHELL_OPAQUE_BUILTINS.has(key) || this.shellMode && this.dialect === "cmd" && CMD_OPAQUE_BUILTINS.has(key) || this.shellMode && this.dialect === "powershell" && POWERSHELL_OPAQUE.has(key)) {
|
|
2273
|
+
if (OPAQUE_RUNNERS.has(key) || this.shellMode && this.dialect === "posix" && SHELL_OPAQUE_BUILTINS.has(key) || this.shellMode && this.dialect === "cmd" && CMD_OPAQUE_BUILTINS.has(key) || this.shellMode && (this.dialect === "powershell" || this.dialect === "powershell5") && POWERSHELL_OPAQUE.has(key)) {
|
|
2258
2274
|
if (approved) return;
|
|
2259
2275
|
if (this.askMode || rule?.effect === "ask") {
|
|
2260
2276
|
this.addAsk(argv, "nested", rule);
|
|
@@ -2342,7 +2358,7 @@ var LineChecker = class {
|
|
|
2342
2358
|
}
|
|
2343
2359
|
/** Check a redirection target from every possible cwd. */
|
|
2344
2360
|
checkRedirectTarget(word, access, cwds) {
|
|
2345
|
-
if (this.dialect === "powershell" && !word.quoted && word.value.toLowerCase() === "$null") return;
|
|
2361
|
+
if ((this.dialect === "powershell" || this.dialect === "powershell5") && !word.quoted && word.value.toLowerCase() === "$null") return;
|
|
2346
2362
|
if (word.dynamic) throw new Refusal("unsupported-syntax", `The redirection target \`${word.raw}\` must be a literal path (no variables, globs or \`~\`).`);
|
|
2347
2363
|
const value = word.value;
|
|
2348
2364
|
if (this.dialect === "posix" && DEVICE_TARGETS.has(value) || this.dialect === "cmd" && CMD_DEVICE_TARGETS.has(value.toLowerCase())) return;
|
|
@@ -2560,7 +2576,7 @@ function checkSimpleCommand(checker, simple, cwds, dialect) {
|
|
|
2560
2576
|
const first = simple.words[0];
|
|
2561
2577
|
if (first === void 0) return cwds;
|
|
2562
2578
|
const executable = first.dynamic ? "" : executableBasename(first.value, dialect).toLowerCase();
|
|
2563
|
-
const powershell = dialect === "powershell";
|
|
2579
|
+
const powershell = dialect === "powershell" || dialect === "powershell5";
|
|
2564
2580
|
if (executable === "cd" || powershell && POWERSHELL_CD_NAMES.has(executable)) {
|
|
2565
2581
|
checker.assertExecutableNotDenied(executable, simple.words.map((word) => word.value));
|
|
2566
2582
|
if (powershell) {
|
|
@@ -3812,7 +3828,10 @@ var OutputSpiller = class {
|
|
|
3812
3828
|
const where = file === void 0 ? "the rest was dropped" : `full output saved to ${file}`;
|
|
3813
3829
|
const marker = `[showing ${tail.length === 0 ? `lines 1-${head.length}` : head.length === 0 ? `lines ${total - tail.length + 1}-${total}` : `lines 1-${head.length} and ${total - tail.length + 1}-${total}`} of ${total}; ${where}]`;
|
|
3814
3830
|
const parts = [...head.length > 0 ? [head.join("\n")] : [], marker];
|
|
3815
|
-
if (tail.length > 0)
|
|
3831
|
+
if (tail.length > 0) {
|
|
3832
|
+
parts.push(tail.join("\n"));
|
|
3833
|
+
if (file !== void 0) parts.push(`[full output saved to ${file}]`);
|
|
3834
|
+
}
|
|
3816
3835
|
return {
|
|
3817
3836
|
value: parts.join("\n"),
|
|
3818
3837
|
truncation: {
|
|
@@ -3866,6 +3885,26 @@ var OutputSpiller = class {
|
|
|
3866
3885
|
}
|
|
3867
3886
|
};
|
|
3868
3887
|
|
|
3888
|
+
//#endregion
|
|
3889
|
+
//#region ../../packages/ai-workspace/src/policy/approval-helpers.ts
|
|
3890
|
+
/** The longest refusal reason passed on to the model, in characters. */
|
|
3891
|
+
const MAX_REFUSAL_REASON = 500;
|
|
3892
|
+
/** Whether two command patterns are the same (strings by value, RegExps by source and flags). */
|
|
3893
|
+
function samePattern(a, b) {
|
|
3894
|
+
if (typeof a === "string" || typeof b === "string") return a === b;
|
|
3895
|
+
return a.source === b.source && a.flags === b.flags;
|
|
3896
|
+
}
|
|
3897
|
+
/**
|
|
3898
|
+
* The tail of a refusal message for an owner's reason: `"."` without one,
|
|
3899
|
+
* otherwise `" — <reason>"` cut at {@link MAX_REFUSAL_REASON} characters with
|
|
3900
|
+
* a visible marker.
|
|
3901
|
+
*/
|
|
3902
|
+
function refusalReason(reason) {
|
|
3903
|
+
const text = typeof reason === "string" ? reason.trim() : "";
|
|
3904
|
+
if (text.length === 0) return ".";
|
|
3905
|
+
return ` — ${text.length > 500 ? `${text.slice(0, 500)}… [truncated]` : text}`;
|
|
3906
|
+
}
|
|
3907
|
+
|
|
3869
3908
|
//#endregion
|
|
3870
3909
|
//#region ../../packages/ai-workspace/src/shell-jobs.ts
|
|
3871
3910
|
/** Finished commands kept for a last `shell_output` read before the oldest are pruned. */
|
|
@@ -4338,6 +4377,7 @@ var Ops = class {
|
|
|
4338
4377
|
return {
|
|
4339
4378
|
content: spilled?.content ?? content,
|
|
4340
4379
|
...spilled !== void 0 ? { truncation: spilled.truncation } : {},
|
|
4380
|
+
...spilled?.truncation.path !== void 0 ? { spilledTo: [spilled.truncation.path] } : {},
|
|
4341
4381
|
startLine: offset,
|
|
4342
4382
|
endLine,
|
|
4343
4383
|
totalLines,
|
|
@@ -4504,12 +4544,18 @@ var Ops = class {
|
|
|
4504
4544
|
} catch {
|
|
4505
4545
|
decision = void 0;
|
|
4506
4546
|
}
|
|
4507
|
-
if (decision?.allow !== true)
|
|
4508
|
-
|
|
4509
|
-
|
|
4510
|
-
|
|
4511
|
-
|
|
4512
|
-
|
|
4547
|
+
if (decision?.allow !== true) {
|
|
4548
|
+
const reason = refusalReason(decision?.reason);
|
|
4549
|
+
throw new WorkspacePolicyError(`Running ${asks.map((ask) => `\`${ask.argv.join(" ")}\``).join(", ")} was not approved${reason}`, {
|
|
4550
|
+
type: "denied-command",
|
|
4551
|
+
reason: "approval-denied",
|
|
4552
|
+
command
|
|
4553
|
+
});
|
|
4554
|
+
}
|
|
4555
|
+
if (decision.remember !== void 0) {
|
|
4556
|
+
const patterns = Array.isArray(decision.remember) ? decision.remember : [decision.remember];
|
|
4557
|
+
for (const pattern of patterns) if (!this.approvals.some((entry) => samePattern(entry, pattern))) this.approvals.push(pattern);
|
|
4558
|
+
}
|
|
4513
4559
|
}
|
|
4514
4560
|
async exec(command, opts) {
|
|
4515
4561
|
await this.assertCommandAllowed(command, opts?.signal);
|
|
@@ -4545,7 +4591,8 @@ var Ops = class {
|
|
|
4545
4591
|
truncated: stdout.truncated || stderr.truncated,
|
|
4546
4592
|
timedOut: result.timedOut && !cancelled,
|
|
4547
4593
|
cancelled,
|
|
4548
|
-
...truncation !== void 0 ? { truncation } : {}
|
|
4594
|
+
...truncation !== void 0 ? { truncation } : {},
|
|
4595
|
+
...spilledField(truncation)
|
|
4549
4596
|
};
|
|
4550
4597
|
}
|
|
4551
4598
|
async startShell(command, opts) {
|
|
@@ -4592,7 +4639,8 @@ var Ops = class {
|
|
|
4592
4639
|
stdout: stdout.value,
|
|
4593
4640
|
stderr: stderr.value,
|
|
4594
4641
|
truncated: true,
|
|
4595
|
-
truncation
|
|
4642
|
+
truncation,
|
|
4643
|
+
...spilledField(truncation)
|
|
4596
4644
|
};
|
|
4597
4645
|
}
|
|
4598
4646
|
/**
|
|
@@ -4750,7 +4798,8 @@ var Ops = class {
|
|
|
4750
4798
|
matches: fitted.items,
|
|
4751
4799
|
total: matches.length,
|
|
4752
4800
|
truncated: true,
|
|
4753
|
-
truncation: fitted.truncation
|
|
4801
|
+
truncation: fitted.truncation,
|
|
4802
|
+
...spilledField({ stdout: fitted.truncation })
|
|
4754
4803
|
};
|
|
4755
4804
|
}
|
|
4756
4805
|
async globResult(pattern) {
|
|
@@ -4759,7 +4808,8 @@ var Ops = class {
|
|
|
4759
4808
|
return fitted.truncation === void 0 ? { paths: all } : {
|
|
4760
4809
|
paths: fitted.items,
|
|
4761
4810
|
truncated: true,
|
|
4762
|
-
truncation: fitted.truncation
|
|
4811
|
+
truncation: fitted.truncation,
|
|
4812
|
+
...spilledField({ stdout: fitted.truncation })
|
|
4763
4813
|
};
|
|
4764
4814
|
}
|
|
4765
4815
|
async glob(pattern) {
|
|
@@ -4792,6 +4842,11 @@ var Ops = class {
|
|
|
4792
4842
|
return denyPaths !== void 0 && denyPaths.length > 0 && matchesAnyGlob(relativePath, denyPaths);
|
|
4793
4843
|
}
|
|
4794
4844
|
};
|
|
4845
|
+
/** `{ spilledTo }` for every spill file named in the truncation metadata, or nothing. */
|
|
4846
|
+
function spilledField(truncation) {
|
|
4847
|
+
const paths = [truncation?.stdout?.path, truncation?.stderr?.path].filter((file) => file !== void 0);
|
|
4848
|
+
return paths.length > 0 ? { spilledTo: paths } : {};
|
|
4849
|
+
}
|
|
4795
4850
|
/** Combine per-stream truncation metadata; `undefined` when neither stream was clipped. */
|
|
4796
4851
|
function streamTruncation(stdout, stderr) {
|
|
4797
4852
|
if (stdout === void 0 && stderr === void 0) return;
|
|
@@ -6218,7 +6273,8 @@ function runShellInputSchema(background) {
|
|
|
6218
6273
|
const DIALECT_RULES = {
|
|
6219
6274
|
posix: "Use POSIX shell syntax: `;`, `&&`, `||`, pipes, quotes, `$( )` and redirections are supported, and every command in the line is checked. Compound statements (`if`, `for`), brace expansion, arithmetic and `&` are not.",
|
|
6220
6275
|
cmd: "Use cmd.exe syntax: `&&`, `||`, `&`, `|` and simple redirections are supported, and every command in the line is checked. `%` and `!` expansion, `^`, parentheses and `if` / `for` are not.",
|
|
6221
|
-
powershell: "Use PowerShell syntax: `;`, `&&`, `||`, pipes, `$( )`, the call operator `&` and the redirections `>`, `>>`, `2>` and `2>&1` are supported, and every command in the line is checked (aliases such as `rm` and `Remove-Item` are treated alike). Script blocks, `Invoke-Expression`, here-strings, splatting and `if` / `foreach` are not."
|
|
6276
|
+
powershell: "Use PowerShell syntax: `;`, `&&`, `||`, pipes, `$( )`, the call operator `&` and the redirections `>`, `>>`, `2>` and `2>&1` are supported, and every command in the line is checked (aliases such as `rm` and `Remove-Item` are treated alike). Script blocks, `Invoke-Expression`, here-strings, splatting and `if` / `foreach` are not.",
|
|
6277
|
+
powershell5: "Use Windows PowerShell 5.1 syntax: `;`, pipes, `$( )`, the call operator `&` and the redirections `>`, `>>`, `2>` and `2>&1` are supported, and every command in the line is checked (aliases such as `rm` and `Remove-Item` are treated alike). This version cannot chain with `&&` or `||`: put one command per call, or join with `;` (which runs the next command whatever the exit code). Script blocks, `Invoke-Expression`, here-strings, splatting and `if` / `foreach` are not supported."
|
|
6222
6278
|
};
|
|
6223
6279
|
const PLATFORM_NAMES = {
|
|
6224
6280
|
win32: "Windows",
|
|
@@ -6302,7 +6358,7 @@ const runTestsInputSchema = withJsonSchema({ "~standard": {
|
|
|
6302
6358
|
} }, RUN_TESTS_JSON_SCHEMA);
|
|
6303
6359
|
/** Quote the test pattern as one literal argument for the line's interpreter. */
|
|
6304
6360
|
function quotePattern(pattern, interpreter, dialect, platform = process.platform) {
|
|
6305
|
-
if (dialect === "powershell" || interpreter === "powershell") return `'${pattern.replace(/['\u2018\u2019\u201A\u201B]/g, "$&$&")}'`;
|
|
6361
|
+
if (dialect === "powershell" || dialect === "powershell5" || interpreter === "powershell") return `'${pattern.replace(/['\u2018\u2019\u201A\u201B]/g, "$&$&")}'`;
|
|
6306
6362
|
if (interpreter === "bash" || interpreter === "sh" && platform !== "win32") return `'${pattern.replace(/'/g, "'\\''")}'`;
|
|
6307
6363
|
return `"${pattern}"`;
|
|
6308
6364
|
}
|
|
@@ -6727,7 +6783,17 @@ var WorkspaceImpl = class WorkspaceImpl {
|
|
|
6727
6783
|
approvals: []
|
|
6728
6784
|
};
|
|
6729
6785
|
this.resolvedShell = resolveShell(policy.shell);
|
|
6730
|
-
|
|
6786
|
+
const approvals = this.state.approvals;
|
|
6787
|
+
this.shell = {
|
|
6788
|
+
classify: (line) => classifyShellCommand(line, policy),
|
|
6789
|
+
remembered: () => [...approvals],
|
|
6790
|
+
forget: (pattern) => {
|
|
6791
|
+
const index = approvals.findIndex((entry) => samePattern(entry, pattern));
|
|
6792
|
+
if (index === -1) return false;
|
|
6793
|
+
approvals.splice(index, 1);
|
|
6794
|
+
return true;
|
|
6795
|
+
}
|
|
6796
|
+
};
|
|
6731
6797
|
const backend = selectBackend(policy);
|
|
6732
6798
|
this.ops = createOps(backend, policy, {
|
|
6733
6799
|
jobs: this.state.jobs,
|