@ordewell/core 0.6.2 → 0.6.4
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/{IFileSystem-DuEiG_kA.d.ts → IFileSystem-DQEKyC6H.d.ts} +1 -1
- package/dist/{IFileSystem-d_SV7jAH.d.mts → IFileSystem-Qj2MWHn_.d.mts} +1 -1
- package/dist/{ModeResolver-DXxV4n_V.d.ts → ModeResolver-ByNpC0EP.d.ts} +1 -1
- package/dist/{ModeResolver-DIReYAOq.d.mts → ModeResolver-rqaY_4Ex.d.mts} +1 -1
- package/dist/{Task-ib_U0YP7.d.mts → Task-xqdxugt2.d.mts} +31 -1
- package/dist/{Task-ib_U0YP7.d.ts → Task-xqdxugt2.d.ts} +31 -1
- package/dist/index.d.mts +86 -81
- package/dist/index.d.ts +86 -81
- package/dist/index.js +194 -38
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +185 -29
- package/dist/index.mjs.map +1 -1
- package/dist/order-labels.d.mts +1 -1
- package/dist/order-labels.d.ts +1 -1
- package/dist/{parsing-C8Z22twN.d.ts → parsing-COPG8-Ep.d.ts} +2 -2
- package/dist/{parsing-DjRJJX-y.d.mts → parsing-DDZ1rUYa.d.mts} +2 -2
- package/dist/parsing.d.mts +3 -3
- package/dist/parsing.d.ts +3 -3
- package/dist/{plan-utils-2K_N5ZJe.d.ts → plan-utils-Br8UAoiu.d.ts} +2 -2
- package/dist/{plan-utils-D94CJ7CA.d.mts → plan-utils-DMgajMvd.d.mts} +2 -2
- package/dist/plan-utils.d.mts +3 -3
- package/dist/plan-utils.d.ts +3 -3
- package/dist/testing.d.mts +2 -2
- package/dist/testing.d.ts +2 -2
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2987,7 +2987,10 @@ var WRAPPER_FAMILY = {
|
|
|
2987
2987
|
// The multi-call binary ships its own `rm`, `mv` and `sh`, so the refused
|
|
2988
2988
|
// name arrives as its first argument.
|
|
2989
2989
|
busybox: { booleanFlags: ["--list", "--install", ...HELP_FLAGS] },
|
|
2990
|
-
command: { booleanFlags: ["-p"], noExecFlags: ["-v", "-V"] }
|
|
2990
|
+
command: { booleanFlags: ["-p"], noExecFlags: ["-v", "-V"] },
|
|
2991
|
+
// Runs the named shell builtin, so `builtin eval …` is an `eval`. Classified
|
|
2992
|
+
// on its own name it only asked, and a grant for `builtin echo` covered it.
|
|
2993
|
+
builtin: {}
|
|
2991
2994
|
};
|
|
2992
2995
|
var XARGS_SPEC = {
|
|
2993
2996
|
valueFlags: [
|
|
@@ -3119,6 +3122,8 @@ function lex(command, nested, dialect) {
|
|
|
3119
3122
|
let unbalanced = false;
|
|
3120
3123
|
let grouped = false;
|
|
3121
3124
|
let joinedBy;
|
|
3125
|
+
let followable = true;
|
|
3126
|
+
let bareSegment = false;
|
|
3122
3127
|
let tokens = [];
|
|
3123
3128
|
let expandable = false;
|
|
3124
3129
|
let current = "";
|
|
@@ -3148,9 +3153,20 @@ function lex(command, nested, dialect) {
|
|
|
3148
3153
|
current = "";
|
|
3149
3154
|
started = false;
|
|
3150
3155
|
};
|
|
3151
|
-
const endSegment = (nextPiped, next = "other") => {
|
|
3156
|
+
const endSegment = (nextPiped, next = "other", last = false) => {
|
|
3152
3157
|
endToken();
|
|
3153
|
-
if (
|
|
3158
|
+
if (!last && next === "other") followable = false;
|
|
3159
|
+
if (tokens.length > 0) {
|
|
3160
|
+
const seg = toSegment(tokens, piped, dialect);
|
|
3161
|
+
if (!seg.binary) {
|
|
3162
|
+
followable = false;
|
|
3163
|
+
bareSegment = true;
|
|
3164
|
+
}
|
|
3165
|
+
segments.push({ ...seg, expandable, inputs, joinedBy });
|
|
3166
|
+
} else if (!last || segments.length > 0 || joinedBy !== void 0) {
|
|
3167
|
+
followable = false;
|
|
3168
|
+
bareSegment = true;
|
|
3169
|
+
}
|
|
3154
3170
|
tokens = [];
|
|
3155
3171
|
inputs = [];
|
|
3156
3172
|
expandable = false;
|
|
@@ -3305,8 +3321,8 @@ function lex(command, nested, dialect) {
|
|
|
3305
3321
|
i++;
|
|
3306
3322
|
}
|
|
3307
3323
|
if (quote !== "") unbalanced = true;
|
|
3308
|
-
endSegment(false);
|
|
3309
|
-
return { segments, unsafeRedirect, processSubstitution, unbalanced, grouped };
|
|
3324
|
+
endSegment(false, "other", true);
|
|
3325
|
+
return { segments, unsafeRedirect, processSubstitution, unbalanced, grouped, followable, bareSegment };
|
|
3310
3326
|
}
|
|
3311
3327
|
function binaryName(token, dialect) {
|
|
3312
3328
|
const base = token.split(/[/\\]/).pop() ?? "";
|
|
@@ -3450,7 +3466,8 @@ function lexAll(command, dialect, cdpathSet = false) {
|
|
|
3450
3466
|
const queue = [];
|
|
3451
3467
|
const top = lex(command, queue, dialect);
|
|
3452
3468
|
const all = { ...top, segments: top.segments.filter((s) => s.binary) };
|
|
3453
|
-
|
|
3469
|
+
markShellState(all.segments, top.bareSegment);
|
|
3470
|
+
if (top.followable && !top.grouped && queue.length === 0 && !cdpathSet) followCd(all.segments);
|
|
3454
3471
|
for (let depth = 0; depth < 32 && queue.length > 0; depth++) {
|
|
3455
3472
|
const inner = lex(queue.shift(), queue, dialect);
|
|
3456
3473
|
all.segments.push(...inner.segments.filter((s) => s.binary));
|
|
@@ -3467,22 +3484,51 @@ function literalCdTarget(seg) {
|
|
|
3467
3484
|
return target === "" || target.startsWith("-") || target.startsWith("~") || /[*?[]/.test(target) ? void 0 : target;
|
|
3468
3485
|
}
|
|
3469
3486
|
function followCd(segments) {
|
|
3470
|
-
if (segments.some((s) => s.joinedBy === "other")) return;
|
|
3487
|
+
if (segments.some((s) => s.joinedBy === "other" || SHELL_STATE_COMMANDS.has(s.binary))) return;
|
|
3488
|
+
const inPipeline = (i) => segments[i].joinedBy === "pipe" || segments[i + 1]?.joinedBy === "pipe";
|
|
3489
|
+
if (segments.some((s, i) => s.binary === "cd" && !inPipeline(i) && literalCdTarget(s) === void 0)) return;
|
|
3471
3490
|
let chain = [];
|
|
3472
3491
|
segments.forEach((seg, i) => {
|
|
3473
3492
|
seg.cwd = chain;
|
|
3474
|
-
const
|
|
3475
|
-
const target = inPipeline ? void 0 : literalCdTarget(seg);
|
|
3493
|
+
const target = inPipeline(i) ? void 0 : literalCdTarget(seg);
|
|
3476
3494
|
if (target !== void 0) chain = [...chain, target];
|
|
3477
3495
|
});
|
|
3478
3496
|
}
|
|
3497
|
+
function markShellState(segments, bareSegment) {
|
|
3498
|
+
if (!bareSegment && !segments.some((s) => SHELL_STATE_COMMANDS.has(s.binary))) return;
|
|
3499
|
+
for (const seg of segments) if (seg.binary === "cd") seg.cdSteered = true;
|
|
3500
|
+
}
|
|
3501
|
+
var SHELL_STATE_COMMANDS = /* @__PURE__ */ new Set([
|
|
3502
|
+
"export",
|
|
3503
|
+
"set",
|
|
3504
|
+
"shopt",
|
|
3505
|
+
"declare",
|
|
3506
|
+
"typeset",
|
|
3507
|
+
"readonly",
|
|
3508
|
+
"local",
|
|
3509
|
+
"unset",
|
|
3510
|
+
"let",
|
|
3511
|
+
"source",
|
|
3512
|
+
".",
|
|
3513
|
+
"eval",
|
|
3514
|
+
"exec",
|
|
3515
|
+
"pushd",
|
|
3516
|
+
"popd",
|
|
3517
|
+
"alias",
|
|
3518
|
+
"unalias",
|
|
3519
|
+
"builtin",
|
|
3520
|
+
"command",
|
|
3521
|
+
"enable",
|
|
3522
|
+
"hash",
|
|
3523
|
+
"trap"
|
|
3524
|
+
]);
|
|
3479
3525
|
function looksLikePath(arg) {
|
|
3480
3526
|
if (arg.startsWith("--") && arg.includes("=")) return looksLikePath(arg.slice(arg.indexOf("=") + 1));
|
|
3481
3527
|
return arg.startsWith("/") || arg.startsWith("~") || arg.startsWith("../") || arg === ".." || arg.startsWith("./") || /^[A-Za-z]:/.test(arg) || arg.startsWith("\\") || arg.startsWith("..\\") || arg.startsWith(".\\") || /(^|[\\/])\.\.([\\/]|$)/.test(arg);
|
|
3482
3528
|
}
|
|
3483
3529
|
function pathRefs(command, opts = {}) {
|
|
3484
3530
|
const dialect = dialectFor(opts.dialect);
|
|
3485
|
-
return lexAll(command, dialect, opts.cdpathSet).segments.flatMap((seg) => {
|
|
3531
|
+
return lexAll(command.trim(), dialect, opts.cdpathSet).segments.flatMap((seg) => {
|
|
3486
3532
|
const patterns = patternArgs(seg);
|
|
3487
3533
|
const cwd = seg.cwd ?? [];
|
|
3488
3534
|
return [...seg.args.filter((_, i) => !patterns.has(i)), ...seg.inputs].flatMap(pathIn).map((path27) => ({ path: path27, cwd }));
|
|
@@ -3650,7 +3696,7 @@ function scopeFor(seg) {
|
|
|
3650
3696
|
return [seg.binary, ...lead].join(" ");
|
|
3651
3697
|
}
|
|
3652
3698
|
function isAuto(seg, opts) {
|
|
3653
|
-
if (seg.binary === "cd") return !opts.cdpathSet && literalCdTarget(seg) !== void 0;
|
|
3699
|
+
if (seg.binary === "cd") return !opts.cdpathSet && !seg.cdSteered && literalCdTarget(seg) !== void 0;
|
|
3654
3700
|
if (!AUTO_COMMANDS.includes(seg.binary)) return false;
|
|
3655
3701
|
if (seg.expandable) return false;
|
|
3656
3702
|
if (seg.binary !== "git") return true;
|
|
@@ -3674,6 +3720,9 @@ function refusalFor(seg) {
|
|
|
3674
3720
|
if (seg.binary === "source" || seg.binary === ".") {
|
|
3675
3721
|
return `"${seg.binary}" runs a file's contents as commands, which this classifier cannot inspect. Use the read-only research tools, or describe it as a task.`;
|
|
3676
3722
|
}
|
|
3723
|
+
if (seg.binary === "enable" && seg.args.some((a) => /^-[a-zA-Z]*f/.test(a))) {
|
|
3724
|
+
return `"enable -f" loads a shared library into the shell, which runs its code. Use the read-only research tools, or describe it as a task.`;
|
|
3725
|
+
}
|
|
3677
3726
|
if (SHELL_KEYWORDS.includes(seg.binary)) {
|
|
3678
3727
|
return `"${seg.binary}" is a shell keyword or compound-command opener. This classifier only inspects the command that runs first, and a keyword hides the real one from it. Run the inner command directly, or describe it as a task.`;
|
|
3679
3728
|
}
|
|
@@ -3784,6 +3833,9 @@ function classifyCommand(command, opts = {}) {
|
|
|
3784
3833
|
}
|
|
3785
3834
|
const nonAuto = unwrapped.filter((u) => u.runner !== void 0 || !isAuto(u.seg, opts));
|
|
3786
3835
|
if (nonAuto.length === 0) return { tier: "auto", scope: "" };
|
|
3836
|
+
if (nonAuto.some((u) => u.seg.binary === "cd" || u.seg.binary !== "command" && SHELL_STATE_COMMANDS.has(u.seg.binary))) {
|
|
3837
|
+
return { tier: "ask", scope: trimmed };
|
|
3838
|
+
}
|
|
3787
3839
|
const scope = [...new Set(nonAuto.map((u) => u.runner ? `${u.runner} ${scopeFor(u.seg)}` : scopeFor(u.seg)))].sort().join(" + ");
|
|
3788
3840
|
return { tier: "ask", scope };
|
|
3789
3841
|
}
|
|
@@ -3852,6 +3904,9 @@ function researchShellWarning(shell) {
|
|
|
3852
3904
|
if (shell.dialect !== "cmd") return null;
|
|
3853
3905
|
return "No POSIX shell was found on this machine, so research commands run through cmd.exe, where most read-only tools (ls, cat, wc, head, grep, find) do not exist. Installing Git for Windows provides one and restores the full research toolset.";
|
|
3854
3906
|
}
|
|
3907
|
+
function researchPolicyOptions(shell) {
|
|
3908
|
+
return { dialect: shell.dialect, cdpathSet: !!process.env.CDPATH };
|
|
3909
|
+
}
|
|
3855
3910
|
|
|
3856
3911
|
// src/services/pathScope.ts
|
|
3857
3912
|
var os = __toESM(require("os"));
|
|
@@ -4085,7 +4140,7 @@ ${refs.output.trim()}`);
|
|
|
4085
4140
|
return [...found].map(([scope, abs]) => ({ abs, scope }));
|
|
4086
4141
|
}
|
|
4087
4142
|
policyOptions() {
|
|
4088
|
-
return
|
|
4143
|
+
return researchPolicyOptions(this.researchShell);
|
|
4089
4144
|
}
|
|
4090
4145
|
/**
|
|
4091
4146
|
* Three tiers (see `commandPolicy.ts`): read-only inspection runs silently,
|
|
@@ -10532,16 +10587,15 @@ function nonPromptingFs(fs16) {
|
|
|
10532
10587
|
findSymbol: (symbol, opts) => withinWorkspace(opts?.path) ? fs16.findSymbol(symbol, opts) : Promise.resolve(outsideRefusal(opts.path)),
|
|
10533
10588
|
listDir: (p, depth) => withinWorkspace(p) ? fs16.listDir(p, depth) : Promise.resolve(outsideRefusal(p)),
|
|
10534
10589
|
bash: async (command, signal) => {
|
|
10535
|
-
const
|
|
10536
|
-
const policy = { dialect, cdpathSet: !!process.env.CDPATH };
|
|
10590
|
+
const policy = researchPolicyOptions(resolveResearchShell());
|
|
10537
10591
|
const { tier, reason } = classifyCommand(command, policy);
|
|
10538
10592
|
if (tier !== "auto") {
|
|
10539
10593
|
return refused2(
|
|
10540
10594
|
tier === "refuse" ? `Command refused: ${reason}` : `"${command}" needs user approval, and research agents run in the background where nothing can be approved. Use the read-only research tools, and note in your digest that this command would answer the question.`
|
|
10541
10595
|
);
|
|
10542
10596
|
}
|
|
10543
|
-
const escaping = pathRefs(command, policy).
|
|
10544
|
-
if (escaping) return outsideRefusal(escaping.
|
|
10597
|
+
const escaping = pathRefs(command, policy).map((ref) => resolveRef(fs16.getWorkspaceRoot(), ref)).find((r) => !r.inside);
|
|
10598
|
+
if (escaping) return outsideRefusal(escaping.abs);
|
|
10545
10599
|
return fs16.bash(command, signal);
|
|
10546
10600
|
}
|
|
10547
10601
|
};
|
|
@@ -13578,11 +13632,17 @@ function buildShellInvocation(command, args) {
|
|
|
13578
13632
|
const inner = [command, ...args].map(posixShellQuote).join(" ");
|
|
13579
13633
|
return { shellPath: "/bin/bash", shellArgs: ["-lc", inner] };
|
|
13580
13634
|
}
|
|
13635
|
+
var BSD_SCRIPT_PLATFORMS = /* @__PURE__ */ new Set(["darwin", "freebsd", "openbsd", "netbsd"]);
|
|
13636
|
+
var BSD_SCRIPT_FEED = 'exec 5<&0; p=$(mktemp) || exit 1; { cat <&5 & echo $! >"$p"; wait; } 2>/dev/null | { script -q -e -F /dev/null /bin/sh -c "$0"; s=$?; kill "$(cat "$p")" 2>/dev/null; rm -f "$p"; exit $s; }';
|
|
13581
13637
|
function wrapWithPty(command, args, opts = {}) {
|
|
13582
13638
|
const inner = [command, ...args].map(posixShellQuote).join(" ");
|
|
13583
13639
|
const watcher = opts.controlChannel ? 'exec 4<&0; ( while read -r C R <&3; do stty cols "$C" rows "$R" <&4; done ) & ' : "";
|
|
13584
13640
|
const setup = opts.size ? `stty cols ${opts.size.cols} rows ${opts.size.rows}; ` : "";
|
|
13585
|
-
|
|
13641
|
+
const line = `${watcher}${setup}${inner}`;
|
|
13642
|
+
if (BSD_SCRIPT_PLATFORMS.has(opts.platform ?? process.platform)) {
|
|
13643
|
+
return { command: "/bin/sh", args: ["-c", BSD_SCRIPT_FEED, line] };
|
|
13644
|
+
}
|
|
13645
|
+
return { command: "script", args: ["-q", "-e", "-f", "-c", line, "/dev/null"] };
|
|
13586
13646
|
}
|
|
13587
13647
|
|
|
13588
13648
|
// src/services/blockingPrompts.ts
|
|
@@ -13777,24 +13837,37 @@ var Block = /* @__PURE__ */ ((Block2) => {
|
|
|
13777
13837
|
Block2[Block2["IfHeadlessSession"] = 8] = "IfHeadlessSession";
|
|
13778
13838
|
Block2[Block2["IfInteractiveVariant"] = 9] = "IfInteractiveVariant";
|
|
13779
13839
|
Block2[Block2["IfProjectTrust"] = 10] = "IfProjectTrust";
|
|
13840
|
+
Block2[Block2["IfHeadlessModel"] = 11] = "IfHeadlessModel";
|
|
13780
13841
|
return Block2;
|
|
13781
13842
|
})(Block || {});
|
|
13782
13843
|
function isInteractive(ctx) {
|
|
13783
13844
|
return ctx.interactive ?? !ctx.headless;
|
|
13784
13845
|
}
|
|
13846
|
+
function majorVersion(version) {
|
|
13847
|
+
const match = version?.match(/(\d+)\.\d+/);
|
|
13848
|
+
return match ? Number(match[1]) : void 0;
|
|
13849
|
+
}
|
|
13850
|
+
function versionedInvocation(manifest, ctx) {
|
|
13851
|
+
const major = majorVersion(ctx.runnerVersion);
|
|
13852
|
+
if (major === void 0) return void 0;
|
|
13853
|
+
return (manifest.runner.versioned ?? []).filter((v) => major >= v.minMajor).sort((a, b) => b.minMajor - a.minMajor)[0];
|
|
13854
|
+
}
|
|
13785
13855
|
function resolveArgs(manifest, ctx) {
|
|
13786
13856
|
const args = [];
|
|
13787
13857
|
const env = {};
|
|
13788
|
-
|
|
13789
|
-
|
|
13858
|
+
const versioned = versionedInvocation(manifest, ctx);
|
|
13859
|
+
const argsTemplate = versioned?.argsTemplate ?? manifest.runner.argsTemplate;
|
|
13860
|
+
const envTemplate = versioned ? versioned.env : manifest.runner.env;
|
|
13861
|
+
if (envTemplate) {
|
|
13862
|
+
for (const [key, value] of Object.entries(envTemplate)) {
|
|
13790
13863
|
const resolved = substituteSimple(value, ctx);
|
|
13791
13864
|
if (resolved.length > 0) env[key] = resolved;
|
|
13792
13865
|
}
|
|
13793
13866
|
}
|
|
13794
13867
|
let block = 0 /* None */;
|
|
13795
13868
|
let include = true;
|
|
13796
|
-
for (let i = 0; i <
|
|
13797
|
-
const raw =
|
|
13869
|
+
for (let i = 0; i < argsTemplate.length; i++) {
|
|
13870
|
+
const raw = argsTemplate[i];
|
|
13798
13871
|
const blockOpen = parseBlockOpen(raw);
|
|
13799
13872
|
if (blockOpen !== null) {
|
|
13800
13873
|
if (block !== 0 /* None */) throw new ResolveError(`Nested conditional block at position ${i}: "${raw}"`);
|
|
@@ -13853,6 +13926,8 @@ function parseBlockOpen(raw) {
|
|
|
13853
13926
|
return 9 /* IfInteractiveVariant */;
|
|
13854
13927
|
case "{{if projectTrust}}":
|
|
13855
13928
|
return 10 /* IfProjectTrust */;
|
|
13929
|
+
case "{{if headlessModel}}":
|
|
13930
|
+
return 11 /* IfHeadlessModel */;
|
|
13856
13931
|
default:
|
|
13857
13932
|
return null;
|
|
13858
13933
|
}
|
|
@@ -13892,6 +13967,9 @@ function shouldIncludeBlock(block, ctx) {
|
|
|
13892
13967
|
// there is no cwd to name.
|
|
13893
13968
|
case 10 /* IfProjectTrust */:
|
|
13894
13969
|
return isInteractive(ctx) && !!ctx.cwd;
|
|
13970
|
+
// A model flag that only the non-interactive subcommand takes.
|
|
13971
|
+
case 11 /* IfHeadlessModel */:
|
|
13972
|
+
return !!ctx.model && !isInteractive(ctx);
|
|
13895
13973
|
default:
|
|
13896
13974
|
return true;
|
|
13897
13975
|
}
|
|
@@ -13937,6 +14015,10 @@ function resolveToken(token, manifest, ctx) {
|
|
|
13937
14015
|
if (token === "{{model}}") return ctx.model || "";
|
|
13938
14016
|
if (token === "{{thinkingEffort}}") return ctx.thinkingEffort || "";
|
|
13939
14017
|
if (token === "{{mode}}") return ctx.mode || "build";
|
|
14018
|
+
if (token === "{{modelWithVariant}}") {
|
|
14019
|
+
if (!ctx.model) return "";
|
|
14020
|
+
return ctx.thinkingEffort ? `${ctx.model}#${ctx.thinkingEffort}` : ctx.model;
|
|
14021
|
+
}
|
|
13940
14022
|
if (token === "{{projectTrust}}") {
|
|
13941
14023
|
if (!ctx.cwd) return "";
|
|
13942
14024
|
return `projects.${JSON.stringify(ctx.cwd)}.trust_level="trusted"`;
|
|
@@ -14011,13 +14093,21 @@ function resolveOpencodeVariantConfig(ctx) {
|
|
|
14011
14093
|
}
|
|
14012
14094
|
return JSON.stringify(config);
|
|
14013
14095
|
}
|
|
14096
|
+
function resolveOpencodeSessionConfig(ctx) {
|
|
14097
|
+
const mode = ctx.mode || "build";
|
|
14098
|
+
const pinned = resolveOpencodeVariantConfig(ctx);
|
|
14099
|
+
const config = pinned ? JSON.parse(pinned) : {};
|
|
14100
|
+
config.default_agent = mode;
|
|
14101
|
+
if (ctx.model && !pinned) config.agent = { [mode]: { model: ctx.model } };
|
|
14102
|
+
return JSON.stringify(config);
|
|
14103
|
+
}
|
|
14014
14104
|
function substituteSimple(value, ctx) {
|
|
14015
14105
|
const conditioned = value.replace(/\{\{if (\w+)\}\}([\s\S]*?)\{\{\/if\}\}/g, (match, name, body) => {
|
|
14016
14106
|
const block = parseBlockOpen(`{{if ${name}}}`);
|
|
14017
14107
|
if (block === null) return match;
|
|
14018
14108
|
return shouldIncludeBlock(block, ctx) ? body : "";
|
|
14019
14109
|
});
|
|
14020
|
-
return conditioned.replace(/\{\{opencodeVariantConfig\}\}/g, () => resolveOpencodeVariantConfig(ctx)).replace(/\{\{prompt\}\}/g, ctx.prompt).replace(/\{\{model\}\}/g, ctx.model || "").replace(/\{\{thinkingEffort\}\}/g, ctx.thinkingEffort || "").replace(/\{\{mode\}\}/g, ctx.mode || "build");
|
|
14110
|
+
return conditioned.replace(/\{\{opencodeVariantConfig\}\}/g, () => resolveOpencodeVariantConfig(ctx)).replace(/\{\{opencodeSessionConfig\}\}/g, () => resolveOpencodeSessionConfig(ctx)).replace(/\{\{prompt\}\}/g, ctx.prompt).replace(/\{\{model\}\}/g, ctx.model || "").replace(/\{\{thinkingEffort\}\}/g, ctx.thinkingEffort || "").replace(/\{\{mode\}\}/g, ctx.mode || "build");
|
|
14021
14111
|
}
|
|
14022
14112
|
|
|
14023
14113
|
// src/utils/launch.ts
|
|
@@ -23445,12 +23535,32 @@ function buildRunnerInvocation(opts) {
|
|
|
23445
23535
|
mode: opts.mode ?? "build",
|
|
23446
23536
|
headless: opts.headless,
|
|
23447
23537
|
interactive: opts.interactive,
|
|
23448
|
-
cwd: opts.cwd
|
|
23538
|
+
cwd: opts.cwd,
|
|
23539
|
+
runnerVersion: opts.runnerVersion
|
|
23449
23540
|
});
|
|
23450
23541
|
}
|
|
23451
23542
|
|
|
23452
23543
|
// src/services/HeadlessRunner.ts
|
|
23544
|
+
var import_child_process9 = require("child_process");
|
|
23545
|
+
|
|
23546
|
+
// src/services/runnerVersion.ts
|
|
23453
23547
|
var import_child_process8 = require("child_process");
|
|
23548
|
+
var VERSION_TIMEOUT_MS = 5e3;
|
|
23549
|
+
var cache = /* @__PURE__ */ new Map();
|
|
23550
|
+
var probeRunnerVersion = (command, PATH) => {
|
|
23551
|
+
const key = `${command}\0${PATH}`;
|
|
23552
|
+
let pending = cache.get(key);
|
|
23553
|
+
if (!pending) {
|
|
23554
|
+
pending = new Promise((resolve7) => {
|
|
23555
|
+
(0, import_child_process8.execFile)(command, ["--version"], { env: { ...process.env, PATH }, timeout: VERSION_TIMEOUT_MS }, (err, stdout) => {
|
|
23556
|
+
const line = String(stdout ?? "").trim();
|
|
23557
|
+
resolve7(err || !line ? void 0 : line);
|
|
23558
|
+
});
|
|
23559
|
+
});
|
|
23560
|
+
cache.set(key, pending);
|
|
23561
|
+
}
|
|
23562
|
+
return pending;
|
|
23563
|
+
};
|
|
23454
23564
|
|
|
23455
23565
|
// src/services/AbstractRunner.ts
|
|
23456
23566
|
var import_events = require("events");
|
|
@@ -23505,7 +23615,7 @@ var AbstractRunner = class {
|
|
|
23505
23615
|
function defaultHasScriptCmd() {
|
|
23506
23616
|
if (process.platform === "win32") return false;
|
|
23507
23617
|
try {
|
|
23508
|
-
(0,
|
|
23618
|
+
(0, import_child_process9.execSync)("which script", { stdio: "ignore" });
|
|
23509
23619
|
return true;
|
|
23510
23620
|
} catch {
|
|
23511
23621
|
return false;
|
|
@@ -23572,6 +23682,10 @@ Process error: ${err.message}
|
|
|
23572
23682
|
this.process.stdin.write(text2);
|
|
23573
23683
|
}
|
|
23574
23684
|
}
|
|
23685
|
+
/** Closes the runner's stdin: what it has read so far is all it will get. */
|
|
23686
|
+
endInput() {
|
|
23687
|
+
this.process?.stdin?.end();
|
|
23688
|
+
}
|
|
23575
23689
|
/** PTY resize requests from a surface that owns the terminal rendering the wrapper. */
|
|
23576
23690
|
writeControl(text2) {
|
|
23577
23691
|
if (this.controlStream && !this.controlStream.destroyed) {
|
|
@@ -23584,6 +23698,7 @@ var HeadlessRunner = class extends AbstractRunner {
|
|
|
23584
23698
|
hasScriptCmd;
|
|
23585
23699
|
resolvePath;
|
|
23586
23700
|
launchDeps;
|
|
23701
|
+
runnerVersion;
|
|
23587
23702
|
spawnCount = 0;
|
|
23588
23703
|
/**
|
|
23589
23704
|
* Session shape: a piped subprocess is not a terminal, so runners get their
|
|
@@ -23595,10 +23710,11 @@ var HeadlessRunner = class extends AbstractRunner {
|
|
|
23595
23710
|
defaultInteractive = false;
|
|
23596
23711
|
constructor(deps = {}) {
|
|
23597
23712
|
super();
|
|
23598
|
-
this.spawnImpl = deps.spawnImpl ??
|
|
23713
|
+
this.spawnImpl = deps.spawnImpl ?? import_child_process9.spawn;
|
|
23599
23714
|
this.hasScriptCmd = deps.hasScriptCmd ?? defaultHasScriptCmd;
|
|
23600
23715
|
this.resolvePath = deps.resolvePath ?? augmentedPath;
|
|
23601
23716
|
this.launchDeps = { resolvePath: this.resolvePath, ...deps.launchDeps };
|
|
23717
|
+
this.runnerVersion = deps.runnerVersion ?? probeRunnerVersion;
|
|
23602
23718
|
}
|
|
23603
23719
|
/**
|
|
23604
23720
|
* Unique per spawn, not per task: a retry reuses its task id and ids often
|
|
@@ -23616,6 +23732,9 @@ var HeadlessRunner = class extends AbstractRunner {
|
|
|
23616
23732
|
/** Everything up to, but not including, spawning — so a surface that owns its own child reaches the same decisions. */
|
|
23617
23733
|
async prepareLaunch(opts, ptyOptions) {
|
|
23618
23734
|
const interactive = this.defaultInteractive;
|
|
23735
|
+
const resolvedPath = await this.resolvePath();
|
|
23736
|
+
const manifest = opts.registry?.get(opts.runner)?.manifest;
|
|
23737
|
+
const runnerVersion = manifest?.runner.versioned?.length ? await this.runnerVersion(manifest.runner.command, resolvedPath) : void 0;
|
|
23619
23738
|
const invocation = buildRunnerInvocation({
|
|
23620
23739
|
runner: opts.runner,
|
|
23621
23740
|
prompt: opts.prompt,
|
|
@@ -23626,18 +23745,18 @@ var HeadlessRunner = class extends AbstractRunner {
|
|
|
23626
23745
|
headless: opts.headless ?? true,
|
|
23627
23746
|
interactive,
|
|
23628
23747
|
cwd: opts.cwd,
|
|
23748
|
+
runnerVersion,
|
|
23629
23749
|
registry: opts.registry
|
|
23630
23750
|
});
|
|
23631
|
-
const manifest = opts.registry?.get(opts.runner)?.manifest;
|
|
23632
23751
|
const pty = (manifest?.runner.requiresTty === true || interactive) && this.hasScriptCmd();
|
|
23633
|
-
const
|
|
23634
|
-
const { command, args } = pty ? wrapWithPty(invocation.command, invocation.args, ptyOptions) : { command: invocation.command, args: invocation.args };
|
|
23752
|
+
const { command, args } = pty ? wrapWithPty(invocation.command, invocation.args, { platform: this.launchDeps.platform, ...ptyOptions }) : { command: invocation.command, args: invocation.args };
|
|
23635
23753
|
return {
|
|
23636
23754
|
launch: await planDirectLaunch(command, args, this.launchDeps),
|
|
23637
23755
|
resolvedPath,
|
|
23638
23756
|
env: { ...opts.env, ...invocation.env },
|
|
23639
23757
|
pty,
|
|
23640
|
-
submitPromptKey: invocation.submitPromptKey
|
|
23758
|
+
submitPromptKey: invocation.submitPromptKey,
|
|
23759
|
+
closeStdin: !pty && !interactive && invocation.promptInArgs
|
|
23641
23760
|
};
|
|
23642
23761
|
}
|
|
23643
23762
|
async spawn(opts) {
|
|
@@ -23648,20 +23767,21 @@ var HeadlessRunner = class extends AbstractRunner {
|
|
|
23648
23767
|
console.error(`[headless] Starting ${opts.runner} [${modeStr}] (${opts.modelId || "default"}) for task ${opts.taskId.slice(0, 8)}${prepared.pty ? " (PTY)" : ""}`);
|
|
23649
23768
|
session.start(prepared.launch, opts.cwd, prepared.resolvedPath, prepared.env);
|
|
23650
23769
|
if (prepared.submitPromptKey) session.write("\r");
|
|
23770
|
+
if (prepared.closeStdin) session.endInput();
|
|
23651
23771
|
this.registerSession(id, session);
|
|
23652
23772
|
return session;
|
|
23653
23773
|
}
|
|
23654
23774
|
};
|
|
23655
23775
|
|
|
23656
23776
|
// src/services/TmuxRunner.ts
|
|
23657
|
-
var
|
|
23777
|
+
var import_child_process11 = require("child_process");
|
|
23658
23778
|
var import_util5 = require("util");
|
|
23659
23779
|
var import_os = require("os");
|
|
23660
23780
|
var import_path2 = require("path");
|
|
23661
23781
|
var import_fs3 = require("fs");
|
|
23662
23782
|
|
|
23663
23783
|
// src/utils/tmux.ts
|
|
23664
|
-
var
|
|
23784
|
+
var import_child_process10 = require("child_process");
|
|
23665
23785
|
function tmuxSessionName(port) {
|
|
23666
23786
|
return `ordewell-${port}`;
|
|
23667
23787
|
}
|
|
@@ -23674,7 +23794,7 @@ function tmuxWindowName(taskId, planSessionId) {
|
|
|
23674
23794
|
return `t-${scope}${slug(taskId).slice(0, 16)}`;
|
|
23675
23795
|
}
|
|
23676
23796
|
var defaultProbe = () => {
|
|
23677
|
-
(0,
|
|
23797
|
+
(0, import_child_process10.execSync)(process.platform === "win32" ? "where tmux" : "which tmux", { stdio: "ignore" });
|
|
23678
23798
|
};
|
|
23679
23799
|
function hasTmux(probe = defaultProbe) {
|
|
23680
23800
|
try {
|
|
@@ -23686,7 +23806,7 @@ function hasTmux(probe = defaultProbe) {
|
|
|
23686
23806
|
}
|
|
23687
23807
|
var defaultHasBin = (bin) => {
|
|
23688
23808
|
try {
|
|
23689
|
-
(0,
|
|
23809
|
+
(0, import_child_process10.execSync)(`${process.platform === "win32" ? "where" : "which"} ${bin}`, { stdio: "ignore" });
|
|
23690
23810
|
return true;
|
|
23691
23811
|
} catch {
|
|
23692
23812
|
return false;
|
|
@@ -23711,7 +23831,7 @@ function clipboardCopyCommand(hasBin = defaultHasBin, platform = process.platfor
|
|
|
23711
23831
|
var EXIT_RE = /<<<ORDEWELL_TMUX_EXIT:(\d+)>>>/;
|
|
23712
23832
|
var LIVENESS_INTERVAL_MS = 5e3;
|
|
23713
23833
|
var LIVENESS_MISSES = 2;
|
|
23714
|
-
var execFileAsync2 = (0, import_util5.promisify)(
|
|
23834
|
+
var execFileAsync2 = (0, import_util5.promisify)(import_child_process11.execFile);
|
|
23715
23835
|
var defaultExecFile3 = async (command, args) => {
|
|
23716
23836
|
const { stdout, stderr } = await execFileAsync2(command, args);
|
|
23717
23837
|
return { stdout: String(stdout), stderr: String(stderr) };
|
|
@@ -23861,6 +23981,7 @@ var TmuxRunner = class extends AbstractRunner {
|
|
|
23861
23981
|
pollIntervalMs;
|
|
23862
23982
|
logDir;
|
|
23863
23983
|
clipboardCommand;
|
|
23984
|
+
runnerVersion;
|
|
23864
23985
|
/** Retries get a freshly named window so a failed attempt's output stays inspectable. */
|
|
23865
23986
|
attempts = /* @__PURE__ */ new Map();
|
|
23866
23987
|
ready = null;
|
|
@@ -23873,6 +23994,7 @@ var TmuxRunner = class extends AbstractRunner {
|
|
|
23873
23994
|
this.pollIntervalMs = deps.pollIntervalMs ?? 500;
|
|
23874
23995
|
this.logDir = deps.logDir ?? (0, import_os.tmpdir)();
|
|
23875
23996
|
this.clipboardCommand = deps.clipboardCommand ?? (() => clipboardCopyCommand());
|
|
23997
|
+
this.runnerVersion = deps.runnerVersion ?? probeRunnerVersion;
|
|
23876
23998
|
}
|
|
23877
23999
|
/**
|
|
23878
24000
|
* Reaps a session orphaned by a crashed prior daemon on the same port, then
|
|
@@ -23956,6 +24078,8 @@ var TmuxRunner = class extends AbstractRunner {
|
|
|
23956
24078
|
}
|
|
23957
24079
|
}
|
|
23958
24080
|
async spawn(opts) {
|
|
24081
|
+
const manifest = opts.registry?.get(opts.runner)?.manifest;
|
|
24082
|
+
const runnerVersion = manifest?.runner.versioned?.length ? await this.runnerVersion(manifest.runner.command, await this.resolvePath()) : void 0;
|
|
23959
24083
|
const invocation = buildRunnerInvocation({
|
|
23960
24084
|
runner: opts.runner,
|
|
23961
24085
|
prompt: opts.prompt,
|
|
@@ -23971,6 +24095,7 @@ var TmuxRunner = class extends AbstractRunner {
|
|
|
23971
24095
|
headless: opts.headless ?? true,
|
|
23972
24096
|
interactive: true,
|
|
23973
24097
|
cwd: opts.cwd,
|
|
24098
|
+
runnerVersion,
|
|
23974
24099
|
registry: opts.registry
|
|
23975
24100
|
});
|
|
23976
24101
|
await this.ensureSession();
|
|
@@ -24000,7 +24125,7 @@ var TmuxRunner = class extends AbstractRunner {
|
|
|
24000
24125
|
|
|
24001
24126
|
// src/services/StructuredRunner.ts
|
|
24002
24127
|
var import_events2 = require("events");
|
|
24003
|
-
var
|
|
24128
|
+
var import_child_process12 = require("child_process");
|
|
24004
24129
|
var DEFAULT_INTERRUPT_GRACE_MS = 5e3;
|
|
24005
24130
|
var TOOL_ARG_MAX_CHARS = 120;
|
|
24006
24131
|
var TURN_OPENING_EVENTS = /* @__PURE__ */ new Set(["assistant_text", "assistant_text_delta", "thinking", "thinking_delta", "tool_call"]);
|
|
@@ -24374,7 +24499,7 @@ var StructuredRunner = class extends AbstractRunner {
|
|
|
24374
24499
|
const session = new StructuredSession(id, opts.taskId, {
|
|
24375
24500
|
runner: opts.runner,
|
|
24376
24501
|
deps: {
|
|
24377
|
-
spawn:
|
|
24502
|
+
spawn: import_child_process12.spawn,
|
|
24378
24503
|
fetch: globalThis.fetch,
|
|
24379
24504
|
...this.processDeps,
|
|
24380
24505
|
// Already resolved by the caller (ADR-0016); resolving it again here
|
|
@@ -24469,7 +24594,7 @@ function tokensMatch(presented, expected) {
|
|
|
24469
24594
|
var fs13 = __toESM(require("fs"));
|
|
24470
24595
|
var path24 = __toESM(require("path"));
|
|
24471
24596
|
var os6 = __toESM(require("os"));
|
|
24472
|
-
var
|
|
24597
|
+
var import_child_process13 = require("child_process");
|
|
24473
24598
|
var import_crypto6 = require("crypto");
|
|
24474
24599
|
|
|
24475
24600
|
// src/plugins/FsPluginStore.ts
|
|
@@ -24887,6 +25012,37 @@ var OPENCODE_MANIFEST = {
|
|
|
24887
25012
|
env: {
|
|
24888
25013
|
OPENCODE_CONFIG_CONTENT: "{{if interactiveVariant}}{{opencodeVariantConfig}}{{/if}}"
|
|
24889
25014
|
},
|
|
25015
|
+
// OpenCode 2.x changed both shapes. Its TUI takes no `--model`/`--agent`,
|
|
25016
|
+
// so they ride on OPENCODE_CONFIG_CONTENT instead, and its `run` has no
|
|
25017
|
+
// `--variant` — the variant is a `#suffix` on the model id. Both run
|
|
25018
|
+
// `--standalone`: without it the TUI and `run` attach to the user's
|
|
25019
|
+
// background service, which neither reads this process's config nor
|
|
25020
|
+
// starts in the task's directory and environment.
|
|
25021
|
+
versioned: [{
|
|
25022
|
+
minMajor: 2,
|
|
25023
|
+
argsTemplate: [
|
|
25024
|
+
"{{if headlessSession}}",
|
|
25025
|
+
"run",
|
|
25026
|
+
"--agent",
|
|
25027
|
+
"{{mode}}",
|
|
25028
|
+
"{{/if}}",
|
|
25029
|
+
"{{if headlessModel}}",
|
|
25030
|
+
"--model",
|
|
25031
|
+
"{{modelWithVariant}}",
|
|
25032
|
+
"{{/if}}",
|
|
25033
|
+
"--standalone",
|
|
25034
|
+
"{{if buildMode}}",
|
|
25035
|
+
"--auto",
|
|
25036
|
+
"{{/if}}",
|
|
25037
|
+
"{{if interactive}}",
|
|
25038
|
+
"--prompt",
|
|
25039
|
+
"{{/if}}",
|
|
25040
|
+
"{{prompt}}"
|
|
25041
|
+
],
|
|
25042
|
+
env: {
|
|
25043
|
+
OPENCODE_CONFIG_CONTENT: "{{if interactive}}{{opencodeSessionConfig}}{{/if}}"
|
|
25044
|
+
}
|
|
25045
|
+
}],
|
|
24890
25046
|
promptInArgs: true,
|
|
24891
25047
|
requiresTty: true,
|
|
24892
25048
|
// `--prompt` only pre-fills the TUI's composer; it does not submit it.
|
|
@@ -25005,7 +25161,7 @@ function isReservedRunnerName(name) {
|
|
|
25005
25161
|
return RESERVED_RUNNER_NAMES.has(name.toLowerCase());
|
|
25006
25162
|
}
|
|
25007
25163
|
function gitClone(url, destDir) {
|
|
25008
|
-
(0,
|
|
25164
|
+
(0, import_child_process13.execFileSync)("git", ["clone", "--depth", "1", "--", url, destDir], {
|
|
25009
25165
|
stdio: "pipe",
|
|
25010
25166
|
timeout: 3e4,
|
|
25011
25167
|
// A repository that demands credentials should fail, not hang on a prompt.
|