@wrongstack/tools 0.289.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/auto-proceed-loop-guard.d.ts +128 -0
- package/dist/auto-proceed-loop-guard.d.ts.map +1 -0
- package/dist/auto-proceed-loop-guard.js +46 -0
- package/dist/auto-proceed-loop-guard.js.map +7 -0
- 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 +662 -203
- package/dist/builtin.js.map +4 -4
- package/dist/codebase-index/background-indexer.d.ts +1 -1
- package/dist/codebase-index/background-indexer.d.ts.map +1 -1
- package/dist/codebase-index/index.js +181 -104
- package/dist/codebase-index/index.js.map +3 -3
- package/dist/codebase-index/indexer.d.ts.map +1 -1
- package/dist/codebase-index/refs-extractor.d.ts +2 -17
- package/dist/codebase-index/refs-extractor.d.ts.map +1 -1
- package/dist/codebase-index/ts-parser.d.ts.map +1 -1
- package/dist/codebase-index/worker.js +164 -96
- package/dist/codebase-index/worker.js.map +3 -3
- package/dist/codebase-index/writer.d.ts +33 -0
- package/dist/codebase-index/writer.d.ts.map +1 -1
- 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 +765 -237
- package/dist/index.js.map +4 -4
- package/dist/kanban.d.ts +10 -0
- package/dist/kanban.d.ts.map +1 -1
- package/dist/kanban.js +34 -17
- package/dist/kanban.js.map +2 -2
- package/dist/pack.js +662 -203
- package/dist/pack.js.map +4 -4
- package/dist/plan.js.map +2 -2
- package/dist/read.d.ts.map +1 -1
- package/dist/read.js.map +2 -2
- package/dist/session-kanban.d.ts.map +1 -1
- package/dist/session-kanban.js +60 -3
- package/dist/session-kanban.js.map +2 -2
- package/dist/task.js.map +2 -2
- package/dist/todo.js.map +2 -2
- package/package.json +9 -5
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]) {
|
|
5731
5747
|
return {
|
|
5732
|
-
|
|
5733
|
-
signal:
|
|
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]) {
|
|
5757
|
+
return {
|
|
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();
|
|
@@ -8099,6 +8225,53 @@ function codebaseIndexDirOverride(ctx) {
|
|
|
8099
8225
|
const v = ctx.meta?.["codebaseIndexDir"];
|
|
8100
8226
|
return typeof v === "string" ? v : void 0;
|
|
8101
8227
|
}
|
|
8228
|
+
var StorePool = class {
|
|
8229
|
+
stores = /* @__PURE__ */ new Map();
|
|
8230
|
+
key(projectRoot, indexDir) {
|
|
8231
|
+
return `${projectRoot}\0${indexDir ?? ""}`;
|
|
8232
|
+
}
|
|
8233
|
+
/** Borrow a store. Creates it on first access for this key. */
|
|
8234
|
+
acquire(projectRoot, opts) {
|
|
8235
|
+
const k = this.key(projectRoot, opts?.indexDir);
|
|
8236
|
+
let store = this.stores.get(k);
|
|
8237
|
+
if (!store) {
|
|
8238
|
+
store = new IndexStore(projectRoot, { indexDir: opts?.indexDir });
|
|
8239
|
+
this.stores.set(k, store);
|
|
8240
|
+
}
|
|
8241
|
+
return store;
|
|
8242
|
+
}
|
|
8243
|
+
/** Return the store to the pool. The connection stays warm for subsequent
|
|
8244
|
+
* operations on the same (projectRoot, indexDir). */
|
|
8245
|
+
release(_store) {
|
|
8246
|
+
}
|
|
8247
|
+
/** Close every pooled connection and drain the pool. Call on shutdown. */
|
|
8248
|
+
closeAll() {
|
|
8249
|
+
for (const store of this.stores.values()) {
|
|
8250
|
+
try {
|
|
8251
|
+
store.close();
|
|
8252
|
+
} catch {
|
|
8253
|
+
}
|
|
8254
|
+
}
|
|
8255
|
+
this.stores.clear();
|
|
8256
|
+
}
|
|
8257
|
+
/** Remove one store from the pool. Used by tests that need isolation. */
|
|
8258
|
+
evict(projectRoot, indexDir) {
|
|
8259
|
+
const k = this.key(projectRoot, indexDir);
|
|
8260
|
+
const store = this.stores.get(k);
|
|
8261
|
+
if (store) {
|
|
8262
|
+
try {
|
|
8263
|
+
store.close();
|
|
8264
|
+
} catch {
|
|
8265
|
+
}
|
|
8266
|
+
this.stores.delete(k);
|
|
8267
|
+
}
|
|
8268
|
+
}
|
|
8269
|
+
/** True when the pool holds a connection for the given key. */
|
|
8270
|
+
has(projectRoot, indexDir) {
|
|
8271
|
+
return this.stores.has(this.key(projectRoot, indexDir));
|
|
8272
|
+
}
|
|
8273
|
+
};
|
|
8274
|
+
var indexStorePool = new StorePool();
|
|
8102
8275
|
var warningSilenced = false;
|
|
8103
8276
|
function silenceSqliteExperimentalWarning() {
|
|
8104
8277
|
if (warningSilenced) return;
|
|
@@ -8316,7 +8489,7 @@ var IndexStore = class _IndexStore {
|
|
|
8316
8489
|
return this.runWithRetry(() => {
|
|
8317
8490
|
this.db.exec("BEGIN IMMEDIATE");
|
|
8318
8491
|
try {
|
|
8319
|
-
const maxRows = this.
|
|
8492
|
+
const maxRows = this.stmt("SELECT MAX(id) AS m FROM symbols").all();
|
|
8320
8493
|
let nextId = (maxRows[0]?.m ?? 0) + 1;
|
|
8321
8494
|
const stmt = this.db.prepare(
|
|
8322
8495
|
`INSERT INTO symbols(id, lang, kind, name, file, line, col, signature, doc_comment, scope, text, file_fk)
|
|
@@ -8588,7 +8761,7 @@ var IndexStore = class _IndexStore {
|
|
|
8588
8761
|
return { results, total: candidates.length };
|
|
8589
8762
|
}
|
|
8590
8763
|
getAllIndexable() {
|
|
8591
|
-
return this.
|
|
8764
|
+
return this.stmt("SELECT id, text FROM symbols").all().map(({ id, text }) => ({ id, text }));
|
|
8592
8765
|
}
|
|
8593
8766
|
/**
|
|
8594
8767
|
* Largest symbol id currently in the table (0 when empty). New ids must be
|
|
@@ -8598,7 +8771,7 @@ var IndexStore = class _IndexStore {
|
|
|
8598
8771
|
* `symbols.id`). Ids may have gaps — that is fine.
|
|
8599
8772
|
*/
|
|
8600
8773
|
getMaxSymbolId() {
|
|
8601
|
-
const rows = this.
|
|
8774
|
+
const rows = this.stmt("SELECT MAX(id) AS m FROM symbols").all();
|
|
8602
8775
|
return rows[0]?.m ?? 0;
|
|
8603
8776
|
}
|
|
8604
8777
|
// ─── Stats ───────────────────────────────────────────────────────────────────
|
|
@@ -8606,14 +8779,14 @@ var IndexStore = class _IndexStore {
|
|
|
8606
8779
|
const sizeBytes = this.sizeBytes();
|
|
8607
8780
|
const lastRows = this.db.prepare("SELECT value FROM metadata WHERE key = 'last_indexed'").all();
|
|
8608
8781
|
const lastIndexed = lastRows.length ? Number(lastRows[0]?.value) : null;
|
|
8609
|
-
const totalRows = this.
|
|
8782
|
+
const totalRows = this.stmt("SELECT COUNT(*) FROM symbols").all();
|
|
8610
8783
|
const totalSymbols = totalRows[0] ? Number(totalRows[0]["COUNT(*)"]) : 0;
|
|
8611
|
-
const fileRows = this.
|
|
8784
|
+
const fileRows = this.stmt("SELECT COUNT(*) FROM files").all();
|
|
8612
8785
|
const totalFiles = fileRows[0] ? Number(fileRows[0]["COUNT(*)"]) : 0;
|
|
8613
|
-
const langRows = this.
|
|
8786
|
+
const langRows = this.stmt("SELECT lang, COUNT(*) FROM symbols GROUP BY lang").all();
|
|
8614
8787
|
const byLang = {};
|
|
8615
8788
|
for (const row of langRows) byLang[row.lang] = Number(row["COUNT(*)"]);
|
|
8616
|
-
const kindRows = this.
|
|
8789
|
+
const kindRows = this.stmt("SELECT kind, COUNT(*) FROM symbols GROUP BY kind").all();
|
|
8617
8790
|
const byKind = {};
|
|
8618
8791
|
for (const row of kindRows) byKind[row.kind] = Number(row["COUNT(*)"]);
|
|
8619
8792
|
return {
|
|
@@ -8645,11 +8818,14 @@ var IndexStore = class _IndexStore {
|
|
|
8645
8818
|
this.runWithRetry(() => {
|
|
8646
8819
|
this.db.exec("BEGIN IMMEDIATE");
|
|
8647
8820
|
try {
|
|
8648
|
-
this.db.exec("
|
|
8649
|
-
this.db.exec("
|
|
8650
|
-
this.db.exec("
|
|
8651
|
-
|
|
8821
|
+
this.db.exec("DROP TABLE IF EXISTS refs");
|
|
8822
|
+
this.db.exec("DROP TABLE IF EXISTS symbols");
|
|
8823
|
+
this.db.exec("DROP TABLE IF EXISTS files");
|
|
8824
|
+
this.db.exec("DROP TABLE IF EXISTS metadata");
|
|
8825
|
+
if (this.ftsAvailable) this.db.exec("DROP TABLE IF EXISTS symbols_fts");
|
|
8652
8826
|
this.db.exec("COMMIT");
|
|
8827
|
+
this.stmtCache.clear();
|
|
8828
|
+
this.initSchema();
|
|
8653
8829
|
} catch (err) {
|
|
8654
8830
|
this.db.exec("ROLLBACK");
|
|
8655
8831
|
throw err;
|
|
@@ -8734,7 +8910,7 @@ var IndexStore = class _IndexStore {
|
|
|
8734
8910
|
).run(...options.deleteForFiles);
|
|
8735
8911
|
this.db.prepare(`DELETE FROM symbols WHERE file IN (${placeholders})`).run(...options.deleteForFiles);
|
|
8736
8912
|
}
|
|
8737
|
-
const maxRows = this.
|
|
8913
|
+
const maxRows = this.stmt("SELECT MAX(id) AS m FROM symbols").all();
|
|
8738
8914
|
let nextId = (maxRows[0]?.m ?? 0) + 1;
|
|
8739
8915
|
const symStmt = this.db.prepare(
|
|
8740
8916
|
`INSERT INTO symbols(id, lang, kind, name, file, line, col, signature, doc_comment, scope, text, file_fk)
|
|
@@ -8918,7 +9094,7 @@ var IndexStore = class _IndexStore {
|
|
|
8918
9094
|
* symbol resolved in package B). Node metadata includes symbol/file counts.
|
|
8919
9095
|
*/
|
|
8920
9096
|
getPackageGraph() {
|
|
8921
|
-
const symbols = this.db.prepare("SELECT file, id
|
|
9097
|
+
const symbols = this.db.prepare("SELECT file, id FROM symbols ORDER BY id").all();
|
|
8922
9098
|
const pkgNodes = /* @__PURE__ */ new Map();
|
|
8923
9099
|
const fileToPkg = /* @__PURE__ */ new Map();
|
|
8924
9100
|
const symbolToPkg = /* @__PURE__ */ new Map();
|
|
@@ -9009,15 +9185,18 @@ var IndexStore = class _IndexStore {
|
|
|
9009
9185
|
* derived from cross-file symbol references within the package.
|
|
9010
9186
|
*/
|
|
9011
9187
|
getFileGraph(packageFilter) {
|
|
9012
|
-
const
|
|
9013
|
-
const
|
|
9014
|
-
|
|
9015
|
-
);
|
|
9016
|
-
|
|
9188
|
+
const allFiles = this.db.prepare("SELECT DISTINCT file FROM symbols").all();
|
|
9189
|
+
const pkgFilePaths = allFiles.filter((f) => (_IndexStore.derivePackage(f.file) ?? "(root)") === packageFilter).map((f) => f.file);
|
|
9190
|
+
const localFiles = new Set(pkgFilePaths);
|
|
9191
|
+
if (localFiles.size === 0) return { nodes: [], edges: [] };
|
|
9192
|
+
const filePlaceholders = [...localFiles].map(() => "?").join(",");
|
|
9193
|
+
const pkgSyms = this.db.prepare(
|
|
9194
|
+
`SELECT file, id, name, kind, lang, line FROM symbols WHERE file IN (${filePlaceholders}) ORDER BY id`
|
|
9195
|
+
).all(...pkgFilePaths);
|
|
9017
9196
|
const fileNodes = /* @__PURE__ */ new Map();
|
|
9018
9197
|
const symToFile = /* @__PURE__ */ new Map();
|
|
9019
9198
|
const fileStats = /* @__PURE__ */ new Map();
|
|
9020
|
-
for (const s of
|
|
9199
|
+
for (const s of pkgSyms) {
|
|
9021
9200
|
symToFile.set(s.id, s.file);
|
|
9022
9201
|
const current = fileStats.get(s.file);
|
|
9023
9202
|
fileStats.set(s.file, {
|
|
@@ -9025,8 +9204,6 @@ var IndexStore = class _IndexStore {
|
|
|
9025
9204
|
lang: current?.lang ?? s.lang
|
|
9026
9205
|
});
|
|
9027
9206
|
}
|
|
9028
|
-
const localFiles = new Set(pkgSyms.map((s) => s.file));
|
|
9029
|
-
const indexedFiles = new Set(allSymbols.map((s) => s.file));
|
|
9030
9207
|
const ensureFileNode = (file) => {
|
|
9031
9208
|
if (fileNodes.has(file)) return;
|
|
9032
9209
|
const stats = fileStats.get(file);
|
|
@@ -9044,11 +9221,32 @@ var IndexStore = class _IndexStore {
|
|
|
9044
9221
|
for (const file of localFiles) {
|
|
9045
9222
|
ensureFileNode(file);
|
|
9046
9223
|
}
|
|
9224
|
+
const indexedFiles = new Set(allFiles.map((f) => f.file));
|
|
9047
9225
|
const refRows = this.db.prepare(
|
|
9048
9226
|
`SELECT r.from_id, r.to_id, r.call_type
|
|
9049
9227
|
FROM refs r
|
|
9050
|
-
WHERE r.
|
|
9051
|
-
|
|
9228
|
+
WHERE (r.from_id IN (SELECT id FROM symbols WHERE file IN (${filePlaceholders}))
|
|
9229
|
+
OR r.to_id IN (SELECT id FROM symbols WHERE file IN (${filePlaceholders})))
|
|
9230
|
+
AND r.to_id IS NOT NULL`
|
|
9231
|
+
).all(...pkgFilePaths, ...pkgFilePaths);
|
|
9232
|
+
const knownSymIds = new Set(pkgSyms.map((s) => s.id));
|
|
9233
|
+
const crossRefIds = /* @__PURE__ */ new Set();
|
|
9234
|
+
for (const r of refRows) {
|
|
9235
|
+
if (!knownSymIds.has(r.from_id)) crossRefIds.add(r.from_id);
|
|
9236
|
+
if (!knownSymIds.has(r.to_id)) crossRefIds.add(r.to_id);
|
|
9237
|
+
}
|
|
9238
|
+
if (crossRefIds.size > 0) {
|
|
9239
|
+
const crossPlaceholders = [...crossRefIds].map(() => "?").join(",");
|
|
9240
|
+
const extras = this.db.prepare(
|
|
9241
|
+
`SELECT id, file FROM symbols WHERE id IN (${crossPlaceholders})`
|
|
9242
|
+
).all(...crossRefIds);
|
|
9243
|
+
for (const x of extras) {
|
|
9244
|
+
symToFile.set(x.id, x.file);
|
|
9245
|
+
if (!fileStats.has(x.file)) {
|
|
9246
|
+
fileStats.set(x.file, { count: 0, lang: "ts" });
|
|
9247
|
+
}
|
|
9248
|
+
}
|
|
9249
|
+
}
|
|
9052
9250
|
const edgeMap = /* @__PURE__ */ new Map();
|
|
9053
9251
|
for (const r of refRows) {
|
|
9054
9252
|
if (r.call_type === "import") continue;
|
|
@@ -9070,8 +9268,9 @@ var IndexStore = class _IndexStore {
|
|
|
9070
9268
|
const importRows = this.db.prepare(
|
|
9071
9269
|
`SELECT r.from_id, r.to_name
|
|
9072
9270
|
FROM refs r
|
|
9073
|
-
WHERE r.call_type = 'import'
|
|
9074
|
-
|
|
9271
|
+
WHERE r.call_type = 'import'
|
|
9272
|
+
AND r.from_id IN (SELECT id FROM symbols WHERE file IN (${filePlaceholders}))`
|
|
9273
|
+
).all(...pkgFilePaths);
|
|
9075
9274
|
for (const r of importRows) {
|
|
9076
9275
|
const fromFile = symToFile.get(r.from_id);
|
|
9077
9276
|
if (!fromFile || !localFiles.has(fromFile)) continue;
|
|
@@ -9113,12 +9312,11 @@ var IndexStore = class _IndexStore {
|
|
|
9113
9312
|
* derived from intra-file and cross-file symbol references (who calls whom).
|
|
9114
9313
|
*/
|
|
9115
9314
|
getSymbolGraph(fileFilter) {
|
|
9116
|
-
const
|
|
9117
|
-
"SELECT id, name, kind, lang, file, line, signature, scope FROM symbols ORDER BY
|
|
9118
|
-
).all();
|
|
9119
|
-
const syms = allSymbols.filter((symbol) => symbol.file === fileFilter);
|
|
9315
|
+
const syms = this.db.prepare(
|
|
9316
|
+
"SELECT id, name, kind, lang, file, line, signature, scope FROM symbols WHERE file = ? ORDER BY line, id"
|
|
9317
|
+
).all(fileFilter);
|
|
9120
9318
|
if (syms.length === 0) return { nodes: [], edges: [] };
|
|
9121
|
-
const symById = new Map(
|
|
9319
|
+
const symById = new Map(syms.map((symbol) => [symbol.id, symbol]));
|
|
9122
9320
|
const relatedIds = new Set(syms.map((symbol) => symbol.id));
|
|
9123
9321
|
const toGraphNode = (s) => ({
|
|
9124
9322
|
id: `sym:${s.id}`,
|
|
@@ -9174,6 +9372,15 @@ var IndexStore = class _IndexStore {
|
|
|
9174
9372
|
refType: bestType
|
|
9175
9373
|
});
|
|
9176
9374
|
}
|
|
9375
|
+
const loadedIds = new Set(syms.map((s) => s.id));
|
|
9376
|
+
const missingIds = [...relatedIds].filter((id) => !loadedIds.has(id));
|
|
9377
|
+
if (missingIds.length > 0) {
|
|
9378
|
+
const placeholders = missingIds.map(() => "?").join(",");
|
|
9379
|
+
const extras = this.db.prepare(
|
|
9380
|
+
`SELECT id, name, kind, lang, file, line, signature, scope FROM symbols WHERE id IN (${placeholders})`
|
|
9381
|
+
).all(...missingIds);
|
|
9382
|
+
for (const s of extras) symById.set(s.id, s);
|
|
9383
|
+
}
|
|
9177
9384
|
const nodes = [...relatedIds].map((id) => symById.get(id)).filter((symbol) => symbol !== void 0).sort((a, b) => {
|
|
9178
9385
|
const aExternal = a.file === fileFilter ? 0 : 1;
|
|
9179
9386
|
const bExternal = b.file === fileFilter ? 0 : 1;
|
|
@@ -9199,6 +9406,7 @@ import { Worker } from "node:worker_threads";
|
|
|
9199
9406
|
import { expectDefined as expectDefined6 } from "@wrongstack/core";
|
|
9200
9407
|
import * as fs14 from "node:fs/promises";
|
|
9201
9408
|
import * as path19 from "node:path";
|
|
9409
|
+
import { availableParallelism } from "node:os";
|
|
9202
9410
|
import { compileGlob as compileGlob2 } from "@wrongstack/core";
|
|
9203
9411
|
|
|
9204
9412
|
// src/codebase-index/ts-parser.ts
|
|
@@ -9255,8 +9463,7 @@ function extToLang(ext) {
|
|
|
9255
9463
|
return null;
|
|
9256
9464
|
}
|
|
9257
9465
|
}
|
|
9258
|
-
function getSignature(node, sourceFile) {
|
|
9259
|
-
const printer = ts.createPrinter({});
|
|
9466
|
+
function getSignature(printer, node, sourceFile) {
|
|
9260
9467
|
const raw = printer.printNode(ts.EmitHint.Unspecified, node, sourceFile);
|
|
9261
9468
|
return raw.replace(/\s+/g, " ").slice(0, 500);
|
|
9262
9469
|
}
|
|
@@ -9275,28 +9482,14 @@ function getJsDoc(node, sourceFile) {
|
|
|
9275
9482
|
}
|
|
9276
9483
|
return "";
|
|
9277
9484
|
}
|
|
9278
|
-
function
|
|
9279
|
-
|
|
9280
|
-
|
|
9281
|
-
|
|
9282
|
-
|
|
9283
|
-
|
|
9284
|
-
return false;
|
|
9285
|
-
}
|
|
9286
|
-
function buildScope(node) {
|
|
9287
|
-
const parts = [];
|
|
9288
|
-
let current = node.parent;
|
|
9289
|
-
while (current) {
|
|
9290
|
-
if (ts.isClassDeclaration(current) || ts.isInterfaceDeclaration(current) || ts.isEnumDeclaration(current) || ts.isTypeAliasDeclaration(current)) {
|
|
9291
|
-
parts.unshift(current.name?.text ?? "Anon");
|
|
9292
|
-
} else if (ts.isMethodDeclaration(current) || ts.isGetAccessor(current) || ts.isSetAccessor(current) || ts.isPropertyDeclaration(current) || ts.isFunctionDeclaration(current)) {
|
|
9293
|
-
if (current.name && ts.isIdentifier(current.name)) {
|
|
9294
|
-
parts.unshift(current.name.text);
|
|
9295
|
-
}
|
|
9485
|
+
function pushScopeName(node, parts) {
|
|
9486
|
+
if (ts.isClassDeclaration(node) || ts.isInterfaceDeclaration(node) || ts.isEnumDeclaration(node) || ts.isTypeAliasDeclaration(node)) {
|
|
9487
|
+
parts.push(node.name?.text ?? "Anon");
|
|
9488
|
+
} else if (ts.isMethodDeclaration(node) || ts.isGetAccessor(node) || ts.isSetAccessor(node) || ts.isPropertyDeclaration(node) || ts.isFunctionDeclaration(node)) {
|
|
9489
|
+
if (node.name && ts.isIdentifier(node.name)) {
|
|
9490
|
+
parts.push(node.name.text);
|
|
9296
9491
|
}
|
|
9297
|
-
current = current.parent;
|
|
9298
9492
|
}
|
|
9299
|
-
return parts.join(".");
|
|
9300
9493
|
}
|
|
9301
9494
|
function parseSymbols(opts) {
|
|
9302
9495
|
const { file, content, lang } = opts;
|
|
@@ -9307,45 +9500,39 @@ function parseSymbols(opts) {
|
|
|
9307
9500
|
return { file, lang, symbols: [], mtimeMs: Date.now() };
|
|
9308
9501
|
}
|
|
9309
9502
|
const symbols = [];
|
|
9310
|
-
|
|
9503
|
+
const refs = [];
|
|
9504
|
+
const printer = ts.createPrinter({});
|
|
9505
|
+
function visit(node, funcDepth, scopeParts) {
|
|
9311
9506
|
const kind = kindOf(node);
|
|
9312
9507
|
if (kind) {
|
|
9313
|
-
if ((kind === "const" || kind === "let" || kind === "var" || kind === "parameter") &&
|
|
9314
|
-
|
|
9315
|
-
|
|
9508
|
+
if ((kind === "const" || kind === "let" || kind === "var" || kind === "parameter") && funcDepth > 0) {
|
|
9509
|
+
} else {
|
|
9510
|
+
const nameNode = node.name;
|
|
9511
|
+
if (!nameNode || !ts.isIdentifier(nameNode)) {
|
|
9512
|
+
return;
|
|
9513
|
+
}
|
|
9514
|
+
const name = nameNode.text;
|
|
9515
|
+
const pos2 = nameNode.getStart(sourceFile);
|
|
9516
|
+
const { line: line2, character } = sourceFile.getLineAndCharacterOfPosition(pos2);
|
|
9517
|
+
const scope = scopeParts.join(".");
|
|
9518
|
+
const signature = getSignature(printer, node, sourceFile);
|
|
9519
|
+
const docComment = getJsDoc(node, sourceFile);
|
|
9520
|
+
const text = [name, signature, docComment].filter(Boolean).join(" | ");
|
|
9521
|
+
symbols.push({
|
|
9522
|
+
id: 0,
|
|
9523
|
+
lang,
|
|
9524
|
+
kind,
|
|
9525
|
+
name,
|
|
9526
|
+
file,
|
|
9527
|
+
line: line2 + 1,
|
|
9528
|
+
col: character,
|
|
9529
|
+
signature,
|
|
9530
|
+
docComment,
|
|
9531
|
+
scope,
|
|
9532
|
+
text
|
|
9533
|
+
});
|
|
9316
9534
|
}
|
|
9317
|
-
const nameNode = node.name;
|
|
9318
|
-
if (!nameNode || !ts.isIdentifier(nameNode)) return;
|
|
9319
|
-
const name = nameNode.text;
|
|
9320
|
-
const pos = nameNode.getStart(sourceFile);
|
|
9321
|
-
const { line, character } = sourceFile.getLineAndCharacterOfPosition(pos);
|
|
9322
|
-
const scope = buildScope(node);
|
|
9323
|
-
const signature = getSignature(node, sourceFile);
|
|
9324
|
-
const docComment = getJsDoc(node, sourceFile);
|
|
9325
|
-
const text = [name, signature, docComment].filter(Boolean).join(" | ");
|
|
9326
|
-
symbols.push({
|
|
9327
|
-
id: 0,
|
|
9328
|
-
lang,
|
|
9329
|
-
kind,
|
|
9330
|
-
name,
|
|
9331
|
-
file,
|
|
9332
|
-
line: line + 1,
|
|
9333
|
-
col: character,
|
|
9334
|
-
signature,
|
|
9335
|
-
docComment,
|
|
9336
|
-
scope,
|
|
9337
|
-
text
|
|
9338
|
-
});
|
|
9339
9535
|
}
|
|
9340
|
-
ts.forEachChild(node, visit);
|
|
9341
|
-
}
|
|
9342
|
-
visit(sourceFile);
|
|
9343
|
-
const refs = extractRefs(sourceFile);
|
|
9344
|
-
return { file, lang, symbols, refs, mtimeMs: Date.now() };
|
|
9345
|
-
}
|
|
9346
|
-
function extractRefs(sourceFile) {
|
|
9347
|
-
const refs = [];
|
|
9348
|
-
function visit(node) {
|
|
9349
9536
|
const pos = node.getStart(sourceFile);
|
|
9350
9537
|
const { line } = sourceFile.getLineAndCharacterOfPosition(pos);
|
|
9351
9538
|
const lineNum = line + 1;
|
|
@@ -9370,10 +9557,14 @@ function extractRefs(sourceFile) {
|
|
|
9370
9557
|
const moduleName = getModuleName(node);
|
|
9371
9558
|
if (moduleName) refs.push({ fromId: 0, toName: moduleName, callType: "import", line: lineNum });
|
|
9372
9559
|
}
|
|
9373
|
-
|
|
9560
|
+
const scopeIdx = scopeParts.length;
|
|
9561
|
+
pushScopeName(node, scopeParts);
|
|
9562
|
+
const childFuncDepth = ts.isFunctionLike(node) ? funcDepth + 1 : funcDepth;
|
|
9563
|
+
ts.forEachChild(node, (child) => visit(child, childFuncDepth, scopeParts));
|
|
9564
|
+
scopeParts.length = scopeIdx;
|
|
9374
9565
|
}
|
|
9375
|
-
visit(sourceFile);
|
|
9376
|
-
return deduplicateRefs(refs);
|
|
9566
|
+
visit(sourceFile, 0, []);
|
|
9567
|
+
return { file, lang, symbols, refs: deduplicateRefs(refs), mtimeMs: Date.now() };
|
|
9377
9568
|
}
|
|
9378
9569
|
function getTypeName(name) {
|
|
9379
9570
|
if (ts.isIdentifier(name)) return name.text;
|
|
@@ -10258,9 +10449,9 @@ function parseSymbols5(opts) {
|
|
|
10258
10449
|
function regexParse2(opts) {
|
|
10259
10450
|
const { file, content, lang } = opts;
|
|
10260
10451
|
const symbols = [];
|
|
10261
|
-
const
|
|
10262
|
-
const isPackageJson =
|
|
10263
|
-
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";
|
|
10264
10455
|
const isJsonSchema = content.includes("$schema") || content.includes("$id") || content.includes("$ref");
|
|
10265
10456
|
const isOpenApi = content.includes("openapi") || content.includes("swagger");
|
|
10266
10457
|
const lines = content.split("\n");
|
|
@@ -10655,7 +10846,7 @@ async function loadGitignoreMatcher(projectRoot) {
|
|
|
10655
10846
|
|
|
10656
10847
|
// src/codebase-index/indexer.ts
|
|
10657
10848
|
var YIELD_EVERY_N = 50;
|
|
10658
|
-
var PARALLEL_BATCH =
|
|
10849
|
+
var PARALLEL_BATCH = Math.min(availableParallelism() * 4, 40);
|
|
10659
10850
|
function yieldEventLoop() {
|
|
10660
10851
|
return new Promise((resolve15) => setImmediate(resolve15));
|
|
10661
10852
|
}
|
|
@@ -10833,11 +11024,14 @@ async function runIndexerWithStore(store, opts) {
|
|
|
10833
11024
|
if (!force) {
|
|
10834
11025
|
for (const meta of store.getAllFileMetas()) existingMeta.set(meta.file, meta);
|
|
10835
11026
|
}
|
|
11027
|
+
let filesSinceLastYield = 0;
|
|
10836
11028
|
for (let batchStart = 0; batchStart < files.length; batchStart += PARALLEL_BATCH) {
|
|
10837
11029
|
const batchEnd = Math.min(batchStart + PARALLEL_BATCH, files.length);
|
|
10838
11030
|
const batchFiles = files.slice(batchStart, batchEnd);
|
|
10839
11031
|
opts.onProgress?.(batchEnd, files.length);
|
|
10840
|
-
|
|
11032
|
+
filesSinceLastYield += batchFiles.length;
|
|
11033
|
+
if (filesSinceLastYield >= YIELD_EVERY_N) {
|
|
11034
|
+
filesSinceLastYield = 0;
|
|
10841
11035
|
await yieldEventLoop();
|
|
10842
11036
|
throwIfAborted(signal);
|
|
10843
11037
|
}
|
|
@@ -11048,7 +11242,7 @@ async function indexService(args, hooks = {}) {
|
|
|
11048
11242
|
});
|
|
11049
11243
|
}
|
|
11050
11244
|
function searchService(args) {
|
|
11051
|
-
const store =
|
|
11245
|
+
const store = indexStorePool.acquire(args.projectRoot, { indexDir: args.indexDir });
|
|
11052
11246
|
try {
|
|
11053
11247
|
return store.searchRanked(
|
|
11054
11248
|
args.query,
|
|
@@ -11061,39 +11255,39 @@ function searchService(args) {
|
|
|
11061
11255
|
args.limit
|
|
11062
11256
|
);
|
|
11063
11257
|
} finally {
|
|
11064
|
-
|
|
11258
|
+
indexStorePool.release(store);
|
|
11065
11259
|
}
|
|
11066
11260
|
}
|
|
11067
11261
|
function statsService(args) {
|
|
11068
|
-
const store =
|
|
11262
|
+
const store = indexStorePool.acquire(args.projectRoot, { indexDir: args.indexDir });
|
|
11069
11263
|
try {
|
|
11070
11264
|
return store.getStats();
|
|
11071
11265
|
} finally {
|
|
11072
|
-
|
|
11266
|
+
indexStorePool.release(store);
|
|
11073
11267
|
}
|
|
11074
11268
|
}
|
|
11075
11269
|
function packageGraphService(args) {
|
|
11076
|
-
const store =
|
|
11270
|
+
const store = indexStorePool.acquire(args.projectRoot, { indexDir: args.indexDir });
|
|
11077
11271
|
try {
|
|
11078
11272
|
return store.getPackageGraph();
|
|
11079
11273
|
} finally {
|
|
11080
|
-
|
|
11274
|
+
indexStorePool.release(store);
|
|
11081
11275
|
}
|
|
11082
11276
|
}
|
|
11083
11277
|
function fileGraphService(args) {
|
|
11084
|
-
const store =
|
|
11278
|
+
const store = indexStorePool.acquire(args.projectRoot, { indexDir: args.indexDir });
|
|
11085
11279
|
try {
|
|
11086
11280
|
return store.getFileGraph(args.packageFilter);
|
|
11087
11281
|
} finally {
|
|
11088
|
-
|
|
11282
|
+
indexStorePool.release(store);
|
|
11089
11283
|
}
|
|
11090
11284
|
}
|
|
11091
11285
|
function symbolGraphService(args) {
|
|
11092
|
-
const store =
|
|
11286
|
+
const store = indexStorePool.acquire(args.projectRoot, { indexDir: args.indexDir });
|
|
11093
11287
|
try {
|
|
11094
11288
|
return store.getSymbolGraph(args.fileFilter);
|
|
11095
11289
|
} finally {
|
|
11096
|
-
|
|
11290
|
+
indexStorePool.release(store);
|
|
11097
11291
|
}
|
|
11098
11292
|
}
|
|
11099
11293
|
|
|
@@ -11210,10 +11404,19 @@ function terminateWorker(reason) {
|
|
|
11210
11404
|
if (w) void w.terminate().catch(() => {
|
|
11211
11405
|
});
|
|
11212
11406
|
}
|
|
11213
|
-
function shutdownCodebaseIndexHost() {
|
|
11407
|
+
async function shutdownCodebaseIndexHost() {
|
|
11214
11408
|
cancelPendingReindexes();
|
|
11215
|
-
|
|
11409
|
+
indexStorePool.closeAll();
|
|
11410
|
+
const w = worker;
|
|
11411
|
+
worker = null;
|
|
11412
|
+
failAllPending(new Error("codebase-index host shut down"));
|
|
11216
11413
|
workerUnavailable = false;
|
|
11414
|
+
if (w) {
|
|
11415
|
+
try {
|
|
11416
|
+
await w.terminate();
|
|
11417
|
+
} catch {
|
|
11418
|
+
}
|
|
11419
|
+
}
|
|
11217
11420
|
}
|
|
11218
11421
|
function callIndexOp(op, args, opts) {
|
|
11219
11422
|
const w = ensureWorker();
|
|
@@ -13212,7 +13415,244 @@ init_output_spool();
|
|
|
13212
13415
|
init_util();
|
|
13213
13416
|
init_process_registry();
|
|
13214
13417
|
init_win32_resolve();
|
|
13215
|
-
|
|
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";
|
|
13216
13656
|
var DEFAULT_ALLOWED_COMMANDS = /* @__PURE__ */ new Set([
|
|
13217
13657
|
// JS / TS toolchain
|
|
13218
13658
|
"node",
|
|
@@ -14017,6 +14457,19 @@ var execTool = {
|
|
|
14017
14457
|
const args = (input.args ?? []).slice(0, MAX_ARGS);
|
|
14018
14458
|
const timeout = Math.max(1, Math.min(input.timeout ?? DEFAULT_TIMEOUT_MS3, DEFAULT_TIMEOUT_MS3));
|
|
14019
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
|
+
}
|
|
14020
14473
|
const argError = validateArgs(cmd, args);
|
|
14021
14474
|
if (argError) {
|
|
14022
14475
|
return {
|
|
@@ -14067,7 +14520,7 @@ function runCommand(cmd, args, cwd, timeout, signal, sessionId, danger) {
|
|
|
14067
14520
|
let timedOut = false;
|
|
14068
14521
|
const spool = createOutputSpool({ tool: `exec-${cmd}`, thresholdBytes: MAX_OUTPUT2 });
|
|
14069
14522
|
const resolved = resolveWin32Command(cmd);
|
|
14070
|
-
const needsShell =
|
|
14523
|
+
const needsShell = isWin4 && (resolved.endsWith(".cmd") || resolved.endsWith(".bat"));
|
|
14071
14524
|
const shim = needsShell ? buildWin32CmdShimInvocation(resolved, args) : null;
|
|
14072
14525
|
const spawnCmd = shim?.command ?? resolved;
|
|
14073
14526
|
const spawnArgs = shim?.args ?? args;
|
|
@@ -14092,7 +14545,7 @@ function runCommand(cmd, args, cwd, timeout, signal, sessionId, danger) {
|
|
|
14092
14545
|
env: buildChildEnv2(sessionId),
|
|
14093
14546
|
stdio: ["ignore", "pipe", "pipe"],
|
|
14094
14547
|
windowsHide: true,
|
|
14095
|
-
...
|
|
14548
|
+
...isWin4 ? {} : { signal },
|
|
14096
14549
|
...shim ? { windowsVerbatimArguments: shim.windowsVerbatimArguments } : {}
|
|
14097
14550
|
});
|
|
14098
14551
|
} catch (err) {
|
|
@@ -14131,7 +14584,7 @@ function runCommand(cmd, args, cwd, timeout, signal, sessionId, danger) {
|
|
|
14131
14584
|
const isAbort = err && err.code === "ABORT_ERR";
|
|
14132
14585
|
const stderrText = isAbort ? `Aborted: ${err.message}` : err.message;
|
|
14133
14586
|
clearTimeout(timer);
|
|
14134
|
-
if (
|
|
14587
|
+
if (isWin4) signal.removeEventListener("abort", onAbort);
|
|
14135
14588
|
if (typeof pid === "number") registry.unregister(pid);
|
|
14136
14589
|
registry.afterCall(Date.now() - startedAt, true);
|
|
14137
14590
|
spool.finalize();
|
|
@@ -14164,7 +14617,7 @@ function runCommand(cmd, args, cwd, timeout, signal, sessionId, danger) {
|
|
|
14164
14617
|
if (typeof pid === "number") registry.kill(pid, { force: true });
|
|
14165
14618
|
else child.kill("SIGTERM");
|
|
14166
14619
|
};
|
|
14167
|
-
if (
|
|
14620
|
+
if (isWin4) {
|
|
14168
14621
|
if (signal.aborted) onAbort();
|
|
14169
14622
|
else signal.addEventListener("abort", onAbort, { once: true });
|
|
14170
14623
|
}
|
|
@@ -14184,7 +14637,7 @@ function runCommand(cmd, args, cwd, timeout, signal, sessionId, danger) {
|
|
|
14184
14637
|
});
|
|
14185
14638
|
child.on("close", (code) => {
|
|
14186
14639
|
clearTimeout(timer);
|
|
14187
|
-
if (
|
|
14640
|
+
if (isWin4) signal.removeEventListener("abort", onAbort);
|
|
14188
14641
|
if (typeof pid === "number") registry.unregister(pid);
|
|
14189
14642
|
const durationMs = Date.now() - startedAt;
|
|
14190
14643
|
const exitCode = killed ? 124 : code ?? 1;
|
|
@@ -14995,7 +15448,7 @@ function runGit2(args, cwd, signal) {
|
|
|
14995
15448
|
|
|
14996
15449
|
// src/glob.ts
|
|
14997
15450
|
import * as fs20 from "node:fs/promises";
|
|
14998
|
-
import * as
|
|
15451
|
+
import * as path25 from "node:path";
|
|
14999
15452
|
import { compileGlob as compileGlob3 } from "@wrongstack/core";
|
|
15000
15453
|
|
|
15001
15454
|
// src/_concurrency.ts
|
|
@@ -15099,7 +15552,7 @@ var globTool = {
|
|
|
15099
15552
|
if (DEFAULT_IGNORE2.includes(name)) continue;
|
|
15100
15553
|
if (ignored.includes(name)) continue;
|
|
15101
15554
|
const rel = relPrefix ? `${relPrefix}/${name}` : name;
|
|
15102
|
-
const full =
|
|
15555
|
+
const full = path25.join(dir, name);
|
|
15103
15556
|
if (e.isDirectory()) {
|
|
15104
15557
|
subdirs.push({ full, rel });
|
|
15105
15558
|
} else if (e.isFile()) {
|
|
@@ -15143,7 +15596,7 @@ var globTool = {
|
|
|
15143
15596
|
};
|
|
15144
15597
|
async function readGitignore(dir) {
|
|
15145
15598
|
try {
|
|
15146
|
-
const raw = await fs20.readFile(
|
|
15599
|
+
const raw = await fs20.readFile(path25.join(dir, ".gitignore"), "utf8");
|
|
15147
15600
|
return raw.split("\n").map((l) => l.trim()).filter((l) => l && !l.startsWith("#"));
|
|
15148
15601
|
} catch {
|
|
15149
15602
|
return [];
|
|
@@ -15154,7 +15607,7 @@ async function readGitignore(dir) {
|
|
|
15154
15607
|
import { expectDefined as expectDefined7 } from "@wrongstack/core";
|
|
15155
15608
|
import { spawn as spawn10 } from "node:child_process";
|
|
15156
15609
|
import * as fs21 from "node:fs/promises";
|
|
15157
|
-
import * as
|
|
15610
|
+
import * as path26 from "node:path";
|
|
15158
15611
|
import { buildChildEnv as buildChildEnv5, compileGlob as compileGlob4, ToolValidationError as ToolValidationError4 } from "@wrongstack/core";
|
|
15159
15612
|
|
|
15160
15613
|
// src/_regex.ts
|
|
@@ -15547,7 +16000,7 @@ async function runNative(input, base, mode, limit, signal) {
|
|
|
15547
16000
|
if (stopped) return;
|
|
15548
16001
|
if (DEFAULT_IGNORE3.includes(e.name)) continue;
|
|
15549
16002
|
if (e.isSymbolicLink()) continue;
|
|
15550
|
-
const full =
|
|
16003
|
+
const full = path26.join(dir, e.name);
|
|
15551
16004
|
if (e.isDirectory()) {
|
|
15552
16005
|
subdirs.push(full);
|
|
15553
16006
|
} else if (e.isFile()) {
|
|
@@ -16135,56 +16588,56 @@ function jmespathSearch(data, query) {
|
|
|
16135
16588
|
}
|
|
16136
16589
|
function validateJsonSchema(data, schema) {
|
|
16137
16590
|
const errors = [];
|
|
16138
|
-
function check(value, s,
|
|
16591
|
+
function check(value, s, path34) {
|
|
16139
16592
|
if (s["type"]) {
|
|
16140
16593
|
const expectedType = s["type"];
|
|
16141
16594
|
const actualType = Array.isArray(value) ? "array" : value === null ? "null" : typeof value;
|
|
16142
16595
|
if (expectedType === "integer") {
|
|
16143
|
-
if (!Number.isInteger(value)) errors.push(`${
|
|
16596
|
+
if (!Number.isInteger(value)) errors.push(`${path34}: expected integer, got ${actualType}`);
|
|
16144
16597
|
} else if (expectedType !== actualType) {
|
|
16145
|
-
errors.push(`${
|
|
16598
|
+
errors.push(`${path34}: expected ${expectedType}, got ${actualType}`);
|
|
16146
16599
|
}
|
|
16147
16600
|
}
|
|
16148
16601
|
if (typeof value === "string" && s["format"] === "uri" && value) {
|
|
16149
16602
|
try {
|
|
16150
16603
|
new URL(value);
|
|
16151
16604
|
} catch {
|
|
16152
|
-
errors.push(`${
|
|
16605
|
+
errors.push(`${path34}: not a valid URI`);
|
|
16153
16606
|
}
|
|
16154
16607
|
}
|
|
16155
16608
|
if (typeof value === "string" && s["pattern"]) {
|
|
16156
16609
|
const re = new RegExp(s["pattern"]);
|
|
16157
|
-
if (!re.test(value)) errors.push(`${
|
|
16610
|
+
if (!re.test(value)) errors.push(`${path34}: does not match pattern ${s["pattern"]}`);
|
|
16158
16611
|
}
|
|
16159
16612
|
if (typeof value === "string" && s["minLength"] !== void 0 && value.length < s["minLength"]) {
|
|
16160
|
-
errors.push(`${
|
|
16613
|
+
errors.push(`${path34}: string too short (min ${s["minLength"]})`);
|
|
16161
16614
|
}
|
|
16162
16615
|
if (typeof value === "string" && s["maxLength"] !== void 0 && value.length > s["maxLength"]) {
|
|
16163
|
-
errors.push(`${
|
|
16616
|
+
errors.push(`${path34}: string too long (max ${s["maxLength"]})`);
|
|
16164
16617
|
}
|
|
16165
16618
|
if (typeof value === "number" && s["minimum"] !== void 0 && value < s["minimum"]) {
|
|
16166
|
-
errors.push(`${
|
|
16619
|
+
errors.push(`${path34}: below minimum ${s["minimum"]}`);
|
|
16167
16620
|
}
|
|
16168
16621
|
if (typeof value === "number" && s["maximum"] !== void 0 && value > s["maximum"]) {
|
|
16169
|
-
errors.push(`${
|
|
16622
|
+
errors.push(`${path34}: above maximum ${s["maximum"]}`);
|
|
16170
16623
|
}
|
|
16171
16624
|
if (Array.isArray(value) && s["items"] && Array.isArray(s["items"])) {
|
|
16172
16625
|
for (let i = 0; i < value.length; i++) {
|
|
16173
|
-
check(value[i], s["items"], `${
|
|
16626
|
+
check(value[i], s["items"], `${path34}[${i}]`);
|
|
16174
16627
|
}
|
|
16175
16628
|
}
|
|
16176
16629
|
if (typeof value === "object" && value !== null && !Array.isArray(value) && s["properties"]) {
|
|
16177
16630
|
const props = s["properties"];
|
|
16178
16631
|
for (const [k, propSchema] of Object.entries(props)) {
|
|
16179
|
-
check(value[k], propSchema, `${
|
|
16632
|
+
check(value[k], propSchema, `${path34}.${k}`);
|
|
16180
16633
|
}
|
|
16181
16634
|
}
|
|
16182
16635
|
}
|
|
16183
16636
|
check(data, schema, "$");
|
|
16184
16637
|
return { valid: errors.length === 0, errors };
|
|
16185
16638
|
}
|
|
16186
|
-
function simpleQuery(data,
|
|
16187
|
-
const parts =
|
|
16639
|
+
function simpleQuery(data, path34) {
|
|
16640
|
+
const parts = path34.replace(/\[(\d+)\]/g, ".$1").split(".").filter(Boolean);
|
|
16188
16641
|
let current = data;
|
|
16189
16642
|
for (const part of parts) {
|
|
16190
16643
|
if (current === null || current === void 0) return void 0;
|
|
@@ -16252,7 +16705,7 @@ import {
|
|
|
16252
16705
|
duplicateBoard,
|
|
16253
16706
|
exportBoardAsMarkdown,
|
|
16254
16707
|
exportBoardToTaskGraph,
|
|
16255
|
-
|
|
16708
|
+
createBoardFromText,
|
|
16256
16709
|
getBoard as getBoard2,
|
|
16257
16710
|
getKanbanOrchestrationSnapshot,
|
|
16258
16711
|
getKanbanQueueHealth,
|
|
@@ -16287,7 +16740,7 @@ import {
|
|
|
16287
16740
|
|
|
16288
16741
|
// src/session-kanban.ts
|
|
16289
16742
|
import { watch } from "node:fs";
|
|
16290
|
-
import { basename as
|
|
16743
|
+
import { basename as basename10, dirname as dirname13 } from "node:path";
|
|
16291
16744
|
import {
|
|
16292
16745
|
deserializeTaskGraph,
|
|
16293
16746
|
GlobalMailbox,
|
|
@@ -16317,12 +16770,17 @@ var SESSION_KANBAN_COLUMNS = [
|
|
|
16317
16770
|
];
|
|
16318
16771
|
var boardQueue = /* @__PURE__ */ new Map();
|
|
16319
16772
|
var boardEnsures = /* @__PURE__ */ new Map();
|
|
16773
|
+
var pendingMirrors = /* @__PURE__ */ new Map();
|
|
16774
|
+
var activeMirrors = /* @__PURE__ */ new Set();
|
|
16320
16775
|
var bindings = /* @__PURE__ */ new WeakMap();
|
|
16321
16776
|
var suppressedTodoMirrors = /* @__PURE__ */ new WeakSet();
|
|
16322
16777
|
var activeSessionBoards = /* @__PURE__ */ new Map();
|
|
16323
16778
|
function boardKey(projectRoot, sessionId) {
|
|
16324
16779
|
return `${projectRoot}\0${sessionId}`;
|
|
16325
16780
|
}
|
|
16781
|
+
function mirrorKey(projectRoot, sessionId, sourceSystem) {
|
|
16782
|
+
return `${boardKey(projectRoot, sessionId)}\0${sourceSystem}`;
|
|
16783
|
+
}
|
|
16326
16784
|
function sessionTag(sessionId) {
|
|
16327
16785
|
return `session:${sessionId}`;
|
|
16328
16786
|
}
|
|
@@ -16458,6 +16916,43 @@ async function projectGraph(projectRoot, sessionId, graph, sourceSystem) {
|
|
|
16458
16916
|
return result?.board ?? null;
|
|
16459
16917
|
});
|
|
16460
16918
|
}
|
|
16919
|
+
function queueLatestMirror(projectRoot, sessionId, graph, sourceSystem) {
|
|
16920
|
+
if (!projectRoot || !sessionId || process.env[MIRROR_DISABLED_ENV] === "0") return;
|
|
16921
|
+
const key = mirrorKey(projectRoot, sessionId, sourceSystem);
|
|
16922
|
+
pendingMirrors.set(key, { projectRoot, sessionId, graph, sourceSystem });
|
|
16923
|
+
if (activeMirrors.has(key)) return;
|
|
16924
|
+
activeMirrors.add(key);
|
|
16925
|
+
void (async () => {
|
|
16926
|
+
try {
|
|
16927
|
+
for (; ; ) {
|
|
16928
|
+
const pending2 = pendingMirrors.get(key);
|
|
16929
|
+
if (!pending2) break;
|
|
16930
|
+
pendingMirrors.delete(key);
|
|
16931
|
+
try {
|
|
16932
|
+
await projectGraph(
|
|
16933
|
+
pending2.projectRoot,
|
|
16934
|
+
pending2.sessionId,
|
|
16935
|
+
pending2.graph,
|
|
16936
|
+
pending2.sourceSystem
|
|
16937
|
+
);
|
|
16938
|
+
} catch {
|
|
16939
|
+
}
|
|
16940
|
+
}
|
|
16941
|
+
} finally {
|
|
16942
|
+
activeMirrors.delete(key);
|
|
16943
|
+
const pending2 = pendingMirrors.get(key);
|
|
16944
|
+
if (pending2) {
|
|
16945
|
+
pendingMirrors.delete(key);
|
|
16946
|
+
queueLatestMirror(
|
|
16947
|
+
pending2.projectRoot,
|
|
16948
|
+
pending2.sessionId,
|
|
16949
|
+
pending2.graph,
|
|
16950
|
+
pending2.sourceSystem
|
|
16951
|
+
);
|
|
16952
|
+
}
|
|
16953
|
+
}
|
|
16954
|
+
})();
|
|
16955
|
+
}
|
|
16461
16956
|
function todoListToSerializedGraph(todos, sessionId) {
|
|
16462
16957
|
const nodes = todos.map((todo, index) => ({
|
|
16463
16958
|
id: todo.id,
|
|
@@ -16608,13 +17103,28 @@ Reassess your current plan before continuing; do not rely on the initial todo sn
|
|
|
16608
17103
|
}
|
|
16609
17104
|
}
|
|
16610
17105
|
function mirrorSessionTodosToKanban(projectRoot, todos, sessionId) {
|
|
16611
|
-
|
|
17106
|
+
queueLatestMirror(
|
|
17107
|
+
projectRoot,
|
|
17108
|
+
sessionId,
|
|
17109
|
+
todoListToSerializedGraph(todos, sessionId),
|
|
17110
|
+
"session-todo"
|
|
17111
|
+
);
|
|
16612
17112
|
}
|
|
16613
17113
|
function mirrorSessionTasksToKanban(projectRoot, tasks, sessionId) {
|
|
16614
|
-
|
|
17114
|
+
queueLatestMirror(
|
|
17115
|
+
projectRoot,
|
|
17116
|
+
sessionId,
|
|
17117
|
+
taskFileToSerializedGraph(tasks, sessionId),
|
|
17118
|
+
"session-task"
|
|
17119
|
+
);
|
|
16615
17120
|
}
|
|
16616
17121
|
function mirrorSessionPlanToKanban(projectRoot, items, sessionId) {
|
|
16617
|
-
|
|
17122
|
+
queueLatestMirror(
|
|
17123
|
+
projectRoot,
|
|
17124
|
+
sessionId,
|
|
17125
|
+
planFileToSerializedGraph(items, sessionId),
|
|
17126
|
+
"session-plan"
|
|
17127
|
+
);
|
|
16618
17128
|
}
|
|
16619
17129
|
function attachSessionKanbanMirror(context) {
|
|
16620
17130
|
const existing = bindings.get(context);
|
|
@@ -16712,8 +17222,8 @@ function attachSessionKanbanMirror(context) {
|
|
|
16712
17222
|
const name = filename?.toString();
|
|
16713
17223
|
const currentPlanPath = context.meta["plan.path"];
|
|
16714
17224
|
const currentTaskPath = context.meta["task.path"];
|
|
16715
|
-
const planName = typeof currentPlanPath === "string" ?
|
|
16716
|
-
const taskName = typeof currentTaskPath === "string" ?
|
|
17225
|
+
const planName = typeof currentPlanPath === "string" ? basename10(currentPlanPath) : "";
|
|
17226
|
+
const taskName = typeof currentTaskPath === "string" ? basename10(currentTaskPath) : "";
|
|
16717
17227
|
if (name && name !== planName && name !== taskName) return;
|
|
16718
17228
|
if (timer) clearTimeout(timer);
|
|
16719
17229
|
timer = setTimeout(() => fireAndForget(refreshFiles()), 60);
|
|
@@ -17158,7 +17668,7 @@ var kanbanTool = {
|
|
|
17158
17668
|
}
|
|
17159
17669
|
case "generate_board": {
|
|
17160
17670
|
if (!input.description) return fail("generate_board requires description.");
|
|
17161
|
-
const boardInput =
|
|
17671
|
+
const boardInput = createBoardFromText({
|
|
17162
17672
|
description: input.description,
|
|
17163
17673
|
...input.title !== void 0 ? { title: input.title } : {},
|
|
17164
17674
|
...input.context !== void 0 ? { context: input.context } : {},
|
|
@@ -17543,20 +18053,31 @@ var kanbanTool = {
|
|
|
17543
18053
|
if (!input.boardId || !input.taskId)
|
|
17544
18054
|
return fail("mark_assignment requires boardId and taskId.");
|
|
17545
18055
|
const assignmentStatus = input.assignmentStatus ?? (input.status === "completed" ? "completed" : input.error ? "failed" : void 0);
|
|
17546
|
-
const board = await updateTaskAssignment(
|
|
17547
|
-
|
|
17548
|
-
|
|
17549
|
-
|
|
17550
|
-
|
|
17551
|
-
|
|
17552
|
-
|
|
17553
|
-
|
|
17554
|
-
|
|
17555
|
-
|
|
17556
|
-
|
|
17557
|
-
|
|
17558
|
-
|
|
17559
|
-
|
|
18056
|
+
const board = await updateTaskAssignment(
|
|
18057
|
+
projectRoot,
|
|
18058
|
+
input.boardId,
|
|
18059
|
+
input.taskId,
|
|
18060
|
+
{
|
|
18061
|
+
...assignmentStatus !== void 0 ? { status: assignmentStatus } : {},
|
|
18062
|
+
...input.subagentId !== void 0 ? { subagentId: input.subagentId } : {},
|
|
18063
|
+
...input.runTaskId !== void 0 ? { runTaskId: input.runTaskId } : {},
|
|
18064
|
+
...input.lastResult !== void 0 ? { lastResult: input.lastResult } : {},
|
|
18065
|
+
...input.error !== void 0 ? { error: input.error } : {},
|
|
18066
|
+
...input.agentId !== void 0 ? { agentId: input.agentId } : {},
|
|
18067
|
+
...input.leaseId !== void 0 ? { leaseId: input.leaseId } : {},
|
|
18068
|
+
...input.claimedAt !== void 0 ? { claimedAt: input.claimedAt } : {},
|
|
18069
|
+
...input.heartbeatAt !== void 0 ? { heartbeatAt: input.heartbeatAt } : {},
|
|
18070
|
+
...input.leaseExpiresAt !== void 0 ? { leaseExpiresAt: input.leaseExpiresAt } : {},
|
|
18071
|
+
...input.attempt !== void 0 ? { attempt: input.attempt } : {},
|
|
18072
|
+
...input.maxAttempts !== void 0 ? { maxAttempts: input.maxAttempts } : {}
|
|
18073
|
+
},
|
|
18074
|
+
// Ownership fence: when expectedLeaseId is supplied, the write is
|
|
18075
|
+
// applied only if the current assignment still holds this lease.
|
|
18076
|
+
// This prevents a recovered+reassigned stale worker's terminal
|
|
18077
|
+
// mark_assignment from overwriting the successor's state. The check
|
|
18078
|
+
// is atomic inside updateTaskAssignment's mutateBoard lock.
|
|
18079
|
+
input.expectedLeaseId !== void 0 ? { expectedLeaseId: input.expectedLeaseId } : {}
|
|
18080
|
+
);
|
|
17560
18081
|
return board ? okBoard(board, "Assignment updated.") : fail("Task not found.");
|
|
17561
18082
|
}
|
|
17562
18083
|
case "heartbeat_assignment": {
|
|
@@ -17565,7 +18086,13 @@ var kanbanTool = {
|
|
|
17565
18086
|
}
|
|
17566
18087
|
const board = await heartbeatTaskAssignment(projectRoot, input.boardId, input.taskId, {
|
|
17567
18088
|
...input.heartbeatAt !== void 0 ? { heartbeatAt: input.heartbeatAt } : {},
|
|
17568
|
-
...input.leaseExpiresAt !== void 0 ? { leaseExpiresAt: input.leaseExpiresAt } : {}
|
|
18089
|
+
...input.leaseExpiresAt !== void 0 ? { leaseExpiresAt: input.leaseExpiresAt } : {},
|
|
18090
|
+
// Ownership fence: when expectedLeaseId is supplied, the renewal
|
|
18091
|
+
// is applied only if the current assignment still holds this lease.
|
|
18092
|
+
// This prevents a recovered+reassigned stale worker's heartbeat
|
|
18093
|
+
// from renewing the successor's lease. The check is atomic inside
|
|
18094
|
+
// heartbeatTaskAssignment's mutateBoard lock.
|
|
18095
|
+
...input.expectedLeaseId !== void 0 ? { expectedLeaseId: input.expectedLeaseId } : {}
|
|
17569
18096
|
});
|
|
17570
18097
|
return board ? okBoard(board, "Assignment heartbeat updated.") : fail("Task assignment not found.");
|
|
17571
18098
|
}
|
|
@@ -18156,7 +18683,7 @@ async function dockerLogs(service, lines, filterRe, cwd, signal, since) {
|
|
|
18156
18683
|
}
|
|
18157
18684
|
var DOCKER_LOGS_TIMEOUT_MS = 3e3;
|
|
18158
18685
|
var MAX_TAIL_LINES = 1e5;
|
|
18159
|
-
async function fileLogs(
|
|
18686
|
+
async function fileLogs(path34, lines, filterRe, stream) {
|
|
18160
18687
|
const { createInterface } = await import("node:readline");
|
|
18161
18688
|
const { createReadStream: createReadStream2 } = await import("node:fs");
|
|
18162
18689
|
const entries = [];
|
|
@@ -18165,7 +18692,7 @@ async function fileLogs(path33, lines, filterRe, stream) {
|
|
|
18165
18692
|
let writeIdx = 0;
|
|
18166
18693
|
let totalLines = 0;
|
|
18167
18694
|
const rl = createInterface({
|
|
18168
|
-
input: createReadStream2(
|
|
18695
|
+
input: createReadStream2(path34),
|
|
18169
18696
|
crlfDelay: Number.POSITIVE_INFINITY
|
|
18170
18697
|
});
|
|
18171
18698
|
for await (const line of rl) {
|
|
@@ -18186,7 +18713,7 @@ async function fileLogs(path33, lines, filterRe, stream) {
|
|
|
18186
18713
|
if (parsed) entries.push(parsed);
|
|
18187
18714
|
}
|
|
18188
18715
|
return {
|
|
18189
|
-
source:
|
|
18716
|
+
source: path34,
|
|
18190
18717
|
entries,
|
|
18191
18718
|
total: entries.length,
|
|
18192
18719
|
truncated: totalLines > effLines,
|
|
@@ -18417,8 +18944,8 @@ function parseOutdatedOutput(json2, exitCode) {
|
|
|
18417
18944
|
init_util();
|
|
18418
18945
|
import { spawn as spawn13 } from "node:child_process";
|
|
18419
18946
|
import * as fs23 from "node:fs/promises";
|
|
18420
|
-
import * as
|
|
18421
|
-
import * as
|
|
18947
|
+
import * as os8 from "node:os";
|
|
18948
|
+
import * as path27 from "node:path";
|
|
18422
18949
|
import { buildChildEnv as buildChildEnv8 } from "@wrongstack/core";
|
|
18423
18950
|
var patchTool = {
|
|
18424
18951
|
name: "patch",
|
|
@@ -18454,9 +18981,9 @@ var patchTool = {
|
|
|
18454
18981
|
for (const t of targets) {
|
|
18455
18982
|
const stripped = stripPathComponents(t, strip);
|
|
18456
18983
|
if (!stripped) continue;
|
|
18457
|
-
const candidate =
|
|
18458
|
-
const rel =
|
|
18459
|
-
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)) {
|
|
18460
18987
|
return {
|
|
18461
18988
|
applied: 0,
|
|
18462
18989
|
rejected: 1,
|
|
@@ -18473,11 +19000,11 @@ var patchTool = {
|
|
|
18473
19000
|
beforeContents.set(target, await readTextForTracking(target));
|
|
18474
19001
|
}
|
|
18475
19002
|
}
|
|
18476
|
-
const tmpDir = await fs23.mkdtemp(
|
|
19003
|
+
const tmpDir = await fs23.mkdtemp(path27.join(os8.tmpdir(), ".wstack_patch_"));
|
|
18477
19004
|
try {
|
|
18478
19005
|
await fs23.chmod(tmpDir, 448).catch(() => {
|
|
18479
19006
|
});
|
|
18480
|
-
const patchFile =
|
|
19007
|
+
const patchFile = path27.join(tmpDir, "in.diff");
|
|
18481
19008
|
await fs23.writeFile(patchFile, input.patch, { mode: 384 });
|
|
18482
19009
|
const args = [`-p${strip}`, "--merge", ...dryRun ? ["--dry-run"] : [], "-i", patchFile];
|
|
18483
19010
|
const result = await runPatch(args, dir, opts.signal);
|
|
@@ -19063,7 +19590,7 @@ ${interesting.join("\n")}` : "symbols/imports: (none detected)"
|
|
|
19063
19590
|
import { expectDefined as expectDefined8 } from "@wrongstack/core";
|
|
19064
19591
|
import { spawn as spawn14 } from "node:child_process";
|
|
19065
19592
|
import * as fs25 from "node:fs/promises";
|
|
19066
|
-
import * as
|
|
19593
|
+
import * as path28 from "node:path";
|
|
19067
19594
|
import {
|
|
19068
19595
|
atomicWrite as atomicWrite3,
|
|
19069
19596
|
buildChildEnv as buildChildEnv9,
|
|
@@ -19152,8 +19679,8 @@ var replaceTool = {
|
|
|
19152
19679
|
} catch {
|
|
19153
19680
|
continue;
|
|
19154
19681
|
}
|
|
19155
|
-
const rel =
|
|
19156
|
-
if (rel.startsWith("..") ||
|
|
19682
|
+
const rel = path28.relative(realRoot, realPath);
|
|
19683
|
+
if (rel.startsWith("..") || path28.isAbsolute(rel)) continue;
|
|
19157
19684
|
const stat18 = await fs25.stat(realPath).catch(() => null);
|
|
19158
19685
|
if (!stat18?.isFile()) continue;
|
|
19159
19686
|
let content;
|
|
@@ -19282,7 +19809,7 @@ async function globNative(pattern, base, extraGlob) {
|
|
|
19282
19809
|
}
|
|
19283
19810
|
for (const e of entries) {
|
|
19284
19811
|
if (DEFAULT_IGNORE4.includes(e.name)) continue;
|
|
19285
|
-
const full =
|
|
19812
|
+
const full = path28.join(dir, e.name);
|
|
19286
19813
|
try {
|
|
19287
19814
|
const stat18 = await fs25.lstat(full);
|
|
19288
19815
|
if (stat18.isSymbolicLink()) continue;
|
|
@@ -19309,7 +19836,7 @@ async function globNative(pattern, base, extraGlob) {
|
|
|
19309
19836
|
// src/scaffold.ts
|
|
19310
19837
|
init_util();
|
|
19311
19838
|
import * as fs26 from "node:fs/promises";
|
|
19312
|
-
import * as
|
|
19839
|
+
import * as path29 from "node:path";
|
|
19313
19840
|
import { atomicWrite as atomicWrite4 } from "@wrongstack/core";
|
|
19314
19841
|
var BUILT_IN_TEMPLATES = {
|
|
19315
19842
|
"npm-package": {
|
|
@@ -19459,16 +19986,16 @@ async function handleBuiltIn(name, templateFiles, cwd, ctx, dryRun, vars) {
|
|
|
19459
19986
|
let filesCreated = 0;
|
|
19460
19987
|
for (const [filePath, content] of Object.entries(templateFiles)) {
|
|
19461
19988
|
const resolvedPath = substituteVars(filePath, name, vars);
|
|
19462
|
-
const joinedPath =
|
|
19463
|
-
const root =
|
|
19464
|
-
const target =
|
|
19465
|
-
const rel =
|
|
19466
|
-
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)) {
|
|
19467
19994
|
throw new Error(`scaffold: generated path "${resolvedPath}" would escape project root`);
|
|
19468
19995
|
}
|
|
19469
19996
|
const fullPath = target;
|
|
19470
19997
|
if (!dryRun) {
|
|
19471
|
-
await fs26.mkdir(
|
|
19998
|
+
await fs26.mkdir(path29.dirname(fullPath), { recursive: true });
|
|
19472
19999
|
await atomicWrite4(fullPath, substituteVars(content, name, vars));
|
|
19473
20000
|
}
|
|
19474
20001
|
files.push(resolvedPath);
|
|
@@ -20325,7 +20852,7 @@ ${formatTaskList2(file.tasks)}` : file.tasks.length > 0 ? formatTaskList2(file.t
|
|
|
20325
20852
|
init_spawn_stream();
|
|
20326
20853
|
init_util();
|
|
20327
20854
|
init_legacy_bridge();
|
|
20328
|
-
import * as
|
|
20855
|
+
import * as path30 from "node:path";
|
|
20329
20856
|
var testTool = {
|
|
20330
20857
|
name: "test",
|
|
20331
20858
|
category: "Code Quality",
|
|
@@ -20432,7 +20959,7 @@ async function detectRunner(cwd) {
|
|
|
20432
20959
|
const candidates = ["vitest.config.ts", "jest.config.js", ".mocharc.json"];
|
|
20433
20960
|
for (const f of candidates) {
|
|
20434
20961
|
try {
|
|
20435
|
-
await stat18(
|
|
20962
|
+
await stat18(path30.join(cwd, f));
|
|
20436
20963
|
if (f.includes("vitest")) return "vitest";
|
|
20437
20964
|
if (f.includes("jest")) return "jest";
|
|
20438
20965
|
if (f.includes("mocha")) return "mocha";
|
|
@@ -20903,7 +21430,7 @@ var toolUseTool = {
|
|
|
20903
21430
|
init_util();
|
|
20904
21431
|
import { expectDefined as expectDefined10 } from "@wrongstack/core";
|
|
20905
21432
|
import * as fs28 from "node:fs/promises";
|
|
20906
|
-
import * as
|
|
21433
|
+
import * as path31 from "node:path";
|
|
20907
21434
|
var DEFAULT_IGNORE5 = [
|
|
20908
21435
|
"node_modules",
|
|
20909
21436
|
".git",
|
|
@@ -21074,7 +21601,7 @@ async function walkDir(dir, depth, opts) {
|
|
|
21074
21601
|
opts.lines.push(opts.prefix + branch + displayName);
|
|
21075
21602
|
if (entry.isDirectory() && (opts.maxDepth === 0 || depth < opts.maxDepth)) {
|
|
21076
21603
|
const childPrefix = opts.prefix + connector;
|
|
21077
|
-
await walkDir(
|
|
21604
|
+
await walkDir(path31.join(dir, entry.name), depth + 1, {
|
|
21078
21605
|
...opts,
|
|
21079
21606
|
prefix: childPrefix,
|
|
21080
21607
|
isLast
|
|
@@ -21087,7 +21614,7 @@ async function walkDir(dir, depth, opts) {
|
|
|
21087
21614
|
init_spawn_stream();
|
|
21088
21615
|
init_util();
|
|
21089
21616
|
init_legacy_bridge();
|
|
21090
|
-
import * as
|
|
21617
|
+
import * as path32 from "node:path";
|
|
21091
21618
|
var typecheckTool = {
|
|
21092
21619
|
name: "typecheck",
|
|
21093
21620
|
category: "Code Quality",
|
|
@@ -21190,8 +21717,8 @@ async function findTsConfig(cwd) {
|
|
|
21190
21717
|
const candidates = ["tsconfig.json", "tsconfig.base.json"];
|
|
21191
21718
|
for (const f of candidates) {
|
|
21192
21719
|
try {
|
|
21193
|
-
const s = await stat18(
|
|
21194
|
-
if (s.isFile()) return
|
|
21720
|
+
const s = await stat18(path32.join(cwd, f));
|
|
21721
|
+
if (s.isFile()) return path32.join(cwd, f);
|
|
21195
21722
|
} catch {
|
|
21196
21723
|
}
|
|
21197
21724
|
}
|
|
@@ -21724,7 +22251,7 @@ var builtinToolsPack = {
|
|
|
21724
22251
|
};
|
|
21725
22252
|
|
|
21726
22253
|
// src/process-guardian.ts
|
|
21727
|
-
import * as
|
|
22254
|
+
import * as os9 from "node:os";
|
|
21728
22255
|
var ProcessGuardian = class {
|
|
21729
22256
|
registry;
|
|
21730
22257
|
config;
|
|
@@ -21763,7 +22290,7 @@ var ProcessGuardian = class {
|
|
|
21763
22290
|
event: "process_guardian.started",
|
|
21764
22291
|
instanceId: this.instanceId,
|
|
21765
22292
|
mainPid: process.pid,
|
|
21766
|
-
hostname:
|
|
22293
|
+
hostname: os9.hostname(),
|
|
21767
22294
|
platform: process.platform
|
|
21768
22295
|
}));
|
|
21769
22296
|
}
|
|
@@ -21952,8 +22479,8 @@ var ProcessGuardian = class {
|
|
|
21952
22479
|
instanceId: this.instanceId,
|
|
21953
22480
|
mainPid: process.pid,
|
|
21954
22481
|
protectedCount: this.protectedProcesses.size,
|
|
21955
|
-
platform:
|
|
21956
|
-
hostname:
|
|
22482
|
+
platform: os9.platform(),
|
|
22483
|
+
hostname: os9.hostname(),
|
|
21957
22484
|
uptime: process.uptime()
|
|
21958
22485
|
};
|
|
21959
22486
|
}
|
|
@@ -21982,7 +22509,7 @@ function stopProcessGuardian() {
|
|
|
21982
22509
|
init_process_registry();
|
|
21983
22510
|
|
|
21984
22511
|
// src/ps-slash.ts
|
|
21985
|
-
import * as
|
|
22512
|
+
import * as os10 from "node:os";
|
|
21986
22513
|
var IDLE_THRESHOLD_MS = 2 * 6e4;
|
|
21987
22514
|
var STALE_THRESHOLD_MS2 = 5 * 6e4;
|
|
21988
22515
|
function now() {
|
|
@@ -22023,7 +22550,7 @@ async function listInstances(options = {}) {
|
|
|
22023
22550
|
const mainProc = processes.find((p) => p.spawnMode === "main");
|
|
22024
22551
|
const firstProc = processes.at(0);
|
|
22025
22552
|
const mainPid = mainProc?.pid ?? firstProc?.pid ?? 0;
|
|
22026
|
-
const hostname_ = firstProc?.hostname ??
|
|
22553
|
+
const hostname_ = firstProc?.hostname ?? os10.hostname();
|
|
22027
22554
|
const startedAt = Math.min(...processes.map((p) => p.startedAt));
|
|
22028
22555
|
const lastActivity = Math.max(...processes.map((p) => p.lastHeartbeat));
|
|
22029
22556
|
const age = timestamp - lastActivity;
|
|
@@ -22111,7 +22638,7 @@ async function getGlobalProcessStatus() {
|
|
|
22111
22638
|
mainPid: process.pid,
|
|
22112
22639
|
protectedCount: 0,
|
|
22113
22640
|
platform: process.platform,
|
|
22114
|
-
hostname:
|
|
22641
|
+
hostname: os10.hostname(),
|
|
22115
22642
|
uptime: 0
|
|
22116
22643
|
},
|
|
22117
22644
|
allInstances: instances.map((inst) => ({
|
|
@@ -22296,7 +22823,7 @@ function createGlobalPsSlashCommand() {
|
|
|
22296
22823
|
|
|
22297
22824
|
// src/skill.ts
|
|
22298
22825
|
import * as fs30 from "node:fs/promises";
|
|
22299
|
-
import * as
|
|
22826
|
+
import * as path33 from "node:path";
|
|
22300
22827
|
import {
|
|
22301
22828
|
SKILL_LIMITS,
|
|
22302
22829
|
stripFrontmatter,
|
|
@@ -22343,7 +22870,7 @@ function makeSkillTool(skillLoader) {
|
|
|
22343
22870
|
field: "name"
|
|
22344
22871
|
});
|
|
22345
22872
|
}
|
|
22346
|
-
const dir =
|
|
22873
|
+
const dir = path33.dirname(manifest.path);
|
|
22347
22874
|
let loadedResource;
|
|
22348
22875
|
if (input.resource?.trim()) {
|
|
22349
22876
|
loadedResource = await loadResource(dir, input.resource.trim());
|
|
@@ -22393,15 +22920,15 @@ ${listing}`;
|
|
|
22393
22920
|
}
|
|
22394
22921
|
async function loadResource(skillDir, rel) {
|
|
22395
22922
|
const norm = rel.replace(/\\/g, "/");
|
|
22396
|
-
if (
|
|
22923
|
+
if (path33.isAbsolute(rel) || norm.split("/").some((seg) => seg === "..")) {
|
|
22397
22924
|
throw new ToolValidationError10({
|
|
22398
22925
|
message: `skill: invalid resource path "${rel}"`,
|
|
22399
22926
|
field: "resource"
|
|
22400
22927
|
});
|
|
22401
22928
|
}
|
|
22402
|
-
const absPath =
|
|
22403
|
-
const root =
|
|
22404
|
-
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)) {
|
|
22405
22932
|
throw new ToolValidationError10({
|
|
22406
22933
|
message: `skill: resource "${rel}" escapes the skill directory`,
|
|
22407
22934
|
field: "resource"
|
|
@@ -22442,7 +22969,7 @@ async function walk(root, dir, out) {
|
|
|
22442
22969
|
}
|
|
22443
22970
|
for (const e of entries) {
|
|
22444
22971
|
if (out.length >= MAX_LISTED_RESOURCES) return;
|
|
22445
|
-
const fullPath =
|
|
22972
|
+
const fullPath = path33.join(dir, e.name);
|
|
22446
22973
|
let isDir = e.isDirectory();
|
|
22447
22974
|
if (e.isSymbolicLink()) {
|
|
22448
22975
|
try {
|
|
@@ -22458,7 +22985,7 @@ async function walk(root, dir, out) {
|
|
|
22458
22985
|
if (e.name === "SKILL.md" || e.name === "SKILL.save.md") continue;
|
|
22459
22986
|
try {
|
|
22460
22987
|
const stat18 = await fs30.stat(fullPath);
|
|
22461
|
-
const rel =
|
|
22988
|
+
const rel = path33.relative(root, fullPath).split(path33.sep).join("/");
|
|
22462
22989
|
out.push({ path: rel, bytes: stat18.size });
|
|
22463
22990
|
} catch {
|
|
22464
22991
|
}
|
|
@@ -22681,6 +23208,7 @@ export {
|
|
|
22681
23208
|
builtinTools,
|
|
22682
23209
|
builtinToolsPack,
|
|
22683
23210
|
cancelPendingReindexes,
|
|
23211
|
+
checkExecKillCommand,
|
|
22684
23212
|
codebaseIndexStats,
|
|
22685
23213
|
codebaseIndexTool,
|
|
22686
23214
|
codebaseSearchTool,
|