@ordewell/core 0.6.2 → 0.6.3
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 +184 -37
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +175 -28
- 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.mjs
CHANGED
|
@@ -2477,6 +2477,8 @@ function lex(command, nested, dialect) {
|
|
|
2477
2477
|
let unbalanced = false;
|
|
2478
2478
|
let grouped = false;
|
|
2479
2479
|
let joinedBy;
|
|
2480
|
+
let followable = true;
|
|
2481
|
+
let bareSegment = false;
|
|
2480
2482
|
let tokens = [];
|
|
2481
2483
|
let expandable = false;
|
|
2482
2484
|
let current = "";
|
|
@@ -2506,9 +2508,20 @@ function lex(command, nested, dialect) {
|
|
|
2506
2508
|
current = "";
|
|
2507
2509
|
started = false;
|
|
2508
2510
|
};
|
|
2509
|
-
const endSegment = (nextPiped, next = "other") => {
|
|
2511
|
+
const endSegment = (nextPiped, next = "other", last = false) => {
|
|
2510
2512
|
endToken();
|
|
2511
|
-
if (
|
|
2513
|
+
if (!last && next === "other") followable = false;
|
|
2514
|
+
if (tokens.length > 0) {
|
|
2515
|
+
const seg = toSegment(tokens, piped, dialect);
|
|
2516
|
+
if (!seg.binary) {
|
|
2517
|
+
followable = false;
|
|
2518
|
+
bareSegment = true;
|
|
2519
|
+
}
|
|
2520
|
+
segments.push({ ...seg, expandable, inputs, joinedBy });
|
|
2521
|
+
} else if (!last || segments.length > 0 || joinedBy !== void 0) {
|
|
2522
|
+
followable = false;
|
|
2523
|
+
bareSegment = true;
|
|
2524
|
+
}
|
|
2512
2525
|
tokens = [];
|
|
2513
2526
|
inputs = [];
|
|
2514
2527
|
expandable = false;
|
|
@@ -2663,8 +2676,8 @@ function lex(command, nested, dialect) {
|
|
|
2663
2676
|
i++;
|
|
2664
2677
|
}
|
|
2665
2678
|
if (quote !== "") unbalanced = true;
|
|
2666
|
-
endSegment(false);
|
|
2667
|
-
return { segments, unsafeRedirect, processSubstitution, unbalanced, grouped };
|
|
2679
|
+
endSegment(false, "other", true);
|
|
2680
|
+
return { segments, unsafeRedirect, processSubstitution, unbalanced, grouped, followable, bareSegment };
|
|
2668
2681
|
}
|
|
2669
2682
|
function binaryName(token, dialect) {
|
|
2670
2683
|
const base = token.split(/[/\\]/).pop() ?? "";
|
|
@@ -2808,7 +2821,8 @@ function lexAll(command, dialect, cdpathSet = false) {
|
|
|
2808
2821
|
const queue = [];
|
|
2809
2822
|
const top = lex(command, queue, dialect);
|
|
2810
2823
|
const all = { ...top, segments: top.segments.filter((s) => s.binary) };
|
|
2811
|
-
|
|
2824
|
+
markShellState(all.segments, top.bareSegment);
|
|
2825
|
+
if (top.followable && !top.grouped && queue.length === 0 && !cdpathSet) followCd(all.segments);
|
|
2812
2826
|
for (let depth = 0; depth < 32 && queue.length > 0; depth++) {
|
|
2813
2827
|
const inner = lex(queue.shift(), queue, dialect);
|
|
2814
2828
|
all.segments.push(...inner.segments.filter((s) => s.binary));
|
|
@@ -2825,22 +2839,51 @@ function literalCdTarget(seg) {
|
|
|
2825
2839
|
return target === "" || target.startsWith("-") || target.startsWith("~") || /[*?[]/.test(target) ? void 0 : target;
|
|
2826
2840
|
}
|
|
2827
2841
|
function followCd(segments) {
|
|
2828
|
-
if (segments.some((s) => s.joinedBy === "other")) return;
|
|
2842
|
+
if (segments.some((s) => s.joinedBy === "other" || SHELL_STATE_COMMANDS.has(s.binary))) return;
|
|
2843
|
+
const inPipeline = (i) => segments[i].joinedBy === "pipe" || segments[i + 1]?.joinedBy === "pipe";
|
|
2844
|
+
if (segments.some((s, i) => s.binary === "cd" && !inPipeline(i) && literalCdTarget(s) === void 0)) return;
|
|
2829
2845
|
let chain = [];
|
|
2830
2846
|
segments.forEach((seg, i) => {
|
|
2831
2847
|
seg.cwd = chain;
|
|
2832
|
-
const
|
|
2833
|
-
const target = inPipeline ? void 0 : literalCdTarget(seg);
|
|
2848
|
+
const target = inPipeline(i) ? void 0 : literalCdTarget(seg);
|
|
2834
2849
|
if (target !== void 0) chain = [...chain, target];
|
|
2835
2850
|
});
|
|
2836
2851
|
}
|
|
2852
|
+
function markShellState(segments, bareSegment) {
|
|
2853
|
+
if (!bareSegment && !segments.some((s) => SHELL_STATE_COMMANDS.has(s.binary))) return;
|
|
2854
|
+
for (const seg of segments) if (seg.binary === "cd") seg.cdSteered = true;
|
|
2855
|
+
}
|
|
2856
|
+
var SHELL_STATE_COMMANDS = /* @__PURE__ */ new Set([
|
|
2857
|
+
"export",
|
|
2858
|
+
"set",
|
|
2859
|
+
"shopt",
|
|
2860
|
+
"declare",
|
|
2861
|
+
"typeset",
|
|
2862
|
+
"readonly",
|
|
2863
|
+
"local",
|
|
2864
|
+
"unset",
|
|
2865
|
+
"let",
|
|
2866
|
+
"source",
|
|
2867
|
+
".",
|
|
2868
|
+
"eval",
|
|
2869
|
+
"exec",
|
|
2870
|
+
"pushd",
|
|
2871
|
+
"popd",
|
|
2872
|
+
"alias",
|
|
2873
|
+
"unalias",
|
|
2874
|
+
"builtin",
|
|
2875
|
+
"command",
|
|
2876
|
+
"enable",
|
|
2877
|
+
"hash",
|
|
2878
|
+
"trap"
|
|
2879
|
+
]);
|
|
2837
2880
|
function looksLikePath(arg) {
|
|
2838
2881
|
if (arg.startsWith("--") && arg.includes("=")) return looksLikePath(arg.slice(arg.indexOf("=") + 1));
|
|
2839
2882
|
return arg.startsWith("/") || arg.startsWith("~") || arg.startsWith("../") || arg === ".." || arg.startsWith("./") || /^[A-Za-z]:/.test(arg) || arg.startsWith("\\") || arg.startsWith("..\\") || arg.startsWith(".\\") || /(^|[\\/])\.\.([\\/]|$)/.test(arg);
|
|
2840
2883
|
}
|
|
2841
2884
|
function pathRefs(command, opts = {}) {
|
|
2842
2885
|
const dialect = dialectFor(opts.dialect);
|
|
2843
|
-
return lexAll(command, dialect, opts.cdpathSet).segments.flatMap((seg) => {
|
|
2886
|
+
return lexAll(command.trim(), dialect, opts.cdpathSet).segments.flatMap((seg) => {
|
|
2844
2887
|
const patterns = patternArgs(seg);
|
|
2845
2888
|
const cwd = seg.cwd ?? [];
|
|
2846
2889
|
return [...seg.args.filter((_, i) => !patterns.has(i)), ...seg.inputs].flatMap(pathIn).map((path26) => ({ path: path26, cwd }));
|
|
@@ -3008,7 +3051,7 @@ function scopeFor(seg) {
|
|
|
3008
3051
|
return [seg.binary, ...lead].join(" ");
|
|
3009
3052
|
}
|
|
3010
3053
|
function isAuto(seg, opts) {
|
|
3011
|
-
if (seg.binary === "cd") return !opts.cdpathSet && literalCdTarget(seg) !== void 0;
|
|
3054
|
+
if (seg.binary === "cd") return !opts.cdpathSet && !seg.cdSteered && literalCdTarget(seg) !== void 0;
|
|
3012
3055
|
if (!AUTO_COMMANDS.includes(seg.binary)) return false;
|
|
3013
3056
|
if (seg.expandable) return false;
|
|
3014
3057
|
if (seg.binary !== "git") return true;
|
|
@@ -3210,6 +3253,9 @@ function researchShellWarning(shell) {
|
|
|
3210
3253
|
if (shell.dialect !== "cmd") return null;
|
|
3211
3254
|
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.";
|
|
3212
3255
|
}
|
|
3256
|
+
function researchPolicyOptions(shell) {
|
|
3257
|
+
return { dialect: shell.dialect, cdpathSet: !!process.env.CDPATH };
|
|
3258
|
+
}
|
|
3213
3259
|
|
|
3214
3260
|
// src/services/pathScope.ts
|
|
3215
3261
|
import * as os from "os";
|
|
@@ -3443,7 +3489,7 @@ ${refs.output.trim()}`);
|
|
|
3443
3489
|
return [...found].map(([scope, abs]) => ({ abs, scope }));
|
|
3444
3490
|
}
|
|
3445
3491
|
policyOptions() {
|
|
3446
|
-
return
|
|
3492
|
+
return researchPolicyOptions(this.researchShell);
|
|
3447
3493
|
}
|
|
3448
3494
|
/**
|
|
3449
3495
|
* Three tiers (see `commandPolicy.ts`): read-only inspection runs silently,
|
|
@@ -7726,16 +7772,15 @@ function nonPromptingFs(fs16) {
|
|
|
7726
7772
|
findSymbol: (symbol, opts) => withinWorkspace(opts?.path) ? fs16.findSymbol(symbol, opts) : Promise.resolve(outsideRefusal(opts.path)),
|
|
7727
7773
|
listDir: (p, depth) => withinWorkspace(p) ? fs16.listDir(p, depth) : Promise.resolve(outsideRefusal(p)),
|
|
7728
7774
|
bash: async (command, signal) => {
|
|
7729
|
-
const
|
|
7730
|
-
const policy = { dialect, cdpathSet: !!process.env.CDPATH };
|
|
7775
|
+
const policy = researchPolicyOptions(resolveResearchShell());
|
|
7731
7776
|
const { tier, reason } = classifyCommand(command, policy);
|
|
7732
7777
|
if (tier !== "auto") {
|
|
7733
7778
|
return refused2(
|
|
7734
7779
|
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.`
|
|
7735
7780
|
);
|
|
7736
7781
|
}
|
|
7737
|
-
const escaping = pathRefs(command, policy).
|
|
7738
|
-
if (escaping) return outsideRefusal(escaping.
|
|
7782
|
+
const escaping = pathRefs(command, policy).map((ref) => resolveRef(fs16.getWorkspaceRoot(), ref)).find((r) => !r.inside);
|
|
7783
|
+
if (escaping) return outsideRefusal(escaping.abs);
|
|
7739
7784
|
return fs16.bash(command, signal);
|
|
7740
7785
|
}
|
|
7741
7786
|
};
|
|
@@ -10774,11 +10819,17 @@ function buildShellInvocation(command, args) {
|
|
|
10774
10819
|
const inner = [command, ...args].map(posixShellQuote).join(" ");
|
|
10775
10820
|
return { shellPath: "/bin/bash", shellArgs: ["-lc", inner] };
|
|
10776
10821
|
}
|
|
10822
|
+
var BSD_SCRIPT_PLATFORMS = /* @__PURE__ */ new Set(["darwin", "freebsd", "openbsd", "netbsd"]);
|
|
10823
|
+
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; }';
|
|
10777
10824
|
function wrapWithPty(command, args, opts = {}) {
|
|
10778
10825
|
const inner = [command, ...args].map(posixShellQuote).join(" ");
|
|
10779
10826
|
const watcher = opts.controlChannel ? 'exec 4<&0; ( while read -r C R <&3; do stty cols "$C" rows "$R" <&4; done ) & ' : "";
|
|
10780
10827
|
const setup = opts.size ? `stty cols ${opts.size.cols} rows ${opts.size.rows}; ` : "";
|
|
10781
|
-
|
|
10828
|
+
const line = `${watcher}${setup}${inner}`;
|
|
10829
|
+
if (BSD_SCRIPT_PLATFORMS.has(opts.platform ?? process.platform)) {
|
|
10830
|
+
return { command: "/bin/sh", args: ["-c", BSD_SCRIPT_FEED, line] };
|
|
10831
|
+
}
|
|
10832
|
+
return { command: "script", args: ["-q", "-e", "-f", "-c", line, "/dev/null"] };
|
|
10782
10833
|
}
|
|
10783
10834
|
|
|
10784
10835
|
// src/services/blockingPrompts.ts
|
|
@@ -10973,24 +11024,37 @@ var Block = /* @__PURE__ */ ((Block2) => {
|
|
|
10973
11024
|
Block2[Block2["IfHeadlessSession"] = 8] = "IfHeadlessSession";
|
|
10974
11025
|
Block2[Block2["IfInteractiveVariant"] = 9] = "IfInteractiveVariant";
|
|
10975
11026
|
Block2[Block2["IfProjectTrust"] = 10] = "IfProjectTrust";
|
|
11027
|
+
Block2[Block2["IfHeadlessModel"] = 11] = "IfHeadlessModel";
|
|
10976
11028
|
return Block2;
|
|
10977
11029
|
})(Block || {});
|
|
10978
11030
|
function isInteractive(ctx) {
|
|
10979
11031
|
return ctx.interactive ?? !ctx.headless;
|
|
10980
11032
|
}
|
|
11033
|
+
function majorVersion(version) {
|
|
11034
|
+
const match = version?.match(/(\d+)\.\d+/);
|
|
11035
|
+
return match ? Number(match[1]) : void 0;
|
|
11036
|
+
}
|
|
11037
|
+
function versionedInvocation(manifest, ctx) {
|
|
11038
|
+
const major = majorVersion(ctx.runnerVersion);
|
|
11039
|
+
if (major === void 0) return void 0;
|
|
11040
|
+
return (manifest.runner.versioned ?? []).filter((v) => major >= v.minMajor).sort((a, b) => b.minMajor - a.minMajor)[0];
|
|
11041
|
+
}
|
|
10981
11042
|
function resolveArgs(manifest, ctx) {
|
|
10982
11043
|
const args = [];
|
|
10983
11044
|
const env = {};
|
|
10984
|
-
|
|
10985
|
-
|
|
11045
|
+
const versioned = versionedInvocation(manifest, ctx);
|
|
11046
|
+
const argsTemplate = versioned?.argsTemplate ?? manifest.runner.argsTemplate;
|
|
11047
|
+
const envTemplate = versioned ? versioned.env : manifest.runner.env;
|
|
11048
|
+
if (envTemplate) {
|
|
11049
|
+
for (const [key, value] of Object.entries(envTemplate)) {
|
|
10986
11050
|
const resolved = substituteSimple(value, ctx);
|
|
10987
11051
|
if (resolved.length > 0) env[key] = resolved;
|
|
10988
11052
|
}
|
|
10989
11053
|
}
|
|
10990
11054
|
let block = 0 /* None */;
|
|
10991
11055
|
let include = true;
|
|
10992
|
-
for (let i = 0; i <
|
|
10993
|
-
const raw =
|
|
11056
|
+
for (let i = 0; i < argsTemplate.length; i++) {
|
|
11057
|
+
const raw = argsTemplate[i];
|
|
10994
11058
|
const blockOpen = parseBlockOpen(raw);
|
|
10995
11059
|
if (blockOpen !== null) {
|
|
10996
11060
|
if (block !== 0 /* None */) throw new ResolveError(`Nested conditional block at position ${i}: "${raw}"`);
|
|
@@ -11049,6 +11113,8 @@ function parseBlockOpen(raw) {
|
|
|
11049
11113
|
return 9 /* IfInteractiveVariant */;
|
|
11050
11114
|
case "{{if projectTrust}}":
|
|
11051
11115
|
return 10 /* IfProjectTrust */;
|
|
11116
|
+
case "{{if headlessModel}}":
|
|
11117
|
+
return 11 /* IfHeadlessModel */;
|
|
11052
11118
|
default:
|
|
11053
11119
|
return null;
|
|
11054
11120
|
}
|
|
@@ -11088,6 +11154,9 @@ function shouldIncludeBlock(block, ctx) {
|
|
|
11088
11154
|
// there is no cwd to name.
|
|
11089
11155
|
case 10 /* IfProjectTrust */:
|
|
11090
11156
|
return isInteractive(ctx) && !!ctx.cwd;
|
|
11157
|
+
// A model flag that only the non-interactive subcommand takes.
|
|
11158
|
+
case 11 /* IfHeadlessModel */:
|
|
11159
|
+
return !!ctx.model && !isInteractive(ctx);
|
|
11091
11160
|
default:
|
|
11092
11161
|
return true;
|
|
11093
11162
|
}
|
|
@@ -11133,6 +11202,10 @@ function resolveToken(token, manifest, ctx) {
|
|
|
11133
11202
|
if (token === "{{model}}") return ctx.model || "";
|
|
11134
11203
|
if (token === "{{thinkingEffort}}") return ctx.thinkingEffort || "";
|
|
11135
11204
|
if (token === "{{mode}}") return ctx.mode || "build";
|
|
11205
|
+
if (token === "{{modelWithVariant}}") {
|
|
11206
|
+
if (!ctx.model) return "";
|
|
11207
|
+
return ctx.thinkingEffort ? `${ctx.model}#${ctx.thinkingEffort}` : ctx.model;
|
|
11208
|
+
}
|
|
11136
11209
|
if (token === "{{projectTrust}}") {
|
|
11137
11210
|
if (!ctx.cwd) return "";
|
|
11138
11211
|
return `projects.${JSON.stringify(ctx.cwd)}.trust_level="trusted"`;
|
|
@@ -11207,13 +11280,21 @@ function resolveOpencodeVariantConfig(ctx) {
|
|
|
11207
11280
|
}
|
|
11208
11281
|
return JSON.stringify(config);
|
|
11209
11282
|
}
|
|
11283
|
+
function resolveOpencodeSessionConfig(ctx) {
|
|
11284
|
+
const mode = ctx.mode || "build";
|
|
11285
|
+
const pinned = resolveOpencodeVariantConfig(ctx);
|
|
11286
|
+
const config = pinned ? JSON.parse(pinned) : {};
|
|
11287
|
+
config.default_agent = mode;
|
|
11288
|
+
if (ctx.model && !pinned) config.agent = { [mode]: { model: ctx.model } };
|
|
11289
|
+
return JSON.stringify(config);
|
|
11290
|
+
}
|
|
11210
11291
|
function substituteSimple(value, ctx) {
|
|
11211
11292
|
const conditioned = value.replace(/\{\{if (\w+)\}\}([\s\S]*?)\{\{\/if\}\}/g, (match, name, body) => {
|
|
11212
11293
|
const block = parseBlockOpen(`{{if ${name}}}`);
|
|
11213
11294
|
if (block === null) return match;
|
|
11214
11295
|
return shouldIncludeBlock(block, ctx) ? body : "";
|
|
11215
11296
|
});
|
|
11216
|
-
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");
|
|
11297
|
+
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");
|
|
11217
11298
|
}
|
|
11218
11299
|
|
|
11219
11300
|
// src/utils/launch.ts
|
|
@@ -20138,13 +20219,33 @@ function buildRunnerInvocation(opts) {
|
|
|
20138
20219
|
mode: opts.mode ?? "build",
|
|
20139
20220
|
headless: opts.headless,
|
|
20140
20221
|
interactive: opts.interactive,
|
|
20141
|
-
cwd: opts.cwd
|
|
20222
|
+
cwd: opts.cwd,
|
|
20223
|
+
runnerVersion: opts.runnerVersion
|
|
20142
20224
|
});
|
|
20143
20225
|
}
|
|
20144
20226
|
|
|
20145
20227
|
// src/services/HeadlessRunner.ts
|
|
20146
20228
|
import { spawn as nodeSpawn2, execSync } from "child_process";
|
|
20147
20229
|
|
|
20230
|
+
// src/services/runnerVersion.ts
|
|
20231
|
+
import { execFile as execFile4 } from "child_process";
|
|
20232
|
+
var VERSION_TIMEOUT_MS = 5e3;
|
|
20233
|
+
var cache = /* @__PURE__ */ new Map();
|
|
20234
|
+
var probeRunnerVersion = (command, PATH) => {
|
|
20235
|
+
const key = `${command}\0${PATH}`;
|
|
20236
|
+
let pending = cache.get(key);
|
|
20237
|
+
if (!pending) {
|
|
20238
|
+
pending = new Promise((resolve7) => {
|
|
20239
|
+
execFile4(command, ["--version"], { env: { ...process.env, PATH }, timeout: VERSION_TIMEOUT_MS }, (err, stdout) => {
|
|
20240
|
+
const line = String(stdout ?? "").trim();
|
|
20241
|
+
resolve7(err || !line ? void 0 : line);
|
|
20242
|
+
});
|
|
20243
|
+
});
|
|
20244
|
+
cache.set(key, pending);
|
|
20245
|
+
}
|
|
20246
|
+
return pending;
|
|
20247
|
+
};
|
|
20248
|
+
|
|
20148
20249
|
// src/services/AbstractRunner.ts
|
|
20149
20250
|
import { EventEmitter } from "events";
|
|
20150
20251
|
var AbstractTerminalSession = class {
|
|
@@ -20265,6 +20366,10 @@ Process error: ${err.message}
|
|
|
20265
20366
|
this.process.stdin.write(text);
|
|
20266
20367
|
}
|
|
20267
20368
|
}
|
|
20369
|
+
/** Closes the runner's stdin: what it has read so far is all it will get. */
|
|
20370
|
+
endInput() {
|
|
20371
|
+
this.process?.stdin?.end();
|
|
20372
|
+
}
|
|
20268
20373
|
/** PTY resize requests from a surface that owns the terminal rendering the wrapper. */
|
|
20269
20374
|
writeControl(text) {
|
|
20270
20375
|
if (this.controlStream && !this.controlStream.destroyed) {
|
|
@@ -20277,6 +20382,7 @@ var HeadlessRunner = class extends AbstractRunner {
|
|
|
20277
20382
|
hasScriptCmd;
|
|
20278
20383
|
resolvePath;
|
|
20279
20384
|
launchDeps;
|
|
20385
|
+
runnerVersion;
|
|
20280
20386
|
spawnCount = 0;
|
|
20281
20387
|
/**
|
|
20282
20388
|
* Session shape: a piped subprocess is not a terminal, so runners get their
|
|
@@ -20292,6 +20398,7 @@ var HeadlessRunner = class extends AbstractRunner {
|
|
|
20292
20398
|
this.hasScriptCmd = deps.hasScriptCmd ?? defaultHasScriptCmd;
|
|
20293
20399
|
this.resolvePath = deps.resolvePath ?? augmentedPath;
|
|
20294
20400
|
this.launchDeps = { resolvePath: this.resolvePath, ...deps.launchDeps };
|
|
20401
|
+
this.runnerVersion = deps.runnerVersion ?? probeRunnerVersion;
|
|
20295
20402
|
}
|
|
20296
20403
|
/**
|
|
20297
20404
|
* Unique per spawn, not per task: a retry reuses its task id and ids often
|
|
@@ -20309,6 +20416,9 @@ var HeadlessRunner = class extends AbstractRunner {
|
|
|
20309
20416
|
/** Everything up to, but not including, spawning — so a surface that owns its own child reaches the same decisions. */
|
|
20310
20417
|
async prepareLaunch(opts, ptyOptions) {
|
|
20311
20418
|
const interactive = this.defaultInteractive;
|
|
20419
|
+
const resolvedPath = await this.resolvePath();
|
|
20420
|
+
const manifest = opts.registry?.get(opts.runner)?.manifest;
|
|
20421
|
+
const runnerVersion = manifest?.runner.versioned?.length ? await this.runnerVersion(manifest.runner.command, resolvedPath) : void 0;
|
|
20312
20422
|
const invocation = buildRunnerInvocation({
|
|
20313
20423
|
runner: opts.runner,
|
|
20314
20424
|
prompt: opts.prompt,
|
|
@@ -20319,18 +20429,18 @@ var HeadlessRunner = class extends AbstractRunner {
|
|
|
20319
20429
|
headless: opts.headless ?? true,
|
|
20320
20430
|
interactive,
|
|
20321
20431
|
cwd: opts.cwd,
|
|
20432
|
+
runnerVersion,
|
|
20322
20433
|
registry: opts.registry
|
|
20323
20434
|
});
|
|
20324
|
-
const manifest = opts.registry?.get(opts.runner)?.manifest;
|
|
20325
20435
|
const pty = (manifest?.runner.requiresTty === true || interactive) && this.hasScriptCmd();
|
|
20326
|
-
const
|
|
20327
|
-
const { command, args } = pty ? wrapWithPty(invocation.command, invocation.args, ptyOptions) : { command: invocation.command, args: invocation.args };
|
|
20436
|
+
const { command, args } = pty ? wrapWithPty(invocation.command, invocation.args, { platform: this.launchDeps.platform, ...ptyOptions }) : { command: invocation.command, args: invocation.args };
|
|
20328
20437
|
return {
|
|
20329
20438
|
launch: await planDirectLaunch(command, args, this.launchDeps),
|
|
20330
20439
|
resolvedPath,
|
|
20331
20440
|
env: { ...opts.env, ...invocation.env },
|
|
20332
20441
|
pty,
|
|
20333
|
-
submitPromptKey: invocation.submitPromptKey
|
|
20442
|
+
submitPromptKey: invocation.submitPromptKey,
|
|
20443
|
+
closeStdin: !pty && !interactive && invocation.promptInArgs
|
|
20334
20444
|
};
|
|
20335
20445
|
}
|
|
20336
20446
|
async spawn(opts) {
|
|
@@ -20341,13 +20451,14 @@ var HeadlessRunner = class extends AbstractRunner {
|
|
|
20341
20451
|
console.error(`[headless] Starting ${opts.runner} [${modeStr}] (${opts.modelId || "default"}) for task ${opts.taskId.slice(0, 8)}${prepared.pty ? " (PTY)" : ""}`);
|
|
20342
20452
|
session.start(prepared.launch, opts.cwd, prepared.resolvedPath, prepared.env);
|
|
20343
20453
|
if (prepared.submitPromptKey) session.write("\r");
|
|
20454
|
+
if (prepared.closeStdin) session.endInput();
|
|
20344
20455
|
this.registerSession(id, session);
|
|
20345
20456
|
return session;
|
|
20346
20457
|
}
|
|
20347
20458
|
};
|
|
20348
20459
|
|
|
20349
20460
|
// src/services/TmuxRunner.ts
|
|
20350
|
-
import { execFile as
|
|
20461
|
+
import { execFile as execFile5 } from "child_process";
|
|
20351
20462
|
import { promisify as promisify5 } from "util";
|
|
20352
20463
|
import { tmpdir } from "os";
|
|
20353
20464
|
import { join as join19 } from "path";
|
|
@@ -20404,7 +20515,7 @@ function clipboardCopyCommand(hasBin = defaultHasBin, platform = process.platfor
|
|
|
20404
20515
|
var EXIT_RE = /<<<ORDEWELL_TMUX_EXIT:(\d+)>>>/;
|
|
20405
20516
|
var LIVENESS_INTERVAL_MS = 5e3;
|
|
20406
20517
|
var LIVENESS_MISSES = 2;
|
|
20407
|
-
var execFileAsync2 = promisify5(
|
|
20518
|
+
var execFileAsync2 = promisify5(execFile5);
|
|
20408
20519
|
var defaultExecFile3 = async (command, args) => {
|
|
20409
20520
|
const { stdout, stderr } = await execFileAsync2(command, args);
|
|
20410
20521
|
return { stdout: String(stdout), stderr: String(stderr) };
|
|
@@ -20554,6 +20665,7 @@ var TmuxRunner = class extends AbstractRunner {
|
|
|
20554
20665
|
pollIntervalMs;
|
|
20555
20666
|
logDir;
|
|
20556
20667
|
clipboardCommand;
|
|
20668
|
+
runnerVersion;
|
|
20557
20669
|
/** Retries get a freshly named window so a failed attempt's output stays inspectable. */
|
|
20558
20670
|
attempts = /* @__PURE__ */ new Map();
|
|
20559
20671
|
ready = null;
|
|
@@ -20566,6 +20678,7 @@ var TmuxRunner = class extends AbstractRunner {
|
|
|
20566
20678
|
this.pollIntervalMs = deps.pollIntervalMs ?? 500;
|
|
20567
20679
|
this.logDir = deps.logDir ?? tmpdir();
|
|
20568
20680
|
this.clipboardCommand = deps.clipboardCommand ?? (() => clipboardCopyCommand());
|
|
20681
|
+
this.runnerVersion = deps.runnerVersion ?? probeRunnerVersion;
|
|
20569
20682
|
}
|
|
20570
20683
|
/**
|
|
20571
20684
|
* Reaps a session orphaned by a crashed prior daemon on the same port, then
|
|
@@ -20649,6 +20762,8 @@ var TmuxRunner = class extends AbstractRunner {
|
|
|
20649
20762
|
}
|
|
20650
20763
|
}
|
|
20651
20764
|
async spawn(opts) {
|
|
20765
|
+
const manifest = opts.registry?.get(opts.runner)?.manifest;
|
|
20766
|
+
const runnerVersion = manifest?.runner.versioned?.length ? await this.runnerVersion(manifest.runner.command, await this.resolvePath()) : void 0;
|
|
20652
20767
|
const invocation = buildRunnerInvocation({
|
|
20653
20768
|
runner: opts.runner,
|
|
20654
20769
|
prompt: opts.prompt,
|
|
@@ -20664,6 +20779,7 @@ var TmuxRunner = class extends AbstractRunner {
|
|
|
20664
20779
|
headless: opts.headless ?? true,
|
|
20665
20780
|
interactive: true,
|
|
20666
20781
|
cwd: opts.cwd,
|
|
20782
|
+
runnerVersion,
|
|
20667
20783
|
registry: opts.registry
|
|
20668
20784
|
});
|
|
20669
20785
|
await this.ensureSession();
|
|
@@ -21580,6 +21696,37 @@ var OPENCODE_MANIFEST = {
|
|
|
21580
21696
|
env: {
|
|
21581
21697
|
OPENCODE_CONFIG_CONTENT: "{{if interactiveVariant}}{{opencodeVariantConfig}}{{/if}}"
|
|
21582
21698
|
},
|
|
21699
|
+
// OpenCode 2.x changed both shapes. Its TUI takes no `--model`/`--agent`,
|
|
21700
|
+
// so they ride on OPENCODE_CONFIG_CONTENT instead, and its `run` has no
|
|
21701
|
+
// `--variant` — the variant is a `#suffix` on the model id. Both run
|
|
21702
|
+
// `--standalone`: without it the TUI and `run` attach to the user's
|
|
21703
|
+
// background service, which neither reads this process's config nor
|
|
21704
|
+
// starts in the task's directory and environment.
|
|
21705
|
+
versioned: [{
|
|
21706
|
+
minMajor: 2,
|
|
21707
|
+
argsTemplate: [
|
|
21708
|
+
"{{if headlessSession}}",
|
|
21709
|
+
"run",
|
|
21710
|
+
"--agent",
|
|
21711
|
+
"{{mode}}",
|
|
21712
|
+
"{{/if}}",
|
|
21713
|
+
"{{if headlessModel}}",
|
|
21714
|
+
"--model",
|
|
21715
|
+
"{{modelWithVariant}}",
|
|
21716
|
+
"{{/if}}",
|
|
21717
|
+
"--standalone",
|
|
21718
|
+
"{{if buildMode}}",
|
|
21719
|
+
"--auto",
|
|
21720
|
+
"{{/if}}",
|
|
21721
|
+
"{{if interactive}}",
|
|
21722
|
+
"--prompt",
|
|
21723
|
+
"{{/if}}",
|
|
21724
|
+
"{{prompt}}"
|
|
21725
|
+
],
|
|
21726
|
+
env: {
|
|
21727
|
+
OPENCODE_CONFIG_CONTENT: "{{if interactive}}{{opencodeSessionConfig}}{{/if}}"
|
|
21728
|
+
}
|
|
21729
|
+
}],
|
|
21583
21730
|
promptInArgs: true,
|
|
21584
21731
|
requiresTty: true,
|
|
21585
21732
|
// `--prompt` only pre-fills the TUI's composer; it does not submit it.
|