@wrongstack/tools 0.291.0 → 0.291.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/bash-kill-guard.d.ts +10 -1
- package/dist/bash-kill-guard.d.ts.map +1 -1
- package/dist/bash.js +140 -14
- package/dist/bash.js.map +2 -2
- package/dist/builtin.js +464 -90
- package/dist/builtin.js.map +4 -4
- package/dist/exec-kill-guard.d.ts +29 -0
- package/dist/exec-kill-guard.d.ts.map +1 -0
- package/dist/exec.d.ts.map +1 -1
- package/dist/exec.js +670 -10
- package/dist/exec.js.map +4 -4
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +490 -113
- package/dist/index.js.map +4 -4
- package/dist/pack.js +464 -90
- package/dist/pack.js.map +4 -4
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -709,8 +709,8 @@ var init_process_registry = __esm({
|
|
|
709
709
|
if (p.killed) return true;
|
|
710
710
|
if (p.protected) return false;
|
|
711
711
|
const { force = false, graceMs = DEFAULT_GRACE_MS } = opts;
|
|
712
|
-
const
|
|
713
|
-
if (
|
|
712
|
+
const isWin5 = os.platform() === "win32";
|
|
713
|
+
if (isWin5) {
|
|
714
714
|
const liveRealChild = p.child.exitCode === null && typeof p.child.pid === "number";
|
|
715
715
|
const directFallback = () => {
|
|
716
716
|
if (p.child.exitCode === null) {
|
|
@@ -2608,8 +2608,8 @@ async function scanDirectory(directory, depth, profiles, limits, state, extraIgn
|
|
|
2608
2608
|
collectFileEvidence(directory, fullPath, entry.name, profiles, state);
|
|
2609
2609
|
}
|
|
2610
2610
|
}
|
|
2611
|
-
function collectFileEvidence(directory, fullPath,
|
|
2612
|
-
const lower =
|
|
2611
|
+
function collectFileEvidence(directory, fullPath, basename11, profiles, state) {
|
|
2612
|
+
const lower = basename11.toLowerCase();
|
|
2613
2613
|
const extension = path4.extname(lower);
|
|
2614
2614
|
for (const profile of profiles) {
|
|
2615
2615
|
const detector = profile.detectors.find(
|
|
@@ -2620,7 +2620,7 @@ function collectFileEvidence(directory, fullPath, basename10, profiles, state) {
|
|
|
2620
2620
|
candidate.evidence.push({
|
|
2621
2621
|
kind: detector.kind,
|
|
2622
2622
|
path: fullPath,
|
|
2623
|
-
value:
|
|
2623
|
+
value: basename11,
|
|
2624
2624
|
weight: detector.weight
|
|
2625
2625
|
});
|
|
2626
2626
|
if (detector.kind === "manifest" || detector.kind === "config") {
|
|
@@ -3937,15 +3937,15 @@ async function changedPaths(before, after, beforeSizes, afterSizes) {
|
|
|
3937
3937
|
const beforeSet = new Set(before);
|
|
3938
3938
|
const afterSet = new Set(after);
|
|
3939
3939
|
const changed = /* @__PURE__ */ new Set();
|
|
3940
|
-
for (const
|
|
3941
|
-
if (!beforeSet.has(
|
|
3940
|
+
for (const path34 of after) {
|
|
3941
|
+
if (!beforeSet.has(path34)) changed.add(path34);
|
|
3942
3942
|
}
|
|
3943
|
-
for (const
|
|
3944
|
-
if (!afterSet.has(
|
|
3943
|
+
for (const path34 of before) {
|
|
3944
|
+
if (!afterSet.has(path34)) changed.add(path34);
|
|
3945
3945
|
}
|
|
3946
3946
|
if (beforeSizes && afterSizes) {
|
|
3947
|
-
for (const
|
|
3948
|
-
if (beforeSizes.get(
|
|
3947
|
+
for (const path34 of after) {
|
|
3948
|
+
if (beforeSizes.get(path34) !== afterSizes.get(path34)) changed.add(path34);
|
|
3949
3949
|
}
|
|
3950
3950
|
}
|
|
3951
3951
|
return [...changed].sort();
|
|
@@ -5103,8 +5103,8 @@ function normalizeShell(value) {
|
|
|
5103
5103
|
if (v === "pwsh" || v === "pwsh.exe") return "pwsh";
|
|
5104
5104
|
return void 0;
|
|
5105
5105
|
}
|
|
5106
|
-
function resolveSessionShell(
|
|
5107
|
-
if (
|
|
5106
|
+
function resolveSessionShell(platform6, env, deps = {}) {
|
|
5107
|
+
if (platform6 !== "win32") return void 0;
|
|
5108
5108
|
const override = normalizeShell(env.get("WRONGSTACK_SHELL"));
|
|
5109
5109
|
if (override) return override;
|
|
5110
5110
|
const hasBinary = deps.hasBinary ?? ((bin) => resolveWin32Command(bin) !== bin);
|
|
@@ -5114,11 +5114,11 @@ function resolveSessionShell(platform5, env, deps = {}) {
|
|
|
5114
5114
|
}
|
|
5115
5115
|
function ensureSessionShell(opts = {}) {
|
|
5116
5116
|
const env = opts.env ?? process.env;
|
|
5117
|
-
const
|
|
5118
|
-
if (
|
|
5117
|
+
const platform6 = opts.platform ?? process.platform;
|
|
5118
|
+
if (platform6 !== "win32") return void 0;
|
|
5119
5119
|
const existing = normalizeShell(env["WRONGSTACK_SHELL"]);
|
|
5120
5120
|
if (existing) return existing;
|
|
5121
|
-
const chosen = resolveSessionShell(
|
|
5121
|
+
const chosen = resolveSessionShell(platform6, { get: (k) => env[k] }, { hasBinary: opts.hasBinary }) ?? "cmd";
|
|
5122
5122
|
env["WRONGSTACK_SHELL"] = chosen;
|
|
5123
5123
|
return chosen;
|
|
5124
5124
|
}
|
|
@@ -5693,17 +5693,18 @@ function resetPersistentProcessRegistry() {
|
|
|
5693
5693
|
|
|
5694
5694
|
// src/bash-kill-guard.ts
|
|
5695
5695
|
var isWin2 = os3.platform() === "win32";
|
|
5696
|
+
var SCRIPT_KILL_RE = /^(?:\.\\|\.\/)?(?:kill|terminate|stop)\S*\.(?:ps1|bat|cmd|sh)(?:\s|$)/i;
|
|
5697
|
+
var SCRIPT_KILL_RE_POSIX = /^(?:\.\/)?(?:kill|terminate|stop)\S*\.sh(?:\s|$)/i;
|
|
5698
|
+
var SCRIPT_KILL_FALLBACK_RE = /^\S*(?:kill|terminate|stop)\S*\.(?:ps1|bat|cmd|sh)\b/i;
|
|
5696
5699
|
function extractKillCommand(command) {
|
|
5697
5700
|
const normalized = command.replace(/\s+/g, " ").trim();
|
|
5698
|
-
const shellCMatch = normalized.match(
|
|
5699
|
-
|
|
5700
|
-
|
|
5701
|
-
if (shellCMatch?.[1]) {
|
|
5702
|
-
const inner = shellCMatch[1].trim();
|
|
5701
|
+
const shellCMatch = normalized.match(/^.+?\s+-c\s+(['"])([\s\S]+)\1$/);
|
|
5702
|
+
if (shellCMatch?.[2]) {
|
|
5703
|
+
const inner = shellCMatch[2].trim();
|
|
5703
5704
|
return isKillRelatedCommand(inner) ? inner : null;
|
|
5704
5705
|
}
|
|
5705
5706
|
const shellCUnquoted = normalized.match(
|
|
5706
|
-
|
|
5707
|
+
/^.+?\s+-c\s+(kill(?:\s+-s\s+[a-zA-Z0-9]+|\s+-[a-zA-Z0-9]+)?\s+\d+)$/
|
|
5707
5708
|
);
|
|
5708
5709
|
if (shellCUnquoted?.[1]) {
|
|
5709
5710
|
return shellCUnquoted[1];
|
|
@@ -5715,22 +5716,47 @@ function isKillRelatedCommand(cmd) {
|
|
|
5715
5716
|
if (isWin2) {
|
|
5716
5717
|
if (/^taskkill\s/i.test(normalized)) return true;
|
|
5717
5718
|
if (/^tskill\s/i.test(normalized)) return true;
|
|
5719
|
+
if (/^(stop-process|kill|stop)\s/i.test(normalized)) return true;
|
|
5720
|
+
if (/^wmic\s+process\s/i.test(normalized) && /\bdelete\b/i.test(normalized)) return true;
|
|
5721
|
+
if (SCRIPT_KILL_RE.test(normalized)) return true;
|
|
5718
5722
|
return false;
|
|
5719
5723
|
}
|
|
5720
5724
|
if (/^kill(\s|$)/.test(normalized)) return true;
|
|
5721
5725
|
if (/^(pkill|killall|pgrep|skill)\s/.test(normalized)) return true;
|
|
5722
5726
|
if (/^\/proc\/\d+\/(?:kill|fd)/.test(normalized)) return true;
|
|
5727
|
+
if (SCRIPT_KILL_RE_POSIX.test(normalized)) return true;
|
|
5723
5728
|
return false;
|
|
5724
5729
|
}
|
|
5725
5730
|
function parseKillCommand(command) {
|
|
5726
5731
|
const normalized = command.replace(/\s+/g, " ").trim();
|
|
5727
5732
|
if (isWin2) {
|
|
5728
|
-
const
|
|
5729
|
-
|
|
5730
|
-
|
|
5733
|
+
const hasTaskkillForce = /(?:^|\s)\/F(?=\s|$)/i.test(normalized);
|
|
5734
|
+
const isSimpleTaskkill = /^taskkill\s+/i.test(normalized) && !/[|&<>]/.test(normalized);
|
|
5735
|
+
const taskkillPidMatch = isSimpleTaskkill ? normalized.match(/(?:^|\s)\/PID\s+(\d+)(?=\s|$)/i) : null;
|
|
5736
|
+
if (taskkillPidMatch?.[1]) {
|
|
5737
|
+
return {
|
|
5738
|
+
pid: parseInt(taskkillPidMatch[1], 10),
|
|
5739
|
+
signal: hasTaskkillForce ? "FORCE" : "TERM",
|
|
5740
|
+
isGroupKill: false,
|
|
5741
|
+
isAllKill: false,
|
|
5742
|
+
originalCommand: command
|
|
5743
|
+
};
|
|
5744
|
+
}
|
|
5745
|
+
const taskkillImMatch = isSimpleTaskkill ? normalized.match(/(?:^|\s)\/IM\s+([^\s/]+)(?=\s|$)/i) : null;
|
|
5746
|
+
if (taskkillImMatch?.[1]) {
|
|
5747
|
+
return {
|
|
5748
|
+
name: taskkillImMatch[1],
|
|
5749
|
+
signal: hasTaskkillForce ? "FORCE" : "TERM",
|
|
5750
|
+
isGroupKill: false,
|
|
5751
|
+
isAllKill: false,
|
|
5752
|
+
originalCommand: command
|
|
5753
|
+
};
|
|
5754
|
+
}
|
|
5755
|
+
const taskkillFiMatch = isSimpleTaskkill ? normalized.match(/(?:^|\s)\/FI\s+"IMAGENAME\s+eq\s+([^"]+)"(?=\s|$)/i) : null;
|
|
5756
|
+
if (taskkillFiMatch?.[1]) {
|
|
5731
5757
|
return {
|
|
5732
|
-
|
|
5733
|
-
signal:
|
|
5758
|
+
name: taskkillFiMatch[1],
|
|
5759
|
+
signal: hasTaskkillForce ? "FORCE" : "TERM",
|
|
5734
5760
|
isGroupKill: false,
|
|
5735
5761
|
isAllKill: false,
|
|
5736
5762
|
originalCommand: command
|
|
@@ -5738,18 +5764,109 @@ function parseKillCommand(command) {
|
|
|
5738
5764
|
}
|
|
5739
5765
|
const tskillMatch = normalized.match(/^tskill\s+(\d+)/i);
|
|
5740
5766
|
if (tskillMatch?.[1]) {
|
|
5741
|
-
const pidStr = tskillMatch[1];
|
|
5742
5767
|
return {
|
|
5743
|
-
pid: parseInt(
|
|
5768
|
+
pid: parseInt(tskillMatch[1], 10),
|
|
5744
5769
|
signal: "TERM",
|
|
5745
5770
|
isGroupKill: false,
|
|
5746
5771
|
isAllKill: false,
|
|
5747
5772
|
originalCommand: command
|
|
5748
5773
|
};
|
|
5749
5774
|
}
|
|
5775
|
+
const isStopProcIdCommand = /^(?:stop-process|kill)(?:\s+-(?:id|pid)\s+\d+|\s+-[a-zA-Z]+)+$/i.test(normalized);
|
|
5776
|
+
const stopProcIdMatch = normalized.match(/(?:^|\s)-(?:id|pid)\s+(\d+)(?=\s|$)/i);
|
|
5777
|
+
if (isStopProcIdCommand && stopProcIdMatch?.[1]) {
|
|
5778
|
+
return {
|
|
5779
|
+
pid: parseInt(stopProcIdMatch[1], 10),
|
|
5780
|
+
signal: "FORCE",
|
|
5781
|
+
isGroupKill: false,
|
|
5782
|
+
isAllKill: false,
|
|
5783
|
+
originalCommand: command
|
|
5784
|
+
};
|
|
5785
|
+
}
|
|
5786
|
+
const killSignalOptionMatch = normalized.match(/^kill\s+-s\s+([a-zA-Z0-9]+)\s+(\d+)$/i);
|
|
5787
|
+
if (killSignalOptionMatch?.[1] && killSignalOptionMatch[2]) {
|
|
5788
|
+
return {
|
|
5789
|
+
pid: parseInt(killSignalOptionMatch[2], 10),
|
|
5790
|
+
signal: killSignalOptionMatch[1].toUpperCase(),
|
|
5791
|
+
isGroupKill: false,
|
|
5792
|
+
isAllKill: false,
|
|
5793
|
+
originalCommand: command
|
|
5794
|
+
};
|
|
5795
|
+
}
|
|
5796
|
+
const killPosixMatch = normalized.match(/^kill\s+(?:(-[a-zA-Z0-9]+)\s+)?(\d+)$/);
|
|
5797
|
+
if (killPosixMatch?.[2]) {
|
|
5798
|
+
const sig = killPosixMatch[1] ? killPosixMatch[1].slice(1).toUpperCase() : "TERM";
|
|
5799
|
+
return {
|
|
5800
|
+
pid: parseInt(killPosixMatch[2], 10),
|
|
5801
|
+
signal: sig,
|
|
5802
|
+
isGroupKill: false,
|
|
5803
|
+
isAllKill: false,
|
|
5804
|
+
originalCommand: command
|
|
5805
|
+
};
|
|
5806
|
+
}
|
|
5807
|
+
const stopProcNameMatch = normalized.match(
|
|
5808
|
+
/^(?:stop-process|kill)\s+-(?:name|n)\s+(?:['"]([a-zA-Z0-9_.-]+)['"]|([a-zA-Z0-9_.-]+))(?:\s|$)/i
|
|
5809
|
+
);
|
|
5810
|
+
const stopProcName = stopProcNameMatch?.[1] ?? stopProcNameMatch?.[2];
|
|
5811
|
+
if (stopProcName) {
|
|
5812
|
+
return {
|
|
5813
|
+
name: stopProcName,
|
|
5814
|
+
signal: "FORCE",
|
|
5815
|
+
isGroupKill: false,
|
|
5816
|
+
isAllKill: false,
|
|
5817
|
+
originalCommand: command
|
|
5818
|
+
};
|
|
5819
|
+
}
|
|
5820
|
+
const stopProcStandalone = normalized.match(
|
|
5821
|
+
/^(?:stop-process|kill)\s+['"]?([a-zA-Z][a-zA-Z0-9_.-]+)['"]?$/i
|
|
5822
|
+
);
|
|
5823
|
+
if (stopProcStandalone?.[1]) {
|
|
5824
|
+
return {
|
|
5825
|
+
name: stopProcStandalone[1],
|
|
5826
|
+
signal: "FORCE",
|
|
5827
|
+
isGroupKill: false,
|
|
5828
|
+
isAllKill: false,
|
|
5829
|
+
originalCommand: command
|
|
5830
|
+
};
|
|
5831
|
+
}
|
|
5832
|
+
const wmicMatch = normalized.match(
|
|
5833
|
+
/^wmic\s+process\s+where\s+['"]?(?:name\s*=\s*['"]?)([a-zA-Z0-9_.-]+)/i
|
|
5834
|
+
);
|
|
5835
|
+
if (wmicMatch?.[1]) {
|
|
5836
|
+
return {
|
|
5837
|
+
name: wmicMatch[1],
|
|
5838
|
+
signal: "FORCE",
|
|
5839
|
+
isGroupKill: false,
|
|
5840
|
+
isAllKill: false,
|
|
5841
|
+
originalCommand: command
|
|
5842
|
+
};
|
|
5843
|
+
}
|
|
5844
|
+
const killScriptMatch = normalized.match(SCRIPT_KILL_RE);
|
|
5845
|
+
if (killScriptMatch) {
|
|
5846
|
+
return {
|
|
5847
|
+
name: "kill-script",
|
|
5848
|
+
// sentinel — isKillProtected always blocks "kill-script"
|
|
5849
|
+
signal: "FORCE",
|
|
5850
|
+
isGroupKill: false,
|
|
5851
|
+
isAllKill: false,
|
|
5852
|
+
originalCommand: command
|
|
5853
|
+
};
|
|
5854
|
+
}
|
|
5750
5855
|
return null;
|
|
5751
5856
|
}
|
|
5752
|
-
const
|
|
5857
|
+
const signalOptionMatch = normalized.match(/^kill\s+-s\s+([a-zA-Z0-9]+)\s+(\d+|-?\d+)$/i);
|
|
5858
|
+
if (signalOptionMatch?.[1] && signalOptionMatch[2]) {
|
|
5859
|
+
const pidOrGroup = signalOptionMatch[2];
|
|
5860
|
+
const isGroupKill = pidOrGroup.startsWith("-");
|
|
5861
|
+
return {
|
|
5862
|
+
pid: parseInt(isGroupKill ? pidOrGroup.slice(1) : pidOrGroup, 10),
|
|
5863
|
+
signal: signalOptionMatch[1].toUpperCase(),
|
|
5864
|
+
isGroupKill,
|
|
5865
|
+
isAllKill: false,
|
|
5866
|
+
originalCommand: command
|
|
5867
|
+
};
|
|
5868
|
+
}
|
|
5869
|
+
const simpleMatch = normalized.match(/^kill\s+(?:(-[a-zA-Z0-9]+)\s+)?(\d+|-?\d+)$/);
|
|
5753
5870
|
if (simpleMatch) {
|
|
5754
5871
|
const signal = simpleMatch[1] ?? "-TERM";
|
|
5755
5872
|
const pidOrGroup = simpleMatch[2];
|
|
@@ -5809,6 +5926,9 @@ async function getProtectedEntries() {
|
|
|
5809
5926
|
}
|
|
5810
5927
|
async function isKillProtected(kill) {
|
|
5811
5928
|
const registry = getPersistentProcessRegistry();
|
|
5929
|
+
if (kill.name === "kill-script") {
|
|
5930
|
+
return true;
|
|
5931
|
+
}
|
|
5812
5932
|
if (kill.name) {
|
|
5813
5933
|
const entries = await getProtectedEntries();
|
|
5814
5934
|
const killNameLower = kill.name.toLowerCase();
|
|
@@ -5848,6 +5968,12 @@ async function checkAndBlockKillCommand(command) {
|
|
|
5848
5968
|
reason: `Blocked: complex kill pipeline detected \u2014 "${killCmd.slice(0, 50)}..."`
|
|
5849
5969
|
};
|
|
5850
5970
|
}
|
|
5971
|
+
if (SCRIPT_KILL_FALLBACK_RE.test(killCmd)) {
|
|
5972
|
+
return {
|
|
5973
|
+
blocked: true,
|
|
5974
|
+
reason: `Blocked: script-based kill detected \u2014 "${killCmd.slice(0, 80)}" may target protected WrongStack processes (cannot inspect script body).`
|
|
5975
|
+
};
|
|
5976
|
+
}
|
|
5851
5977
|
return { blocked: false };
|
|
5852
5978
|
}
|
|
5853
5979
|
if (await isKillProtected(parsed)) {
|
|
@@ -5871,8 +5997,8 @@ async function checkAndBlockKillCommand(command) {
|
|
|
5871
5997
|
|
|
5872
5998
|
// src/_shell-pick.ts
|
|
5873
5999
|
var POSIX_DEFAULT = "cmd";
|
|
5874
|
-
function pickShell(
|
|
5875
|
-
if (
|
|
6000
|
+
function pickShell(platform6, command, env) {
|
|
6001
|
+
if (platform6 !== "win32") return POSIX_DEFAULT;
|
|
5876
6002
|
const override = env.get("WRONGSTACK_SHELL")?.trim().toLowerCase();
|
|
5877
6003
|
if (override === "cmd" || override === "cmd.exe") return "cmd";
|
|
5878
6004
|
if (override === "powershell" || override === "powershell.exe") return "powershell";
|
|
@@ -6076,10 +6202,10 @@ var bashTool = {
|
|
|
6076
6202
|
}));
|
|
6077
6203
|
}
|
|
6078
6204
|
const timeoutMs = Math.max(1, Math.min(input.timeout_ms ?? DEFAULT_TIMEOUT_MS2, 6e5));
|
|
6079
|
-
const
|
|
6205
|
+
const isWin5 = os4.platform() === "win32";
|
|
6080
6206
|
let plan;
|
|
6081
6207
|
let winShellKind;
|
|
6082
|
-
if (
|
|
6208
|
+
if (isWin5) {
|
|
6083
6209
|
const shell2 = pickShell("win32", input.command, {
|
|
6084
6210
|
get: (k) => process.env[k]
|
|
6085
6211
|
});
|
|
@@ -6108,7 +6234,7 @@ var bashTool = {
|
|
|
6108
6234
|
const shell = plan.bin;
|
|
6109
6235
|
const args = plan.useStdin ? [...plan.argv] : [...plan.argv, input.command];
|
|
6110
6236
|
const env = buildChildEnv2(ctx.session?.id);
|
|
6111
|
-
const detached = !
|
|
6237
|
+
const detached = !isWin5;
|
|
6112
6238
|
const startedAt = Date.now();
|
|
6113
6239
|
if (input.background) {
|
|
6114
6240
|
let buf2 = "";
|
|
@@ -6126,7 +6252,7 @@ var bashTool = {
|
|
|
6126
6252
|
// apply: the child gets a hidden console that grandchildren inherit.
|
|
6127
6253
|
// Windows children survive parent exit either way. POSIX keeps
|
|
6128
6254
|
// detached for the process-group kill semantics.
|
|
6129
|
-
detached: !
|
|
6255
|
+
detached: !isWin5,
|
|
6130
6256
|
windowsHide: true
|
|
6131
6257
|
});
|
|
6132
6258
|
if (plan.useStdin) {
|
|
@@ -6237,7 +6363,7 @@ var bashTool = {
|
|
|
6237
6363
|
stdio: [plan.useStdin ? "pipe" : "ignore", "pipe", "pipe"],
|
|
6238
6364
|
detached,
|
|
6239
6365
|
windowsHide: true,
|
|
6240
|
-
...
|
|
6366
|
+
...isWin5 ? {} : { signal: opts.signal }
|
|
6241
6367
|
});
|
|
6242
6368
|
if (plan.useStdin) {
|
|
6243
6369
|
try {
|
|
@@ -6290,7 +6416,7 @@ var bashTool = {
|
|
|
6290
6416
|
const timers = [];
|
|
6291
6417
|
const spool = createOutputSpool({ tool: "bash", thresholdBytes: MAX_OUTPUT });
|
|
6292
6418
|
function killWithTimeout(child2, timeoutMs2) {
|
|
6293
|
-
if (
|
|
6419
|
+
if (isWin5) {
|
|
6294
6420
|
if (typeof child2.pid === "number" && child2.exitCode === null) {
|
|
6295
6421
|
const attempted = registry.kill(child2.pid, { force: true, graceMs: timeoutMs2 });
|
|
6296
6422
|
if (!attempted) {
|
|
@@ -6331,7 +6457,7 @@ var bashTool = {
|
|
|
6331
6457
|
timers.push(timer);
|
|
6332
6458
|
timer.unref?.();
|
|
6333
6459
|
const onAbort = () => killWithTimeout(child, 2e3);
|
|
6334
|
-
if (
|
|
6460
|
+
if (isWin5) {
|
|
6335
6461
|
if (opts.signal.aborted) onAbort();
|
|
6336
6462
|
else opts.signal.addEventListener("abort", onAbort, { once: true });
|
|
6337
6463
|
}
|
|
@@ -6445,7 +6571,7 @@ ${hint}` : ""),
|
|
|
6445
6571
|
} finally {
|
|
6446
6572
|
for (const t of timers) clearTimeout(t);
|
|
6447
6573
|
spool.finalize();
|
|
6448
|
-
if (
|
|
6574
|
+
if (isWin5) opts.signal.removeEventListener("abort", onAbort);
|
|
6449
6575
|
child.stdout?.off("data", onStdoutData);
|
|
6450
6576
|
child.stderr?.off("data", onStderrData);
|
|
6451
6577
|
child.stdout?.destroy();
|
|
@@ -10323,9 +10449,9 @@ function parseSymbols5(opts) {
|
|
|
10323
10449
|
function regexParse2(opts) {
|
|
10324
10450
|
const { file, content, lang } = opts;
|
|
10325
10451
|
const symbols = [];
|
|
10326
|
-
const
|
|
10327
|
-
const isPackageJson =
|
|
10328
|
-
const isTsconfig =
|
|
10452
|
+
const basename11 = path17.basename(file).toLowerCase();
|
|
10453
|
+
const isPackageJson = basename11 === "package.json";
|
|
10454
|
+
const isTsconfig = basename11 === "tsconfig.json" || basename11 === "tsconfig.build.json";
|
|
10329
10455
|
const isJsonSchema = content.includes("$schema") || content.includes("$id") || content.includes("$ref");
|
|
10330
10456
|
const isOpenApi = content.includes("openapi") || content.includes("swagger");
|
|
10331
10457
|
const lines = content.split("\n");
|
|
@@ -13289,7 +13415,244 @@ init_output_spool();
|
|
|
13289
13415
|
init_util();
|
|
13290
13416
|
init_process_registry();
|
|
13291
13417
|
init_win32_resolve();
|
|
13292
|
-
|
|
13418
|
+
|
|
13419
|
+
// src/exec-kill-guard.ts
|
|
13420
|
+
import * as os7 from "node:os";
|
|
13421
|
+
import * as path24 from "node:path";
|
|
13422
|
+
var isWin3 = os7.platform() === "win32";
|
|
13423
|
+
async function checkExecKillCommand(cmd, args) {
|
|
13424
|
+
if (!cmd) return { blocked: false };
|
|
13425
|
+
const cmdLower = cmd.toLowerCase().trim();
|
|
13426
|
+
const fullCommand = [cmdLower, ...args].join(" ").replace(/\s+/g, " ").trim();
|
|
13427
|
+
if (isWin3) {
|
|
13428
|
+
if (cmdLower === "taskkill" || cmdLower === "taskkill.exe") {
|
|
13429
|
+
const hasForce = args.some((a) => a.toUpperCase() === "/F" || a.toUpperCase() === "-F");
|
|
13430
|
+
const signal = hasForce ? "FORCE" : "TERM";
|
|
13431
|
+
for (let i = 0; i < args.length; i++) {
|
|
13432
|
+
const a = args[i];
|
|
13433
|
+
if (a.toUpperCase() === "/IM" || a.toUpperCase() === "-IM") {
|
|
13434
|
+
const nameArg = args[i + 1];
|
|
13435
|
+
if (nameArg) {
|
|
13436
|
+
const result = await checkKillTarget({ name: nameArg, signal, cmd: fullCommand });
|
|
13437
|
+
if (result.blocked) return result;
|
|
13438
|
+
}
|
|
13439
|
+
}
|
|
13440
|
+
}
|
|
13441
|
+
for (let i = 0; i < args.length; i++) {
|
|
13442
|
+
const a = args[i];
|
|
13443
|
+
if (a.toUpperCase() === "/PID" || a.toUpperCase() === "-PID") {
|
|
13444
|
+
const pidArg = args[i + 1];
|
|
13445
|
+
if (pidArg && /^\d+$/.test(pidArg)) {
|
|
13446
|
+
const result = await checkKillTarget({
|
|
13447
|
+
pid: parseInt(pidArg, 10),
|
|
13448
|
+
signal,
|
|
13449
|
+
cmd: fullCommand
|
|
13450
|
+
});
|
|
13451
|
+
if (result.blocked) return result;
|
|
13452
|
+
}
|
|
13453
|
+
}
|
|
13454
|
+
}
|
|
13455
|
+
for (let i = 0; i < args.length; i++) {
|
|
13456
|
+
const a = args[i];
|
|
13457
|
+
if (a.toUpperCase() === "/FI" || a.toUpperCase() === "-FI") {
|
|
13458
|
+
const filterArg = args[i + 1];
|
|
13459
|
+
if (filterArg) {
|
|
13460
|
+
const nameMatch = filterArg.match(/IMAGENAME\s+eq\s+"?([^"\s]+)/i);
|
|
13461
|
+
if (nameMatch?.[1]) {
|
|
13462
|
+
const result = await checkKillTarget({ name: nameMatch[1], signal, cmd: fullCommand });
|
|
13463
|
+
if (result.blocked) return result;
|
|
13464
|
+
}
|
|
13465
|
+
}
|
|
13466
|
+
}
|
|
13467
|
+
}
|
|
13468
|
+
return { blocked: false };
|
|
13469
|
+
}
|
|
13470
|
+
if (cmdLower === "powershell" || cmdLower === "powershell.exe" || cmdLower === "pwsh" || cmdLower === "pwsh.exe" || cmdLower === "cmd" || cmdLower === "cmd.exe") {
|
|
13471
|
+
const shellFlagIndex = args.findIndex((arg) => {
|
|
13472
|
+
const lower = arg.toLowerCase();
|
|
13473
|
+
return lower === "-c" || lower === "-command" || lower === "/c";
|
|
13474
|
+
});
|
|
13475
|
+
if (shellFlagIndex >= 0) {
|
|
13476
|
+
const innerTokens = tokenizeShellCommand(args.slice(shellFlagIndex + 1).join(" "));
|
|
13477
|
+
const innerCommand = innerTokens[0];
|
|
13478
|
+
if (innerCommand) {
|
|
13479
|
+
const result = await checkExecKillCommand(innerCommand, innerTokens.slice(1));
|
|
13480
|
+
if (result.blocked) return result;
|
|
13481
|
+
}
|
|
13482
|
+
}
|
|
13483
|
+
}
|
|
13484
|
+
if (cmdLower === "stop-process" || cmdLower === "kill") {
|
|
13485
|
+
for (let i = 0; i < args.length; i++) {
|
|
13486
|
+
const a = args[i];
|
|
13487
|
+
if (a === "-Name" || a === "-n") {
|
|
13488
|
+
const nameArg = args[i + 1]?.replace(/^['"]|['"]$/g, "");
|
|
13489
|
+
if (nameArg) {
|
|
13490
|
+
const result = await checkKillTarget({
|
|
13491
|
+
name: nameArg,
|
|
13492
|
+
signal: "FORCE",
|
|
13493
|
+
cmd: fullCommand
|
|
13494
|
+
});
|
|
13495
|
+
if (result.blocked) return result;
|
|
13496
|
+
}
|
|
13497
|
+
}
|
|
13498
|
+
if (a === "-Id" || a === "-PID" || a === "-pid") {
|
|
13499
|
+
const pidArg = args[i + 1];
|
|
13500
|
+
if (pidArg && /^\d+$/.test(pidArg)) {
|
|
13501
|
+
const result = await checkKillTarget({
|
|
13502
|
+
pid: parseInt(pidArg, 10),
|
|
13503
|
+
signal: "FORCE",
|
|
13504
|
+
cmd: fullCommand
|
|
13505
|
+
});
|
|
13506
|
+
if (result.blocked) return result;
|
|
13507
|
+
}
|
|
13508
|
+
}
|
|
13509
|
+
}
|
|
13510
|
+
const firstNonFlag = args.find((a) => !a.startsWith("-"));
|
|
13511
|
+
if (firstNonFlag) {
|
|
13512
|
+
const name = firstNonFlag.replace(/^['"]|['"]$/g, "");
|
|
13513
|
+
const result = await checkKillTarget({ name, signal: "TERM", cmd: fullCommand });
|
|
13514
|
+
if (result.blocked) return result;
|
|
13515
|
+
}
|
|
13516
|
+
}
|
|
13517
|
+
if (cmdLower === "wmic" || cmdLower === "wmic.exe") {
|
|
13518
|
+
const joined = args.join(" ").toLowerCase();
|
|
13519
|
+
if (/\bprocess\b/.test(joined) && /\bdelete\b/.test(joined)) {
|
|
13520
|
+
const nameMatch = joined.match(/name\s*=\s*['"]?([^'"]+)/);
|
|
13521
|
+
if (nameMatch?.[1]) {
|
|
13522
|
+
const result = await checkKillTarget({
|
|
13523
|
+
name: nameMatch[1].trim(),
|
|
13524
|
+
signal: "FORCE",
|
|
13525
|
+
cmd: fullCommand
|
|
13526
|
+
});
|
|
13527
|
+
if (result.blocked) return result;
|
|
13528
|
+
}
|
|
13529
|
+
return {
|
|
13530
|
+
blocked: true,
|
|
13531
|
+
reason: "Blocked: wmic process delete targets all matched processes \u2014 would include protected WrongStack processes."
|
|
13532
|
+
};
|
|
13533
|
+
}
|
|
13534
|
+
}
|
|
13535
|
+
if (cmdLower === "node" || cmdLower === "node.exe") {
|
|
13536
|
+
if (args.includes("-e") || args.includes("--eval")) {
|
|
13537
|
+
const evalIdx = args.indexOf("-e") !== -1 ? args.indexOf("-e") : args.indexOf("--eval");
|
|
13538
|
+
const evalCode = args[evalIdx + 1] ?? "";
|
|
13539
|
+
if (/\bprocess\.kill\s*\(/.test(evalCode)) {
|
|
13540
|
+
const pidMatch = evalCode.match(/process\.kill\s*\(\s*(\d+)/);
|
|
13541
|
+
if (pidMatch?.[1]) {
|
|
13542
|
+
const pid = parseInt(pidMatch[1], 10);
|
|
13543
|
+
const result = await checkKillTarget({ pid, signal: "SIGTERM", cmd: fullCommand });
|
|
13544
|
+
if (result.blocked) return result;
|
|
13545
|
+
}
|
|
13546
|
+
return {
|
|
13547
|
+
blocked: true,
|
|
13548
|
+
reason: "Blocked: node -e with process.kill() \u2014 would target protected WrongStack process(es)."
|
|
13549
|
+
};
|
|
13550
|
+
}
|
|
13551
|
+
}
|
|
13552
|
+
}
|
|
13553
|
+
} else {
|
|
13554
|
+
if (cmdLower === "kill") {
|
|
13555
|
+
for (const a of args) {
|
|
13556
|
+
const num = a.replace(/^-/, "");
|
|
13557
|
+
if (/^\d+$/.test(num)) {
|
|
13558
|
+
const pid = parseInt(num, 10);
|
|
13559
|
+
const result = await checkKillTarget({ pid, signal: "SIGTERM", cmd: fullCommand });
|
|
13560
|
+
if (result.blocked) return result;
|
|
13561
|
+
}
|
|
13562
|
+
}
|
|
13563
|
+
}
|
|
13564
|
+
if (cmdLower === "pkill" || cmdLower === "killall") {
|
|
13565
|
+
const firstNonFlag = args.find((a) => !a.startsWith("-"));
|
|
13566
|
+
if (firstNonFlag) {
|
|
13567
|
+
const result = await checkKillTarget({
|
|
13568
|
+
name: firstNonFlag,
|
|
13569
|
+
signal: "SIGTERM",
|
|
13570
|
+
cmd: fullCommand
|
|
13571
|
+
});
|
|
13572
|
+
if (result.blocked) return result;
|
|
13573
|
+
}
|
|
13574
|
+
}
|
|
13575
|
+
}
|
|
13576
|
+
return { blocked: false };
|
|
13577
|
+
}
|
|
13578
|
+
function tokenizeShellCommand(command) {
|
|
13579
|
+
const tokens = [];
|
|
13580
|
+
let current = "";
|
|
13581
|
+
let quote = null;
|
|
13582
|
+
for (const char of command.trim()) {
|
|
13583
|
+
if (quote) {
|
|
13584
|
+
if (char === quote) quote = null;
|
|
13585
|
+
else current += char;
|
|
13586
|
+
continue;
|
|
13587
|
+
}
|
|
13588
|
+
if (char === '"' || char === "'") {
|
|
13589
|
+
quote = char;
|
|
13590
|
+
} else if (/\s/.test(char)) {
|
|
13591
|
+
if (current) {
|
|
13592
|
+
tokens.push(current);
|
|
13593
|
+
current = "";
|
|
13594
|
+
}
|
|
13595
|
+
} else {
|
|
13596
|
+
current += char;
|
|
13597
|
+
}
|
|
13598
|
+
}
|
|
13599
|
+
if (current) tokens.push(current);
|
|
13600
|
+
return tokens;
|
|
13601
|
+
}
|
|
13602
|
+
async function checkKillTarget(target) {
|
|
13603
|
+
const registry = getPersistentProcessRegistry();
|
|
13604
|
+
if (target.pid !== void 0) {
|
|
13605
|
+
const blocked = await registry.shouldBlockKill(target.pid);
|
|
13606
|
+
if (blocked) {
|
|
13607
|
+
return {
|
|
13608
|
+
blocked: true,
|
|
13609
|
+
reason: `Blocked: kill ${target.signal} PID ${target.pid} targets a protected WrongStack process (${target.cmd.slice(0, 80)}).`
|
|
13610
|
+
};
|
|
13611
|
+
}
|
|
13612
|
+
if (target.pid === process.pid) {
|
|
13613
|
+
return {
|
|
13614
|
+
blocked: true,
|
|
13615
|
+
reason: "Blocked: cannot kill the current WrongStack process."
|
|
13616
|
+
};
|
|
13617
|
+
}
|
|
13618
|
+
if (target.pid === process.ppid) {
|
|
13619
|
+
return {
|
|
13620
|
+
blocked: true,
|
|
13621
|
+
reason: "Blocked: cannot kill the parent terminal hosting WrongStack."
|
|
13622
|
+
};
|
|
13623
|
+
}
|
|
13624
|
+
return { blocked: false };
|
|
13625
|
+
}
|
|
13626
|
+
if (target.name) {
|
|
13627
|
+
const nameLower = target.name.toLowerCase().replace(/\.exe$/, "");
|
|
13628
|
+
if (nameLower.includes("wrongstack")) {
|
|
13629
|
+
return {
|
|
13630
|
+
blocked: true,
|
|
13631
|
+
reason: `Blocked: kill ${target.signal} '${target.name}' targets a WrongStack process name.`
|
|
13632
|
+
};
|
|
13633
|
+
}
|
|
13634
|
+
const currentImage = path24.basename(process.execPath).toLowerCase().replace(/\.exe$/, "");
|
|
13635
|
+
const targetsNodeRuntime = nameLower === "node" || nameLower.startsWith("node");
|
|
13636
|
+
if (targetsNodeRuntime && currentImage === "node") {
|
|
13637
|
+
return {
|
|
13638
|
+
blocked: true,
|
|
13639
|
+
reason: `Blocked: kill ${target.signal} '${target.name}' would kill the active WrongStack node.exe runtime.`
|
|
13640
|
+
};
|
|
13641
|
+
}
|
|
13642
|
+
const protectedPids = await registry.getAllProtectedPids();
|
|
13643
|
+
if (protectedPids.length > 0 && targetsNodeRuntime) {
|
|
13644
|
+
return {
|
|
13645
|
+
blocked: true,
|
|
13646
|
+
reason: `Blocked: kill ${target.signal} '${target.name}' would kill all node.exe processes including active WrongStack instance(s).`
|
|
13647
|
+
};
|
|
13648
|
+
}
|
|
13649
|
+
return { blocked: false };
|
|
13650
|
+
}
|
|
13651
|
+
return { blocked: false };
|
|
13652
|
+
}
|
|
13653
|
+
|
|
13654
|
+
// src/exec.ts
|
|
13655
|
+
var isWin4 = process.platform === "win32";
|
|
13293
13656
|
var DEFAULT_ALLOWED_COMMANDS = /* @__PURE__ */ new Set([
|
|
13294
13657
|
// JS / TS toolchain
|
|
13295
13658
|
"node",
|
|
@@ -14094,6 +14457,19 @@ var execTool = {
|
|
|
14094
14457
|
const args = (input.args ?? []).slice(0, MAX_ARGS);
|
|
14095
14458
|
const timeout = Math.max(1, Math.min(input.timeout ?? DEFAULT_TIMEOUT_MS3, DEFAULT_TIMEOUT_MS3));
|
|
14096
14459
|
const danger = detectDanger(cmd, args, dangerBypass);
|
|
14460
|
+
const killCheck = await checkExecKillCommand(cmd, args);
|
|
14461
|
+
if (killCheck.blocked) {
|
|
14462
|
+
return {
|
|
14463
|
+
command: cmd,
|
|
14464
|
+
args,
|
|
14465
|
+
stdout: "",
|
|
14466
|
+
stderr: killCheck.reason ?? "Kill command blocked: targets a protected WrongStack process.",
|
|
14467
|
+
exitCode: 1,
|
|
14468
|
+
truncated: false,
|
|
14469
|
+
allowed: false,
|
|
14470
|
+
danger
|
|
14471
|
+
};
|
|
14472
|
+
}
|
|
14097
14473
|
const argError = validateArgs(cmd, args);
|
|
14098
14474
|
if (argError) {
|
|
14099
14475
|
return {
|
|
@@ -14144,7 +14520,7 @@ function runCommand(cmd, args, cwd, timeout, signal, sessionId, danger) {
|
|
|
14144
14520
|
let timedOut = false;
|
|
14145
14521
|
const spool = createOutputSpool({ tool: `exec-${cmd}`, thresholdBytes: MAX_OUTPUT2 });
|
|
14146
14522
|
const resolved = resolveWin32Command(cmd);
|
|
14147
|
-
const needsShell =
|
|
14523
|
+
const needsShell = isWin4 && (resolved.endsWith(".cmd") || resolved.endsWith(".bat"));
|
|
14148
14524
|
const shim = needsShell ? buildWin32CmdShimInvocation(resolved, args) : null;
|
|
14149
14525
|
const spawnCmd = shim?.command ?? resolved;
|
|
14150
14526
|
const spawnArgs = shim?.args ?? args;
|
|
@@ -14169,7 +14545,7 @@ function runCommand(cmd, args, cwd, timeout, signal, sessionId, danger) {
|
|
|
14169
14545
|
env: buildChildEnv2(sessionId),
|
|
14170
14546
|
stdio: ["ignore", "pipe", "pipe"],
|
|
14171
14547
|
windowsHide: true,
|
|
14172
|
-
...
|
|
14548
|
+
...isWin4 ? {} : { signal },
|
|
14173
14549
|
...shim ? { windowsVerbatimArguments: shim.windowsVerbatimArguments } : {}
|
|
14174
14550
|
});
|
|
14175
14551
|
} catch (err) {
|
|
@@ -14208,7 +14584,7 @@ function runCommand(cmd, args, cwd, timeout, signal, sessionId, danger) {
|
|
|
14208
14584
|
const isAbort = err && err.code === "ABORT_ERR";
|
|
14209
14585
|
const stderrText = isAbort ? `Aborted: ${err.message}` : err.message;
|
|
14210
14586
|
clearTimeout(timer);
|
|
14211
|
-
if (
|
|
14587
|
+
if (isWin4) signal.removeEventListener("abort", onAbort);
|
|
14212
14588
|
if (typeof pid === "number") registry.unregister(pid);
|
|
14213
14589
|
registry.afterCall(Date.now() - startedAt, true);
|
|
14214
14590
|
spool.finalize();
|
|
@@ -14241,7 +14617,7 @@ function runCommand(cmd, args, cwd, timeout, signal, sessionId, danger) {
|
|
|
14241
14617
|
if (typeof pid === "number") registry.kill(pid, { force: true });
|
|
14242
14618
|
else child.kill("SIGTERM");
|
|
14243
14619
|
};
|
|
14244
|
-
if (
|
|
14620
|
+
if (isWin4) {
|
|
14245
14621
|
if (signal.aborted) onAbort();
|
|
14246
14622
|
else signal.addEventListener("abort", onAbort, { once: true });
|
|
14247
14623
|
}
|
|
@@ -14261,7 +14637,7 @@ function runCommand(cmd, args, cwd, timeout, signal, sessionId, danger) {
|
|
|
14261
14637
|
});
|
|
14262
14638
|
child.on("close", (code) => {
|
|
14263
14639
|
clearTimeout(timer);
|
|
14264
|
-
if (
|
|
14640
|
+
if (isWin4) signal.removeEventListener("abort", onAbort);
|
|
14265
14641
|
if (typeof pid === "number") registry.unregister(pid);
|
|
14266
14642
|
const durationMs = Date.now() - startedAt;
|
|
14267
14643
|
const exitCode = killed ? 124 : code ?? 1;
|
|
@@ -15072,7 +15448,7 @@ function runGit2(args, cwd, signal) {
|
|
|
15072
15448
|
|
|
15073
15449
|
// src/glob.ts
|
|
15074
15450
|
import * as fs20 from "node:fs/promises";
|
|
15075
|
-
import * as
|
|
15451
|
+
import * as path25 from "node:path";
|
|
15076
15452
|
import { compileGlob as compileGlob3 } from "@wrongstack/core";
|
|
15077
15453
|
|
|
15078
15454
|
// src/_concurrency.ts
|
|
@@ -15176,7 +15552,7 @@ var globTool = {
|
|
|
15176
15552
|
if (DEFAULT_IGNORE2.includes(name)) continue;
|
|
15177
15553
|
if (ignored.includes(name)) continue;
|
|
15178
15554
|
const rel = relPrefix ? `${relPrefix}/${name}` : name;
|
|
15179
|
-
const full =
|
|
15555
|
+
const full = path25.join(dir, name);
|
|
15180
15556
|
if (e.isDirectory()) {
|
|
15181
15557
|
subdirs.push({ full, rel });
|
|
15182
15558
|
} else if (e.isFile()) {
|
|
@@ -15220,7 +15596,7 @@ var globTool = {
|
|
|
15220
15596
|
};
|
|
15221
15597
|
async function readGitignore(dir) {
|
|
15222
15598
|
try {
|
|
15223
|
-
const raw = await fs20.readFile(
|
|
15599
|
+
const raw = await fs20.readFile(path25.join(dir, ".gitignore"), "utf8");
|
|
15224
15600
|
return raw.split("\n").map((l) => l.trim()).filter((l) => l && !l.startsWith("#"));
|
|
15225
15601
|
} catch {
|
|
15226
15602
|
return [];
|
|
@@ -15231,7 +15607,7 @@ async function readGitignore(dir) {
|
|
|
15231
15607
|
import { expectDefined as expectDefined7 } from "@wrongstack/core";
|
|
15232
15608
|
import { spawn as spawn10 } from "node:child_process";
|
|
15233
15609
|
import * as fs21 from "node:fs/promises";
|
|
15234
|
-
import * as
|
|
15610
|
+
import * as path26 from "node:path";
|
|
15235
15611
|
import { buildChildEnv as buildChildEnv5, compileGlob as compileGlob4, ToolValidationError as ToolValidationError4 } from "@wrongstack/core";
|
|
15236
15612
|
|
|
15237
15613
|
// src/_regex.ts
|
|
@@ -15624,7 +16000,7 @@ async function runNative(input, base, mode, limit, signal) {
|
|
|
15624
16000
|
if (stopped) return;
|
|
15625
16001
|
if (DEFAULT_IGNORE3.includes(e.name)) continue;
|
|
15626
16002
|
if (e.isSymbolicLink()) continue;
|
|
15627
|
-
const full =
|
|
16003
|
+
const full = path26.join(dir, e.name);
|
|
15628
16004
|
if (e.isDirectory()) {
|
|
15629
16005
|
subdirs.push(full);
|
|
15630
16006
|
} else if (e.isFile()) {
|
|
@@ -16212,56 +16588,56 @@ function jmespathSearch(data, query) {
|
|
|
16212
16588
|
}
|
|
16213
16589
|
function validateJsonSchema(data, schema) {
|
|
16214
16590
|
const errors = [];
|
|
16215
|
-
function check(value, s,
|
|
16591
|
+
function check(value, s, path34) {
|
|
16216
16592
|
if (s["type"]) {
|
|
16217
16593
|
const expectedType = s["type"];
|
|
16218
16594
|
const actualType = Array.isArray(value) ? "array" : value === null ? "null" : typeof value;
|
|
16219
16595
|
if (expectedType === "integer") {
|
|
16220
|
-
if (!Number.isInteger(value)) errors.push(`${
|
|
16596
|
+
if (!Number.isInteger(value)) errors.push(`${path34}: expected integer, got ${actualType}`);
|
|
16221
16597
|
} else if (expectedType !== actualType) {
|
|
16222
|
-
errors.push(`${
|
|
16598
|
+
errors.push(`${path34}: expected ${expectedType}, got ${actualType}`);
|
|
16223
16599
|
}
|
|
16224
16600
|
}
|
|
16225
16601
|
if (typeof value === "string" && s["format"] === "uri" && value) {
|
|
16226
16602
|
try {
|
|
16227
16603
|
new URL(value);
|
|
16228
16604
|
} catch {
|
|
16229
|
-
errors.push(`${
|
|
16605
|
+
errors.push(`${path34}: not a valid URI`);
|
|
16230
16606
|
}
|
|
16231
16607
|
}
|
|
16232
16608
|
if (typeof value === "string" && s["pattern"]) {
|
|
16233
16609
|
const re = new RegExp(s["pattern"]);
|
|
16234
|
-
if (!re.test(value)) errors.push(`${
|
|
16610
|
+
if (!re.test(value)) errors.push(`${path34}: does not match pattern ${s["pattern"]}`);
|
|
16235
16611
|
}
|
|
16236
16612
|
if (typeof value === "string" && s["minLength"] !== void 0 && value.length < s["minLength"]) {
|
|
16237
|
-
errors.push(`${
|
|
16613
|
+
errors.push(`${path34}: string too short (min ${s["minLength"]})`);
|
|
16238
16614
|
}
|
|
16239
16615
|
if (typeof value === "string" && s["maxLength"] !== void 0 && value.length > s["maxLength"]) {
|
|
16240
|
-
errors.push(`${
|
|
16616
|
+
errors.push(`${path34}: string too long (max ${s["maxLength"]})`);
|
|
16241
16617
|
}
|
|
16242
16618
|
if (typeof value === "number" && s["minimum"] !== void 0 && value < s["minimum"]) {
|
|
16243
|
-
errors.push(`${
|
|
16619
|
+
errors.push(`${path34}: below minimum ${s["minimum"]}`);
|
|
16244
16620
|
}
|
|
16245
16621
|
if (typeof value === "number" && s["maximum"] !== void 0 && value > s["maximum"]) {
|
|
16246
|
-
errors.push(`${
|
|
16622
|
+
errors.push(`${path34}: above maximum ${s["maximum"]}`);
|
|
16247
16623
|
}
|
|
16248
16624
|
if (Array.isArray(value) && s["items"] && Array.isArray(s["items"])) {
|
|
16249
16625
|
for (let i = 0; i < value.length; i++) {
|
|
16250
|
-
check(value[i], s["items"], `${
|
|
16626
|
+
check(value[i], s["items"], `${path34}[${i}]`);
|
|
16251
16627
|
}
|
|
16252
16628
|
}
|
|
16253
16629
|
if (typeof value === "object" && value !== null && !Array.isArray(value) && s["properties"]) {
|
|
16254
16630
|
const props = s["properties"];
|
|
16255
16631
|
for (const [k, propSchema] of Object.entries(props)) {
|
|
16256
|
-
check(value[k], propSchema, `${
|
|
16632
|
+
check(value[k], propSchema, `${path34}.${k}`);
|
|
16257
16633
|
}
|
|
16258
16634
|
}
|
|
16259
16635
|
}
|
|
16260
16636
|
check(data, schema, "$");
|
|
16261
16637
|
return { valid: errors.length === 0, errors };
|
|
16262
16638
|
}
|
|
16263
|
-
function simpleQuery(data,
|
|
16264
|
-
const parts =
|
|
16639
|
+
function simpleQuery(data, path34) {
|
|
16640
|
+
const parts = path34.replace(/\[(\d+)\]/g, ".$1").split(".").filter(Boolean);
|
|
16265
16641
|
let current = data;
|
|
16266
16642
|
for (const part of parts) {
|
|
16267
16643
|
if (current === null || current === void 0) return void 0;
|
|
@@ -16364,7 +16740,7 @@ import {
|
|
|
16364
16740
|
|
|
16365
16741
|
// src/session-kanban.ts
|
|
16366
16742
|
import { watch } from "node:fs";
|
|
16367
|
-
import { basename as
|
|
16743
|
+
import { basename as basename10, dirname as dirname13 } from "node:path";
|
|
16368
16744
|
import {
|
|
16369
16745
|
deserializeTaskGraph,
|
|
16370
16746
|
GlobalMailbox,
|
|
@@ -16846,8 +17222,8 @@ function attachSessionKanbanMirror(context) {
|
|
|
16846
17222
|
const name = filename?.toString();
|
|
16847
17223
|
const currentPlanPath = context.meta["plan.path"];
|
|
16848
17224
|
const currentTaskPath = context.meta["task.path"];
|
|
16849
|
-
const planName = typeof currentPlanPath === "string" ?
|
|
16850
|
-
const taskName = typeof currentTaskPath === "string" ?
|
|
17225
|
+
const planName = typeof currentPlanPath === "string" ? basename10(currentPlanPath) : "";
|
|
17226
|
+
const taskName = typeof currentTaskPath === "string" ? basename10(currentTaskPath) : "";
|
|
16851
17227
|
if (name && name !== planName && name !== taskName) return;
|
|
16852
17228
|
if (timer) clearTimeout(timer);
|
|
16853
17229
|
timer = setTimeout(() => fireAndForget(refreshFiles()), 60);
|
|
@@ -18307,7 +18683,7 @@ async function dockerLogs(service, lines, filterRe, cwd, signal, since) {
|
|
|
18307
18683
|
}
|
|
18308
18684
|
var DOCKER_LOGS_TIMEOUT_MS = 3e3;
|
|
18309
18685
|
var MAX_TAIL_LINES = 1e5;
|
|
18310
|
-
async function fileLogs(
|
|
18686
|
+
async function fileLogs(path34, lines, filterRe, stream) {
|
|
18311
18687
|
const { createInterface } = await import("node:readline");
|
|
18312
18688
|
const { createReadStream: createReadStream2 } = await import("node:fs");
|
|
18313
18689
|
const entries = [];
|
|
@@ -18316,7 +18692,7 @@ async function fileLogs(path33, lines, filterRe, stream) {
|
|
|
18316
18692
|
let writeIdx = 0;
|
|
18317
18693
|
let totalLines = 0;
|
|
18318
18694
|
const rl = createInterface({
|
|
18319
|
-
input: createReadStream2(
|
|
18695
|
+
input: createReadStream2(path34),
|
|
18320
18696
|
crlfDelay: Number.POSITIVE_INFINITY
|
|
18321
18697
|
});
|
|
18322
18698
|
for await (const line of rl) {
|
|
@@ -18337,7 +18713,7 @@ async function fileLogs(path33, lines, filterRe, stream) {
|
|
|
18337
18713
|
if (parsed) entries.push(parsed);
|
|
18338
18714
|
}
|
|
18339
18715
|
return {
|
|
18340
|
-
source:
|
|
18716
|
+
source: path34,
|
|
18341
18717
|
entries,
|
|
18342
18718
|
total: entries.length,
|
|
18343
18719
|
truncated: totalLines > effLines,
|
|
@@ -18568,8 +18944,8 @@ function parseOutdatedOutput(json2, exitCode) {
|
|
|
18568
18944
|
init_util();
|
|
18569
18945
|
import { spawn as spawn13 } from "node:child_process";
|
|
18570
18946
|
import * as fs23 from "node:fs/promises";
|
|
18571
|
-
import * as
|
|
18572
|
-
import * as
|
|
18947
|
+
import * as os8 from "node:os";
|
|
18948
|
+
import * as path27 from "node:path";
|
|
18573
18949
|
import { buildChildEnv as buildChildEnv8 } from "@wrongstack/core";
|
|
18574
18950
|
var patchTool = {
|
|
18575
18951
|
name: "patch",
|
|
@@ -18605,9 +18981,9 @@ var patchTool = {
|
|
|
18605
18981
|
for (const t of targets) {
|
|
18606
18982
|
const stripped = stripPathComponents(t, strip);
|
|
18607
18983
|
if (!stripped) continue;
|
|
18608
|
-
const candidate =
|
|
18609
|
-
const rel =
|
|
18610
|
-
if (rel.startsWith("..") ||
|
|
18984
|
+
const candidate = path27.resolve(dir, stripped);
|
|
18985
|
+
const rel = path27.relative(ctx.projectRoot, candidate);
|
|
18986
|
+
if (rel.startsWith("..") || path27.isAbsolute(rel)) {
|
|
18611
18987
|
return {
|
|
18612
18988
|
applied: 0,
|
|
18613
18989
|
rejected: 1,
|
|
@@ -18624,11 +19000,11 @@ var patchTool = {
|
|
|
18624
19000
|
beforeContents.set(target, await readTextForTracking(target));
|
|
18625
19001
|
}
|
|
18626
19002
|
}
|
|
18627
|
-
const tmpDir = await fs23.mkdtemp(
|
|
19003
|
+
const tmpDir = await fs23.mkdtemp(path27.join(os8.tmpdir(), ".wstack_patch_"));
|
|
18628
19004
|
try {
|
|
18629
19005
|
await fs23.chmod(tmpDir, 448).catch(() => {
|
|
18630
19006
|
});
|
|
18631
|
-
const patchFile =
|
|
19007
|
+
const patchFile = path27.join(tmpDir, "in.diff");
|
|
18632
19008
|
await fs23.writeFile(patchFile, input.patch, { mode: 384 });
|
|
18633
19009
|
const args = [`-p${strip}`, "--merge", ...dryRun ? ["--dry-run"] : [], "-i", patchFile];
|
|
18634
19010
|
const result = await runPatch(args, dir, opts.signal);
|
|
@@ -19214,7 +19590,7 @@ ${interesting.join("\n")}` : "symbols/imports: (none detected)"
|
|
|
19214
19590
|
import { expectDefined as expectDefined8 } from "@wrongstack/core";
|
|
19215
19591
|
import { spawn as spawn14 } from "node:child_process";
|
|
19216
19592
|
import * as fs25 from "node:fs/promises";
|
|
19217
|
-
import * as
|
|
19593
|
+
import * as path28 from "node:path";
|
|
19218
19594
|
import {
|
|
19219
19595
|
atomicWrite as atomicWrite3,
|
|
19220
19596
|
buildChildEnv as buildChildEnv9,
|
|
@@ -19303,8 +19679,8 @@ var replaceTool = {
|
|
|
19303
19679
|
} catch {
|
|
19304
19680
|
continue;
|
|
19305
19681
|
}
|
|
19306
|
-
const rel =
|
|
19307
|
-
if (rel.startsWith("..") ||
|
|
19682
|
+
const rel = path28.relative(realRoot, realPath);
|
|
19683
|
+
if (rel.startsWith("..") || path28.isAbsolute(rel)) continue;
|
|
19308
19684
|
const stat18 = await fs25.stat(realPath).catch(() => null);
|
|
19309
19685
|
if (!stat18?.isFile()) continue;
|
|
19310
19686
|
let content;
|
|
@@ -19433,7 +19809,7 @@ async function globNative(pattern, base, extraGlob) {
|
|
|
19433
19809
|
}
|
|
19434
19810
|
for (const e of entries) {
|
|
19435
19811
|
if (DEFAULT_IGNORE4.includes(e.name)) continue;
|
|
19436
|
-
const full =
|
|
19812
|
+
const full = path28.join(dir, e.name);
|
|
19437
19813
|
try {
|
|
19438
19814
|
const stat18 = await fs25.lstat(full);
|
|
19439
19815
|
if (stat18.isSymbolicLink()) continue;
|
|
@@ -19460,7 +19836,7 @@ async function globNative(pattern, base, extraGlob) {
|
|
|
19460
19836
|
// src/scaffold.ts
|
|
19461
19837
|
init_util();
|
|
19462
19838
|
import * as fs26 from "node:fs/promises";
|
|
19463
|
-
import * as
|
|
19839
|
+
import * as path29 from "node:path";
|
|
19464
19840
|
import { atomicWrite as atomicWrite4 } from "@wrongstack/core";
|
|
19465
19841
|
var BUILT_IN_TEMPLATES = {
|
|
19466
19842
|
"npm-package": {
|
|
@@ -19610,16 +19986,16 @@ async function handleBuiltIn(name, templateFiles, cwd, ctx, dryRun, vars) {
|
|
|
19610
19986
|
let filesCreated = 0;
|
|
19611
19987
|
for (const [filePath, content] of Object.entries(templateFiles)) {
|
|
19612
19988
|
const resolvedPath = substituteVars(filePath, name, vars);
|
|
19613
|
-
const joinedPath =
|
|
19614
|
-
const root =
|
|
19615
|
-
const target =
|
|
19616
|
-
const rel =
|
|
19617
|
-
if (rel.startsWith("..") ||
|
|
19989
|
+
const joinedPath = path29.join(cwd, resolvedPath);
|
|
19990
|
+
const root = path29.resolve(ctx.projectRoot);
|
|
19991
|
+
const target = path29.resolve(joinedPath);
|
|
19992
|
+
const rel = path29.relative(root, target);
|
|
19993
|
+
if (rel.startsWith("..") || path29.isAbsolute(rel)) {
|
|
19618
19994
|
throw new Error(`scaffold: generated path "${resolvedPath}" would escape project root`);
|
|
19619
19995
|
}
|
|
19620
19996
|
const fullPath = target;
|
|
19621
19997
|
if (!dryRun) {
|
|
19622
|
-
await fs26.mkdir(
|
|
19998
|
+
await fs26.mkdir(path29.dirname(fullPath), { recursive: true });
|
|
19623
19999
|
await atomicWrite4(fullPath, substituteVars(content, name, vars));
|
|
19624
20000
|
}
|
|
19625
20001
|
files.push(resolvedPath);
|
|
@@ -20476,7 +20852,7 @@ ${formatTaskList2(file.tasks)}` : file.tasks.length > 0 ? formatTaskList2(file.t
|
|
|
20476
20852
|
init_spawn_stream();
|
|
20477
20853
|
init_util();
|
|
20478
20854
|
init_legacy_bridge();
|
|
20479
|
-
import * as
|
|
20855
|
+
import * as path30 from "node:path";
|
|
20480
20856
|
var testTool = {
|
|
20481
20857
|
name: "test",
|
|
20482
20858
|
category: "Code Quality",
|
|
@@ -20583,7 +20959,7 @@ async function detectRunner(cwd) {
|
|
|
20583
20959
|
const candidates = ["vitest.config.ts", "jest.config.js", ".mocharc.json"];
|
|
20584
20960
|
for (const f of candidates) {
|
|
20585
20961
|
try {
|
|
20586
|
-
await stat18(
|
|
20962
|
+
await stat18(path30.join(cwd, f));
|
|
20587
20963
|
if (f.includes("vitest")) return "vitest";
|
|
20588
20964
|
if (f.includes("jest")) return "jest";
|
|
20589
20965
|
if (f.includes("mocha")) return "mocha";
|
|
@@ -21054,7 +21430,7 @@ var toolUseTool = {
|
|
|
21054
21430
|
init_util();
|
|
21055
21431
|
import { expectDefined as expectDefined10 } from "@wrongstack/core";
|
|
21056
21432
|
import * as fs28 from "node:fs/promises";
|
|
21057
|
-
import * as
|
|
21433
|
+
import * as path31 from "node:path";
|
|
21058
21434
|
var DEFAULT_IGNORE5 = [
|
|
21059
21435
|
"node_modules",
|
|
21060
21436
|
".git",
|
|
@@ -21225,7 +21601,7 @@ async function walkDir(dir, depth, opts) {
|
|
|
21225
21601
|
opts.lines.push(opts.prefix + branch + displayName);
|
|
21226
21602
|
if (entry.isDirectory() && (opts.maxDepth === 0 || depth < opts.maxDepth)) {
|
|
21227
21603
|
const childPrefix = opts.prefix + connector;
|
|
21228
|
-
await walkDir(
|
|
21604
|
+
await walkDir(path31.join(dir, entry.name), depth + 1, {
|
|
21229
21605
|
...opts,
|
|
21230
21606
|
prefix: childPrefix,
|
|
21231
21607
|
isLast
|
|
@@ -21238,7 +21614,7 @@ async function walkDir(dir, depth, opts) {
|
|
|
21238
21614
|
init_spawn_stream();
|
|
21239
21615
|
init_util();
|
|
21240
21616
|
init_legacy_bridge();
|
|
21241
|
-
import * as
|
|
21617
|
+
import * as path32 from "node:path";
|
|
21242
21618
|
var typecheckTool = {
|
|
21243
21619
|
name: "typecheck",
|
|
21244
21620
|
category: "Code Quality",
|
|
@@ -21341,8 +21717,8 @@ async function findTsConfig(cwd) {
|
|
|
21341
21717
|
const candidates = ["tsconfig.json", "tsconfig.base.json"];
|
|
21342
21718
|
for (const f of candidates) {
|
|
21343
21719
|
try {
|
|
21344
|
-
const s = await stat18(
|
|
21345
|
-
if (s.isFile()) return
|
|
21720
|
+
const s = await stat18(path32.join(cwd, f));
|
|
21721
|
+
if (s.isFile()) return path32.join(cwd, f);
|
|
21346
21722
|
} catch {
|
|
21347
21723
|
}
|
|
21348
21724
|
}
|
|
@@ -21875,7 +22251,7 @@ var builtinToolsPack = {
|
|
|
21875
22251
|
};
|
|
21876
22252
|
|
|
21877
22253
|
// src/process-guardian.ts
|
|
21878
|
-
import * as
|
|
22254
|
+
import * as os9 from "node:os";
|
|
21879
22255
|
var ProcessGuardian = class {
|
|
21880
22256
|
registry;
|
|
21881
22257
|
config;
|
|
@@ -21914,7 +22290,7 @@ var ProcessGuardian = class {
|
|
|
21914
22290
|
event: "process_guardian.started",
|
|
21915
22291
|
instanceId: this.instanceId,
|
|
21916
22292
|
mainPid: process.pid,
|
|
21917
|
-
hostname:
|
|
22293
|
+
hostname: os9.hostname(),
|
|
21918
22294
|
platform: process.platform
|
|
21919
22295
|
}));
|
|
21920
22296
|
}
|
|
@@ -22103,8 +22479,8 @@ var ProcessGuardian = class {
|
|
|
22103
22479
|
instanceId: this.instanceId,
|
|
22104
22480
|
mainPid: process.pid,
|
|
22105
22481
|
protectedCount: this.protectedProcesses.size,
|
|
22106
|
-
platform:
|
|
22107
|
-
hostname:
|
|
22482
|
+
platform: os9.platform(),
|
|
22483
|
+
hostname: os9.hostname(),
|
|
22108
22484
|
uptime: process.uptime()
|
|
22109
22485
|
};
|
|
22110
22486
|
}
|
|
@@ -22133,7 +22509,7 @@ function stopProcessGuardian() {
|
|
|
22133
22509
|
init_process_registry();
|
|
22134
22510
|
|
|
22135
22511
|
// src/ps-slash.ts
|
|
22136
|
-
import * as
|
|
22512
|
+
import * as os10 from "node:os";
|
|
22137
22513
|
var IDLE_THRESHOLD_MS = 2 * 6e4;
|
|
22138
22514
|
var STALE_THRESHOLD_MS2 = 5 * 6e4;
|
|
22139
22515
|
function now() {
|
|
@@ -22174,7 +22550,7 @@ async function listInstances(options = {}) {
|
|
|
22174
22550
|
const mainProc = processes.find((p) => p.spawnMode === "main");
|
|
22175
22551
|
const firstProc = processes.at(0);
|
|
22176
22552
|
const mainPid = mainProc?.pid ?? firstProc?.pid ?? 0;
|
|
22177
|
-
const hostname_ = firstProc?.hostname ??
|
|
22553
|
+
const hostname_ = firstProc?.hostname ?? os10.hostname();
|
|
22178
22554
|
const startedAt = Math.min(...processes.map((p) => p.startedAt));
|
|
22179
22555
|
const lastActivity = Math.max(...processes.map((p) => p.lastHeartbeat));
|
|
22180
22556
|
const age = timestamp - lastActivity;
|
|
@@ -22262,7 +22638,7 @@ async function getGlobalProcessStatus() {
|
|
|
22262
22638
|
mainPid: process.pid,
|
|
22263
22639
|
protectedCount: 0,
|
|
22264
22640
|
platform: process.platform,
|
|
22265
|
-
hostname:
|
|
22641
|
+
hostname: os10.hostname(),
|
|
22266
22642
|
uptime: 0
|
|
22267
22643
|
},
|
|
22268
22644
|
allInstances: instances.map((inst) => ({
|
|
@@ -22447,7 +22823,7 @@ function createGlobalPsSlashCommand() {
|
|
|
22447
22823
|
|
|
22448
22824
|
// src/skill.ts
|
|
22449
22825
|
import * as fs30 from "node:fs/promises";
|
|
22450
|
-
import * as
|
|
22826
|
+
import * as path33 from "node:path";
|
|
22451
22827
|
import {
|
|
22452
22828
|
SKILL_LIMITS,
|
|
22453
22829
|
stripFrontmatter,
|
|
@@ -22494,7 +22870,7 @@ function makeSkillTool(skillLoader) {
|
|
|
22494
22870
|
field: "name"
|
|
22495
22871
|
});
|
|
22496
22872
|
}
|
|
22497
|
-
const dir =
|
|
22873
|
+
const dir = path33.dirname(manifest.path);
|
|
22498
22874
|
let loadedResource;
|
|
22499
22875
|
if (input.resource?.trim()) {
|
|
22500
22876
|
loadedResource = await loadResource(dir, input.resource.trim());
|
|
@@ -22544,15 +22920,15 @@ ${listing}`;
|
|
|
22544
22920
|
}
|
|
22545
22921
|
async function loadResource(skillDir, rel) {
|
|
22546
22922
|
const norm = rel.replace(/\\/g, "/");
|
|
22547
|
-
if (
|
|
22923
|
+
if (path33.isAbsolute(rel) || norm.split("/").some((seg) => seg === "..")) {
|
|
22548
22924
|
throw new ToolValidationError10({
|
|
22549
22925
|
message: `skill: invalid resource path "${rel}"`,
|
|
22550
22926
|
field: "resource"
|
|
22551
22927
|
});
|
|
22552
22928
|
}
|
|
22553
|
-
const absPath =
|
|
22554
|
-
const root =
|
|
22555
|
-
if (absPath !== root && !absPath.startsWith(root +
|
|
22929
|
+
const absPath = path33.resolve(skillDir, rel);
|
|
22930
|
+
const root = path33.resolve(skillDir);
|
|
22931
|
+
if (absPath !== root && !absPath.startsWith(root + path33.sep)) {
|
|
22556
22932
|
throw new ToolValidationError10({
|
|
22557
22933
|
message: `skill: resource "${rel}" escapes the skill directory`,
|
|
22558
22934
|
field: "resource"
|
|
@@ -22593,7 +22969,7 @@ async function walk(root, dir, out) {
|
|
|
22593
22969
|
}
|
|
22594
22970
|
for (const e of entries) {
|
|
22595
22971
|
if (out.length >= MAX_LISTED_RESOURCES) return;
|
|
22596
|
-
const fullPath =
|
|
22972
|
+
const fullPath = path33.join(dir, e.name);
|
|
22597
22973
|
let isDir = e.isDirectory();
|
|
22598
22974
|
if (e.isSymbolicLink()) {
|
|
22599
22975
|
try {
|
|
@@ -22609,7 +22985,7 @@ async function walk(root, dir, out) {
|
|
|
22609
22985
|
if (e.name === "SKILL.md" || e.name === "SKILL.save.md") continue;
|
|
22610
22986
|
try {
|
|
22611
22987
|
const stat18 = await fs30.stat(fullPath);
|
|
22612
|
-
const rel =
|
|
22988
|
+
const rel = path33.relative(root, fullPath).split(path33.sep).join("/");
|
|
22613
22989
|
out.push({ path: rel, bytes: stat18.size });
|
|
22614
22990
|
} catch {
|
|
22615
22991
|
}
|
|
@@ -22832,6 +23208,7 @@ export {
|
|
|
22832
23208
|
builtinTools,
|
|
22833
23209
|
builtinToolsPack,
|
|
22834
23210
|
cancelPendingReindexes,
|
|
23211
|
+
checkExecKillCommand,
|
|
22835
23212
|
codebaseIndexStats,
|
|
22836
23213
|
codebaseIndexTool,
|
|
22837
23214
|
codebaseSearchTool,
|