reasonix 0.25.0 → 0.25.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/cli/index.js +527 -102
- package/dist/cli/index.js.map +1 -1
- package/dist/index.js +479 -54
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -1737,8 +1737,8 @@ function computeWait(attempt, initial, cap, retryAfter) {
|
|
|
1737
1737
|
}
|
|
1738
1738
|
function sleep(ms, signal) {
|
|
1739
1739
|
if (ms <= 0) return Promise.resolve();
|
|
1740
|
-
return new Promise((
|
|
1741
|
-
const timer = setTimeout(
|
|
1740
|
+
return new Promise((resolve12, reject) => {
|
|
1741
|
+
const timer = setTimeout(resolve12, ms);
|
|
1742
1742
|
if (signal) {
|
|
1743
1743
|
const onAbort = () => {
|
|
1744
1744
|
clearTimeout(timer);
|
|
@@ -2209,7 +2209,7 @@ function matchesTool(hook, toolName) {
|
|
|
2209
2209
|
}
|
|
2210
2210
|
var HOOK_OUTPUT_CAP_BYTES = 256 * 1024;
|
|
2211
2211
|
function defaultSpawner(input) {
|
|
2212
|
-
return new Promise((
|
|
2212
|
+
return new Promise((resolve12) => {
|
|
2213
2213
|
const child = spawn(input.command, {
|
|
2214
2214
|
cwd: input.cwd,
|
|
2215
2215
|
shell: true,
|
|
@@ -2254,7 +2254,7 @@ function defaultSpawner(input) {
|
|
|
2254
2254
|
child.stderr.on("data", (chunk) => onChunk("stderr", chunk));
|
|
2255
2255
|
child.once("error", (err) => {
|
|
2256
2256
|
clearTimeout(timer);
|
|
2257
|
-
|
|
2257
|
+
resolve12({
|
|
2258
2258
|
exitCode: null,
|
|
2259
2259
|
stdout: Buffer.concat(stdoutChunks).toString("utf8"),
|
|
2260
2260
|
stderr: Buffer.concat(stderrChunks).toString("utf8"),
|
|
@@ -2265,7 +2265,7 @@ function defaultSpawner(input) {
|
|
|
2265
2265
|
});
|
|
2266
2266
|
child.once("close", (code) => {
|
|
2267
2267
|
clearTimeout(timer);
|
|
2268
|
-
|
|
2268
|
+
resolve12({
|
|
2269
2269
|
exitCode: code,
|
|
2270
2270
|
stdout: Buffer.concat(stdoutChunks).toString("utf8").trim(),
|
|
2271
2271
|
stderr: Buffer.concat(stderrChunks).toString("utf8").trim(),
|
|
@@ -4093,8 +4093,8 @@ var CacheFirstLoop = class {
|
|
|
4093
4093
|
}
|
|
4094
4094
|
);
|
|
4095
4095
|
for (let k = 0; k < budget3; k++) {
|
|
4096
|
-
const sample = queue.shift() ?? await new Promise((
|
|
4097
|
-
waiter =
|
|
4096
|
+
const sample = queue.shift() ?? await new Promise((resolve12) => {
|
|
4097
|
+
waiter = resolve12;
|
|
4098
4098
|
});
|
|
4099
4099
|
yield {
|
|
4100
4100
|
turn: this._turn,
|
|
@@ -6335,9 +6335,9 @@ function forkRegistryExcluding(parent, exclude) {
|
|
|
6335
6335
|
}
|
|
6336
6336
|
|
|
6337
6337
|
// src/tools/shell.ts
|
|
6338
|
-
import { spawn as
|
|
6338
|
+
import { spawn as spawn4, spawnSync } from "child_process";
|
|
6339
6339
|
import { existsSync as existsSync5, statSync as statSync3 } from "fs";
|
|
6340
|
-
import * as
|
|
6340
|
+
import * as pathMod4 from "path";
|
|
6341
6341
|
|
|
6342
6342
|
// src/tools/jobs.ts
|
|
6343
6343
|
import { spawn as spawn2 } from "child_process";
|
|
@@ -6627,6 +6627,417 @@ function snapshot(job) {
|
|
|
6627
6627
|
};
|
|
6628
6628
|
}
|
|
6629
6629
|
|
|
6630
|
+
// src/tools/shell-chain.ts
|
|
6631
|
+
import { spawn as spawn3 } from "child_process";
|
|
6632
|
+
import { closeSync, openSync } from "fs";
|
|
6633
|
+
import * as pathMod3 from "path";
|
|
6634
|
+
var UnsupportedSyntaxError = class extends Error {
|
|
6635
|
+
constructor(detail) {
|
|
6636
|
+
super(`run_command: ${detail}`);
|
|
6637
|
+
this.name = "UnsupportedSyntaxError";
|
|
6638
|
+
}
|
|
6639
|
+
};
|
|
6640
|
+
function splitOnChainOps(cmd) {
|
|
6641
|
+
const segs = [];
|
|
6642
|
+
const ops = [];
|
|
6643
|
+
let segStart = 0;
|
|
6644
|
+
let i = 0;
|
|
6645
|
+
let quote2 = null;
|
|
6646
|
+
let atTokenStart = true;
|
|
6647
|
+
while (i < cmd.length) {
|
|
6648
|
+
const ch = cmd[i];
|
|
6649
|
+
if (quote2) {
|
|
6650
|
+
if (ch === quote2) quote2 = null;
|
|
6651
|
+
else if (ch === "\\" && quote2 === '"' && i + 1 < cmd.length) i++;
|
|
6652
|
+
i++;
|
|
6653
|
+
atTokenStart = false;
|
|
6654
|
+
continue;
|
|
6655
|
+
}
|
|
6656
|
+
if (ch === '"' || ch === "'") {
|
|
6657
|
+
quote2 = ch;
|
|
6658
|
+
i++;
|
|
6659
|
+
atTokenStart = false;
|
|
6660
|
+
continue;
|
|
6661
|
+
}
|
|
6662
|
+
if (ch === " " || ch === " ") {
|
|
6663
|
+
i++;
|
|
6664
|
+
atTokenStart = true;
|
|
6665
|
+
continue;
|
|
6666
|
+
}
|
|
6667
|
+
if (atTokenStart) {
|
|
6668
|
+
let op = null;
|
|
6669
|
+
let opLen = 0;
|
|
6670
|
+
const next = cmd[i + 1];
|
|
6671
|
+
if (ch === "|" && next === "|") {
|
|
6672
|
+
op = "||";
|
|
6673
|
+
opLen = 2;
|
|
6674
|
+
} else if (ch === "&" && next === "&") {
|
|
6675
|
+
op = "&&";
|
|
6676
|
+
opLen = 2;
|
|
6677
|
+
} else if (ch === "|") {
|
|
6678
|
+
op = "|";
|
|
6679
|
+
opLen = 1;
|
|
6680
|
+
} else if (ch === ";") {
|
|
6681
|
+
op = ";";
|
|
6682
|
+
opLen = 1;
|
|
6683
|
+
}
|
|
6684
|
+
if (op !== null) {
|
|
6685
|
+
segs.push(cmd.slice(segStart, i));
|
|
6686
|
+
ops.push(op);
|
|
6687
|
+
i += opLen;
|
|
6688
|
+
segStart = i;
|
|
6689
|
+
atTokenStart = true;
|
|
6690
|
+
continue;
|
|
6691
|
+
}
|
|
6692
|
+
}
|
|
6693
|
+
i++;
|
|
6694
|
+
atTokenStart = false;
|
|
6695
|
+
}
|
|
6696
|
+
segs.push(cmd.slice(segStart));
|
|
6697
|
+
return { segs, ops };
|
|
6698
|
+
}
|
|
6699
|
+
function parseSegment(segStr) {
|
|
6700
|
+
const argv = [];
|
|
6701
|
+
const redirects = [];
|
|
6702
|
+
let cur = "";
|
|
6703
|
+
let curHasContent = false;
|
|
6704
|
+
let pending = null;
|
|
6705
|
+
let quote2 = null;
|
|
6706
|
+
const flush = () => {
|
|
6707
|
+
if (!curHasContent && cur.length === 0) return;
|
|
6708
|
+
if (pending) {
|
|
6709
|
+
redirects.push({ kind: pending, target: cur });
|
|
6710
|
+
pending = null;
|
|
6711
|
+
} else {
|
|
6712
|
+
argv.push(cur);
|
|
6713
|
+
}
|
|
6714
|
+
cur = "";
|
|
6715
|
+
curHasContent = false;
|
|
6716
|
+
};
|
|
6717
|
+
let i = 0;
|
|
6718
|
+
while (i < segStr.length) {
|
|
6719
|
+
const ch = segStr[i];
|
|
6720
|
+
if (quote2) {
|
|
6721
|
+
if (ch === quote2) {
|
|
6722
|
+
quote2 = null;
|
|
6723
|
+
} else if (ch === "\\" && quote2 === '"' && i + 1 < segStr.length) {
|
|
6724
|
+
cur += segStr[++i] ?? "";
|
|
6725
|
+
curHasContent = true;
|
|
6726
|
+
} else {
|
|
6727
|
+
cur += ch;
|
|
6728
|
+
curHasContent = true;
|
|
6729
|
+
}
|
|
6730
|
+
i++;
|
|
6731
|
+
continue;
|
|
6732
|
+
}
|
|
6733
|
+
if (ch === '"' || ch === "'") {
|
|
6734
|
+
quote2 = ch;
|
|
6735
|
+
curHasContent = true;
|
|
6736
|
+
i++;
|
|
6737
|
+
continue;
|
|
6738
|
+
}
|
|
6739
|
+
if (ch === " " || ch === " ") {
|
|
6740
|
+
flush();
|
|
6741
|
+
i++;
|
|
6742
|
+
continue;
|
|
6743
|
+
}
|
|
6744
|
+
if (cur.length === 0 && !curHasContent) {
|
|
6745
|
+
const remaining = segStr.slice(i);
|
|
6746
|
+
let matched = null;
|
|
6747
|
+
if (remaining.startsWith("2>&1")) matched = { op: "2>&1", len: 4 };
|
|
6748
|
+
else if (remaining.startsWith("&>")) matched = { op: "&>", len: 2 };
|
|
6749
|
+
else if (remaining.startsWith("2>>")) matched = { op: "2>>", len: 3 };
|
|
6750
|
+
else if (remaining.startsWith("2>")) matched = { op: "2>", len: 2 };
|
|
6751
|
+
else if (remaining.startsWith(">>")) matched = { op: ">>", len: 2 };
|
|
6752
|
+
else if (remaining.startsWith(">")) matched = { op: ">", len: 1 };
|
|
6753
|
+
else if (remaining.startsWith("<<")) {
|
|
6754
|
+
throw new UnsupportedSyntaxError(
|
|
6755
|
+
`shell operator "<<" is not supported \u2014 heredoc / here-string is not implemented; pass input via a "<" file or the binary's --input flag`
|
|
6756
|
+
);
|
|
6757
|
+
} else if (remaining.startsWith("<")) matched = { op: "<", len: 1 };
|
|
6758
|
+
if (matched) {
|
|
6759
|
+
if (pending !== null) {
|
|
6760
|
+
throw new UnsupportedSyntaxError(
|
|
6761
|
+
`redirect "${pending}" is missing a target file before "${matched.op}"`
|
|
6762
|
+
);
|
|
6763
|
+
}
|
|
6764
|
+
if (matched.op === "2>&1") {
|
|
6765
|
+
redirects.push({ kind: "2>&1", target: "" });
|
|
6766
|
+
} else {
|
|
6767
|
+
pending = matched.op;
|
|
6768
|
+
}
|
|
6769
|
+
i += matched.len;
|
|
6770
|
+
continue;
|
|
6771
|
+
}
|
|
6772
|
+
if (ch === "&") {
|
|
6773
|
+
throw new UnsupportedSyntaxError(
|
|
6774
|
+
'shell operator "&" is not supported \u2014 background runs need run_background, not run_command. Wrap a literal `&` arg in quotes.'
|
|
6775
|
+
);
|
|
6776
|
+
}
|
|
6777
|
+
}
|
|
6778
|
+
cur += ch;
|
|
6779
|
+
curHasContent = true;
|
|
6780
|
+
i++;
|
|
6781
|
+
}
|
|
6782
|
+
if (quote2) throw new Error(`unclosed ${quote2} in command`);
|
|
6783
|
+
flush();
|
|
6784
|
+
if (pending) throw new UnsupportedSyntaxError(`redirect "${pending}" is missing a target file`);
|
|
6785
|
+
if (argv.length === 0 && redirects.length > 0) {
|
|
6786
|
+
throw new UnsupportedSyntaxError(
|
|
6787
|
+
"redirect without a command \u2014 segment must have at least one program argument"
|
|
6788
|
+
);
|
|
6789
|
+
}
|
|
6790
|
+
validateRedirectFds(redirects);
|
|
6791
|
+
return { argv, redirects };
|
|
6792
|
+
}
|
|
6793
|
+
function validateRedirectFds(redirects) {
|
|
6794
|
+
let stdin5 = 0;
|
|
6795
|
+
let stdout4 = 0;
|
|
6796
|
+
let stderr = 0;
|
|
6797
|
+
for (const r of redirects) {
|
|
6798
|
+
if (r.kind === "<") stdin5++;
|
|
6799
|
+
else if (r.kind === ">" || r.kind === ">>") stdout4++;
|
|
6800
|
+
else if (r.kind === "2>" || r.kind === "2>>" || r.kind === "2>&1") stderr++;
|
|
6801
|
+
else if (r.kind === "&>") {
|
|
6802
|
+
stdout4++;
|
|
6803
|
+
stderr++;
|
|
6804
|
+
}
|
|
6805
|
+
}
|
|
6806
|
+
if (stdin5 > 1) throw new UnsupportedSyntaxError("multiple `<` stdin redirects in one segment");
|
|
6807
|
+
if (stdout4 > 1)
|
|
6808
|
+
throw new UnsupportedSyntaxError(
|
|
6809
|
+
"multiple stdout redirects in one segment (`>` / `>>` / `&>` conflict)"
|
|
6810
|
+
);
|
|
6811
|
+
if (stderr > 1)
|
|
6812
|
+
throw new UnsupportedSyntaxError(
|
|
6813
|
+
"multiple stderr redirects in one segment (`2>` / `2>>` / `&>` / `2>&1` conflict)"
|
|
6814
|
+
);
|
|
6815
|
+
}
|
|
6816
|
+
function parseCommandChain(cmd) {
|
|
6817
|
+
const { segs, ops } = splitOnChainOps(cmd);
|
|
6818
|
+
const segments = [];
|
|
6819
|
+
for (let i = 0; i < segs.length; i++) {
|
|
6820
|
+
const trimmed = segs[i].trim();
|
|
6821
|
+
if (trimmed.length === 0) {
|
|
6822
|
+
const op = i === 0 ? ops[0] : ops[i - 1];
|
|
6823
|
+
throw new UnsupportedSyntaxError(
|
|
6824
|
+
i === 0 ? `empty segment before "${op}"` : i === segs.length - 1 ? `chain ends with "${op}"` : `empty segment between "${ops[i - 1]}" and "${ops[i]}"`
|
|
6825
|
+
);
|
|
6826
|
+
}
|
|
6827
|
+
segments.push(parseSegment(trimmed));
|
|
6828
|
+
}
|
|
6829
|
+
if (ops.length === 0 && segments[0].redirects.length === 0) return null;
|
|
6830
|
+
return { segments, ops };
|
|
6831
|
+
}
|
|
6832
|
+
function chainAllowed(chain, isAllowed2) {
|
|
6833
|
+
for (const seg of chain.segments) {
|
|
6834
|
+
if (!isAllowed2(seg.argv.join(" "))) return false;
|
|
6835
|
+
}
|
|
6836
|
+
return true;
|
|
6837
|
+
}
|
|
6838
|
+
function groupChain(chain) {
|
|
6839
|
+
const groups = [{ segments: [chain.segments[0]], opBefore: null }];
|
|
6840
|
+
for (let i = 0; i < chain.ops.length; i++) {
|
|
6841
|
+
const op = chain.ops[i];
|
|
6842
|
+
const next = chain.segments[i + 1];
|
|
6843
|
+
if (op === "|") {
|
|
6844
|
+
groups[groups.length - 1].segments.push(next);
|
|
6845
|
+
} else {
|
|
6846
|
+
groups.push({ segments: [next], opBefore: op });
|
|
6847
|
+
}
|
|
6848
|
+
}
|
|
6849
|
+
return groups;
|
|
6850
|
+
}
|
|
6851
|
+
async function runChain(chain, opts) {
|
|
6852
|
+
const groups = groupChain(chain);
|
|
6853
|
+
const buf = new OutputBuffer(opts.maxOutputChars * 2 * 4);
|
|
6854
|
+
const deadline = Date.now() + opts.timeoutSec * 1e3;
|
|
6855
|
+
let lastExit = 0;
|
|
6856
|
+
let timedOut = false;
|
|
6857
|
+
for (const group of groups) {
|
|
6858
|
+
if (group.opBefore === "&&" && lastExit !== 0) continue;
|
|
6859
|
+
if (group.opBefore === "||" && lastExit === 0) continue;
|
|
6860
|
+
const remainingMs = deadline - Date.now();
|
|
6861
|
+
if (remainingMs <= 0) {
|
|
6862
|
+
timedOut = true;
|
|
6863
|
+
break;
|
|
6864
|
+
}
|
|
6865
|
+
const result = await runPipeGroup(group.segments, {
|
|
6866
|
+
cwd: opts.cwd,
|
|
6867
|
+
timeoutMs: remainingMs,
|
|
6868
|
+
buf,
|
|
6869
|
+
signal: opts.signal
|
|
6870
|
+
});
|
|
6871
|
+
lastExit = result.exitCode;
|
|
6872
|
+
if (result.timedOut) {
|
|
6873
|
+
timedOut = true;
|
|
6874
|
+
break;
|
|
6875
|
+
}
|
|
6876
|
+
if (opts.signal?.aborted) break;
|
|
6877
|
+
}
|
|
6878
|
+
const output = buf.toString();
|
|
6879
|
+
const truncated = output.length > opts.maxOutputChars ? `${output.slice(0, opts.maxOutputChars)}
|
|
6880
|
+
|
|
6881
|
+
[\u2026 truncated ${output.length - opts.maxOutputChars} chars \u2026]` : output;
|
|
6882
|
+
return { exitCode: lastExit, output: truncated, timedOut };
|
|
6883
|
+
}
|
|
6884
|
+
function openRedirects(redirects, cwd) {
|
|
6885
|
+
let stdinFd = null;
|
|
6886
|
+
let stdoutFd = null;
|
|
6887
|
+
let stderrFd = null;
|
|
6888
|
+
let mergeStderrToStdout = false;
|
|
6889
|
+
let bothFd = null;
|
|
6890
|
+
const toClose = [];
|
|
6891
|
+
const open = (target, flags) => {
|
|
6892
|
+
const resolved = pathMod3.resolve(cwd, target);
|
|
6893
|
+
const fd = openSync(resolved, flags);
|
|
6894
|
+
toClose.push(fd);
|
|
6895
|
+
return fd;
|
|
6896
|
+
};
|
|
6897
|
+
for (const r of redirects) {
|
|
6898
|
+
if (r.kind === "<") stdinFd = open(r.target, "r");
|
|
6899
|
+
else if (r.kind === ">") stdoutFd = open(r.target, "w");
|
|
6900
|
+
else if (r.kind === ">>") stdoutFd = open(r.target, "a");
|
|
6901
|
+
else if (r.kind === "2>") stderrFd = open(r.target, "w");
|
|
6902
|
+
else if (r.kind === "2>>") stderrFd = open(r.target, "a");
|
|
6903
|
+
else if (r.kind === "&>") {
|
|
6904
|
+
bothFd = open(r.target, "w");
|
|
6905
|
+
stdoutFd = bothFd;
|
|
6906
|
+
stderrFd = bothFd;
|
|
6907
|
+
} else if (r.kind === "2>&1") {
|
|
6908
|
+
mergeStderrToStdout = true;
|
|
6909
|
+
}
|
|
6910
|
+
}
|
|
6911
|
+
return { stdinFd, stdoutFd, stderrFd, mergeStderrToStdout, toClose };
|
|
6912
|
+
}
|
|
6913
|
+
async function runPipeGroup(segments, opts) {
|
|
6914
|
+
const env2 = { ...process.env, PYTHONIOENCODING: "utf-8", PYTHONUTF8: "1" };
|
|
6915
|
+
const children = [];
|
|
6916
|
+
const allFds = [];
|
|
6917
|
+
let timedOut = false;
|
|
6918
|
+
const killAll = () => {
|
|
6919
|
+
for (const c of children) killProcessTree2(c);
|
|
6920
|
+
};
|
|
6921
|
+
const killTimer = setTimeout(() => {
|
|
6922
|
+
timedOut = true;
|
|
6923
|
+
killAll();
|
|
6924
|
+
}, opts.timeoutMs);
|
|
6925
|
+
const onAbort = () => killAll();
|
|
6926
|
+
if (opts.signal?.aborted) {
|
|
6927
|
+
onAbort();
|
|
6928
|
+
} else {
|
|
6929
|
+
opts.signal?.addEventListener("abort", onAbort, { once: true });
|
|
6930
|
+
}
|
|
6931
|
+
try {
|
|
6932
|
+
for (let i = 0; i < segments.length; i++) {
|
|
6933
|
+
const isFirst = i === 0;
|
|
6934
|
+
const isLast = i === segments.length - 1;
|
|
6935
|
+
const seg = segments[i];
|
|
6936
|
+
const io = openRedirects(seg.redirects, opts.cwd);
|
|
6937
|
+
allFds.push(...io.toClose);
|
|
6938
|
+
const { bin, args, spawnOverrides } = prepareSpawn(seg.argv);
|
|
6939
|
+
const stdoutSpec = io.stdoutFd !== null ? io.stdoutFd : "pipe";
|
|
6940
|
+
const stderrSpec = io.stderrFd !== null ? io.stderrFd : io.mergeStderrToStdout ? stdoutSpec : "pipe";
|
|
6941
|
+
const stdinSpec = io.stdinFd !== null ? io.stdinFd : isFirst ? "ignore" : "pipe";
|
|
6942
|
+
const spawnOpts = {
|
|
6943
|
+
cwd: opts.cwd,
|
|
6944
|
+
shell: false,
|
|
6945
|
+
windowsHide: true,
|
|
6946
|
+
env: env2,
|
|
6947
|
+
stdio: [stdinSpec, stdoutSpec, stderrSpec],
|
|
6948
|
+
...spawnOverrides
|
|
6949
|
+
};
|
|
6950
|
+
let child;
|
|
6951
|
+
try {
|
|
6952
|
+
child = spawn3(bin, args, spawnOpts);
|
|
6953
|
+
} catch (err) {
|
|
6954
|
+
for (const fd of allFds) tryClose(fd);
|
|
6955
|
+
killAll();
|
|
6956
|
+
clearTimeout(killTimer);
|
|
6957
|
+
opts.signal?.removeEventListener("abort", onAbort);
|
|
6958
|
+
throw err;
|
|
6959
|
+
}
|
|
6960
|
+
children.push(child);
|
|
6961
|
+
if (!isFirst && io.stdinFd === null) {
|
|
6962
|
+
const prev = children[i - 1];
|
|
6963
|
+
prev.stdout?.on("error", () => {
|
|
6964
|
+
});
|
|
6965
|
+
child.stdin?.on("error", () => {
|
|
6966
|
+
});
|
|
6967
|
+
const prevMergesStderr = segments[i - 1].redirects.some((r) => r.kind === "2>&1") && !!prev.stderr;
|
|
6968
|
+
if (prevMergesStderr && prev.stderr) {
|
|
6969
|
+
prev.stderr.on("error", () => {
|
|
6970
|
+
});
|
|
6971
|
+
let openSources = 2;
|
|
6972
|
+
const closeIfDone = () => {
|
|
6973
|
+
if (--openSources === 0) child.stdin?.end();
|
|
6974
|
+
};
|
|
6975
|
+
prev.stdout?.pipe(child.stdin, { end: false });
|
|
6976
|
+
prev.stderr.pipe(child.stdin, { end: false });
|
|
6977
|
+
prev.stdout?.once("end", closeIfDone);
|
|
6978
|
+
prev.stderr.once("end", closeIfDone);
|
|
6979
|
+
} else {
|
|
6980
|
+
prev.stdout?.pipe(child.stdin);
|
|
6981
|
+
}
|
|
6982
|
+
}
|
|
6983
|
+
if (child.stderr && io.stderrFd === null && !(io.mergeStderrToStdout && !isLast)) {
|
|
6984
|
+
child.stderr.on("data", (chunk) => opts.buf.push(toBuf(chunk)));
|
|
6985
|
+
}
|
|
6986
|
+
if (isLast && child.stdout && io.stdoutFd === null) {
|
|
6987
|
+
child.stdout.on("data", (chunk) => opts.buf.push(toBuf(chunk)));
|
|
6988
|
+
if (io.mergeStderrToStdout && child.stderr && io.stderrFd === null) {
|
|
6989
|
+
child.stderr.removeAllListeners("data");
|
|
6990
|
+
child.stderr.on("data", (chunk) => opts.buf.push(toBuf(chunk)));
|
|
6991
|
+
}
|
|
6992
|
+
}
|
|
6993
|
+
}
|
|
6994
|
+
const exits = await Promise.all(
|
|
6995
|
+
children.map(
|
|
6996
|
+
(c) => new Promise((resolve12) => {
|
|
6997
|
+
c.once("error", () => resolve12(null));
|
|
6998
|
+
c.once("close", (code) => resolve12(code));
|
|
6999
|
+
})
|
|
7000
|
+
)
|
|
7001
|
+
);
|
|
7002
|
+
return { exitCode: exits[exits.length - 1] ?? null, timedOut };
|
|
7003
|
+
} finally {
|
|
7004
|
+
for (const fd of allFds) tryClose(fd);
|
|
7005
|
+
clearTimeout(killTimer);
|
|
7006
|
+
opts.signal?.removeEventListener("abort", onAbort);
|
|
7007
|
+
}
|
|
7008
|
+
}
|
|
7009
|
+
function tryClose(fd) {
|
|
7010
|
+
try {
|
|
7011
|
+
closeSync(fd);
|
|
7012
|
+
} catch {
|
|
7013
|
+
}
|
|
7014
|
+
}
|
|
7015
|
+
function toBuf(chunk) {
|
|
7016
|
+
return typeof chunk === "string" ? Buffer.from(chunk) : chunk;
|
|
7017
|
+
}
|
|
7018
|
+
var OutputBuffer = class {
|
|
7019
|
+
constructor(cap) {
|
|
7020
|
+
this.cap = cap;
|
|
7021
|
+
}
|
|
7022
|
+
cap;
|
|
7023
|
+
chunks = [];
|
|
7024
|
+
bytes = 0;
|
|
7025
|
+
push(b) {
|
|
7026
|
+
if (this.bytes >= this.cap) return;
|
|
7027
|
+
const remaining = this.cap - this.bytes;
|
|
7028
|
+
if (b.length > remaining) {
|
|
7029
|
+
this.chunks.push(b.subarray(0, remaining));
|
|
7030
|
+
this.bytes = this.cap;
|
|
7031
|
+
} else {
|
|
7032
|
+
this.chunks.push(b);
|
|
7033
|
+
this.bytes += b.length;
|
|
7034
|
+
}
|
|
7035
|
+
}
|
|
7036
|
+
toString() {
|
|
7037
|
+
return smartDecodeOutput(Buffer.concat(this.chunks));
|
|
7038
|
+
}
|
|
7039
|
+
};
|
|
7040
|
+
|
|
6630
7041
|
// src/tools/shell.ts
|
|
6631
7042
|
function killProcessTree2(child) {
|
|
6632
7043
|
if (!child.pid || child.killed) return;
|
|
@@ -6798,17 +7209,31 @@ function isAllowed(cmd, extra = []) {
|
|
|
6798
7209
|
}
|
|
6799
7210
|
return false;
|
|
6800
7211
|
}
|
|
7212
|
+
function isCommandAllowed(cmd, extra = []) {
|
|
7213
|
+
let chain;
|
|
7214
|
+
try {
|
|
7215
|
+
chain = parseCommandChain(cmd);
|
|
7216
|
+
} catch {
|
|
7217
|
+
return false;
|
|
7218
|
+
}
|
|
7219
|
+
if (chain === null) return isAllowed(cmd, extra);
|
|
7220
|
+
return chainAllowed(chain, (seg) => isAllowed(seg, extra));
|
|
7221
|
+
}
|
|
6801
7222
|
async function runCommand(cmd, opts) {
|
|
7223
|
+
const timeoutSec = opts.timeoutSec ?? DEFAULT_TIMEOUT_SEC;
|
|
7224
|
+
const maxChars = opts.maxOutputChars ?? DEFAULT_MAX_OUTPUT_CHARS;
|
|
6802
7225
|
const argv = tokenizeCommand(cmd);
|
|
6803
7226
|
if (argv.length === 0) throw new Error("run_command: empty command");
|
|
6804
|
-
const
|
|
6805
|
-
if (
|
|
6806
|
-
|
|
6807
|
-
|
|
6808
|
-
|
|
7227
|
+
const chain = parseCommandChain(cmd);
|
|
7228
|
+
if (chain !== null) {
|
|
7229
|
+
return await runChain(chain, {
|
|
7230
|
+
cwd: opts.cwd,
|
|
7231
|
+
timeoutSec,
|
|
7232
|
+
maxOutputChars: maxChars,
|
|
7233
|
+
signal: opts.signal
|
|
7234
|
+
});
|
|
6809
7235
|
}
|
|
6810
|
-
const timeoutMs =
|
|
6811
|
-
const maxChars = opts.maxOutputChars ?? DEFAULT_MAX_OUTPUT_CHARS;
|
|
7236
|
+
const timeoutMs = timeoutSec * 1e3;
|
|
6812
7237
|
const spawnOpts = {
|
|
6813
7238
|
cwd: opts.cwd,
|
|
6814
7239
|
shell: false,
|
|
@@ -6826,10 +7251,10 @@ async function runCommand(cmd, opts) {
|
|
|
6826
7251
|
};
|
|
6827
7252
|
const { bin, args, spawnOverrides } = prepareSpawn(argv);
|
|
6828
7253
|
const effectiveSpawnOpts = { ...spawnOpts, ...spawnOverrides };
|
|
6829
|
-
return await new Promise((
|
|
7254
|
+
return await new Promise((resolve12, reject) => {
|
|
6830
7255
|
let child;
|
|
6831
7256
|
try {
|
|
6832
|
-
child =
|
|
7257
|
+
child = spawn4(bin, args, effectiveSpawnOpts);
|
|
6833
7258
|
} catch (err) {
|
|
6834
7259
|
reject(err);
|
|
6835
7260
|
return;
|
|
@@ -6880,7 +7305,7 @@ async function runCommand(cmd, opts) {
|
|
|
6880
7305
|
const output = buf.length > maxChars ? `${buf.slice(0, maxChars)}
|
|
6881
7306
|
|
|
6882
7307
|
[\u2026 truncated ${buf.length - maxChars} chars \u2026]` : buf;
|
|
6883
|
-
|
|
7308
|
+
resolve12({ exitCode: code, output, timedOut });
|
|
6884
7309
|
});
|
|
6885
7310
|
});
|
|
6886
7311
|
}
|
|
@@ -6902,16 +7327,16 @@ function resolveExecutable(cmd, opts = {}) {
|
|
|
6902
7327
|
const platform = opts.platform ?? process.platform;
|
|
6903
7328
|
if (platform !== "win32") return cmd;
|
|
6904
7329
|
if (!cmd) return cmd;
|
|
6905
|
-
if (cmd.includes("/") || cmd.includes("\\") ||
|
|
6906
|
-
if (
|
|
7330
|
+
if (cmd.includes("/") || cmd.includes("\\") || pathMod4.isAbsolute(cmd)) return cmd;
|
|
7331
|
+
if (pathMod4.extname(cmd)) return cmd;
|
|
6907
7332
|
const env2 = opts.env ?? process.env;
|
|
6908
7333
|
const pathExt = (env2.PATHEXT ?? ".COM;.EXE;.BAT;.CMD").split(";").map((e) => e.trim()).filter(Boolean);
|
|
6909
|
-
const delimiter2 = opts.pathDelimiter ?? (platform === "win32" ? ";" :
|
|
7334
|
+
const delimiter2 = opts.pathDelimiter ?? (platform === "win32" ? ";" : pathMod4.delimiter);
|
|
6910
7335
|
const pathDirs = (env2.PATH ?? "").split(delimiter2).filter(Boolean);
|
|
6911
7336
|
const isFile = opts.isFile ?? defaultIsFile;
|
|
6912
7337
|
for (const dir of pathDirs) {
|
|
6913
7338
|
for (const ext of pathExt) {
|
|
6914
|
-
const full =
|
|
7339
|
+
const full = pathMod4.win32.join(dir, cmd + ext);
|
|
6915
7340
|
if (isFile(full)) return full;
|
|
6916
7341
|
}
|
|
6917
7342
|
}
|
|
@@ -6981,8 +7406,8 @@ function withUtf8Codepage(cmdline) {
|
|
|
6981
7406
|
function isBareWindowsName(s) {
|
|
6982
7407
|
if (!s) return false;
|
|
6983
7408
|
if (s.includes("/") || s.includes("\\")) return false;
|
|
6984
|
-
if (
|
|
6985
|
-
if (
|
|
7409
|
+
if (pathMod4.isAbsolute(s)) return false;
|
|
7410
|
+
if (pathMod4.extname(s)) return false;
|
|
6986
7411
|
return true;
|
|
6987
7412
|
}
|
|
6988
7413
|
function quoteForCmdExe(arg) {
|
|
@@ -7001,7 +7426,7 @@ var NeedsConfirmationError = class extends Error {
|
|
|
7001
7426
|
}
|
|
7002
7427
|
};
|
|
7003
7428
|
function registerShellTools(registry, opts) {
|
|
7004
|
-
const rootDir =
|
|
7429
|
+
const rootDir = pathMod4.resolve(opts.rootDir);
|
|
7005
7430
|
const timeoutSec = opts.timeoutSec ?? DEFAULT_TIMEOUT_SEC;
|
|
7006
7431
|
const maxOutputChars = opts.maxOutputChars ?? DEFAULT_MAX_OUTPUT_CHARS;
|
|
7007
7432
|
const jobs2 = opts.jobs ?? new JobRegistry();
|
|
@@ -7012,7 +7437,7 @@ function registerShellTools(registry, opts) {
|
|
|
7012
7437
|
const isAllowAll = typeof opts.allowAll === "function" ? opts.allowAll : () => opts.allowAll === true;
|
|
7013
7438
|
registry.register({
|
|
7014
7439
|
name: "run_command",
|
|
7015
|
-
description: "Run a shell command in the project root and return its combined stdout+stderr.\n\nConstraints (read these before the first call):\n\u2022
|
|
7440
|
+
description: "Run a shell command in the project root and return its combined stdout+stderr.\n\nConstraints (read these before the first call):\n\u2022 Chain operators `|`, `||`, `&&`, `;` ARE supported \u2014 parsed natively, no shell invoked, so semantics are identical on Windows / macOS / Linux. Each chain segment is allowlist-checked individually: `git status | grep main` runs if both halves are allowed.\n\u2022 File redirects ARE supported: `>` truncate, `>>` append, `<` stdin from file, `2>` / `2>>` stderr to file, `2>&1` merge stderr\u2192stdout, `&>` both to file. Targets resolve relative to the project root. At most one redirect per fd per segment.\n\u2022 Background `&`, heredoc `<<`, command substitution `$(\u2026)`, subshells `(\u2026)`, and process substitution `<(\u2026)` are NOT supported. Wrap a literal `&` arg in quotes; for input use a `<` file or the binary's own --input flag.\n\u2022 Env-var expansion `$VAR` is NOT performed \u2014 `$VAR` is passed as a literal string. Use the binary's own --env flag or substitute the value yourself.\n\u2022 `cd` DOES NOT PERSIST between calls \u2014 each call spawns a fresh process rooted at the project. If a tool needs a subdirectory, pass it via the tool's own flag (`npm --prefix`, `cargo -C`, `git -C`, `pytest tests/\u2026`), NOT via a preceding `cd`.\n\u2022 Glob patterns (`*.ts`) are passed through as literal arguments \u2014 no shell expansion. Use `grep -r`, `rg`, `find -name`, etc.\n\u2022 Avoid commands with unbounded output (`netstat -ano`, `find /`, etc.) \u2014 they waste tokens. Filter at source: `netstat -ano -p TCP`, `find src -name '*.ts'`, `grep -c`, `wc -l`.\n\nCommon read-only inspection and test/lint/typecheck commands run immediately; anything that could mutate state, install dependencies, or touch the network is refused until the user confirms it in the TUI. Prefer this over asking the user to run a command manually \u2014 after edits, run the project's tests to verify.",
|
|
7016
7441
|
// Plan-mode gate: allow allowlisted commands through (git status,
|
|
7017
7442
|
// cargo check, ls, grep …) so the model can actually investigate
|
|
7018
7443
|
// during planning. Anything that would otherwise trigger a
|
|
@@ -7021,14 +7446,14 @@ function registerShellTools(registry, opts) {
|
|
|
7021
7446
|
if (isAllowAll()) return true;
|
|
7022
7447
|
const cmd = typeof args?.command === "string" ? args.command.trim() : "";
|
|
7023
7448
|
if (!cmd) return false;
|
|
7024
|
-
return
|
|
7449
|
+
return isCommandAllowed(cmd, getExtraAllowed());
|
|
7025
7450
|
},
|
|
7026
7451
|
parameters: {
|
|
7027
7452
|
type: "object",
|
|
7028
7453
|
properties: {
|
|
7029
7454
|
command: {
|
|
7030
7455
|
type: "string",
|
|
7031
|
-
description: 'Full command line.
|
|
7456
|
+
description: 'Full command line. POSIX-ish quoting. Chain operators `|`, `||`, `&&`, `;` and file redirects `>` / `>>` / `<` / `2>` / `2>>` / `2>&1` / `&>` work natively (no shell). Background `&`, heredoc `<<`, env-var expansion `$VAR`, and command substitution `$(\u2026)` are rejected (or passed through as literal in the case of `$VAR`). To pass an operator character as a literal argument (e.g. a regex), wrap it in quotes: `grep "a|b" file.txt`.'
|
|
7032
7457
|
},
|
|
7033
7458
|
timeoutSec: {
|
|
7034
7459
|
type: "integer",
|
|
@@ -7040,7 +7465,7 @@ function registerShellTools(registry, opts) {
|
|
|
7040
7465
|
fn: async (args, ctx) => {
|
|
7041
7466
|
const cmd = args.command.trim();
|
|
7042
7467
|
if (!cmd) throw new Error("run_command: empty command");
|
|
7043
|
-
if (!isAllowAll() && !
|
|
7468
|
+
if (!isAllowAll() && !isCommandAllowed(cmd, getExtraAllowed())) {
|
|
7044
7469
|
throw new NeedsConfirmationError(cmd);
|
|
7045
7470
|
}
|
|
7046
7471
|
const effectiveTimeout = Math.max(1, Math.min(600, args.timeoutSec ?? timeoutSec));
|
|
@@ -7404,11 +7829,11 @@ ${i + 1}. ${r.title}`);
|
|
|
7404
7829
|
|
|
7405
7830
|
// src/env.ts
|
|
7406
7831
|
import { readFileSync as readFileSync7 } from "fs";
|
|
7407
|
-
import { resolve as
|
|
7832
|
+
import { resolve as resolve6 } from "path";
|
|
7408
7833
|
function loadDotenv(path6 = ".env") {
|
|
7409
7834
|
let raw;
|
|
7410
7835
|
try {
|
|
7411
|
-
raw = readFileSync7(
|
|
7836
|
+
raw = readFileSync7(resolve6(process.cwd(), path6), "utf8");
|
|
7412
7837
|
} catch {
|
|
7413
7838
|
return;
|
|
7414
7839
|
}
|
|
@@ -8193,7 +8618,7 @@ var McpClient = class {
|
|
|
8193
8618
|
const id = this.nextId++;
|
|
8194
8619
|
const frame = { jsonrpc: "2.0", id, method, params };
|
|
8195
8620
|
let abortHandler = null;
|
|
8196
|
-
const promise = new Promise((
|
|
8621
|
+
const promise = new Promise((resolve12, reject) => {
|
|
8197
8622
|
const timeout = setTimeout(() => {
|
|
8198
8623
|
this.pending.delete(id);
|
|
8199
8624
|
if (abortHandler && signal) signal.removeEventListener("abort", abortHandler);
|
|
@@ -8202,7 +8627,7 @@ var McpClient = class {
|
|
|
8202
8627
|
);
|
|
8203
8628
|
}, this.requestTimeoutMs);
|
|
8204
8629
|
this.pending.set(id, {
|
|
8205
|
-
resolve:
|
|
8630
|
+
resolve: resolve12,
|
|
8206
8631
|
reject,
|
|
8207
8632
|
timeout
|
|
8208
8633
|
});
|
|
@@ -8284,7 +8709,7 @@ var McpClient = class {
|
|
|
8284
8709
|
};
|
|
8285
8710
|
|
|
8286
8711
|
// src/mcp/stdio.ts
|
|
8287
|
-
import { spawn as
|
|
8712
|
+
import { spawn as spawn5 } from "child_process";
|
|
8288
8713
|
var StdioTransport = class {
|
|
8289
8714
|
child;
|
|
8290
8715
|
queue = [];
|
|
@@ -8299,14 +8724,14 @@ var StdioTransport = class {
|
|
|
8299
8724
|
opts.command,
|
|
8300
8725
|
...(opts.args ?? []).map((a) => quoteArg(a, process.platform === "win32"))
|
|
8301
8726
|
].join(" ");
|
|
8302
|
-
this.child =
|
|
8727
|
+
this.child = spawn5(line, [], {
|
|
8303
8728
|
env: env2,
|
|
8304
8729
|
cwd: opts.cwd,
|
|
8305
8730
|
stdio: ["pipe", "pipe", "inherit"],
|
|
8306
8731
|
shell: true
|
|
8307
8732
|
});
|
|
8308
8733
|
} else {
|
|
8309
|
-
this.child =
|
|
8734
|
+
this.child = spawn5(opts.command, opts.args ?? [], {
|
|
8310
8735
|
env: env2,
|
|
8311
8736
|
cwd: opts.cwd,
|
|
8312
8737
|
stdio: ["pipe", "pipe", "inherit"]
|
|
@@ -8325,12 +8750,12 @@ var StdioTransport = class {
|
|
|
8325
8750
|
}
|
|
8326
8751
|
async send(message) {
|
|
8327
8752
|
if (this.closed) throw new Error("MCP transport is closed");
|
|
8328
|
-
return new Promise((
|
|
8753
|
+
return new Promise((resolve12, reject) => {
|
|
8329
8754
|
const line = `${JSON.stringify(message)}
|
|
8330
8755
|
`;
|
|
8331
8756
|
this.child.stdin.write(line, "utf8", (err) => {
|
|
8332
8757
|
if (err) reject(err);
|
|
8333
|
-
else
|
|
8758
|
+
else resolve12();
|
|
8334
8759
|
});
|
|
8335
8760
|
});
|
|
8336
8761
|
}
|
|
@@ -8341,8 +8766,8 @@ var StdioTransport = class {
|
|
|
8341
8766
|
continue;
|
|
8342
8767
|
}
|
|
8343
8768
|
if (this.closed) return;
|
|
8344
|
-
const next = await new Promise((
|
|
8345
|
-
this.waiters.push(
|
|
8769
|
+
const next = await new Promise((resolve12) => {
|
|
8770
|
+
this.waiters.push(resolve12);
|
|
8346
8771
|
});
|
|
8347
8772
|
if (next === null) return;
|
|
8348
8773
|
yield next;
|
|
@@ -8411,8 +8836,8 @@ var SseTransport = class {
|
|
|
8411
8836
|
constructor(opts) {
|
|
8412
8837
|
this.url = opts.url;
|
|
8413
8838
|
this.headers = opts.headers ?? {};
|
|
8414
|
-
this.endpointReady = new Promise((
|
|
8415
|
-
this.resolveEndpoint =
|
|
8839
|
+
this.endpointReady = new Promise((resolve12, reject) => {
|
|
8840
|
+
this.resolveEndpoint = resolve12;
|
|
8416
8841
|
this.rejectEndpoint = reject;
|
|
8417
8842
|
});
|
|
8418
8843
|
this.endpointReady.catch(() => void 0);
|
|
@@ -8439,8 +8864,8 @@ var SseTransport = class {
|
|
|
8439
8864
|
continue;
|
|
8440
8865
|
}
|
|
8441
8866
|
if (this.closed) return;
|
|
8442
|
-
const next = await new Promise((
|
|
8443
|
-
this.waiters.push(
|
|
8867
|
+
const next = await new Promise((resolve12) => {
|
|
8868
|
+
this.waiters.push(resolve12);
|
|
8444
8869
|
});
|
|
8445
8870
|
if (next === null) return;
|
|
8446
8871
|
yield next;
|
|
@@ -8626,8 +9051,8 @@ var StreamableHttpTransport = class {
|
|
|
8626
9051
|
continue;
|
|
8627
9052
|
}
|
|
8628
9053
|
if (this.closed) return;
|
|
8629
|
-
const next = await new Promise((
|
|
8630
|
-
this.waiters.push(
|
|
9054
|
+
const next = await new Promise((resolve12) => {
|
|
9055
|
+
this.waiters.push(resolve12);
|
|
8631
9056
|
});
|
|
8632
9057
|
if (next === null) return;
|
|
8633
9058
|
yield next;
|
|
@@ -8798,7 +9223,7 @@ async function trySection(load) {
|
|
|
8798
9223
|
|
|
8799
9224
|
// src/code/edit-blocks.ts
|
|
8800
9225
|
import { existsSync as existsSync7, mkdirSync as mkdirSync4, readFileSync as readFileSync10, unlinkSync as unlinkSync2, writeFileSync as writeFileSync4 } from "fs";
|
|
8801
|
-
import { dirname as dirname6, resolve as
|
|
9226
|
+
import { dirname as dirname6, resolve as resolve7 } from "path";
|
|
8802
9227
|
var BLOCK_RE = /^(\S[^\n]*)\n<{7} SEARCH\n([\s\S]*?)\n?={7}\n([\s\S]*?)\n?>{7} REPLACE/gm;
|
|
8803
9228
|
function parseEditBlocks(text) {
|
|
8804
9229
|
const out = [];
|
|
@@ -8816,8 +9241,8 @@ function parseEditBlocks(text) {
|
|
|
8816
9241
|
return out;
|
|
8817
9242
|
}
|
|
8818
9243
|
function applyEditBlock(block2, rootDir) {
|
|
8819
|
-
const absRoot =
|
|
8820
|
-
const absTarget =
|
|
9244
|
+
const absRoot = resolve7(rootDir);
|
|
9245
|
+
const absTarget = resolve7(absRoot, block2.path);
|
|
8821
9246
|
if (absTarget !== absRoot && !absTarget.startsWith(`${absRoot}${sep()}`)) {
|
|
8822
9247
|
return {
|
|
8823
9248
|
path: block2.path,
|
|
@@ -8870,7 +9295,7 @@ function applyEditBlocks(blocks, rootDir) {
|
|
|
8870
9295
|
return blocks.map((b) => applyEditBlock(b, rootDir));
|
|
8871
9296
|
}
|
|
8872
9297
|
function toWholeFileEditBlock(path6, content, rootDir) {
|
|
8873
|
-
const abs =
|
|
9298
|
+
const abs = resolve7(rootDir, path6);
|
|
8874
9299
|
let search = "";
|
|
8875
9300
|
if (existsSync7(abs)) {
|
|
8876
9301
|
try {
|
|
@@ -8882,13 +9307,13 @@ function toWholeFileEditBlock(path6, content, rootDir) {
|
|
|
8882
9307
|
return { path: path6, search, replace: content, offset: 0 };
|
|
8883
9308
|
}
|
|
8884
9309
|
function snapshotBeforeEdits(blocks, rootDir) {
|
|
8885
|
-
const absRoot =
|
|
9310
|
+
const absRoot = resolve7(rootDir);
|
|
8886
9311
|
const seen = /* @__PURE__ */ new Set();
|
|
8887
9312
|
const snapshots = [];
|
|
8888
9313
|
for (const b of blocks) {
|
|
8889
9314
|
if (seen.has(b.path)) continue;
|
|
8890
9315
|
seen.add(b.path);
|
|
8891
|
-
const abs =
|
|
9316
|
+
const abs = resolve7(absRoot, b.path);
|
|
8892
9317
|
if (!existsSync7(abs)) {
|
|
8893
9318
|
snapshots.push({ path: b.path, prevContent: null });
|
|
8894
9319
|
continue;
|
|
@@ -8902,9 +9327,9 @@ function snapshotBeforeEdits(blocks, rootDir) {
|
|
|
8902
9327
|
return snapshots;
|
|
8903
9328
|
}
|
|
8904
9329
|
function restoreSnapshots(snapshots, rootDir) {
|
|
8905
|
-
const absRoot =
|
|
9330
|
+
const absRoot = resolve7(rootDir);
|
|
8906
9331
|
return snapshots.map((snap) => {
|
|
8907
|
-
const abs =
|
|
9332
|
+
const abs = resolve7(absRoot, snap.path);
|
|
8908
9333
|
if (abs !== absRoot && !abs.startsWith(`${absRoot}${sep()}`)) {
|
|
8909
9334
|
return {
|
|
8910
9335
|
path: snap.path,
|
|
@@ -11082,10 +11507,10 @@ function render(element, opts = {}) {
|
|
|
11082
11507
|
let resolveExit = () => {
|
|
11083
11508
|
};
|
|
11084
11509
|
let exitError;
|
|
11085
|
-
const exitPromise = new Promise((
|
|
11510
|
+
const exitPromise = new Promise((resolve12, reject) => {
|
|
11086
11511
|
resolveExit = () => {
|
|
11087
11512
|
if (exitError) reject(exitError);
|
|
11088
|
-
else
|
|
11513
|
+
else resolve12();
|
|
11089
11514
|
};
|
|
11090
11515
|
});
|
|
11091
11516
|
let destroyed = false;
|
|
@@ -11189,16 +11614,16 @@ var JsonlEventSink = class {
|
|
|
11189
11614
|
this.buffered++;
|
|
11190
11615
|
}
|
|
11191
11616
|
flush() {
|
|
11192
|
-
return new Promise((
|
|
11193
|
-
if (this.buffered === 0) return
|
|
11617
|
+
return new Promise((resolve12) => {
|
|
11618
|
+
if (this.buffered === 0) return resolve12();
|
|
11194
11619
|
this.stream.uncork();
|
|
11195
11620
|
this.buffered = 0;
|
|
11196
|
-
|
|
11621
|
+
resolve12();
|
|
11197
11622
|
});
|
|
11198
11623
|
}
|
|
11199
11624
|
close() {
|
|
11200
|
-
return new Promise((
|
|
11201
|
-
this.stream.end(() =>
|
|
11625
|
+
return new Promise((resolve12) => {
|
|
11626
|
+
this.stream.end(() => resolve12());
|
|
11202
11627
|
});
|
|
11203
11628
|
}
|
|
11204
11629
|
};
|
|
@@ -13748,7 +14173,7 @@ import { existsSync as existsSync18, readFileSync as readFileSync18, statSync as
|
|
|
13748
14173
|
import { join as join17 } from "path";
|
|
13749
14174
|
|
|
13750
14175
|
// src/index/semantic/ollama-launcher.ts
|
|
13751
|
-
import { spawn as
|
|
14176
|
+
import { spawn as spawn6, spawnSync as spawnSync2 } from "child_process";
|
|
13752
14177
|
import { setTimeout as sleep2 } from "timers/promises";
|
|
13753
14178
|
function findOllamaBinary() {
|
|
13754
14179
|
const cmd = process.platform === "win32" ? "where" : "which";
|
|
@@ -13773,7 +14198,7 @@ async function checkOllamaStatus(modelName, baseUrl) {
|
|
|
13773
14198
|
}
|
|
13774
14199
|
async function startOllamaDaemon(opts = {}) {
|
|
13775
14200
|
const timeoutMs = opts.timeoutMs ?? 15e3;
|
|
13776
|
-
const child =
|
|
14201
|
+
const child = spawn6("ollama", ["serve"], {
|
|
13777
14202
|
detached: true,
|
|
13778
14203
|
stdio: "ignore",
|
|
13779
14204
|
windowsHide: true
|
|
@@ -13790,8 +14215,8 @@ async function startOllamaDaemon(opts = {}) {
|
|
|
13790
14215
|
return { ready: false, pid };
|
|
13791
14216
|
}
|
|
13792
14217
|
async function pullOllamaModel(modelName, opts = {}) {
|
|
13793
|
-
return new Promise((
|
|
13794
|
-
const child =
|
|
14218
|
+
return new Promise((resolve12) => {
|
|
14219
|
+
const child = spawn6("ollama", ["pull", modelName], {
|
|
13795
14220
|
stdio: ["ignore", "pipe", "pipe"],
|
|
13796
14221
|
windowsHide: true
|
|
13797
14222
|
});
|
|
@@ -13802,8 +14227,8 @@ async function pullOllamaModel(modelName, opts = {}) {
|
|
|
13802
14227
|
}
|
|
13803
14228
|
streamLines(child.stdout, (l) => opts.onLine?.(l, "stdout"));
|
|
13804
14229
|
streamLines(child.stderr, (l) => opts.onLine?.(l, "stderr"));
|
|
13805
|
-
child.once("exit", (code) =>
|
|
13806
|
-
child.once("error", () =>
|
|
14230
|
+
child.once("exit", (code) => resolve12(code ?? -1));
|
|
14231
|
+
child.once("error", () => resolve12(-1));
|
|
13807
14232
|
});
|
|
13808
14233
|
}
|
|
13809
14234
|
function streamLines(stream, cb) {
|
|
@@ -15056,7 +15481,7 @@ var MAX_BODY_BYTES = 256 * 1024;
|
|
|
15056
15481
|
async function readBody(req) {
|
|
15057
15482
|
let total = 0;
|
|
15058
15483
|
const chunks = [];
|
|
15059
|
-
return new Promise((
|
|
15484
|
+
return new Promise((resolve12, reject) => {
|
|
15060
15485
|
req.on("data", (chunk) => {
|
|
15061
15486
|
total += chunk.length;
|
|
15062
15487
|
if (total > MAX_BODY_BYTES) {
|
|
@@ -15066,7 +15491,7 @@ async function readBody(req) {
|
|
|
15066
15491
|
}
|
|
15067
15492
|
chunks.push(chunk);
|
|
15068
15493
|
});
|
|
15069
|
-
req.on("end", () =>
|
|
15494
|
+
req.on("end", () => resolve12(Buffer.concat(chunks).toString("utf8")));
|
|
15070
15495
|
req.on("error", reject);
|
|
15071
15496
|
});
|
|
15072
15497
|
}
|
|
@@ -15143,7 +15568,7 @@ function startDashboardServer(ctx, opts = {}) {
|
|
|
15143
15568
|
const token = opts.token ?? mintToken();
|
|
15144
15569
|
const host = opts.host ?? "127.0.0.1";
|
|
15145
15570
|
const port = opts.port ?? 0;
|
|
15146
|
-
return new Promise((
|
|
15571
|
+
return new Promise((resolve12, reject) => {
|
|
15147
15572
|
const server = createServer((req, res) => {
|
|
15148
15573
|
dispatch(req, res, ctx, token).catch((err) => {
|
|
15149
15574
|
if (!res.headersSent) {
|
|
@@ -15164,7 +15589,7 @@ function startDashboardServer(ctx, opts = {}) {
|
|
|
15164
15589
|
server.close(() => doneResolve());
|
|
15165
15590
|
setTimeout(() => server.closeAllConnections?.(), 1e3).unref();
|
|
15166
15591
|
});
|
|
15167
|
-
|
|
15592
|
+
resolve12({ url, token, port: finalPort, close });
|
|
15168
15593
|
});
|
|
15169
15594
|
});
|
|
15170
15595
|
}
|
|
@@ -23484,7 +23909,7 @@ function resolvePreset(name) {
|
|
|
23484
23909
|
// src/cli/commands/doctor.ts
|
|
23485
23910
|
import { existsSync as existsSync22, statSync as statSync12 } from "fs";
|
|
23486
23911
|
import { homedir as homedir10 } from "os";
|
|
23487
|
-
import { dirname as dirname16, join as join20, resolve as
|
|
23912
|
+
import { dirname as dirname16, join as join20, resolve as resolve8 } from "path";
|
|
23488
23913
|
async function runDoctorChecks(projectRoot) {
|
|
23489
23914
|
return Promise.all([
|
|
23490
23915
|
checkApiKey(),
|
|
@@ -23766,7 +24191,7 @@ async function checkProject(projectRoot) {
|
|
|
23766
24191
|
}
|
|
23767
24192
|
async function doctorCommand() {
|
|
23768
24193
|
loadDotenv();
|
|
23769
|
-
const projectRoot =
|
|
24194
|
+
const projectRoot = resolve8(process.cwd());
|
|
23770
24195
|
console.log(`${color(`reasonix ${VERSION} \xB7 doctor`, "1")} (cwd: ${projectRoot})`);
|
|
23771
24196
|
console.log(` home: ${homedir10()}`);
|
|
23772
24197
|
console.log("");
|
|
@@ -24285,9 +24710,9 @@ var handlers3 = { dashboard: dashboard2 };
|
|
|
24285
24710
|
// src/code/checkpoints.ts
|
|
24286
24711
|
import { existsSync as existsSync24, mkdirSync as mkdirSync13, readFileSync as readFileSync23, rmSync as rmSync2, writeFileSync as writeFileSync12 } from "fs";
|
|
24287
24712
|
import { homedir as homedir11 } from "os";
|
|
24288
|
-
import { dirname as dirname17, join as join21, relative as relative4, resolve as
|
|
24713
|
+
import { dirname as dirname17, join as join21, relative as relative4, resolve as resolve9, sep as sep3 } from "path";
|
|
24289
24714
|
function sanitizeRoot(rootDir) {
|
|
24290
|
-
return
|
|
24715
|
+
return resolve9(rootDir).replace(/[\\/:]+/g, "_").replace(/^_+/, "");
|
|
24291
24716
|
}
|
|
24292
24717
|
function storeRoot(rootDir) {
|
|
24293
24718
|
return join21(homedir11(), ".reasonix", "sessions", sanitizeRoot(rootDir), "checkpoints");
|
|
@@ -24332,7 +24757,7 @@ function loadCheckpoint(rootDir, id) {
|
|
|
24332
24757
|
}
|
|
24333
24758
|
}
|
|
24334
24759
|
function createCheckpoint(opts) {
|
|
24335
|
-
const absRoot =
|
|
24760
|
+
const absRoot = resolve9(opts.rootDir);
|
|
24336
24761
|
const id = `cp-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 6)}`;
|
|
24337
24762
|
const files = [];
|
|
24338
24763
|
let bytes = 0;
|
|
@@ -24340,7 +24765,7 @@ function createCheckpoint(opts) {
|
|
|
24340
24765
|
for (const p of opts.paths) {
|
|
24341
24766
|
if (seen.has(p)) continue;
|
|
24342
24767
|
seen.add(p);
|
|
24343
|
-
const abs =
|
|
24768
|
+
const abs = resolve9(absRoot, p);
|
|
24344
24769
|
if (abs !== absRoot && !abs.startsWith(`${absRoot}${sep3}`)) continue;
|
|
24345
24770
|
const rel = relative4(absRoot, abs).split(sep3).join("/");
|
|
24346
24771
|
if (existsSync24(abs)) {
|
|
@@ -24389,14 +24814,14 @@ function findCheckpoint(rootDir, idOrName) {
|
|
|
24389
24814
|
}
|
|
24390
24815
|
function restoreCheckpoint(rootDir, id) {
|
|
24391
24816
|
const cp = loadCheckpoint(rootDir, id);
|
|
24392
|
-
const absRoot =
|
|
24817
|
+
const absRoot = resolve9(rootDir);
|
|
24393
24818
|
const result = { restored: [], removed: [], skipped: [] };
|
|
24394
24819
|
if (!cp) {
|
|
24395
24820
|
result.skipped.push({ path: "(checkpoint)", reason: `not found: ${id}` });
|
|
24396
24821
|
return result;
|
|
24397
24822
|
}
|
|
24398
24823
|
for (const f of cp.files) {
|
|
24399
|
-
const abs =
|
|
24824
|
+
const abs = resolve9(absRoot, f.path);
|
|
24400
24825
|
if (abs !== absRoot && !abs.startsWith(`${absRoot}${sep3}`)) {
|
|
24401
24826
|
result.skipped.push({ path: f.path, reason: "path escapes rootDir" });
|
|
24402
24827
|
continue;
|
|
@@ -24782,7 +25207,7 @@ var handlers4 = {
|
|
|
24782
25207
|
|
|
24783
25208
|
// src/cli/ui/slash/handlers/init.ts
|
|
24784
25209
|
import { existsSync as existsSync25 } from "fs";
|
|
24785
|
-
import * as
|
|
25210
|
+
import * as pathMod5 from "path";
|
|
24786
25211
|
var INIT_PROMPT = [
|
|
24787
25212
|
"# Task: Initialize REASONIX.md",
|
|
24788
25213
|
"",
|
|
@@ -24846,7 +25271,7 @@ var init = (args, _loop, ctx) => {
|
|
|
24846
25271
|
return { info: t("handlers.init.codeOnly") };
|
|
24847
25272
|
}
|
|
24848
25273
|
const force = (args[0] ?? "").toLowerCase() === "force";
|
|
24849
|
-
const target =
|
|
25274
|
+
const target = pathMod5.join(ctx.codeRoot, "REASONIX.md");
|
|
24850
25275
|
if (existsSync25(target) && !force) {
|
|
24851
25276
|
return {
|
|
24852
25277
|
info: [
|
|
@@ -27529,11 +27954,11 @@ function AppInner({
|
|
|
27529
27954
|
if (key.escape && busy) {
|
|
27530
27955
|
if (abortedThisTurn.current) return;
|
|
27531
27956
|
abortedThisTurn.current = true;
|
|
27532
|
-
const
|
|
27533
|
-
if (
|
|
27957
|
+
const resolve12 = editReviewResolveRef.current;
|
|
27958
|
+
if (resolve12) {
|
|
27534
27959
|
editReviewResolveRef.current = null;
|
|
27535
27960
|
setPendingEditReview(null);
|
|
27536
|
-
|
|
27961
|
+
resolve12({ choice: "reject" });
|
|
27537
27962
|
}
|
|
27538
27963
|
if (activeLoopRef.current) stopLoop();
|
|
27539
27964
|
loop2.abort();
|
|
@@ -27940,11 +28365,11 @@ function AppInner({
|
|
|
27940
28365
|
handleStagedInputSubmitRef.current(text ?? "", { plan: plan3, mode: choice }).catch(() => void 0);
|
|
27941
28366
|
},
|
|
27942
28367
|
resolveEditReview: (choice) => {
|
|
27943
|
-
const
|
|
27944
|
-
if (
|
|
28368
|
+
const resolve12 = editReviewResolveRef.current;
|
|
28369
|
+
if (resolve12) {
|
|
27945
28370
|
editReviewResolveRef.current = null;
|
|
27946
28371
|
setPendingEditReview(null);
|
|
27947
|
-
|
|
28372
|
+
resolve12({ choice, denyContext: void 0 });
|
|
27948
28373
|
}
|
|
27949
28374
|
},
|
|
27950
28375
|
resolveReviseConfirm: (choice) => {
|
|
@@ -28994,10 +29419,10 @@ Continue executing from the next pending step. Call mark_step_complete after eac
|
|
|
28994
29419
|
{
|
|
28995
29420
|
block: pendingEditReview,
|
|
28996
29421
|
onChoose: (choice, denyContext) => {
|
|
28997
|
-
const
|
|
28998
|
-
if (
|
|
29422
|
+
const resolve12 = editReviewResolveRef.current;
|
|
29423
|
+
if (resolve12) {
|
|
28999
29424
|
editReviewResolveRef.current = null;
|
|
29000
|
-
|
|
29425
|
+
resolve12({ choice, denyContext });
|
|
29001
29426
|
}
|
|
29002
29427
|
}
|
|
29003
29428
|
}
|
|
@@ -29359,10 +29784,10 @@ async function chatCommand(opts) {
|
|
|
29359
29784
|
|
|
29360
29785
|
// src/cli/commands/code.tsx
|
|
29361
29786
|
import { readFileSync as readFileSync24 } from "fs";
|
|
29362
|
-
import { basename as basename2, resolve as
|
|
29787
|
+
import { basename as basename2, resolve as resolve10 } from "path";
|
|
29363
29788
|
async function codeCommand(opts = {}) {
|
|
29364
29789
|
const { codeSystemPrompt: codeSystemPrompt2 } = await import("./prompt-YUL7CYKY.js");
|
|
29365
|
-
const rootDir =
|
|
29790
|
+
const rootDir = resolve10(opts.dir ?? process.cwd());
|
|
29366
29791
|
const session = opts.noSession ? void 0 : `code-${sanitizeName(basename2(rootDir))}`;
|
|
29367
29792
|
const tools = new ToolRegistry();
|
|
29368
29793
|
const jobs2 = new JobRegistry();
|
|
@@ -29400,7 +29825,7 @@ async function codeCommand(opts = {}) {
|
|
|
29400
29825
|
process.stderr.write("--system-append is empty \u2014 no prompt text will be appended\n");
|
|
29401
29826
|
}
|
|
29402
29827
|
if (opts.systemAppendFile) {
|
|
29403
|
-
const filePath =
|
|
29828
|
+
const filePath = resolve10(opts.systemAppendFile);
|
|
29404
29829
|
try {
|
|
29405
29830
|
systemAppendFileContents = readFileSync24(filePath, "utf8");
|
|
29406
29831
|
} catch (err) {
|
|
@@ -29433,7 +29858,7 @@ async function codeCommand(opts = {}) {
|
|
|
29433
29858
|
}
|
|
29434
29859
|
|
|
29435
29860
|
// src/cli/commands/commit.ts
|
|
29436
|
-
import { spawn as
|
|
29861
|
+
import { spawn as spawn7, spawnSync as spawnSync4 } from "child_process";
|
|
29437
29862
|
import { mkdtempSync, readFileSync as readFileSync25, unlinkSync as unlinkSync6, writeFileSync as writeFileSync13 } from "fs";
|
|
29438
29863
|
import { tmpdir } from "os";
|
|
29439
29864
|
import { join as join23 } from "path";
|
|
@@ -29612,7 +30037,7 @@ function editInExternal(initial) {
|
|
|
29612
30037
|
return cleaned || null;
|
|
29613
30038
|
}
|
|
29614
30039
|
function commitWithMessage(message) {
|
|
29615
|
-
const child =
|
|
30040
|
+
const child = spawn7("git", ["commit", "-F", "-"], {
|
|
29616
30041
|
stdio: ["pipe", "inherit", "inherit"]
|
|
29617
30042
|
});
|
|
29618
30043
|
child.stdin.write(message);
|
|
@@ -30204,7 +30629,7 @@ function mapReplacer(_key, value) {
|
|
|
30204
30629
|
}
|
|
30205
30630
|
|
|
30206
30631
|
// src/cli/commands/index.ts
|
|
30207
|
-
import { resolve as
|
|
30632
|
+
import { resolve as resolve11 } from "path";
|
|
30208
30633
|
|
|
30209
30634
|
// src/index/semantic/preflight.ts
|
|
30210
30635
|
import { stdin as stdin3, stdout as stdout2 } from "process";
|
|
@@ -30278,7 +30703,7 @@ async function confirm(question, defaultYes) {
|
|
|
30278
30703
|
|
|
30279
30704
|
// src/cli/commands/index.ts
|
|
30280
30705
|
async function indexCommand(opts = {}) {
|
|
30281
|
-
const root =
|
|
30706
|
+
const root = resolve11(opts.dir ?? process.cwd());
|
|
30282
30707
|
const tty2 = process.stderr.isTTY === true && process.stdin.isTTY === true;
|
|
30283
30708
|
const model2 = opts.model ?? process.env.REASONIX_EMBED_MODEL ?? "nomic-embed-text";
|
|
30284
30709
|
const preflightOk = await ollamaPreflight({
|
|
@@ -31904,7 +32329,7 @@ async function setupCommand(_opts = {}) {
|
|
|
31904
32329
|
}
|
|
31905
32330
|
|
|
31906
32331
|
// src/cli/commands/update.ts
|
|
31907
|
-
import { spawn as
|
|
32332
|
+
import { spawn as spawn8 } from "child_process";
|
|
31908
32333
|
function planUpdate(input) {
|
|
31909
32334
|
const diff = compareVersions(input.current, input.latest);
|
|
31910
32335
|
if (diff > 0) {
|
|
@@ -31934,13 +32359,13 @@ function planUpdate(input) {
|
|
|
31934
32359
|
};
|
|
31935
32360
|
}
|
|
31936
32361
|
function defaultSpawn(argv) {
|
|
31937
|
-
return new Promise((
|
|
31938
|
-
const child =
|
|
32362
|
+
return new Promise((resolve12, reject) => {
|
|
32363
|
+
const child = spawn8(argv[0], argv.slice(1), {
|
|
31939
32364
|
stdio: "inherit",
|
|
31940
32365
|
shell: process.platform === "win32"
|
|
31941
32366
|
});
|
|
31942
32367
|
child.once("error", reject);
|
|
31943
|
-
child.once("exit", (code) =>
|
|
32368
|
+
child.once("exit", (code) => resolve12(code ?? 1));
|
|
31944
32369
|
});
|
|
31945
32370
|
}
|
|
31946
32371
|
async function updateCommand(opts = {}) {
|