@ordewell/core 0.11.1 → 0.11.2
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/AUTHORS.md +4 -0
- package/dist/{IRunner-B5l9PCTv.d.ts → IRunner-DOMtDTVt.d.ts} +50 -14
- package/dist/{IRunner-DBgDx_Mr.d.mts → IRunner-ihalYfTo.d.mts} +50 -14
- package/dist/{chunk-4QXOWEGC.mjs → chunk-7WNLN63J.mjs} +11 -4
- package/dist/chunk-7WNLN63J.mjs.map +1 -0
- package/dist/index.d.mts +44 -12
- package/dist/index.d.ts +44 -12
- package/dist/index.js +901 -497
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +878 -480
- package/dist/index.mjs.map +1 -1
- package/dist/plan-utils.d.mts +2 -2
- package/dist/plan-utils.d.ts +2 -2
- package/dist/plan-utils.js +7 -0
- package/dist/plan-utils.js.map +1 -1
- package/dist/plan-utils.mjs +3 -1
- package/dist/{runSettingsText-CAXiTpsj.d.ts → runSettingsText-CYMhTWfw.d.ts} +110 -67
- package/dist/{runSettingsText-BKwWs052.d.mts → runSettingsText-tnmvdSfS.d.mts} +110 -67
- package/dist/testing.d.mts +1 -1
- package/dist/testing.d.ts +1 -1
- package/package.json +1 -1
- package/dist/chunk-4QXOWEGC.mjs.map +0 -1
package/dist/index.mjs
CHANGED
|
@@ -47,6 +47,7 @@ import {
|
|
|
47
47
|
assignmentConsequence,
|
|
48
48
|
assignmentIssue,
|
|
49
49
|
assignmentIssueText,
|
|
50
|
+
attachingLabel,
|
|
50
51
|
automergeSummary,
|
|
51
52
|
autorunSummary,
|
|
52
53
|
awaitingLabel,
|
|
@@ -231,7 +232,7 @@ import {
|
|
|
231
232
|
withPatch,
|
|
232
233
|
withoutSealedMark,
|
|
233
234
|
worstSeverity
|
|
234
|
-
} from "./chunk-
|
|
235
|
+
} from "./chunk-7WNLN63J.mjs";
|
|
235
236
|
import {
|
|
236
237
|
DEFAULT_LOOP_ROUNDS,
|
|
237
238
|
DEFAULT_SCRIPT_TIMEOUT_MS,
|
|
@@ -918,9 +919,9 @@ function literalCdTarget(seg) {
|
|
|
918
919
|
return target === "" || target.startsWith("-") || target.startsWith("~") || /[*?[]/.test(target) ? void 0 : target;
|
|
919
920
|
}
|
|
920
921
|
function followCd(segments) {
|
|
921
|
-
if (segments.some((s) => s.joinedBy === "other" ||
|
|
922
|
+
if (segments.some((s) => s.joinedBy === "other" || movesShell(s))) return;
|
|
922
923
|
const inPipeline = (i) => segments[i].joinedBy === "pipe" || segments[i + 1]?.joinedBy === "pipe";
|
|
923
|
-
if (segments.some((s, i) => s.binary === "cd" && !inPipeline(i) && literalCdTarget(s) === void 0)) return;
|
|
924
|
+
if (segments.some((s, i) => s.binary.toLowerCase() === "cd" && !inPipeline(i) && literalCdTarget(s) === void 0)) return;
|
|
924
925
|
let chain = [];
|
|
925
926
|
segments.forEach((seg, i) => {
|
|
926
927
|
seg.cwd = chain;
|
|
@@ -929,7 +930,7 @@ function followCd(segments) {
|
|
|
929
930
|
});
|
|
930
931
|
}
|
|
931
932
|
function markShellState(segments, bareSegment) {
|
|
932
|
-
if (!bareSegment && !segments.some(
|
|
933
|
+
if (!bareSegment && !segments.some(movesShell)) return;
|
|
933
934
|
for (const seg of segments) if (seg.binary === "cd") seg.cdSteered = true;
|
|
934
935
|
}
|
|
935
936
|
var SHELL_STATE_COMMANDS = /* @__PURE__ */ new Set([
|
|
@@ -948,6 +949,7 @@ var SHELL_STATE_COMMANDS = /* @__PURE__ */ new Set([
|
|
|
948
949
|
"exec",
|
|
949
950
|
"pushd",
|
|
950
951
|
"popd",
|
|
952
|
+
"chdir",
|
|
951
953
|
"alias",
|
|
952
954
|
"unalias",
|
|
953
955
|
"builtin",
|
|
@@ -956,6 +958,9 @@ var SHELL_STATE_COMMANDS = /* @__PURE__ */ new Set([
|
|
|
956
958
|
"hash",
|
|
957
959
|
"trap"
|
|
958
960
|
]);
|
|
961
|
+
function movesShell(seg) {
|
|
962
|
+
return SHELL_STATE_COMMANDS.has(seg.binary.toLowerCase());
|
|
963
|
+
}
|
|
959
964
|
|
|
960
965
|
// src/services/commandRunners.ts
|
|
961
966
|
var HELP_FLAGS = ["--help", "--version"];
|
|
@@ -1811,8 +1816,9 @@ var FLAG_POLICY = {
|
|
|
1811
1816
|
sort: {
|
|
1812
1817
|
cluster: true,
|
|
1813
1818
|
// Absent on purpose: `-o`/`--output` writes a file with no redirect for the
|
|
1814
|
-
// redirect check to see,
|
|
1815
|
-
//
|
|
1819
|
+
// redirect check to see, `-T`/`--temporary-directory` names where sort writes
|
|
1820
|
+
// its run files, and `--compress-program` names a program that sort runs
|
|
1821
|
+
// over its own temporary files — a permitted binary as an interpreter.
|
|
1816
1822
|
booleans: [
|
|
1817
1823
|
"-b",
|
|
1818
1824
|
"-c",
|
|
@@ -1851,11 +1857,9 @@ var FLAG_POLICY = {
|
|
|
1851
1857
|
"-k",
|
|
1852
1858
|
"-t",
|
|
1853
1859
|
"-S",
|
|
1854
|
-
"-T",
|
|
1855
1860
|
"--key",
|
|
1856
1861
|
"--field-separator",
|
|
1857
1862
|
"--buffer-size",
|
|
1858
|
-
"--temporary-directory",
|
|
1859
1863
|
"--random-source",
|
|
1860
1864
|
"--batch-size",
|
|
1861
1865
|
"--parallel",
|
|
@@ -2482,23 +2486,28 @@ function scanFlags(seg) {
|
|
|
2482
2486
|
const booleans = /* @__PURE__ */ new Set([...spec.booleans ?? [], ...UNIVERSAL_FLAGS]);
|
|
2483
2487
|
const values = new Set(spec.values ?? []);
|
|
2484
2488
|
const operands = [];
|
|
2489
|
+
const operandAt = [];
|
|
2490
|
+
const operand = (at) => {
|
|
2491
|
+
operands.push(seg.args[at]);
|
|
2492
|
+
operandAt.push(at);
|
|
2493
|
+
};
|
|
2485
2494
|
let i = 0;
|
|
2486
2495
|
while (i < seg.args.length) {
|
|
2487
2496
|
const token = seg.args[i];
|
|
2488
2497
|
if (token === "--" && !spec.expressionArgs) {
|
|
2489
|
-
|
|
2498
|
+
for (let j = i + 1; j < seg.args.length; j++) operand(j);
|
|
2490
2499
|
break;
|
|
2491
2500
|
}
|
|
2492
2501
|
if (!/^-./.test(token)) {
|
|
2493
|
-
|
|
2502
|
+
operand(i);
|
|
2494
2503
|
i++;
|
|
2495
2504
|
continue;
|
|
2496
2505
|
}
|
|
2497
2506
|
const shape = matchFlag(spec, booleans, values, token);
|
|
2498
|
-
if (shape === void 0) return { unknown: flagLabel(token), operands };
|
|
2507
|
+
if (shape === void 0) return { unknown: flagLabel(token), operands, operandAt };
|
|
2499
2508
|
i += shape === "value" && takesNextToken(seg.args[i + 1]) ? 2 : 1;
|
|
2500
2509
|
}
|
|
2501
|
-
return { operands };
|
|
2510
|
+
return { operands, operandAt };
|
|
2502
2511
|
}
|
|
2503
2512
|
function subcommandOf(seg) {
|
|
2504
2513
|
const scan = scanFlags(seg);
|
|
@@ -2651,6 +2660,8 @@ function sedRefusalFor(binary, scripts) {
|
|
|
2651
2660
|
return `The "${binary}" script runs a shell command (the "e" command or the "e" flag of "s"), which this classifier cannot inspect. ${INSPECT_OR_TASK}`;
|
|
2652
2661
|
case "write":
|
|
2653
2662
|
return `The "${binary}" script writes a file (the "w"/"W" command or the "w" flag of "s"). You are a read-only planner \u2014 print the output instead, or describe the write as a task.`;
|
|
2663
|
+
case "read":
|
|
2664
|
+
return `The "${binary}" script reads a file it names itself (the "r"/"R" command), which path confinement cannot see. Read that file directly instead.`;
|
|
2654
2665
|
default:
|
|
2655
2666
|
return `This classifier could not read the "${binary}" script well enough to rule out a shell command or a file write in it. Rewrite it with plain print, delete and substitute commands, or describe the work as a task.`;
|
|
2656
2667
|
}
|
|
@@ -2771,6 +2782,7 @@ function sedScriptHazard(script) {
|
|
|
2771
2782
|
if (cmd === void 0) return "unreadable";
|
|
2772
2783
|
if (cmd === "e") return "exec";
|
|
2773
2784
|
if (cmd === "w" || cmd === "W") return "write";
|
|
2785
|
+
if (cmd === "r" || cmd === "R") return "read";
|
|
2774
2786
|
if (cmd === "{" || cmd === "}") continue;
|
|
2775
2787
|
if (cmd === "#") {
|
|
2776
2788
|
while (i < n && script[i] !== "\n") i++;
|
|
@@ -2792,7 +2804,7 @@ function sedScriptHazard(script) {
|
|
|
2792
2804
|
if (!commandEnds()) return "unreadable";
|
|
2793
2805
|
continue;
|
|
2794
2806
|
}
|
|
2795
|
-
if ("
|
|
2807
|
+
if ("aic".includes(cmd)) {
|
|
2796
2808
|
while (i < n && script[i] !== "\n") i += script[i] === "\\" ? 2 : 1;
|
|
2797
2809
|
continue;
|
|
2798
2810
|
}
|
|
@@ -2892,6 +2904,12 @@ function awkRefusalFor(binary, programs) {
|
|
|
2892
2904
|
return `The "${binary}" program pipes to or from a command ("|"), which runs it through a shell this classifier cannot inspect. ${INSPECT_OR_TASK}`;
|
|
2893
2905
|
case "write":
|
|
2894
2906
|
return `The "${binary}" program redirects its output to a file (">"). You are a read-only planner \u2014 print to standard output instead, or describe the write as a task.`;
|
|
2907
|
+
case "read":
|
|
2908
|
+
return `The "${binary}" program reads a file it names itself ("getline <" or an "ARGV" entry), which path confinement cannot see. Name the file as an operand instead.`;
|
|
2909
|
+
case "environment":
|
|
2910
|
+
return `The "${binary}" program reads the environment ("ENVIRON"), which holds credentials. Run printenv for the one variable you need, which asks first.`;
|
|
2911
|
+
case "symbol-table":
|
|
2912
|
+
return `The "${binary}" program uses "SYMTAB", which gawk reads as every global by name, "ENVIRON" and "ARGV" included. Name the variable you need directly.`;
|
|
2895
2913
|
case "extension":
|
|
2896
2914
|
return `The "${binary}" program uses an "@" form, which loads an extension, includes a source file or calls a function by name \u2014 none of which this classifier can inspect. ${INSPECT_OR_TASK}`;
|
|
2897
2915
|
default:
|
|
@@ -2933,6 +2951,7 @@ function awkProgramHazard(program) {
|
|
|
2933
2951
|
let slash = "regex";
|
|
2934
2952
|
let depth = 0;
|
|
2935
2953
|
let printDepth;
|
|
2954
|
+
let getlineDepth;
|
|
2936
2955
|
let continues = false;
|
|
2937
2956
|
const readRegex = () => {
|
|
2938
2957
|
i++;
|
|
@@ -2999,7 +3018,10 @@ function awkProgramHazard(program) {
|
|
|
2999
3018
|
continue;
|
|
3000
3019
|
}
|
|
3001
3020
|
if (c === "\n" || c === ";") {
|
|
3002
|
-
if (c === ";" || !continues)
|
|
3021
|
+
if (c === ";" || !continues) {
|
|
3022
|
+
printDepth = void 0;
|
|
3023
|
+
getlineDepth = void 0;
|
|
3024
|
+
}
|
|
3003
3025
|
slash = "regex";
|
|
3004
3026
|
i++;
|
|
3005
3027
|
continue;
|
|
@@ -3050,6 +3072,12 @@ function awkProgramHazard(program) {
|
|
|
3050
3072
|
slash = "unclear";
|
|
3051
3073
|
continue;
|
|
3052
3074
|
}
|
|
3075
|
+
if (c === "<") {
|
|
3076
|
+
if (getlineDepth !== void 0 && depth >= getlineDepth) return "read";
|
|
3077
|
+
i++;
|
|
3078
|
+
slash = "unclear";
|
|
3079
|
+
continue;
|
|
3080
|
+
}
|
|
3053
3081
|
if (c === "(" || c === "[") {
|
|
3054
3082
|
depth++;
|
|
3055
3083
|
i++;
|
|
@@ -3058,12 +3086,16 @@ function awkProgramHazard(program) {
|
|
|
3058
3086
|
}
|
|
3059
3087
|
if (c === ")" || c === "]") {
|
|
3060
3088
|
depth--;
|
|
3089
|
+
if (getlineDepth !== void 0 && depth < getlineDepth) getlineDepth = void 0;
|
|
3061
3090
|
i++;
|
|
3062
3091
|
slash = "divide";
|
|
3063
3092
|
continue;
|
|
3064
3093
|
}
|
|
3065
3094
|
if (c === "{" || c === "}") {
|
|
3066
|
-
if (c === "}")
|
|
3095
|
+
if (c === "}") {
|
|
3096
|
+
printDepth = void 0;
|
|
3097
|
+
getlineDepth = void 0;
|
|
3098
|
+
}
|
|
3067
3099
|
i++;
|
|
3068
3100
|
slash = "regex";
|
|
3069
3101
|
continue;
|
|
@@ -3084,6 +3116,10 @@ function awkProgramHazard(program) {
|
|
|
3084
3116
|
while (i < n && /[A-Za-z0-9_.]/.test(program[i])) i++;
|
|
3085
3117
|
const word = program.slice(start, i);
|
|
3086
3118
|
if (word === "system") return "exec";
|
|
3119
|
+
if (word === "ENVIRON") return "environment";
|
|
3120
|
+
if (word === "ARGV") return "read";
|
|
3121
|
+
if (word === "SYMTAB") return "symbol-table";
|
|
3122
|
+
if (word === "getline") getlineDepth = depth;
|
|
3087
3123
|
if (word === "print" || word === "printf") printDepth = depth;
|
|
3088
3124
|
slash = AWK_KEYWORDS.includes(word) ? "unclear" : "divide";
|
|
3089
3125
|
continue;
|
|
@@ -3970,6 +4006,36 @@ var GIT_READONLY_SUBCOMMANDS = [
|
|
|
3970
4006
|
"whatchanged",
|
|
3971
4007
|
"cat-file"
|
|
3972
4008
|
];
|
|
4009
|
+
var onlyAction = (...reads) => (after) => after[0] === void 0 || reads.includes(after[0]);
|
|
4010
|
+
var GIT_PROMPTABLE = {
|
|
4011
|
+
"ls-remote": () => true,
|
|
4012
|
+
"for-each-ref": () => true,
|
|
4013
|
+
"show-ref": () => true,
|
|
4014
|
+
"name-rev": () => true,
|
|
4015
|
+
"merge-base": () => true,
|
|
4016
|
+
"count-objects": () => true,
|
|
4017
|
+
"cherry": () => true,
|
|
4018
|
+
"range-diff": () => true,
|
|
4019
|
+
"diff-tree": () => true,
|
|
4020
|
+
"diff-files": () => true,
|
|
4021
|
+
"diff-index": () => true,
|
|
4022
|
+
"show-branch": () => true,
|
|
4023
|
+
"annotate": () => true,
|
|
4024
|
+
"check-ignore": () => true,
|
|
4025
|
+
"check-attr": () => true,
|
|
4026
|
+
"check-ref-format": () => true,
|
|
4027
|
+
"check-mailmap": () => true,
|
|
4028
|
+
"var": () => true,
|
|
4029
|
+
"version": () => true,
|
|
4030
|
+
"worktree": onlyAction("list"),
|
|
4031
|
+
"notes": onlyAction("list", "show"),
|
|
4032
|
+
"bisect": onlyAction("log"),
|
|
4033
|
+
"sparse-checkout": onlyAction("list"),
|
|
4034
|
+
"reflog": onlyAction("show", "exists"),
|
|
4035
|
+
"symbolic-ref": (after) => after.filter((a) => !a.startsWith("-")).length <= 1,
|
|
4036
|
+
// `-w` writes the object; git's shared flag set keeps it for `git diff -w`.
|
|
4037
|
+
"hash-object": (after) => !after.some((a) => /^-[^-]*w/.test(a))
|
|
4038
|
+
};
|
|
3973
4039
|
var REFUSED_COMMANDS = [
|
|
3974
4040
|
"rm",
|
|
3975
4041
|
"rmdir",
|
|
@@ -4061,7 +4127,28 @@ var REFUSED_SUBCOMMANDS = {
|
|
|
4061
4127
|
"clone",
|
|
4062
4128
|
"fetch",
|
|
4063
4129
|
"pull",
|
|
4064
|
-
"submodule"
|
|
4130
|
+
"submodule",
|
|
4131
|
+
"add",
|
|
4132
|
+
"rm",
|
|
4133
|
+
"mv",
|
|
4134
|
+
"format-patch",
|
|
4135
|
+
"replace",
|
|
4136
|
+
"mergetool",
|
|
4137
|
+
"repack",
|
|
4138
|
+
"update-index",
|
|
4139
|
+
"read-tree",
|
|
4140
|
+
"checkout-index",
|
|
4141
|
+
"merge-file",
|
|
4142
|
+
"bundle",
|
|
4143
|
+
"commit-tree",
|
|
4144
|
+
"write-tree",
|
|
4145
|
+
"mktree",
|
|
4146
|
+
"mktag",
|
|
4147
|
+
"unpack-objects",
|
|
4148
|
+
"fast-import",
|
|
4149
|
+
"pack-refs",
|
|
4150
|
+
"prune-packed",
|
|
4151
|
+
"maintenance"
|
|
4065
4152
|
],
|
|
4066
4153
|
npm: ["publish", "unpublish", "deprecate", "owner", "access", "token", "login", "logout"],
|
|
4067
4154
|
yarn: ["publish", "npm"],
|
|
@@ -4271,7 +4358,7 @@ function pathRefs(command, opts = {}) {
|
|
|
4271
4358
|
return lexAll(command.trim(), dialect, opts.cdpathSet).segments.flatMap((seg) => {
|
|
4272
4359
|
const patterns = patternArgs(seg);
|
|
4273
4360
|
const cwd = seg.cwd ?? [];
|
|
4274
|
-
return [...seg.args.filter((_, i) => !patterns.has(i)), ...seg.inputs].flatMap(pathIn).map((
|
|
4361
|
+
return [...seg.args.filter((_, i) => !patterns.has(i)), ...seg.inputs].flatMap(pathIn).map((path47) => ({ path: path47, cwd }));
|
|
4275
4362
|
});
|
|
4276
4363
|
}
|
|
4277
4364
|
function excludedBy(command, allow, opts = {}) {
|
|
@@ -4378,7 +4465,9 @@ function patternArgs(seg) {
|
|
|
4378
4465
|
var SCOPE_LEAD_ARGS = 2;
|
|
4379
4466
|
function scopeFor(seg) {
|
|
4380
4467
|
const name = seg.binaryPath ?? seg.binary;
|
|
4381
|
-
|
|
4468
|
+
const program = programName(seg.binary);
|
|
4469
|
+
if (!MULTIPLEXERS.includes(program)) return name;
|
|
4470
|
+
if (program === "git") return gitScope(seg, name);
|
|
4382
4471
|
const lead = [];
|
|
4383
4472
|
for (const arg of seg.args) {
|
|
4384
4473
|
if (arg.startsWith("-")) break;
|
|
@@ -4387,6 +4476,14 @@ function scopeFor(seg) {
|
|
|
4387
4476
|
}
|
|
4388
4477
|
return [name, ...lead].join(" ");
|
|
4389
4478
|
}
|
|
4479
|
+
function gitScope(seg, name) {
|
|
4480
|
+
const scan = scanFlags({ ...seg, binary: "git" });
|
|
4481
|
+
if (!scan || scan.unknown || scan.operands[0] === "ls-remote") return [name, ...seg.args].map(scopeWord).join(" ");
|
|
4482
|
+
return [name, ...scan.operands.slice(0, SCOPE_LEAD_ARGS)].join(" ");
|
|
4483
|
+
}
|
|
4484
|
+
function scopeWord(word) {
|
|
4485
|
+
return word === "" || /[\s"'\\]/.test(word) ? JSON.stringify(word) : word;
|
|
4486
|
+
}
|
|
4390
4487
|
function climbsByGlob(seg) {
|
|
4391
4488
|
if (!seg.globbed) return false;
|
|
4392
4489
|
const patterns = patternArgs(seg);
|
|
@@ -4408,6 +4505,27 @@ function swallowsRefName(arg) {
|
|
|
4408
4505
|
const values = FLAG_POLICY.git.values ?? [];
|
|
4409
4506
|
return [...arg.slice(1)].some((c) => c !== "n" && values.includes(`-${c}`));
|
|
4410
4507
|
}
|
|
4508
|
+
function gitArgsAfterSubcommand(seg) {
|
|
4509
|
+
const at = scanFlags(seg)?.operandAt[0];
|
|
4510
|
+
return at === void 0 ? [] : seg.args.slice(at + 1);
|
|
4511
|
+
}
|
|
4512
|
+
var REMOTE_NAME = /^[A-Za-z0-9_][A-Za-z0-9_.-]*$/;
|
|
4513
|
+
function namesConfiguredRemote(seg, after) {
|
|
4514
|
+
const destination2 = scanFlags({ ...seg, args: after })?.operands[0];
|
|
4515
|
+
return destination2 === void 0 || REMOTE_NAME.test(destination2);
|
|
4516
|
+
}
|
|
4517
|
+
function programName(binary) {
|
|
4518
|
+
return binary.toLowerCase().replace(/\.exe$/, "");
|
|
4519
|
+
}
|
|
4520
|
+
function isRemoteListing(seg) {
|
|
4521
|
+
const git2 = { ...seg, binary: programName(seg.binary) };
|
|
4522
|
+
return git2.binary === "git" && subcommandOf(git2) === "ls-remote";
|
|
4523
|
+
}
|
|
4524
|
+
function remoteListingTexts(seg) {
|
|
4525
|
+
if (!isRemoteListing(seg)) return [];
|
|
4526
|
+
const git2 = { ...seg, binary: "git" };
|
|
4527
|
+
return scanFlags({ ...git2, args: gitArgsAfterSubcommand(git2) })?.operands ?? [];
|
|
4528
|
+
}
|
|
4411
4529
|
function refusalFor(seg, trustedResolves) {
|
|
4412
4530
|
const web = webCommand(seg, trustedResolves);
|
|
4413
4531
|
if (web?.reason) return web.reason;
|
|
@@ -4445,8 +4563,24 @@ function refusalFor(seg, trustedResolves) {
|
|
|
4445
4563
|
}
|
|
4446
4564
|
if (seg.binary === "git") {
|
|
4447
4565
|
const sub = subcommandOf(seg);
|
|
4566
|
+
const after = gitArgsAfterSubcommand(seg);
|
|
4567
|
+
if (sub !== void 0 && !GIT_READONLY_SUBCOMMANDS.includes(sub)) {
|
|
4568
|
+
const reads = GIT_PROMPTABLE[sub];
|
|
4569
|
+
if (!reads) {
|
|
4570
|
+
return `"git ${sub}" is not a git subcommand known only to read, so it may write the repository. You are a read-only planner: ${EFFECT_ROUTE}`;
|
|
4571
|
+
}
|
|
4572
|
+
if (!reads(after)) {
|
|
4573
|
+
return `"git ${[sub, ...after].join(" ")}" is not a read-only form of "git ${sub}". You are a read-only planner: ${EFFECT_ROUTE}`;
|
|
4574
|
+
}
|
|
4575
|
+
}
|
|
4576
|
+
if (sub === "ls-remote" && !namesConfiguredRemote(seg, after)) {
|
|
4577
|
+
return `"git ls-remote" given a URL, an address or a path reaches it through git's own transport, which resolves the host itself and follows a redirect, so the origin approval, address guard and pinned connection a web read gets (ADR-0027) cannot hold for it. Name a remote the repository configures, such as "git ls-remote origin", or fetch the page with your web fetch tool.`;
|
|
4578
|
+
}
|
|
4579
|
+
if (sub === "ls-remote" && (seg.expandable || seg.globbed || after.some((a) => a.includes("~")))) {
|
|
4580
|
+
return `The shell rewrites part of this "git ls-remote" before git sees it (a variable, substitution, glob or "~"), so what it sends the remote, and which repository it lists, cannot be checked for a secret or approved. Write the remote and patterns literally, in single quotes where they hold a "*", and re-run.`;
|
|
4581
|
+
}
|
|
4448
4582
|
if (sub === "branch" || sub === "tag") {
|
|
4449
|
-
const flag2 = seg.args.find((a) => ["-D", "-d", "-m", "-M", "--delete", "--move"].includes(a)) ??
|
|
4583
|
+
const flag2 = seg.args.find((a) => ["-D", "-d", "-m", "-M", "--delete", "--move"].includes(a)) ?? after.find(swallowsRefName);
|
|
4450
4584
|
if (flag2) {
|
|
4451
4585
|
return `"git ${sub} ${flag2}" mutates refs. You are a read-only planner: ${EFFECT_ROUTE}`;
|
|
4452
4586
|
}
|
|
@@ -4585,10 +4719,10 @@ function classifyCommand(command, opts = {}) {
|
|
|
4585
4719
|
return { tier: "refuse", scope: "", reason: `curl and wget are refused in Full and allowlist mode. ${web.effect ? EFFECT_REFUSAL_HINT : READ_REFUSAL_HINT}` };
|
|
4586
4720
|
}
|
|
4587
4721
|
const allow = opts.allow;
|
|
4588
|
-
if (web && allow && web.urls.some((url) => scopeExcluded(allow, `${seg.binary
|
|
4722
|
+
if (web && allow && web.urls.some((url) => scopeExcluded(allow, `${programName(seg.binary)} ${url.origin}/*`))) {
|
|
4589
4723
|
return { tier: "refuse", scope: "", reason: `The web origin is excluded by the planner allowlist. ${READ_REFUSAL_HINT}` };
|
|
4590
4724
|
}
|
|
4591
|
-
const reason = refusalFor({ ...seg, binary: seg.binary
|
|
4725
|
+
const reason = refusalFor({ ...seg, binary: programName(seg.binary) }, opts.trustedWebResolves);
|
|
4592
4726
|
if (reason) return { tier: "refuse", scope: "", reason: withWrapperNote(reason, wrappers) };
|
|
4593
4727
|
if (runner && !XARGS_TARGETS.includes(seg.binary)) {
|
|
4594
4728
|
return {
|
|
@@ -4598,23 +4732,24 @@ function classifyCommand(command, opts = {}) {
|
|
|
4598
4732
|
};
|
|
4599
4733
|
}
|
|
4600
4734
|
}
|
|
4601
|
-
if (unwrapped.some(({ seg }) => seg.binary
|
|
4735
|
+
if (unwrapped.some(({ seg }) => programName(seg.binary) === "wget") && wgetVersionCommands(command, opts.dialect).includes(void 0)) {
|
|
4602
4736
|
return { tier: "refuse", scope: "", reason: `The wget executable cannot be checked safely in this working directory. ${READ_REFUSAL_HINT}` };
|
|
4603
4737
|
}
|
|
4604
4738
|
const nonAuto = unwrapped.filter((u) => u.runner !== void 0 || u.wrappers.some((w) => w !== w.toLowerCase()) || !isAuto(u.seg, opts));
|
|
4605
4739
|
if (nonAuto.length === 0) return { tier: "auto", scope: "" };
|
|
4740
|
+
const changesDirectory = unwrapped.some(({ seg }) => seg.binary.toLowerCase() === "cd");
|
|
4606
4741
|
const lineScoped = nonAuto.some((u) => {
|
|
4607
4742
|
const name = u.seg.binary.toLowerCase();
|
|
4608
|
-
return name === "cd" || name !== "command" && SHELL_STATE_COMMANDS.has(name) || climbsByGlob(u.seg);
|
|
4743
|
+
return name === "cd" || name !== "command" && SHELL_STATE_COMMANDS.has(name) || climbsByGlob(u.seg) || isRemoteListing(u.seg) && (changesDirectory || u.wrappers.length > 0 || nonAuto.length > 1);
|
|
4609
4744
|
});
|
|
4610
4745
|
const webUrls = nonAuto.flatMap((u) => webCommand(u.seg, opts.trustedWebResolves)?.urls ?? []);
|
|
4611
|
-
const
|
|
4746
|
+
const outboundTexts = nonAuto.flatMap((u) => webCommand(u.seg, opts.trustedWebResolves)?.outboundTexts ?? remoteListingTexts(u.seg));
|
|
4612
4747
|
if (lineScoped && webUrls.length === 0) {
|
|
4613
|
-
return { tier: "ask", scope: trimmed };
|
|
4748
|
+
return { tier: "ask", scope: trimmed, ...outboundTexts.length ? { outboundTexts } : {} };
|
|
4614
4749
|
}
|
|
4615
4750
|
const scopes = [...new Set(nonAuto.flatMap((u) => {
|
|
4616
4751
|
const web = webCommand(u.seg, opts.trustedWebResolves);
|
|
4617
|
-
return web ? web.urls.map((url) => `${u.seg.binary
|
|
4752
|
+
return web ? web.urls.map((url) => `${programName(u.seg.binary)} ${url.origin}/*`) : [lineScoped ? trimmed : u.runner ? `${u.runner} ${scopeFor(u.seg)}` : scopeFor(u.seg)];
|
|
4618
4753
|
}))].sort();
|
|
4619
4754
|
const scope = scopes.join(" + ");
|
|
4620
4755
|
const perInvocation = nonAuto.some((u) => webCommand(u.seg, opts.trustedWebResolves)?.perInvocation);
|
|
@@ -4622,7 +4757,8 @@ function classifyCommand(command, opts = {}) {
|
|
|
4622
4757
|
return {
|
|
4623
4758
|
tier: "ask",
|
|
4624
4759
|
scope,
|
|
4625
|
-
...webUrls.length ? { webUrls
|
|
4760
|
+
...webUrls.length ? { webUrls } : {},
|
|
4761
|
+
...outboundTexts.length ? { outboundTexts } : {},
|
|
4626
4762
|
...scopes.length > 1 && webUrls.length ? { scopes } : {},
|
|
4627
4763
|
...perInvocation ? { perInvocation } : {},
|
|
4628
4764
|
...allowedBy ? { allowedBy } : {}
|
|
@@ -6176,7 +6312,7 @@ ${TASK_CONTINUE}`);
|
|
|
6176
6312
|
command = disableWebCommandStartup(command, this.researchShell.dialect);
|
|
6177
6313
|
const { approval, root } = site;
|
|
6178
6314
|
const allow = approval.allowlist?.();
|
|
6179
|
-
const { tier, scope, reason, allowedBy, perInvocation, scopes: commandScopes, webUrls,
|
|
6315
|
+
const { tier, scope, reason, allowedBy, perInvocation, scopes: commandScopes, webUrls, outboundTexts } = classifyCommand(command, {
|
|
6180
6316
|
...this.policyOptions(),
|
|
6181
6317
|
...approval.commandPolicy?.(),
|
|
6182
6318
|
...allow ? { allow } : {}
|
|
@@ -6201,8 +6337,9 @@ ${TASK_CONTINUE}`);
|
|
|
6201
6337
|
if (site.task?.sealed) return this.runSealed(site, command, request, "after", signal);
|
|
6202
6338
|
if (tier === "refuse") return this.sealedInstead(site, command, request, refusal(`Command refused: ${reason}${site.task ? `
|
|
6203
6339
|
${TASK_UNSEALED} ${TASK_CONTINUE}` : ""}`), signal);
|
|
6204
|
-
|
|
6205
|
-
|
|
6340
|
+
const outbound = [...outboundTexts ?? [], ...(webUrls ?? []).map((url) => url.href)];
|
|
6341
|
+
if (outbound.length && carriesSecret(outbound, [...this.outboundSecrets(), ...sensitiveEnvValues(process.env)])) {
|
|
6342
|
+
return refusal(webUrls?.length ? "Command refused: a web command contains what looks like a secret. Fetch the page with your web fetch tool, without credentials." : "Command refused: what this command sends over the network contains what looks like a secret. Run it without the value.");
|
|
6206
6343
|
}
|
|
6207
6344
|
const vettedAddresses = /* @__PURE__ */ new Map();
|
|
6208
6345
|
for (const url of webUrls ?? []) {
|
|
@@ -6305,7 +6442,7 @@ ${outcome.output}${outcome.success ? "" : `
|
|
|
6305
6442
|
}
|
|
6306
6443
|
let shell;
|
|
6307
6444
|
try {
|
|
6308
|
-
const sealed = site.task?.gitBefore ? { ...site.sealed, gitEntries: site.task.gitBefore.entries.map(({ path:
|
|
6445
|
+
const sealed = site.task?.gitBefore ? { ...site.sealed, gitEntries: site.task.gitBefore.entries.map(({ path: path47, directory }) => ({ path: path47, directory })) } : site.sealed;
|
|
6309
6446
|
shell = await site.sandbox.launchSealed(this.researchShell, sealed);
|
|
6310
6447
|
} catch (err) {
|
|
6311
6448
|
return { refused: refusal(`Command not run: its sealed sandbox could not be prepared (${err instanceof Error ? err.message : String(err)}).`) };
|
|
@@ -7984,6 +8121,7 @@ function enabledRunners(cfg) {
|
|
|
7984
8121
|
}
|
|
7985
8122
|
|
|
7986
8123
|
// src/plugins/builtin/claudeModels.ts
|
|
8124
|
+
import * as path12 from "path";
|
|
7987
8125
|
var ANTHROPIC_EFFORT_LEVELS = ["low", "medium", "high", "xhigh", "max"];
|
|
7988
8126
|
var ANTHROPIC_VARIANT_LABELS = {
|
|
7989
8127
|
adaptive: "Adaptive",
|
|
@@ -8077,6 +8215,53 @@ function parseClaudeHelp(stdout) {
|
|
|
8077
8215
|
}
|
|
8078
8216
|
return models;
|
|
8079
8217
|
}
|
|
8218
|
+
function claudeConfigDir(env) {
|
|
8219
|
+
return env.CLAUDE_CONFIG_DIR?.trim() || "~/.claude";
|
|
8220
|
+
}
|
|
8221
|
+
function claudeManagedSettingsDir(platform = process.platform) {
|
|
8222
|
+
if (platform === "darwin") return "/Library/Application Support/ClaudeCode";
|
|
8223
|
+
if (platform === "win32") return "C:\\Program Files\\ClaudeCode";
|
|
8224
|
+
return "/etc/claude-code";
|
|
8225
|
+
}
|
|
8226
|
+
function readClaudeSettingsModels(files, platform = process.platform) {
|
|
8227
|
+
const managed = claudeManagedSettingsDir(platform);
|
|
8228
|
+
const join43 = platform === "win32" ? path12.win32.join : path12.posix.join;
|
|
8229
|
+
const dropIns = files.list(join43(managed, "managed-settings.d")).filter((name) => name.endsWith(".json") && !name.startsWith(".")).sort().map((name) => join43(managed, "managed-settings.d", name));
|
|
8230
|
+
const ranked = [...dropIns.reverse(), join43(managed, "managed-settings.json"), join43(claudeConfigDir(files.env), "settings.json")];
|
|
8231
|
+
for (const file of ranked) {
|
|
8232
|
+
const content = files.read(file);
|
|
8233
|
+
const lineup = content === null ? null : parseClaudeSettingsModels(content);
|
|
8234
|
+
if (lineup) return lineup;
|
|
8235
|
+
}
|
|
8236
|
+
return [];
|
|
8237
|
+
}
|
|
8238
|
+
function parseClaudeSettingsModels(content) {
|
|
8239
|
+
let parsed;
|
|
8240
|
+
try {
|
|
8241
|
+
parsed = JSON.parse(content);
|
|
8242
|
+
} catch {
|
|
8243
|
+
return null;
|
|
8244
|
+
}
|
|
8245
|
+
const picker = parsed && typeof parsed === "object" ? parsed.modelPicker : void 0;
|
|
8246
|
+
const options = picker && typeof picker === "object" ? picker.options : void 0;
|
|
8247
|
+
if (!Array.isArray(options)) return null;
|
|
8248
|
+
const seen = /* @__PURE__ */ new Set();
|
|
8249
|
+
const models = [];
|
|
8250
|
+
for (const row of options) {
|
|
8251
|
+
if (!row || typeof row !== "object") continue;
|
|
8252
|
+
const { model: raw, label } = row;
|
|
8253
|
+
const model = typeof raw === "string" ? raw.trim() : "";
|
|
8254
|
+
if (!model || seen.has(model)) continue;
|
|
8255
|
+
seen.add(model);
|
|
8256
|
+
models.push({
|
|
8257
|
+
modelId: model,
|
|
8258
|
+
modelLabel: typeof label === "string" && label.trim() ? label.trim() : model,
|
|
8259
|
+
runnerProvider: model.includes("/") ? model.split("/")[0] : void 0,
|
|
8260
|
+
variants: []
|
|
8261
|
+
});
|
|
8262
|
+
}
|
|
8263
|
+
return models;
|
|
8264
|
+
}
|
|
8080
8265
|
|
|
8081
8266
|
// src/plugins/builtin/claude-code.manifest.ts
|
|
8082
8267
|
var TURN_NOT_LOGGED_IN = /not logged in\s*·\s*please run \/login/i;
|
|
@@ -8115,18 +8300,24 @@ var CLAUDE_CODE_MANIFEST = {
|
|
|
8115
8300
|
// 3. canonicalAliases — stable --model contracts (opus/sonnet/haiku/fable)
|
|
8116
8301
|
// merged into any successful result to fill gaps, and used as the full
|
|
8117
8302
|
// last-resort list when both API and CLI are unavailable.
|
|
8303
|
+
//
|
|
8304
|
+
// Whichever source answers, the models the user added to Claude's own
|
|
8305
|
+
// picker (`modelPicker.options` in its managed or user settings, usually
|
|
8306
|
+
// gateway IDs) are appended (settingsModels): the CLI accepts them as
|
|
8307
|
+
// `--model` values though no catalog above lists them.
|
|
8118
8308
|
apiDiscovery: {
|
|
8119
8309
|
url: "https://api.anthropic.com/v1/models?limit=100",
|
|
8120
8310
|
headers: { "anthropic-version": "2023-06-01" },
|
|
8121
8311
|
auth: [
|
|
8122
8312
|
{ type: "env", varName: "ANTHROPIC_API_KEY", header: "X-Api-Key" },
|
|
8123
|
-
{ type: "file", path:
|
|
8313
|
+
{ type: "file", path: (env) => `${claudeConfigDir(env)}/.credentials.json`, jsonPath: "claudeAiOauth.accessToken", header: "Authorization", prefix: "Bearer " }
|
|
8124
8314
|
],
|
|
8125
8315
|
parser: parseAnthropicModels
|
|
8126
8316
|
},
|
|
8127
8317
|
discoveryCommands: [
|
|
8128
8318
|
{ command: "claude", args: ["--help"], parser: parseClaudeHelp }
|
|
8129
8319
|
],
|
|
8320
|
+
settingsModels: (files) => readClaudeSettingsModels(files),
|
|
8130
8321
|
canonicalAliases: [
|
|
8131
8322
|
{ modelId: "opus", modelLabel: "Opus" },
|
|
8132
8323
|
{ modelId: "sonnet", modelLabel: "Sonnet" },
|
|
@@ -8184,7 +8375,7 @@ import { join as join11, resolve as resolve4 } from "path";
|
|
|
8184
8375
|
|
|
8185
8376
|
// src/services/harness/repoConfig.ts
|
|
8186
8377
|
import { existsSync as existsSync3, readFileSync as readFileSync3, realpathSync as realpathSync2, statSync as statSync4 } from "fs";
|
|
8187
|
-
import * as
|
|
8378
|
+
import * as path13 from "path";
|
|
8188
8379
|
|
|
8189
8380
|
// src/services/mcp/OrdewellMcpServer.ts
|
|
8190
8381
|
import { createServer } from "http";
|
|
@@ -8897,12 +9088,12 @@ function checkpointReply(answer2) {
|
|
|
8897
9088
|
return { text: `The checkpoint was withdrawn: ${answer2.why}`, isError: true };
|
|
8898
9089
|
}
|
|
8899
9090
|
}
|
|
8900
|
-
function fieldPath(
|
|
8901
|
-
return
|
|
9091
|
+
function fieldPath(path47) {
|
|
9092
|
+
return path47.reduce((text4, part) => typeof part === "number" ? `${text4}[${part}]` : text4 ? `${text4}.${String(part)}` : String(part), "");
|
|
8902
9093
|
}
|
|
8903
|
-
function valueAt(value,
|
|
9094
|
+
function valueAt(value, path47) {
|
|
8904
9095
|
let current = value;
|
|
8905
|
-
for (const part of
|
|
9096
|
+
for (const part of path47) {
|
|
8906
9097
|
if (typeof part === "number") {
|
|
8907
9098
|
if (!Array.isArray(current)) return void 0;
|
|
8908
9099
|
current = current[part];
|
|
@@ -8924,7 +9115,7 @@ function boundedRefusal(issue, args) {
|
|
|
8924
9115
|
var LEAKED_PARAMETER = /<\/parameter>|<parameter\s+name\s*=\s*"([^"]*)">/g;
|
|
8925
9116
|
function leakedMarkupIn(value) {
|
|
8926
9117
|
const found = [];
|
|
8927
|
-
const walk = (node,
|
|
9118
|
+
const walk = (node, path47) => {
|
|
8928
9119
|
if (typeof node === "string") {
|
|
8929
9120
|
const names = [];
|
|
8930
9121
|
let closing = false;
|
|
@@ -8932,25 +9123,25 @@ function leakedMarkupIn(value) {
|
|
|
8932
9123
|
if (match[1] !== void 0) names.push(match[1]);
|
|
8933
9124
|
else closing = true;
|
|
8934
9125
|
}
|
|
8935
|
-
if (names.length > 0 || closing) found.push({ path:
|
|
9126
|
+
if (names.length > 0 || closing) found.push({ path: path47, names: [...new Set(names)], closing });
|
|
8936
9127
|
return;
|
|
8937
9128
|
}
|
|
8938
9129
|
if (Array.isArray(node)) {
|
|
8939
|
-
node.forEach((item, i) => walk(item, `${
|
|
9130
|
+
node.forEach((item, i) => walk(item, `${path47}[${i}]`));
|
|
8940
9131
|
return;
|
|
8941
9132
|
}
|
|
8942
9133
|
if (node !== null && typeof node === "object") {
|
|
8943
|
-
for (const [key, item] of Object.entries(node)) walk(item,
|
|
9134
|
+
for (const [key, item] of Object.entries(node)) walk(item, path47 ? `${path47}.${key}` : key);
|
|
8944
9135
|
}
|
|
8945
9136
|
};
|
|
8946
9137
|
walk(value, "");
|
|
8947
9138
|
return found;
|
|
8948
9139
|
}
|
|
8949
9140
|
function markupRefusal(leaks) {
|
|
8950
|
-
return leaks.map(({ path:
|
|
9141
|
+
return leaks.map(({ path: path47, names, closing }) => {
|
|
8951
9142
|
const tags = [...names.map((n) => `<parameter name="${n}">`), ...closing ? ["</parameter>"] : []];
|
|
8952
|
-
const guide = names.length > 0 ? ` The parameters ${names.map((n) => JSON.stringify(n)).join(", ")} are inside it; pass them as separate arguments, not inside ${
|
|
8953
|
-
return `${
|
|
9143
|
+
const guide = names.length > 0 ? ` The parameters ${names.map((n) => JSON.stringify(n)).join(", ")} are inside it; pass them as separate arguments, not inside ${path47}.` : " Pass each argument separately, not inside one string.";
|
|
9144
|
+
return `${path47} contains leaked tool-call markup: ${tags.join(", ")}.${guide}`;
|
|
8954
9145
|
}).join("\n");
|
|
8955
9146
|
}
|
|
8956
9147
|
function invalidArguments(name, error, args) {
|
|
@@ -9415,9 +9606,9 @@ function mcpClientConfig(credential) {
|
|
|
9415
9606
|
}
|
|
9416
9607
|
function ownerOnlyConfigFile(fileName, contents) {
|
|
9417
9608
|
const dir = mkdtempSync2(join9(tmpdir2(), "ordewell-mcp-"));
|
|
9418
|
-
const
|
|
9419
|
-
writeFileSync(
|
|
9420
|
-
return { path:
|
|
9609
|
+
const path47 = join9(dir, fileName);
|
|
9610
|
+
writeFileSync(path47, contents, { mode: 384 });
|
|
9611
|
+
return { path: path47, remove: () => rmSync2(dir, { recursive: true, force: true }) };
|
|
9421
9612
|
}
|
|
9422
9613
|
|
|
9423
9614
|
// src/services/harness/repoConfig.ts
|
|
@@ -9440,19 +9631,19 @@ function onDisk(target) {
|
|
|
9440
9631
|
try {
|
|
9441
9632
|
return realpathSync2.native(target);
|
|
9442
9633
|
} catch {
|
|
9443
|
-
return
|
|
9634
|
+
return path13.resolve(target);
|
|
9444
9635
|
}
|
|
9445
9636
|
}
|
|
9446
9637
|
function insideOnDisk(root, target) {
|
|
9447
|
-
const rel =
|
|
9448
|
-
return rel === "" || !rel.startsWith("..") && !
|
|
9638
|
+
const rel = path13.relative(onDisk(root), onDisk(target));
|
|
9639
|
+
return rel === "" || !rel.startsWith("..") && !path13.isAbsolute(rel);
|
|
9449
9640
|
}
|
|
9450
9641
|
function repoLevels(workspace) {
|
|
9451
9642
|
const levels = [];
|
|
9452
|
-
for (let dir =
|
|
9643
|
+
for (let dir = path13.resolve(workspace); ; dir = path13.dirname(dir)) {
|
|
9453
9644
|
levels.unshift(dir);
|
|
9454
|
-
if (existsSync3(
|
|
9455
|
-
if (
|
|
9645
|
+
if (existsSync3(path13.join(dir, ".git"))) return levels;
|
|
9646
|
+
if (path13.dirname(dir) === dir) return [path13.resolve(workspace)];
|
|
9456
9647
|
}
|
|
9457
9648
|
}
|
|
9458
9649
|
function repoRoot(workspace) {
|
|
@@ -9546,33 +9737,33 @@ var TomlReader = class {
|
|
|
9546
9737
|
if (this.i >= this.src.length) return root;
|
|
9547
9738
|
if (this.src.startsWith("[[", this.i)) {
|
|
9548
9739
|
this.i += 2;
|
|
9549
|
-
const
|
|
9740
|
+
const path47 = this.key();
|
|
9550
9741
|
this.expect("]]");
|
|
9551
|
-
const parent = this.tableAt(root,
|
|
9552
|
-
const last =
|
|
9742
|
+
const parent = this.tableAt(root, path47.slice(0, -1));
|
|
9743
|
+
const last = path47[path47.length - 1];
|
|
9553
9744
|
const list = Object.hasOwn(parent, last) ? parent[last] : parent[last] = [];
|
|
9554
|
-
if (!Array.isArray(list)) throw this.fail(`${
|
|
9745
|
+
if (!Array.isArray(list)) throw this.fail(`${path47.join(".")} is not an array of tables`);
|
|
9555
9746
|
table = newTable();
|
|
9556
9747
|
list.push(table);
|
|
9557
9748
|
} else if (this.src[this.i] === "[") {
|
|
9558
9749
|
this.i += 1;
|
|
9559
|
-
const
|
|
9750
|
+
const path47 = this.key();
|
|
9560
9751
|
this.expect("]");
|
|
9561
|
-
table = this.tableAt(root,
|
|
9752
|
+
table = this.tableAt(root, path47);
|
|
9562
9753
|
} else {
|
|
9563
|
-
const
|
|
9754
|
+
const path47 = this.key();
|
|
9564
9755
|
this.skipBlank(false);
|
|
9565
9756
|
this.expect("=");
|
|
9566
9757
|
this.skipBlank(false);
|
|
9567
|
-
const owner = this.tableAt(table,
|
|
9568
|
-
owner[
|
|
9758
|
+
const owner = this.tableAt(table, path47.slice(0, -1));
|
|
9759
|
+
owner[path47[path47.length - 1]] = this.value();
|
|
9569
9760
|
}
|
|
9570
9761
|
this.endOfLine();
|
|
9571
9762
|
}
|
|
9572
9763
|
}
|
|
9573
|
-
tableAt(from,
|
|
9764
|
+
tableAt(from, path47) {
|
|
9574
9765
|
let table = from;
|
|
9575
|
-
for (const part of
|
|
9766
|
+
for (const part of path47) {
|
|
9576
9767
|
const next = Object.hasOwn(table, part) ? table[part] : void 0;
|
|
9577
9768
|
if (next === void 0) table = table[part] = newTable();
|
|
9578
9769
|
else if (isRecord(next)) table = next;
|
|
@@ -9641,10 +9832,10 @@ var TomlReader = class {
|
|
|
9641
9832
|
this.i++;
|
|
9642
9833
|
return table;
|
|
9643
9834
|
}
|
|
9644
|
-
const
|
|
9835
|
+
const path47 = this.key();
|
|
9645
9836
|
this.expect("=");
|
|
9646
9837
|
this.skipBlank(false);
|
|
9647
|
-
this.tableAt(table,
|
|
9838
|
+
this.tableAt(table, path47.slice(0, -1))[path47[path47.length - 1]] = this.value();
|
|
9648
9839
|
this.skipBlank(true);
|
|
9649
9840
|
if (this.src[this.i] === ",") this.i++;
|
|
9650
9841
|
else if (this.src[this.i] !== "}") throw this.fail("expected , or } in an inline table");
|
|
@@ -9789,7 +9980,8 @@ var CODEX_MANIFEST = {
|
|
|
9789
9980
|
appServer: {
|
|
9790
9981
|
command: "codex",
|
|
9791
9982
|
args: ["app-server"],
|
|
9792
|
-
|
|
9983
|
+
// CODEX_HOME moves all of Codex's own files from `~/.codex`.
|
|
9984
|
+
cacheFile: (env) => `${env.CODEX_HOME?.trim() || "~/.codex"}/models_cache.json`
|
|
9793
9985
|
},
|
|
9794
9986
|
canonicalAliases: [
|
|
9795
9987
|
{ modelId: "gpt-5.6-sol", modelLabel: "GPT-5.6-Sol" },
|
|
@@ -10018,8 +10210,7 @@ var LineBuffer = class {
|
|
|
10018
10210
|
// src/services/harness/ClaudeCodeAdapter.ts
|
|
10019
10211
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
10020
10212
|
import { realpathSync as realpathSync7 } from "fs";
|
|
10021
|
-
import * as
|
|
10022
|
-
import * as path20 from "path";
|
|
10213
|
+
import * as path21 from "path";
|
|
10023
10214
|
|
|
10024
10215
|
// src/plugins/resolveArgs.ts
|
|
10025
10216
|
function claudeThinkingArgs(effort) {
|
|
@@ -10057,14 +10248,29 @@ function resolveTaskRunnerFlags(manifest, ctx) {
|
|
|
10057
10248
|
return flags;
|
|
10058
10249
|
}
|
|
10059
10250
|
|
|
10251
|
+
// src/utils/runnerHome.ts
|
|
10252
|
+
import * as os5 from "os";
|
|
10253
|
+
function runnerHomeDir(env, platform = process.platform) {
|
|
10254
|
+
const name = platform === "win32" ? "USERPROFILE" : "HOME";
|
|
10255
|
+
const key = platform === "win32" ? Object.keys(env).filter((k) => k.toUpperCase() === name).pop() : name;
|
|
10256
|
+
const home = key === void 0 ? void 0 : env[key];
|
|
10257
|
+
return home ?? os5.homedir();
|
|
10258
|
+
}
|
|
10259
|
+
function expandRunnerHome(filePath, env = process.env) {
|
|
10260
|
+
if (!filePath.startsWith("~")) return filePath;
|
|
10261
|
+
const home = runnerHomeDir(env);
|
|
10262
|
+
return home ? home + filePath.slice(1) : null;
|
|
10263
|
+
}
|
|
10264
|
+
|
|
10060
10265
|
// src/services/harness/runnerProcess.ts
|
|
10266
|
+
import { Socket } from "net";
|
|
10061
10267
|
import { StringDecoder } from "string_decoder";
|
|
10062
10268
|
|
|
10063
10269
|
// src/utils/shellPath.ts
|
|
10064
10270
|
import { exec } from "child_process";
|
|
10065
10271
|
import { promisify } from "util";
|
|
10066
|
-
import * as
|
|
10067
|
-
import * as
|
|
10272
|
+
import * as os6 from "os";
|
|
10273
|
+
import * as path14 from "path";
|
|
10068
10274
|
var execAsync = promisify(exec);
|
|
10069
10275
|
var LOGIN_SHELL_TIMEOUT_MS = 5e3;
|
|
10070
10276
|
var PATH_MARKER = "__ORDEWELL_PATH__";
|
|
@@ -10073,21 +10279,21 @@ var defaultExec = async (command, options) => {
|
|
|
10073
10279
|
return { stdout: String(stdout) };
|
|
10074
10280
|
};
|
|
10075
10281
|
function posixBinDirs() {
|
|
10076
|
-
const home =
|
|
10282
|
+
const home = os6.homedir();
|
|
10077
10283
|
return [
|
|
10078
|
-
|
|
10079
|
-
|
|
10080
|
-
|
|
10081
|
-
|
|
10082
|
-
|
|
10083
|
-
|
|
10084
|
-
|
|
10284
|
+
path14.join(home, ".local", "bin"),
|
|
10285
|
+
path14.join(home, ".opencode", "bin"),
|
|
10286
|
+
path14.join(home, ".claude", "bin"),
|
|
10287
|
+
path14.join(home, ".bun", "bin"),
|
|
10288
|
+
path14.join(home, ".volta", "bin"),
|
|
10289
|
+
path14.join(home, ".npm-global", "bin"),
|
|
10290
|
+
path14.join(home, "bin"),
|
|
10085
10291
|
"/usr/local/bin",
|
|
10086
10292
|
"/opt/homebrew/bin"
|
|
10087
10293
|
];
|
|
10088
10294
|
}
|
|
10089
10295
|
function windowsBinDirs(env, home) {
|
|
10090
|
-
const join43 = (...parts) =>
|
|
10296
|
+
const join43 = (...parts) => path14.win32.join(...parts);
|
|
10091
10297
|
const under = (base, ...parts) => base ? [join43(base, ...parts)] : [];
|
|
10092
10298
|
const appData = env.APPDATA;
|
|
10093
10299
|
const localAppData = env.LOCALAPPDATA;
|
|
@@ -10124,7 +10330,7 @@ function windowsBinDirs(env, home) {
|
|
|
10124
10330
|
function wellKnownBinDirs(deps = {}) {
|
|
10125
10331
|
const platform = deps.platform ?? process.platform;
|
|
10126
10332
|
if (platform !== "win32") return posixBinDirs();
|
|
10127
|
-
return [...new Set(windowsBinDirs(deps.env ?? process.env, deps.home ??
|
|
10333
|
+
return [...new Set(windowsBinDirs(deps.env ?? process.env, deps.home ?? os6.homedir()))];
|
|
10128
10334
|
}
|
|
10129
10335
|
async function loginShellPath(execImpl) {
|
|
10130
10336
|
if (process.platform === "win32") return null;
|
|
@@ -10147,14 +10353,14 @@ function augmentedPath(execImpl = defaultExec) {
|
|
|
10147
10353
|
const segments = [];
|
|
10148
10354
|
const push = (p) => {
|
|
10149
10355
|
if (!p) return;
|
|
10150
|
-
for (const seg of p.split(
|
|
10356
|
+
for (const seg of p.split(path14.delimiter)) {
|
|
10151
10357
|
if (seg && !segments.includes(seg)) segments.push(seg);
|
|
10152
10358
|
}
|
|
10153
10359
|
};
|
|
10154
10360
|
push(process.env.PATH);
|
|
10155
10361
|
push(await loginShellPath(execImpl));
|
|
10156
10362
|
for (const dir of wellKnownBinDirs()) push(dir);
|
|
10157
|
-
return segments.join(
|
|
10363
|
+
return segments.join(path14.delimiter);
|
|
10158
10364
|
})();
|
|
10159
10365
|
}
|
|
10160
10366
|
return cachedPath;
|
|
@@ -10176,7 +10382,7 @@ function withPath(base, resolvedPath, overrides = {}) {
|
|
|
10176
10382
|
|
|
10177
10383
|
// src/utils/launch.ts
|
|
10178
10384
|
import * as fsSync4 from "fs";
|
|
10179
|
-
import * as
|
|
10385
|
+
import * as path15 from "path";
|
|
10180
10386
|
var DIRECT_EXTENSIONS = [".exe", ".com"];
|
|
10181
10387
|
var BATCH_EXTENSIONS = [".cmd", ".bat"];
|
|
10182
10388
|
var POWERSHELL_EXTENSIONS = [".ps1"];
|
|
@@ -10184,7 +10390,7 @@ var CMD_EXE_MAX_COMMAND_LINE = 8191;
|
|
|
10184
10390
|
var WINDOWS_MAX_COMMAND_LINE = 32767;
|
|
10185
10391
|
function defaultPowerShell() {
|
|
10186
10392
|
const root = process.env.SystemRoot || process.env.windir;
|
|
10187
|
-
return root ?
|
|
10393
|
+
return root ? path15.win32.join(root, "System32", "WindowsPowerShell", "v1.0", "powershell.exe") : "powershell.exe";
|
|
10188
10394
|
}
|
|
10189
10395
|
var POWERSHELL_FLAGS = ["-NoLogo", "-NoProfile", "-NonInteractive", "-ExecutionPolicy", "Bypass", "-File"];
|
|
10190
10396
|
var CommandLineTooLongError = class extends Error {
|
|
@@ -10250,7 +10456,7 @@ Searched: ${searchedPath || "(empty PATH)"}`);
|
|
|
10250
10456
|
};
|
|
10251
10457
|
function isResolvableOnPosixPath(command, PATH, exists) {
|
|
10252
10458
|
if (command.includes("/")) return exists(command);
|
|
10253
|
-
return PATH.split(
|
|
10459
|
+
return PATH.split(path15.delimiter).map((d) => d.trim()).filter(Boolean).some((dir) => exists(path15.join(dir, command)));
|
|
10254
10460
|
}
|
|
10255
10461
|
function isExecutableResolved(command, plan, PATH, deps = {}) {
|
|
10256
10462
|
if ((deps.platform ?? process.platform) === "win32") return plan.file !== command;
|
|
@@ -10290,9 +10496,9 @@ function cmdArgs(line) {
|
|
|
10290
10496
|
return ["/d", "/s", "/c", `"${line}"`];
|
|
10291
10497
|
}
|
|
10292
10498
|
function candidateExtensions(command, pathExt) {
|
|
10293
|
-
const declared = pathExt.split(
|
|
10499
|
+
const declared = pathExt.split(path15.win32.delimiter).map((e) => e.trim().toLowerCase()).filter(Boolean);
|
|
10294
10500
|
const known = /* @__PURE__ */ new Set([...DIRECT_EXTENSIONS, ...BATCH_EXTENSIONS, ...POWERSHELL_EXTENSIONS, ...declared]);
|
|
10295
|
-
const ext =
|
|
10501
|
+
const ext = path15.win32.extname(command).toLowerCase();
|
|
10296
10502
|
if (ext && known.has(ext)) {
|
|
10297
10503
|
const route = BATCH_EXTENSIONS.includes(ext) ? "cmd" : POWERSHELL_EXTENSIONS.includes(ext) ? "powershell" : "direct";
|
|
10298
10504
|
return { direct: [], cmd: [], powershell: [], [route]: [""] };
|
|
@@ -10313,13 +10519,13 @@ async function findWindowsExecutables(command, deps) {
|
|
|
10313
10519
|
const exists = deps.exists ?? defaultExists3;
|
|
10314
10520
|
const pathExt = (deps.pathExt ?? (() => process.env.PATHEXT ?? ""))();
|
|
10315
10521
|
const byRoute = candidateExtensions(command, pathExt);
|
|
10316
|
-
const dirs =
|
|
10317
|
-
const base =
|
|
10522
|
+
const dirs = path15.win32.basename(command) !== command ? [path15.win32.dirname(path15.win32.resolve(command))] : (await (deps.resolvePath ?? augmentedPath)() || "").split(path15.win32.delimiter).map((d) => d.trim().replace(/^"|"$/g, "")).filter(Boolean);
|
|
10523
|
+
const base = path15.win32.basename(command);
|
|
10318
10524
|
const found = [];
|
|
10319
10525
|
for (const route of ROUTES) {
|
|
10320
10526
|
outer: for (const ext of byRoute[route]) {
|
|
10321
10527
|
for (const dir of dirs) {
|
|
10322
|
-
const candidate =
|
|
10528
|
+
const candidate = path15.win32.join(dir, `${base}${ext}`);
|
|
10323
10529
|
if (exists(candidate)) {
|
|
10324
10530
|
found.push({ file: candidate, route });
|
|
10325
10531
|
break outer;
|
|
@@ -10367,7 +10573,7 @@ async function planDirectLaunch(command, args, deps = {}) {
|
|
|
10367
10573
|
|
|
10368
10574
|
// src/utils/workspace.ts
|
|
10369
10575
|
import * as fs3 from "fs";
|
|
10370
|
-
import * as
|
|
10576
|
+
import * as path16 from "path";
|
|
10371
10577
|
var WorkspaceNotFoundError = class extends Error {
|
|
10372
10578
|
constructor(workspace) {
|
|
10373
10579
|
super(`Workspace "${workspace}" does not exist.`);
|
|
@@ -10423,7 +10629,7 @@ function assertWorkspaceIsProject(workspace, deps = {}) {
|
|
|
10423
10629
|
throw new WorkspaceNotAProjectError(workspace);
|
|
10424
10630
|
}
|
|
10425
10631
|
const hasMarker = [...PROJECT_MARKER_DIRS, ...PROJECT_MARKER_FILES].some(
|
|
10426
|
-
(marker2) => exists(
|
|
10632
|
+
(marker2) => exists(path16.join(resolved, marker2))
|
|
10427
10633
|
);
|
|
10428
10634
|
if (!hasMarker) throw new WorkspaceNotAProjectError(workspace);
|
|
10429
10635
|
}
|
|
@@ -10431,12 +10637,12 @@ function assertWorkspaceIsProject(workspace, deps = {}) {
|
|
|
10431
10637
|
// src/services/workspaceEnv.ts
|
|
10432
10638
|
import { execFile as execFile3 } from "child_process";
|
|
10433
10639
|
import * as fs5 from "fs";
|
|
10434
|
-
import * as
|
|
10435
|
-
import * as
|
|
10640
|
+
import * as os7 from "os";
|
|
10641
|
+
import * as path18 from "path";
|
|
10436
10642
|
|
|
10437
10643
|
// src/utils/fsHelpers.ts
|
|
10438
10644
|
import * as fs4 from "fs";
|
|
10439
|
-
import * as
|
|
10645
|
+
import * as path17 from "path";
|
|
10440
10646
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
10441
10647
|
var STATE_DIR = ".ordewell";
|
|
10442
10648
|
function ensureDir(dir) {
|
|
@@ -10445,9 +10651,9 @@ function ensureDir(dir) {
|
|
|
10445
10651
|
}
|
|
10446
10652
|
}
|
|
10447
10653
|
function writeFileAtomic(filePath, content) {
|
|
10448
|
-
const dir =
|
|
10654
|
+
const dir = path17.dirname(filePath);
|
|
10449
10655
|
ensureDir(dir);
|
|
10450
|
-
const tempPath =
|
|
10656
|
+
const tempPath = path17.join(dir, `.${path17.basename(filePath)}.${randomUUID2()}.tmp`);
|
|
10451
10657
|
fs4.writeFileSync(tempPath, content, { flag: "wx" });
|
|
10452
10658
|
try {
|
|
10453
10659
|
fs4.renameSync(tempPath, filePath);
|
|
@@ -10460,7 +10666,7 @@ function writeFileAtomic(filePath, content) {
|
|
|
10460
10666
|
}
|
|
10461
10667
|
}
|
|
10462
10668
|
function getStateDir(baseDir) {
|
|
10463
|
-
return
|
|
10669
|
+
return path17.join(baseDir ?? process.cwd(), STATE_DIR);
|
|
10464
10670
|
}
|
|
10465
10671
|
var IGNORE_FILE = ".gitignore";
|
|
10466
10672
|
var STATE_DIR_IGNORE = "*\n!.gitignore\n!skills/\n!skills/**\n";
|
|
@@ -10468,7 +10674,7 @@ var LEGACY_IGNORES = /* @__PURE__ */ new Set(["*", "*\n"]);
|
|
|
10468
10674
|
function ensureStateDirIgnored(baseDir) {
|
|
10469
10675
|
const dir = getStateDir(baseDir);
|
|
10470
10676
|
ensureDir(dir);
|
|
10471
|
-
const file =
|
|
10677
|
+
const file = path17.join(dir, IGNORE_FILE);
|
|
10472
10678
|
try {
|
|
10473
10679
|
fs4.writeFileSync(file, STATE_DIR_IGNORE, { flag: "wx" });
|
|
10474
10680
|
return;
|
|
@@ -11003,7 +11209,7 @@ function defaultDeps() {
|
|
|
11003
11209
|
}
|
|
11004
11210
|
},
|
|
11005
11211
|
baseEnv: process.env,
|
|
11006
|
-
home:
|
|
11212
|
+
home: os7.homedir()
|
|
11007
11213
|
};
|
|
11008
11214
|
}
|
|
11009
11215
|
var REFUSED2 = new Set(SETTINGS_ENV_REFUSED);
|
|
@@ -11015,7 +11221,7 @@ async function resolveWorkspaceEnv(cwd, overrides = {}) {
|
|
|
11015
11221
|
const direnv = await fromDirenv(checkoutDirOf(cwd, deps), deps);
|
|
11016
11222
|
result.blockedEnvrc = direnv.blocked;
|
|
11017
11223
|
Object.assign(collected, direnv.env);
|
|
11018
|
-
const file = findUp(cwd,
|
|
11224
|
+
const file = findUp(cwd, path18.join(STATE_DIR, WORKSPACE_ENV_FILE), deps);
|
|
11019
11225
|
if (file) {
|
|
11020
11226
|
const content = deps.readFile(file);
|
|
11021
11227
|
if (content !== null) {
|
|
@@ -11032,7 +11238,7 @@ async function resolveWorkspaceEnv(cwd, overrides = {}) {
|
|
|
11032
11238
|
}
|
|
11033
11239
|
var evaluating = /* @__PURE__ */ new Map();
|
|
11034
11240
|
async function workspaceEnvOf(cwd) {
|
|
11035
|
-
const key =
|
|
11241
|
+
const key = path18.resolve(cwd);
|
|
11036
11242
|
let running = evaluating.get(key);
|
|
11037
11243
|
if (!running) {
|
|
11038
11244
|
running = resolveWorkspaceEnv(cwd).then((result) => result.env).finally(() => evaluating.delete(key));
|
|
@@ -11041,11 +11247,11 @@ async function workspaceEnvOf(cwd) {
|
|
|
11041
11247
|
return { ...await running };
|
|
11042
11248
|
}
|
|
11043
11249
|
function checkoutDirOf(cwd, deps) {
|
|
11044
|
-
const parts =
|
|
11250
|
+
const parts = path18.resolve(cwd).split(path18.sep);
|
|
11045
11251
|
const at = parts.findIndex((part, i) => part === STATE_DIR && parts[i + 1] === "worktrees");
|
|
11046
11252
|
if (at < 0 || parts.length < at + 4) return cwd;
|
|
11047
|
-
const root = parts.slice(0, at).join(
|
|
11048
|
-
const mapped =
|
|
11253
|
+
const root = parts.slice(0, at).join(path18.sep) || path18.sep;
|
|
11254
|
+
const mapped = path18.join(root, ...parts.slice(at + 4));
|
|
11049
11255
|
return deps.isDirectory(mapped) ? mapped : root;
|
|
11050
11256
|
}
|
|
11051
11257
|
async function fromDirenv(cwd, deps) {
|
|
@@ -11075,18 +11281,18 @@ async function fromDirenv(cwd, deps) {
|
|
|
11075
11281
|
return { env, blocked: null };
|
|
11076
11282
|
}
|
|
11077
11283
|
function findUp(start, relative17, deps) {
|
|
11078
|
-
let dir =
|
|
11284
|
+
let dir = path18.resolve(start);
|
|
11079
11285
|
for (; ; ) {
|
|
11080
|
-
const candidate =
|
|
11286
|
+
const candidate = path18.join(dir, relative17);
|
|
11081
11287
|
if (deps.readFile(candidate) !== null) return candidate;
|
|
11082
|
-
const parent =
|
|
11288
|
+
const parent = path18.dirname(dir);
|
|
11083
11289
|
if (parent === dir) return null;
|
|
11084
11290
|
dir = parent;
|
|
11085
11291
|
}
|
|
11086
11292
|
}
|
|
11087
11293
|
async function isTracked(file, deps) {
|
|
11088
11294
|
try {
|
|
11089
|
-
const out = await deps.run("git", ["ls-files", "--error-unmatch",
|
|
11295
|
+
const out = await deps.run("git", ["ls-files", "--error-unmatch", path18.basename(file)], { cwd: path18.dirname(file), env: deps.baseEnv });
|
|
11090
11296
|
return out.code === 0;
|
|
11091
11297
|
} catch {
|
|
11092
11298
|
return false;
|
|
@@ -11096,7 +11302,7 @@ function parseEnvFile(content, context, home) {
|
|
|
11096
11302
|
const env = {};
|
|
11097
11303
|
const lookup2 = (name) => env[name] ?? context[name] ?? "";
|
|
11098
11304
|
const expand = (value) => {
|
|
11099
|
-
const tilde = value.startsWith("~/") ?
|
|
11305
|
+
const tilde = value.startsWith("~/") ? path18.join(home, value.slice(2)) : value;
|
|
11100
11306
|
return tilde.replace(/\$\{([A-Za-z_][A-Za-z0-9_]*)\}|\$([A-Za-z_][A-Za-z0-9_]*)/g, (_, braced, bare) => lookup2(braced ?? bare));
|
|
11101
11307
|
};
|
|
11102
11308
|
for (const raw of content.split(/\r?\n/)) {
|
|
@@ -11366,7 +11572,7 @@ function readOnlySessionRules(site) {
|
|
|
11366
11572
|
real = realpathSync4.native(root);
|
|
11367
11573
|
} catch {
|
|
11368
11574
|
}
|
|
11369
|
-
for (const
|
|
11575
|
+
for (const path47 of /* @__PURE__ */ new Set([root, real])) rules.push({ action: "external_directory", resource: `${path47.replace(/[\\/]$/, "")}/*`, effect: "allow" });
|
|
11370
11576
|
}
|
|
11371
11577
|
return rules;
|
|
11372
11578
|
}
|
|
@@ -11492,6 +11698,14 @@ function runnerEnv(resolvedPath, workspace = {}, launch = {}, drop) {
|
|
|
11492
11698
|
// src/services/harness/runnerProcess.ts
|
|
11493
11699
|
var STDERR_TAIL_CHARS = 4e3;
|
|
11494
11700
|
var CLOSE_GRACE_MS = 2e3;
|
|
11701
|
+
function releasePipes(child) {
|
|
11702
|
+
for (const stream of [child.stdin, child.stdout, child.stderr]) {
|
|
11703
|
+
if (stream instanceof Socket) stream.unref();
|
|
11704
|
+
}
|
|
11705
|
+
for (const stream of [child.stdout, child.stderr]) {
|
|
11706
|
+
if (stream instanceof Socket) stream.resume();
|
|
11707
|
+
}
|
|
11708
|
+
}
|
|
11495
11709
|
var RunnerProcess = class {
|
|
11496
11710
|
constructor(deps) {
|
|
11497
11711
|
this.deps = deps;
|
|
@@ -11564,6 +11778,7 @@ var RunnerProcess = class {
|
|
|
11564
11778
|
const grace = setTimeout(() => {
|
|
11565
11779
|
for (const flush of [...this.stdoutEnds]) flush();
|
|
11566
11780
|
this.end({ code, signal });
|
|
11781
|
+
releasePipes(child);
|
|
11567
11782
|
}, CLOSE_GRACE_MS);
|
|
11568
11783
|
grace.unref?.();
|
|
11569
11784
|
void this.ended.then(() => clearTimeout(grace));
|
|
@@ -11662,11 +11877,11 @@ ${tail}` : ""}`;
|
|
|
11662
11877
|
|
|
11663
11878
|
// src/services/sandbox/nativeShellLauncher.ts
|
|
11664
11879
|
import { chmodSync, mkdtempSync as mkdtempSync3, realpathSync as realpathSync5, rmSync as rmSync3, writeFileSync as writeFileSync3 } from "fs";
|
|
11665
|
-
import * as
|
|
11666
|
-
import * as
|
|
11880
|
+
import * as os8 from "os";
|
|
11881
|
+
import * as path19 from "path";
|
|
11667
11882
|
function nativeShellInvocation(platform, env = process.env) {
|
|
11668
11883
|
const shell = platform === "darwin" ? "/bin/zsh" : env.SHELL;
|
|
11669
|
-
return { shell: shell &&
|
|
11884
|
+
return { shell: shell && path19.posix.isAbsolute(shell) ? shell : "/bin/sh", args: ["-c"] };
|
|
11670
11885
|
}
|
|
11671
11886
|
var live = /* @__PURE__ */ new Set();
|
|
11672
11887
|
var exitHook = false;
|
|
@@ -11698,10 +11913,10 @@ function launcherScript(launch) {
|
|
|
11698
11913
|
}
|
|
11699
11914
|
async function createNativeShellLauncher(opts) {
|
|
11700
11915
|
const invocation = nativeShellInvocation(opts.platform ?? process.platform, opts.env);
|
|
11701
|
-
const root = realpathSync5(mkdtempSync3(
|
|
11916
|
+
const root = realpathSync5(mkdtempSync3(path19.join(os8.tmpdir(), "ordewell-native-shell-")));
|
|
11702
11917
|
let shortTmp = null;
|
|
11703
11918
|
try {
|
|
11704
|
-
shortTmp = realpathSync5(mkdtempSync3(
|
|
11919
|
+
shortTmp = realpathSync5(mkdtempSync3(path19.join("/tmp", "ow-")));
|
|
11705
11920
|
const tmpdir7 = shortTmp;
|
|
11706
11921
|
const launch = await opts.sandbox.launchNativeShell({
|
|
11707
11922
|
workspace: opts.workspace,
|
|
@@ -11710,7 +11925,7 @@ async function createNativeShellLauncher(opts) {
|
|
|
11710
11925
|
shellArgs: invocation.args,
|
|
11711
11926
|
...opts.env ? { env: opts.env } : {}
|
|
11712
11927
|
});
|
|
11713
|
-
const prefix =
|
|
11928
|
+
const prefix = path19.join(root, "run");
|
|
11714
11929
|
writeFileSync3(prefix, launcherScript(launch), { mode: 448 });
|
|
11715
11930
|
chmodSync(prefix, 448);
|
|
11716
11931
|
keep(root);
|
|
@@ -12226,9 +12441,8 @@ var ReplyRuns = class {
|
|
|
12226
12441
|
};
|
|
12227
12442
|
|
|
12228
12443
|
// src/services/harness/claudeRepoConfig.ts
|
|
12229
|
-
import * as
|
|
12230
|
-
|
|
12231
|
-
var MEMORY_FILES = ["CLAUDE.md", path19.join(".claude", "CLAUDE.md"), "CLAUDE.local.md"];
|
|
12444
|
+
import * as path20 from "path";
|
|
12445
|
+
var MEMORY_FILES = ["CLAUDE.md", path20.join(".claude", "CLAUDE.md"), "CLAUDE.local.md"];
|
|
12232
12446
|
var MAX_IMPORT_DEPTH = 5;
|
|
12233
12447
|
function importsOf(text4) {
|
|
12234
12448
|
const prose = text4.replace(/```[\s\S]*?```/g, "").replace(/`[^`\n]*`/g, "");
|
|
@@ -12239,11 +12453,11 @@ function withImports(workspace, file, depth, seen, into) {
|
|
|
12239
12453
|
seen.add(file);
|
|
12240
12454
|
const text4 = readText(file);
|
|
12241
12455
|
if (text4 === void 0) return;
|
|
12242
|
-
into.push({ path:
|
|
12456
|
+
into.push({ path: path20.relative(workspace, file), text: text4 });
|
|
12243
12457
|
if (depth >= MAX_IMPORT_DEPTH) return;
|
|
12244
12458
|
for (const target of importsOf(text4)) {
|
|
12245
12459
|
if (target.startsWith("~")) continue;
|
|
12246
|
-
const abs =
|
|
12460
|
+
const abs = path20.resolve(path20.dirname(file), target);
|
|
12247
12461
|
if (insideOnDisk(workspace, abs)) withImports(workspace, abs, depth + 1, seen, into);
|
|
12248
12462
|
}
|
|
12249
12463
|
}
|
|
@@ -12264,12 +12478,12 @@ var CLAUDE_REPO_CONFIG = {
|
|
|
12264
12478
|
instructions(workspace) {
|
|
12265
12479
|
const files = [];
|
|
12266
12480
|
const seen = /* @__PURE__ */ new Set();
|
|
12267
|
-
for (const name of MEMORY_FILES) withImports(workspace,
|
|
12481
|
+
for (const name of MEMORY_FILES) withImports(workspace, path20.join(workspace, name), 0, seen, files);
|
|
12268
12482
|
return files;
|
|
12269
12483
|
},
|
|
12270
12484
|
projectServers(workspace) {
|
|
12271
12485
|
const source = ".mcp.json";
|
|
12272
|
-
const text4 = readText(
|
|
12486
|
+
const text4 = readText(path20.join(workspace, source));
|
|
12273
12487
|
if (text4 === void 0) return NO_SERVERS;
|
|
12274
12488
|
let parsed;
|
|
12275
12489
|
try {
|
|
@@ -12287,13 +12501,14 @@ var CLAUDE_REPO_CONFIG = {
|
|
|
12287
12501
|
return found;
|
|
12288
12502
|
}
|
|
12289
12503
|
};
|
|
12290
|
-
function claudeLocalServers(
|
|
12291
|
-
const
|
|
12292
|
-
const
|
|
12504
|
+
function claudeLocalServers(env, workspace) {
|
|
12505
|
+
const configDir2 = env.CLAUDE_CONFIG_DIR?.trim();
|
|
12506
|
+
const file = expandRunnerHome(configDir2 ? path20.join(configDir2, ".claude.json") : "~/.claude.json", env);
|
|
12507
|
+
const text4 = file === null ? void 0 : readText(file);
|
|
12293
12508
|
if (text4 === void 0) return {};
|
|
12294
12509
|
try {
|
|
12295
12510
|
const parsed = JSON.parse(text4);
|
|
12296
|
-
const project = isRecord(parsed) && isRecord(parsed.projects) ? parsed.projects[
|
|
12511
|
+
const project = isRecord(parsed) && isRecord(parsed.projects) ? parsed.projects[path20.resolve(workspace)] : void 0;
|
|
12297
12512
|
return isRecord(project) && isRecord(project.mcpServers) ? project.mcpServers : {};
|
|
12298
12513
|
} catch {
|
|
12299
12514
|
return {};
|
|
@@ -12548,13 +12763,13 @@ var NO_AUTO_MEMORY = { CLAUDE_CODE_DISABLE_AUTO_MEMORY: "1" };
|
|
|
12548
12763
|
function inSavedOutput(abs, configDir2, sessionId) {
|
|
12549
12764
|
let projects;
|
|
12550
12765
|
try {
|
|
12551
|
-
projects = realpathSync7.native(
|
|
12766
|
+
projects = realpathSync7.native(path21.join(configDir2, "projects"));
|
|
12552
12767
|
} catch {
|
|
12553
12768
|
return false;
|
|
12554
12769
|
}
|
|
12555
|
-
const rel =
|
|
12556
|
-
if (rel.startsWith("..") ||
|
|
12557
|
-
const [project, session, folder] = rel.split(
|
|
12770
|
+
const rel = path21.relative(projects, abs);
|
|
12771
|
+
if (rel.startsWith("..") || path21.isAbsolute(rel)) return false;
|
|
12772
|
+
const [project, session, folder] = rel.split(path21.sep);
|
|
12558
12773
|
return Boolean(project) && session === sessionId && folder === "tool-results";
|
|
12559
12774
|
}
|
|
12560
12775
|
var PATH_TOOLS = {
|
|
@@ -12792,7 +13007,7 @@ var ClaudeCodeAdapter = class extends StdioAgentAdapter {
|
|
|
12792
13007
|
"--append-system-prompt",
|
|
12793
13008
|
opts.systemPrompt
|
|
12794
13009
|
];
|
|
12795
|
-
if (opts.mcp) args.push(...this.ordewellServerArgs(opts.mcp, "planner", this.plannerServers(opts, workspace)));
|
|
13010
|
+
if (opts.mcp) args.push(...this.ordewellServerArgs(opts.mcp, "planner", this.plannerServers(opts, { ...process.env, ...workspace })));
|
|
12796
13011
|
if (opts.model) args.push("--model", opts.model);
|
|
12797
13012
|
if (opts.effort && opts.effort !== "adaptive") args.push("--effort", opts.effort);
|
|
12798
13013
|
if (opts.resumeSessionId) {
|
|
@@ -12800,7 +13015,8 @@ var ClaudeCodeAdapter = class extends StdioAgentAdapter {
|
|
|
12800
13015
|
this.countFrom(opts.resumeSessionId, opts.resumeCost);
|
|
12801
13016
|
this.pendingResume = opts.resumeSessionId;
|
|
12802
13017
|
}
|
|
12803
|
-
|
|
13018
|
+
const env = { ...process.env, ...workspace };
|
|
13019
|
+
this.configDir = expandRunnerHome(claudeConfigDir(env), env);
|
|
12804
13020
|
return {
|
|
12805
13021
|
command: "claude",
|
|
12806
13022
|
args,
|
|
@@ -12870,13 +13086,12 @@ var ClaudeCodeAdapter = class extends StdioAgentAdapter {
|
|
|
12870
13086
|
/**
|
|
12871
13087
|
* What user-only settings stop the CLI loading that is not the
|
|
12872
13088
|
* repository's to choose (ADR-0028): the user's own local-scope servers,
|
|
12873
|
-
*
|
|
12874
|
-
* servers approved for it.
|
|
13089
|
+
* from the account in the environment the planner runs with, and the
|
|
13090
|
+
* project servers approved for it.
|
|
12875
13091
|
*/
|
|
12876
|
-
plannerServers(opts,
|
|
12877
|
-
const configDir2 = workspace.CLAUDE_CONFIG_DIR ?? process.env.CLAUDE_CONFIG_DIR;
|
|
13092
|
+
plannerServers(opts, env) {
|
|
12878
13093
|
return {
|
|
12879
|
-
...claudeLocalServers(
|
|
13094
|
+
...claudeLocalServers(env, opts.cwd),
|
|
12880
13095
|
...Object.fromEntries((opts.projectServers ?? []).map((server4) => [server4.name, server4.definition]))
|
|
12881
13096
|
};
|
|
12882
13097
|
}
|
|
@@ -13640,8 +13855,8 @@ function runProbe(deps, cwd, env, flags) {
|
|
|
13640
13855
|
}
|
|
13641
13856
|
|
|
13642
13857
|
// src/services/harness/codexRepoConfig.ts
|
|
13643
|
-
import * as
|
|
13644
|
-
var CONFIG_FILE =
|
|
13858
|
+
import * as path22 from "path";
|
|
13859
|
+
var CONFIG_FILE = path22.join(".codex", "config.toml");
|
|
13645
13860
|
function stringsOf(value) {
|
|
13646
13861
|
return Array.isArray(value) ? value.filter((v) => typeof v === "string") : [];
|
|
13647
13862
|
}
|
|
@@ -13667,9 +13882,9 @@ var CODEX_REPO_CONFIG = {
|
|
|
13667
13882
|
const files = [];
|
|
13668
13883
|
for (const dir of repoLevels(workspace)) {
|
|
13669
13884
|
for (const name of ["AGENTS.override.md", "AGENTS.md"]) {
|
|
13670
|
-
const text4 = readText(
|
|
13885
|
+
const text4 = readText(path22.join(dir, name));
|
|
13671
13886
|
if (text4 === void 0) continue;
|
|
13672
|
-
files.push({ path:
|
|
13887
|
+
files.push({ path: path22.relative(workspace, path22.join(dir, name)) || name, text: text4 });
|
|
13673
13888
|
break;
|
|
13674
13889
|
}
|
|
13675
13890
|
}
|
|
@@ -13680,10 +13895,10 @@ var CODEX_REPO_CONFIG = {
|
|
|
13680
13895
|
const byName = /* @__PURE__ */ new Map();
|
|
13681
13896
|
const unreadable = [];
|
|
13682
13897
|
for (const dir of repoLevels(workspace)) {
|
|
13683
|
-
const file =
|
|
13898
|
+
const file = path22.join(dir, CONFIG_FILE);
|
|
13684
13899
|
const text4 = readText(file);
|
|
13685
13900
|
if (text4 === void 0) continue;
|
|
13686
|
-
const source =
|
|
13901
|
+
const source = path22.relative(workspace, file);
|
|
13687
13902
|
let parsed;
|
|
13688
13903
|
try {
|
|
13689
13904
|
parsed = parseToml(text4);
|
|
@@ -13703,9 +13918,9 @@ var CODEX_REPO_CONFIG = {
|
|
|
13703
13918
|
};
|
|
13704
13919
|
function untrustedProjects(workspace) {
|
|
13705
13920
|
const projects = {};
|
|
13706
|
-
for (let dir =
|
|
13921
|
+
for (let dir = path22.resolve(workspace); ; dir = path22.dirname(dir)) {
|
|
13707
13922
|
projects[dir] = { trust_level: "untrusted" };
|
|
13708
|
-
if (
|
|
13923
|
+
if (path22.dirname(dir) === dir) return projects;
|
|
13709
13924
|
}
|
|
13710
13925
|
}
|
|
13711
13926
|
|
|
@@ -14826,7 +15041,7 @@ ${this.exitMessage()}`);
|
|
|
14826
15041
|
case "webSearch":
|
|
14827
15042
|
return;
|
|
14828
15043
|
case "fileChange": {
|
|
14829
|
-
const paths = (item.changes ?? []).map((change) => change.path).filter((
|
|
15044
|
+
const paths = (item.changes ?? []).map((change) => change.path).filter((path47) => !!path47);
|
|
14830
15045
|
this.fileChangePaths.set(item.id, paths);
|
|
14831
15046
|
if (this.startOpts?.kind === "task") {
|
|
14832
15047
|
for (const change of item.changes ?? []) this.announceFileChange(item.id, change, emit, subagentId);
|
|
@@ -14929,20 +15144,20 @@ ${this.exitMessage()}`);
|
|
|
14929
15144
|
announceFileChange(itemId, change, emit, subagentId) {
|
|
14930
15145
|
const rows = this.fileChangeRows.get(itemId) ?? /* @__PURE__ */ new Map();
|
|
14931
15146
|
this.fileChangeRows.set(itemId, rows);
|
|
14932
|
-
const
|
|
14933
|
-
const known = rows.get(
|
|
15147
|
+
const path47 = this.shownChangePath(change);
|
|
15148
|
+
const known = rows.get(path47);
|
|
14934
15149
|
if (known) return known;
|
|
14935
|
-
const row = rows.size === 0 ? itemId : `${itemId}:${
|
|
14936
|
-
rows.set(
|
|
14937
|
-
emit({ type: "tool_call", id: row, name: changeName(change), args: { path:
|
|
15150
|
+
const row = rows.size === 0 ? itemId : `${itemId}:${path47}`;
|
|
15151
|
+
rows.set(path47, row);
|
|
15152
|
+
emit({ type: "tool_call", id: row, name: changeName(change), args: { path: path47 }, ...subagentId ? { subagentId } : {} });
|
|
14938
15153
|
return row;
|
|
14939
15154
|
}
|
|
14940
15155
|
/** Relative to the task's worktree, where every path but a stray one lies; a move names both ends. */
|
|
14941
15156
|
shownChangePath(change) {
|
|
14942
|
-
const shown = (
|
|
15157
|
+
const shown = (path47) => {
|
|
14943
15158
|
const cwd = this.startOpts?.cwd;
|
|
14944
|
-
const rel = cwd ? relative8(cwd,
|
|
14945
|
-
return rel && rel !== ".." && !rel.startsWith(`..${sep3}`) && !isAbsolute6(rel) ? rel :
|
|
15159
|
+
const rel = cwd ? relative8(cwd, path47) : path47;
|
|
15160
|
+
return rel && rel !== ".." && !rel.startsWith(`..${sep3}`) && !isAbsolute6(rel) ? rel : path47;
|
|
14946
15161
|
};
|
|
14947
15162
|
const from = shown(change.path ?? "");
|
|
14948
15163
|
return change.kind?.move_path ? `${from} \u2192 ${shown(change.kind.move_path)}` : from;
|
|
@@ -14993,7 +15208,7 @@ import { realpathSync as realpathSync8 } from "fs";
|
|
|
14993
15208
|
|
|
14994
15209
|
// src/services/harness/projectServers.ts
|
|
14995
15210
|
import { createHash } from "crypto";
|
|
14996
|
-
import * as
|
|
15211
|
+
import * as path23 from "path";
|
|
14997
15212
|
function canonical2(value) {
|
|
14998
15213
|
if (Array.isArray(value)) return value.map(canonical2);
|
|
14999
15214
|
if (!isRecord(value)) return value;
|
|
@@ -15004,7 +15219,7 @@ function projectServerScope(server4) {
|
|
|
15004
15219
|
return `mcp-server:${server4.name}@${hash}`;
|
|
15005
15220
|
}
|
|
15006
15221
|
var scopeName = (scope) => scope.slice(0, scope.lastIndexOf("@"));
|
|
15007
|
-
function fileProjectServerApprovals(file =
|
|
15222
|
+
function fileProjectServerApprovals(file = path23.join(globalDataDir(), "project-mcp-servers.json")) {
|
|
15008
15223
|
const load = () => {
|
|
15009
15224
|
const text4 = readText(file);
|
|
15010
15225
|
if (text4 === void 0) return {};
|
|
@@ -16167,8 +16382,8 @@ var OpenCodeAdapter = class extends ProcessLifecycle {
|
|
|
16167
16382
|
this.v2 = new OpenCodeV2({
|
|
16168
16383
|
version,
|
|
16169
16384
|
nativeShell: this.nativeShell?.prefix ?? null,
|
|
16170
|
-
request: (method,
|
|
16171
|
-
json: (method,
|
|
16385
|
+
request: (method, path47, body, signal, timeoutMs) => this.request(method, path47, body, signal, timeoutMs),
|
|
16386
|
+
json: (method, path47, body, signal, timeoutMs) => this.json(method, path47, body, signal, timeoutMs),
|
|
16172
16387
|
openEvents: (signal) => this.deps.fetch(`${this.baseUrl}/api/event`, { signal, headers: this.headers() }),
|
|
16173
16388
|
processEnded: this.processEnded,
|
|
16174
16389
|
isExited: () => this.exited,
|
|
@@ -16805,15 +17020,15 @@ var OpenCodeAdapter = class extends ProcessLifecycle {
|
|
|
16805
17020
|
onActivity
|
|
16806
17021
|
);
|
|
16807
17022
|
}
|
|
16808
|
-
async json(method,
|
|
16809
|
-
const response = await this.request(method,
|
|
16810
|
-
if (!response.ok) throw new OpenCodeHttpError(`${method} ${
|
|
17023
|
+
async json(method, path47, body, signal, timeoutMs) {
|
|
17024
|
+
const response = await this.request(method, path47, body, signal, timeoutMs);
|
|
17025
|
+
if (!response.ok) throw new OpenCodeHttpError(`${method} ${path47} failed: ${response.status} ${response.statusText}`);
|
|
16811
17026
|
return await response.json().catch(() => null);
|
|
16812
17027
|
}
|
|
16813
17028
|
/** `timeoutMs` null is for a turn's own POST, which the server answers when the turn is done. */
|
|
16814
|
-
request(method,
|
|
17029
|
+
request(method, path47, body, signal, timeoutMs = REQUEST_TIMEOUT_MS) {
|
|
16815
17030
|
const deadline = withDeadline(signal, timeoutMs);
|
|
16816
|
-
return this.deps.fetch(`${this.baseUrl}${
|
|
17031
|
+
return this.deps.fetch(`${this.baseUrl}${path47}`, {
|
|
16817
17032
|
method,
|
|
16818
17033
|
headers: this.headers({ "content-type": "application/json" }),
|
|
16819
17034
|
...body === void 0 ? {} : { body: JSON.stringify(body) },
|
|
@@ -16874,8 +17089,8 @@ var OpenCodeAdapter = class extends ProcessLifecycle {
|
|
|
16874
17089
|
};
|
|
16875
17090
|
|
|
16876
17091
|
// src/services/harness/openCodeRepoConfig.ts
|
|
16877
|
-
import * as
|
|
16878
|
-
var CONFIG_FILES = ["opencode.json", "opencode.jsonc",
|
|
17092
|
+
import * as path24 from "path";
|
|
17093
|
+
var CONFIG_FILES = ["opencode.json", "opencode.jsonc", path24.join(".opencode", "opencode.json"), path24.join(".opencode", "opencode.jsonc")];
|
|
16879
17094
|
var INSTRUCTION_NAMES = ["AGENTS.md", "CLAUDE.md", "CONTEXT.md"];
|
|
16880
17095
|
function server3(name, entry, source) {
|
|
16881
17096
|
if (!isRecord(entry)) return "not an object";
|
|
@@ -16894,8 +17109,8 @@ var OPENCODE_REPO_CONFIG = {
|
|
|
16894
17109
|
const levels = repoLevels(workspace);
|
|
16895
17110
|
for (const name of INSTRUCTION_NAMES) {
|
|
16896
17111
|
const files = levels.flatMap((dir) => {
|
|
16897
|
-
const text4 = readText(
|
|
16898
|
-
return text4 === void 0 ? [] : [{ path:
|
|
17112
|
+
const text4 = readText(path24.join(dir, name));
|
|
17113
|
+
return text4 === void 0 ? [] : [{ path: path24.relative(workspace, path24.join(dir, name)) || name, text: text4 }];
|
|
16899
17114
|
});
|
|
16900
17115
|
if (files.length > 0) return files;
|
|
16901
17116
|
}
|
|
@@ -16907,10 +17122,10 @@ var OPENCODE_REPO_CONFIG = {
|
|
|
16907
17122
|
const unreadable = [];
|
|
16908
17123
|
for (const dir of repoLevels(workspace)) {
|
|
16909
17124
|
for (const name of CONFIG_FILES) {
|
|
16910
|
-
const file =
|
|
17125
|
+
const file = path24.join(dir, name);
|
|
16911
17126
|
const text4 = readText(file);
|
|
16912
17127
|
if (text4 === void 0) continue;
|
|
16913
|
-
const source =
|
|
17128
|
+
const source = path24.relative(workspace, file);
|
|
16914
17129
|
let parsed;
|
|
16915
17130
|
try {
|
|
16916
17131
|
parsed = parseJsonc(text4);
|
|
@@ -17099,7 +17314,7 @@ function knownModelId(value, optionIds, shortcuts) {
|
|
|
17099
17314
|
|
|
17100
17315
|
// src/services/ModelCatalog.ts
|
|
17101
17316
|
import * as fs6 from "fs";
|
|
17102
|
-
import * as
|
|
17317
|
+
import * as path25 from "path";
|
|
17103
17318
|
var CACHE_KEY = "model-catalog";
|
|
17104
17319
|
var CACHE_TTL_MS = 60 * 60 * 1e3;
|
|
17105
17320
|
function storageKey(baseUrl) {
|
|
@@ -17152,7 +17367,7 @@ var ModelCatalog = class {
|
|
|
17152
17367
|
const url = (baseUrl || "https://openrouter.ai/api/v1").replace(/\/+$/, "");
|
|
17153
17368
|
const key = storageKey(url);
|
|
17154
17369
|
const cacheDir = globalCacheDir();
|
|
17155
|
-
const cacheFile =
|
|
17370
|
+
const cacheFile = path25.join(cacheDir, `${key.replace(/[:/]/g, "_")}.json`);
|
|
17156
17371
|
if (fs6.existsSync(cacheFile)) fs6.rmSync(cacheFile);
|
|
17157
17372
|
} catch {
|
|
17158
17373
|
}
|
|
@@ -17160,7 +17375,7 @@ var ModelCatalog = class {
|
|
|
17160
17375
|
static readCache(key) {
|
|
17161
17376
|
try {
|
|
17162
17377
|
const cacheDir = globalCacheDir();
|
|
17163
|
-
const cacheFile =
|
|
17378
|
+
const cacheFile = path25.join(cacheDir, `${key.replace(/[:/]/g, "_")}.json`);
|
|
17164
17379
|
if (!fs6.existsSync(cacheFile)) return null;
|
|
17165
17380
|
const raw = fs6.readFileSync(cacheFile, "utf8");
|
|
17166
17381
|
const data = JSON.parse(raw);
|
|
@@ -17174,7 +17389,7 @@ var ModelCatalog = class {
|
|
|
17174
17389
|
try {
|
|
17175
17390
|
const cacheDir = globalCacheDir();
|
|
17176
17391
|
fs6.mkdirSync(cacheDir, { recursive: true });
|
|
17177
|
-
const cacheFile =
|
|
17392
|
+
const cacheFile = path25.join(cacheDir, `${key.replace(/[:/]/g, "_")}.json`);
|
|
17178
17393
|
fs6.writeFileSync(
|
|
17179
17394
|
cacheFile,
|
|
17180
17395
|
JSON.stringify({ models, cachedAt: Date.now() }, null, 2)
|
|
@@ -17185,15 +17400,17 @@ var ModelCatalog = class {
|
|
|
17185
17400
|
};
|
|
17186
17401
|
|
|
17187
17402
|
// src/services/ModelDiscovery.ts
|
|
17188
|
-
import * as
|
|
17403
|
+
import * as path26 from "path";
|
|
17189
17404
|
import { exec as exec2 } from "child_process";
|
|
17190
17405
|
import { promisify as promisify2 } from "util";
|
|
17191
17406
|
import * as fsSync5 from "fs";
|
|
17192
|
-
import * as osMod from "os";
|
|
17193
17407
|
|
|
17194
17408
|
// src/services/harness/appServerModels.ts
|
|
17195
17409
|
import { spawn as nodeSpawn } from "child_process";
|
|
17196
17410
|
var DEFAULT_TIMEOUT_MS3 = 2e4;
|
|
17411
|
+
function definedOnly(env) {
|
|
17412
|
+
return Object.fromEntries(Object.entries(env).filter((entry) => entry[1] !== void 0));
|
|
17413
|
+
}
|
|
17197
17414
|
function parseRpcLine(line) {
|
|
17198
17415
|
try {
|
|
17199
17416
|
const value = JSON.parse(line);
|
|
@@ -17206,10 +17423,9 @@ async function openAppServer(command, args, launch) {
|
|
|
17206
17423
|
const runner = new RunnerProcess({
|
|
17207
17424
|
spawn: nodeSpawn,
|
|
17208
17425
|
fetch: globalThis.fetch,
|
|
17209
|
-
// The cwd is no workspace
|
|
17210
|
-
//
|
|
17211
|
-
//
|
|
17212
|
-
// that can choose whose account is read.
|
|
17426
|
+
// The cwd is no workspace, so it pays no direnv round trip before a spawn
|
|
17427
|
+
// with a deadline. Discovery and the account monitor pass the workspace's
|
|
17428
|
+
// environment themselves, since that can choose whose account is read.
|
|
17213
17429
|
workspaceEnv: async () => ({}),
|
|
17214
17430
|
...launch.deps
|
|
17215
17431
|
});
|
|
@@ -17262,7 +17478,11 @@ async function openAppServer(command, args, launch) {
|
|
|
17262
17478
|
}
|
|
17263
17479
|
var APP_SERVER_CLIENT_INFO = { name: "ordewell", title: "Ordewell", version: "0.1.0" };
|
|
17264
17480
|
async function discoverAppServerModels(command, args, options = {}) {
|
|
17265
|
-
const
|
|
17481
|
+
const env = options.env;
|
|
17482
|
+
const server4 = await openAppServer(command, args, {
|
|
17483
|
+
cwd: process.cwd(),
|
|
17484
|
+
deps: { ...env ? { workspaceEnv: async () => definedOnly(env) } : {}, ...options.deps }
|
|
17485
|
+
});
|
|
17266
17486
|
if (!server4) return null;
|
|
17267
17487
|
const { rpc } = server4;
|
|
17268
17488
|
const asked = (async () => {
|
|
@@ -17289,10 +17509,10 @@ async function discoverAppServerModels(command, args, options = {}) {
|
|
|
17289
17509
|
var execAsync2 = promisify2(exec2);
|
|
17290
17510
|
var defaultExec2 = async (command, options) => {
|
|
17291
17511
|
const PATH = await augmentedPath();
|
|
17292
|
-
const { stdout } = await execAsync2(command, {
|
|
17512
|
+
const { stdout } = await execAsync2(command, { timeout: options?.timeout, maxBuffer: 32 * 1024 * 1024, env: withPath(options?.env ?? process.env, PATH) });
|
|
17293
17513
|
return { stdout: String(stdout) };
|
|
17294
17514
|
};
|
|
17295
|
-
var defaultAppServerClient = (command, args, options) => discoverAppServerModels(command, args, { timeout: options?.timeout });
|
|
17515
|
+
var defaultAppServerClient = (command, args, options) => discoverAppServerModels(command, args, { timeout: options?.timeout, env: options?.env });
|
|
17296
17516
|
function effortVariant(effort) {
|
|
17297
17517
|
return { id: effort, label: effort.charAt(0).toUpperCase() + effort.slice(1) };
|
|
17298
17518
|
}
|
|
@@ -17326,7 +17546,7 @@ function geminiDiscoveryCacheKey(apiKey) {
|
|
|
17326
17546
|
return {
|
|
17327
17547
|
key: `gemini-models:${hash}`,
|
|
17328
17548
|
dir,
|
|
17329
|
-
file:
|
|
17549
|
+
file: path26.join(dir, `gemini_models_${hash}.json`)
|
|
17330
17550
|
};
|
|
17331
17551
|
}
|
|
17332
17552
|
function readGeminiCache(apiKey) {
|
|
@@ -17413,15 +17633,22 @@ function applyVariants(models, manifest) {
|
|
|
17413
17633
|
if (!variants || variants.length === 0) return models;
|
|
17414
17634
|
return models.map((m) => m.variants.length > 0 ? m : { ...m, variants });
|
|
17415
17635
|
}
|
|
17416
|
-
function
|
|
17636
|
+
function listDir(dir) {
|
|
17637
|
+
try {
|
|
17638
|
+
return fsSync5.readdirSync(dir);
|
|
17639
|
+
} catch {
|
|
17640
|
+
return [];
|
|
17641
|
+
}
|
|
17642
|
+
}
|
|
17643
|
+
function resolveApiAuth(authMethods, env) {
|
|
17417
17644
|
for (const method of authMethods) {
|
|
17418
17645
|
try {
|
|
17419
17646
|
let token;
|
|
17420
17647
|
if (method.type === "env") {
|
|
17421
|
-
token =
|
|
17648
|
+
token = env[method.varName];
|
|
17422
17649
|
} else {
|
|
17423
|
-
const filePath = method.path.
|
|
17424
|
-
if (!fsSync5.existsSync(filePath)) continue;
|
|
17650
|
+
const filePath = expandRunnerHome(typeof method.path === "function" ? method.path(env) : method.path, env);
|
|
17651
|
+
if (!filePath || !fsSync5.existsSync(filePath)) continue;
|
|
17425
17652
|
const data = JSON.parse(fsSync5.readFileSync(filePath, "utf8"));
|
|
17426
17653
|
token = method.jsonPath.split(".").reduce((obj, key) => {
|
|
17427
17654
|
if (obj && typeof obj === "object") return obj[key];
|
|
@@ -17437,8 +17664,8 @@ function resolveApiAuth(authMethods) {
|
|
|
17437
17664
|
}
|
|
17438
17665
|
return null;
|
|
17439
17666
|
}
|
|
17440
|
-
async function discoverFromApi(manifest, config, fetchImpl) {
|
|
17441
|
-
const auth = resolveApiAuth(config.auth);
|
|
17667
|
+
async function discoverFromApi(manifest, config, env, fetchImpl) {
|
|
17668
|
+
const auth = resolveApiAuth(config.auth, env);
|
|
17442
17669
|
if (!auth) return null;
|
|
17443
17670
|
try {
|
|
17444
17671
|
const headers = { ...config.headers, [auth.header]: auth.value };
|
|
@@ -17454,7 +17681,7 @@ async function discoverFromApi(manifest, config, fetchImpl) {
|
|
|
17454
17681
|
function wasKilled(err) {
|
|
17455
17682
|
return !!err && typeof err === "object" && err.killed === true;
|
|
17456
17683
|
}
|
|
17457
|
-
async function discoverFromCommand(manifest, execImpl) {
|
|
17684
|
+
async function discoverFromCommand(manifest, execImpl, env) {
|
|
17458
17685
|
const discovery = manifest.modelDiscovery;
|
|
17459
17686
|
const commands = discovery.discoveryCommands ?? [
|
|
17460
17687
|
{ command: discovery.command, args: discovery.args || [], parser: discovery.parser }
|
|
@@ -17463,7 +17690,7 @@ async function discoverFromCommand(manifest, execImpl) {
|
|
|
17463
17690
|
let timedOut2 = false;
|
|
17464
17691
|
for (const cmd of commands) {
|
|
17465
17692
|
try {
|
|
17466
|
-
const { stdout } = await execImpl(`${cmd.command} ${cmd.args.join(" ")}`, { timeout: 45e3 });
|
|
17693
|
+
const { stdout } = await execImpl(`${cmd.command} ${cmd.args.join(" ")}`, { timeout: 45e3, env });
|
|
17467
17694
|
const models = parseModelOutput(stdout, cmd.parser, manifest);
|
|
17468
17695
|
if (models.length > 0) return { models: applyVariants(mergeCanonicalAliases(models, manifest), manifest), timedOut: timedOut2 };
|
|
17469
17696
|
} catch (err) {
|
|
@@ -17529,21 +17756,26 @@ var ModelDiscovery = class {
|
|
|
17529
17756
|
* same background service.
|
|
17530
17757
|
*/
|
|
17531
17758
|
inflight = /* @__PURE__ */ new Map();
|
|
17759
|
+
/** Bumped by every `clear()`: a discovery begun before one answers its own lookups but fills no cache. */
|
|
17760
|
+
generation = 0;
|
|
17532
17761
|
registry;
|
|
17533
17762
|
execImpl;
|
|
17534
17763
|
fetchImpl;
|
|
17535
17764
|
appServerClient;
|
|
17536
17765
|
readFileImpl;
|
|
17537
17766
|
now;
|
|
17538
|
-
|
|
17767
|
+
env;
|
|
17768
|
+
constructor(registry, execImpl = defaultExec2, fetchImpl, appServerClient = defaultAppServerClient, readFileImpl, now = Date.now, env = async () => process.env) {
|
|
17539
17769
|
this.registry = registry;
|
|
17540
17770
|
this.execImpl = execImpl;
|
|
17541
17771
|
this.fetchImpl = fetchImpl;
|
|
17542
17772
|
this.appServerClient = appServerClient;
|
|
17543
17773
|
this.now = now;
|
|
17774
|
+
this.env = env;
|
|
17544
17775
|
this.readFileImpl = readFileImpl ?? ((filePath) => {
|
|
17545
17776
|
try {
|
|
17546
|
-
|
|
17777
|
+
const file = expandRunnerHome(filePath);
|
|
17778
|
+
return file ? fsSync5.readFileSync(file, "utf8") : null;
|
|
17547
17779
|
} catch {
|
|
17548
17780
|
return null;
|
|
17549
17781
|
}
|
|
@@ -17555,16 +17787,17 @@ var ModelDiscovery = class {
|
|
|
17555
17787
|
* null when both fail so `discover()` falls through to the remaining
|
|
17556
17788
|
* methods.
|
|
17557
17789
|
*/
|
|
17558
|
-
async discoverFromAppServer(manifest) {
|
|
17790
|
+
async discoverFromAppServer(manifest, env) {
|
|
17559
17791
|
const config = manifest.modelDiscovery.appServer;
|
|
17560
17792
|
if (!config) return null;
|
|
17561
|
-
const raw = await this.appServerClient(config.command, config.args);
|
|
17793
|
+
const raw = await this.appServerClient(config.command, config.args, { env });
|
|
17562
17794
|
if (raw) {
|
|
17563
17795
|
const models = parseAppServerModels(raw);
|
|
17564
17796
|
if (models.length > 0) return models;
|
|
17565
17797
|
}
|
|
17566
|
-
|
|
17567
|
-
|
|
17798
|
+
const cacheFile = config.cacheFile && expandRunnerHome(typeof config.cacheFile === "function" ? config.cacheFile(env) : config.cacheFile, env);
|
|
17799
|
+
if (cacheFile) {
|
|
17800
|
+
const content = this.readFileImpl(cacheFile);
|
|
17568
17801
|
if (content) {
|
|
17569
17802
|
const models = parseCodexModelsCache(content);
|
|
17570
17803
|
if (models.length > 0) return models;
|
|
@@ -17602,37 +17835,60 @@ var ModelDiscovery = class {
|
|
|
17602
17835
|
if (!manifest) {
|
|
17603
17836
|
return [];
|
|
17604
17837
|
}
|
|
17605
|
-
|
|
17838
|
+
const generation2 = this.generation;
|
|
17839
|
+
const env = await this.env().catch(() => process.env);
|
|
17840
|
+
const found = await this.discoverFromSources(manifest, env);
|
|
17841
|
+
const models = this.withSettingsModels(found.models, manifest, env);
|
|
17842
|
+
if (generation2 !== this.generation) return models;
|
|
17843
|
+
if (found.cacheable && models.length > 0) this.cache.set(runner, models);
|
|
17844
|
+
else this.misses.set(runner, { models, at: this.now() });
|
|
17845
|
+
return models;
|
|
17846
|
+
}
|
|
17847
|
+
async discoverFromSources(manifest, env) {
|
|
17606
17848
|
let cacheable = true;
|
|
17607
17849
|
const discovery = manifest.modelDiscovery;
|
|
17608
17850
|
if (discovery.appServer) {
|
|
17609
|
-
const appServerModels = await this.discoverFromAppServer(manifest);
|
|
17610
|
-
if (appServerModels && appServerModels.length > 0) {
|
|
17611
|
-
this.cache.set(runner, appServerModels);
|
|
17612
|
-
return appServerModels;
|
|
17613
|
-
}
|
|
17851
|
+
const appServerModels = await this.discoverFromAppServer(manifest, env);
|
|
17852
|
+
if (appServerModels && appServerModels.length > 0) return { models: appServerModels, cacheable: true };
|
|
17614
17853
|
cacheable = false;
|
|
17615
17854
|
}
|
|
17616
17855
|
if (discovery.apiDiscovery) {
|
|
17617
|
-
const apiModels = await discoverFromApi(manifest, discovery.apiDiscovery, this.fetchImpl);
|
|
17856
|
+
const apiModels = await discoverFromApi(manifest, discovery.apiDiscovery, env, this.fetchImpl);
|
|
17618
17857
|
if (apiModels && apiModels.length > 0) {
|
|
17619
|
-
models
|
|
17620
|
-
if (cacheable && models.length > 0) this.cache.set(runner, models);
|
|
17621
|
-
return models;
|
|
17858
|
+
return { models: applyVariants(mergeCanonicalAliases(apiModels, manifest), manifest), cacheable };
|
|
17622
17859
|
}
|
|
17623
17860
|
}
|
|
17624
17861
|
if (discovery.method === "command" && (discovery.command || discovery.discoveryCommands?.length)) {
|
|
17625
|
-
const result = await discoverFromCommand(manifest, this.execImpl);
|
|
17626
|
-
models
|
|
17627
|
-
cacheable = !result.fromFallback;
|
|
17628
|
-
} else {
|
|
17629
|
-
models = buildFallbackModels(manifest);
|
|
17862
|
+
const result = await discoverFromCommand(manifest, this.execImpl, env);
|
|
17863
|
+
return { models: result.models, cacheable: !result.fromFallback };
|
|
17630
17864
|
}
|
|
17631
|
-
|
|
17632
|
-
|
|
17633
|
-
|
|
17865
|
+
return { models: buildFallbackModels(manifest), cacheable };
|
|
17866
|
+
}
|
|
17867
|
+
/**
|
|
17868
|
+
* Append the models registered in the runner's own settings. Read on every
|
|
17869
|
+
* discovery rather than cached apart, so a refresh picks up an edit;
|
|
17870
|
+
* whatever the sources already offer keeps its entry and metadata.
|
|
17871
|
+
*/
|
|
17872
|
+
withSettingsModels(models, manifest, env) {
|
|
17873
|
+
const read2 = manifest.modelDiscovery.settingsModels;
|
|
17874
|
+
if (!read2) return models;
|
|
17875
|
+
const files = {
|
|
17876
|
+
read: (filePath) => {
|
|
17877
|
+
const file = expandRunnerHome(filePath, env);
|
|
17878
|
+
return file ? this.readFileImpl(file) : null;
|
|
17879
|
+
},
|
|
17880
|
+
list: (dir) => {
|
|
17881
|
+
const expanded = expandRunnerHome(dir, env);
|
|
17882
|
+
return expanded ? listDir(expanded) : [];
|
|
17883
|
+
},
|
|
17884
|
+
env
|
|
17885
|
+
};
|
|
17886
|
+
const seen = new Set(models.map((m) => m.modelId));
|
|
17887
|
+
const added = read2(files).filter((m) => !seen.has(m.modelId));
|
|
17888
|
+
return added.length > 0 ? [...models, ...applyVariants(added, manifest)] : models;
|
|
17634
17889
|
}
|
|
17635
17890
|
clear() {
|
|
17891
|
+
this.generation++;
|
|
17636
17892
|
this.cache.clear();
|
|
17637
17893
|
this.misses.clear();
|
|
17638
17894
|
this.inflight.clear();
|
|
@@ -17798,7 +18054,7 @@ function parseMaxParallel(value) {
|
|
|
17798
18054
|
|
|
17799
18055
|
// src/services/SettingsService.ts
|
|
17800
18056
|
import * as fs12 from "fs";
|
|
17801
|
-
import * as
|
|
18057
|
+
import * as path33 from "path";
|
|
17802
18058
|
|
|
17803
18059
|
// src/utils/fileLock.ts
|
|
17804
18060
|
import * as fs7 from "fs";
|
|
@@ -18088,7 +18344,7 @@ function stable(value) {
|
|
|
18088
18344
|
}
|
|
18089
18345
|
|
|
18090
18346
|
// src/services/runnerExit.ts
|
|
18091
|
-
var USAGE_LIMIT_RE = /\b(?:usage|session|weekly|daily|monthly) limit\b|\brate limit (?:exceeded|reached)\b|\brate[- ]limited\b|\blimit (?:will )?reset\b|\bquota (?:exceeded|reached)\b|\btoo many requests\b/i;
|
|
18347
|
+
var USAGE_LIMIT_RE = /\b(?:usage|session|weekly|daily|monthly) limit\b|\byou['’]ve hit your (?:[\w'’-]+ ){0,3}?limit\b|\brate limit (?:exceeded|reached)\b|\brate[- ]limited\b|\blimit (?:will )?reset\b|\bquota (?:exceeded|reached)\b|\btoo many requests\b/i;
|
|
18092
18348
|
var USAGE_LIMIT_TAIL = 4096;
|
|
18093
18349
|
function classifyRunnerStop(said2) {
|
|
18094
18350
|
if (typeof said2 !== "string" && said2.usageLimit !== void 0) return said2.usageLimit ? "usage-limit" : "stopped";
|
|
@@ -18316,8 +18572,8 @@ var TurnStream = class {
|
|
|
18316
18572
|
};
|
|
18317
18573
|
|
|
18318
18574
|
// src/services/skillInvocation.ts
|
|
18319
|
-
import * as
|
|
18320
|
-
function abbreviateHome(file, home =
|
|
18575
|
+
import * as os9 from "os";
|
|
18576
|
+
function abbreviateHome(file, home = os9.homedir(), platform = process.platform) {
|
|
18321
18577
|
const win = platform === "win32";
|
|
18322
18578
|
const normal = (p) => win ? p.replace(/\\/g, "/") : p;
|
|
18323
18579
|
const shown = normal(file);
|
|
@@ -18332,13 +18588,13 @@ function modelInvocablePlannerSkills(skills) {
|
|
|
18332
18588
|
function modelInvocableTaskSkills(skills) {
|
|
18333
18589
|
return skills.filter((skill) => skill.appliesTo === "task" && skill.modelInvocable);
|
|
18334
18590
|
}
|
|
18335
|
-
function snapshotSkill(skill, invokedBy, home =
|
|
18591
|
+
function snapshotSkill(skill, invokedBy, home = os9.homedir()) {
|
|
18336
18592
|
return { invokedBy, name: skill.name, source: skill.source, path: abbreviateHome(skill.path, home), content: skill.content };
|
|
18337
18593
|
}
|
|
18338
18594
|
function attachSkill(skill, home) {
|
|
18339
18595
|
return { ...snapshotSkill(skill, "user", home), content: "", attaches: { description: skill.description } };
|
|
18340
18596
|
}
|
|
18341
|
-
function resolveSkillInvocation(text4, skillsService, home =
|
|
18597
|
+
function resolveSkillInvocation(text4, skillsService, home = os9.homedir()) {
|
|
18342
18598
|
const skills = skillTokens(text4).flatMap(({ name }) => {
|
|
18343
18599
|
const skill = skillsService.findSkill(name);
|
|
18344
18600
|
if (!skill?.userInvocable) return [];
|
|
@@ -18402,33 +18658,33 @@ ${skillBlock(entry.skill)}`, timestamp: entry.timestamp });
|
|
|
18402
18658
|
}
|
|
18403
18659
|
|
|
18404
18660
|
// src/services/taskSkills.ts
|
|
18405
|
-
import * as
|
|
18661
|
+
import * as path32 from "path";
|
|
18406
18662
|
|
|
18407
18663
|
// src/services/SkillsService.ts
|
|
18408
18664
|
import { createHash as createHash2 } from "crypto";
|
|
18409
18665
|
import * as fs11 from "fs";
|
|
18410
|
-
import * as
|
|
18666
|
+
import * as path31 from "path";
|
|
18411
18667
|
|
|
18412
18668
|
// src/services/builtinSkills.ts
|
|
18413
|
-
import * as
|
|
18669
|
+
import * as path27 from "path";
|
|
18414
18670
|
import { fileURLToPath } from "url";
|
|
18415
18671
|
function builtinSkillsDir() {
|
|
18416
|
-
const dir = typeof __dirname !== "undefined" && __dirname ? __dirname :
|
|
18672
|
+
const dir = typeof __dirname !== "undefined" && __dirname ? __dirname : path27.dirname(fileURLToPath(
|
|
18417
18673
|
// @ts-expect-error - import.meta is only valid under an ESM module setting
|
|
18418
18674
|
import.meta.url
|
|
18419
18675
|
));
|
|
18420
|
-
return
|
|
18676
|
+
return path27.join(dir, "..", "skills");
|
|
18421
18677
|
}
|
|
18422
18678
|
|
|
18423
18679
|
// src/services/repoGroup.ts
|
|
18424
18680
|
import * as fs10 from "fs";
|
|
18425
|
-
import * as
|
|
18681
|
+
import * as path30 from "path";
|
|
18426
18682
|
|
|
18427
18683
|
// src/services/worktreeBootstrap.ts
|
|
18428
18684
|
import { exec as exec3 } from "child_process";
|
|
18429
18685
|
import { promisify as promisify4 } from "util";
|
|
18430
18686
|
import * as fs9 from "fs";
|
|
18431
|
-
import * as
|
|
18687
|
+
import * as path29 from "path";
|
|
18432
18688
|
|
|
18433
18689
|
// src/services/gitExec.ts
|
|
18434
18690
|
import { execFile as execFile4 } from "child_process";
|
|
@@ -18510,7 +18766,7 @@ function subcommandOf2(args) {
|
|
|
18510
18766
|
|
|
18511
18767
|
// src/services/worktreeLink.ts
|
|
18512
18768
|
import * as fs8 from "fs";
|
|
18513
|
-
import * as
|
|
18769
|
+
import * as path28 from "path";
|
|
18514
18770
|
function linkPath(source, target, platform, hardLink = fs8.linkSync) {
|
|
18515
18771
|
const isDir = fs8.statSync(source).isDirectory();
|
|
18516
18772
|
if (platform !== "win32") {
|
|
@@ -18535,8 +18791,8 @@ function mirrorDir(source, target, opts) {
|
|
|
18535
18791
|
const mirror = (from, to, prefix, top) => {
|
|
18536
18792
|
fs8.mkdirSync(to, { recursive: true });
|
|
18537
18793
|
for (const entry of fs8.readdirSync(from, { withFileTypes: true })) {
|
|
18538
|
-
const name =
|
|
18539
|
-
const at = { from:
|
|
18794
|
+
const name = path28.join(prefix, entry.name);
|
|
18795
|
+
const at = { from: path28.join(from, entry.name), to: path28.join(to, entry.name), name };
|
|
18540
18796
|
if (entry.isSymbolicLink()) links.push(at);
|
|
18541
18797
|
else if (top && entry.isDirectory() && (entry.name.startsWith("@") || entry.name === ".bin")) mirror(at.from, at.to, name, false);
|
|
18542
18798
|
else if (linkPath(at.from, at.to, opts.platform, opts.hardLink) === "copy") copied.push(name);
|
|
@@ -18556,16 +18812,16 @@ function mirrorDir(source, target, opts) {
|
|
|
18556
18812
|
}
|
|
18557
18813
|
function relink(from, to, opts) {
|
|
18558
18814
|
const text4 = fs8.readlinkSync(from);
|
|
18559
|
-
const pointee =
|
|
18815
|
+
const pointee = path28.isAbsolute(text4) ? rebased(text4, opts) : text4;
|
|
18560
18816
|
if (opts.platform !== "win32") {
|
|
18561
|
-
if (!fs8.existsSync(
|
|
18562
|
-
fs8.symlinkSync(rebased(
|
|
18817
|
+
if (!fs8.existsSync(path28.resolve(path28.dirname(to), pointee)) && fs8.existsSync(path28.resolve(path28.dirname(from), pointee))) {
|
|
18818
|
+
fs8.symlinkSync(rebased(path28.resolve(path28.dirname(from), pointee), opts), to);
|
|
18563
18819
|
return "symlink";
|
|
18564
18820
|
}
|
|
18565
18821
|
fs8.symlinkSync(pointee, to);
|
|
18566
18822
|
return "symlink";
|
|
18567
18823
|
}
|
|
18568
|
-
const resolved = fs8.existsSync(
|
|
18824
|
+
const resolved = fs8.existsSync(path28.resolve(path28.dirname(to), pointee)) ? path28.resolve(path28.dirname(to), pointee) : rebased(path28.resolve(path28.dirname(from), pointee), opts);
|
|
18569
18825
|
try {
|
|
18570
18826
|
fs8.statSync(resolved);
|
|
18571
18827
|
} catch {
|
|
@@ -18574,9 +18830,9 @@ function relink(from, to, opts) {
|
|
|
18574
18830
|
return linkPath(resolved, to, opts.platform, opts.hardLink);
|
|
18575
18831
|
}
|
|
18576
18832
|
function rebased(pointee, opts) {
|
|
18577
|
-
const rel =
|
|
18578
|
-
const inside2 = rel !== "" && !rel.startsWith("..") && !
|
|
18579
|
-
return inside2 && !rel.split(/[\\/]/).includes("node_modules") ?
|
|
18833
|
+
const rel = path28.relative(opts.from, pointee);
|
|
18834
|
+
const inside2 = rel !== "" && !rel.startsWith("..") && !path28.isAbsolute(rel);
|
|
18835
|
+
return inside2 && !rel.split(/[\\/]/).includes("node_modules") ? path28.join(opts.to, rel) : pointee;
|
|
18580
18836
|
}
|
|
18581
18837
|
|
|
18582
18838
|
// src/services/worktreeBootstrap.ts
|
|
@@ -18593,7 +18849,7 @@ function lexists(target) {
|
|
|
18593
18849
|
return false;
|
|
18594
18850
|
}
|
|
18595
18851
|
}
|
|
18596
|
-
function
|
|
18852
|
+
function listDir2(dir) {
|
|
18597
18853
|
try {
|
|
18598
18854
|
return fs9.readdirSync(dir).sort();
|
|
18599
18855
|
} catch {
|
|
@@ -18613,18 +18869,18 @@ function matchLinks(root, patterns) {
|
|
|
18613
18869
|
}
|
|
18614
18870
|
function segmentMatches(root, base, segment) {
|
|
18615
18871
|
const under = (name) => base ? `${base}/${name}` : name;
|
|
18616
|
-
if (!/[*?]/.test(segment)) return lexists(
|
|
18872
|
+
if (!/[*?]/.test(segment)) return lexists(path29.join(root, base, segment)) ? [under(segment)] : [];
|
|
18617
18873
|
const pattern = new RegExp(`^${segment.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*").replace(/\?/g, ".")}$`);
|
|
18618
|
-
return
|
|
18874
|
+
return listDir2(path29.join(root, base)).filter((name) => !NEVER_SCANNED.has(name) && pattern.test(name)).map(under);
|
|
18619
18875
|
}
|
|
18620
18876
|
function installDirs(root) {
|
|
18621
18877
|
const packages = matchLinks(root, workspaceGlobs(root)).map((pkg) => `${pkg}/node_modules`);
|
|
18622
|
-
return ["node_modules", ...packages].filter((dir) => lexists(
|
|
18878
|
+
return ["node_modules", ...packages].filter((dir) => lexists(path29.join(root, dir)));
|
|
18623
18879
|
}
|
|
18624
18880
|
function workspaceGlobs(root) {
|
|
18625
18881
|
let manifest;
|
|
18626
18882
|
try {
|
|
18627
|
-
manifest = JSON.parse(fs9.readFileSync(
|
|
18883
|
+
manifest = JSON.parse(fs9.readFileSync(path29.join(root, "package.json"), "utf8"));
|
|
18628
18884
|
} catch {
|
|
18629
18885
|
return [];
|
|
18630
18886
|
}
|
|
@@ -18639,19 +18895,19 @@ async function bootstrap(repo, cwd, opts) {
|
|
|
18639
18895
|
const linked = [];
|
|
18640
18896
|
const copied = [];
|
|
18641
18897
|
const link = (name) => {
|
|
18642
|
-
const target =
|
|
18898
|
+
const target = path29.join(cwd, name);
|
|
18643
18899
|
if (lexists(target)) return;
|
|
18644
|
-
fs9.mkdirSync(
|
|
18645
|
-
if (linkPath(
|
|
18900
|
+
fs9.mkdirSync(path29.dirname(target), { recursive: true });
|
|
18901
|
+
if (linkPath(path29.join(repo.root, name), target, opts.platform) === "copy") copied.push(name);
|
|
18646
18902
|
linked.push(name);
|
|
18647
18903
|
};
|
|
18648
18904
|
const mirror = (name) => {
|
|
18649
|
-
const target =
|
|
18650
|
-
if (lexists(target) || !fs9.existsSync(
|
|
18651
|
-
const source =
|
|
18905
|
+
const target = path29.join(cwd, name);
|
|
18906
|
+
if (lexists(target) || !fs9.existsSync(path29.dirname(target))) return;
|
|
18907
|
+
const source = path29.join(repo.root, name);
|
|
18652
18908
|
if (!fs9.lstatSync(source).isDirectory()) return link(name);
|
|
18653
18909
|
const mirrored = mirrorDir(source, target, { platform: opts.platform, from: repo.root, to: cwd });
|
|
18654
|
-
copied.push(...mirrored.map((entry) =>
|
|
18910
|
+
copied.push(...mirrored.map((entry) => path29.join(name, entry)));
|
|
18655
18911
|
linked.push(name);
|
|
18656
18912
|
};
|
|
18657
18913
|
if (!setup) {
|
|
@@ -18686,12 +18942,12 @@ async function runSetup(command, repo, cwd, resolvePath) {
|
|
|
18686
18942
|
|
|
18687
18943
|
// src/services/repoGroup.ts
|
|
18688
18944
|
function hasGitEntry(dir) {
|
|
18689
|
-
return fs10.existsSync(
|
|
18945
|
+
return fs10.existsSync(path30.join(dir, ".git"));
|
|
18690
18946
|
}
|
|
18691
18947
|
function groupPathOf(listed3) {
|
|
18692
18948
|
const slashed = listed3.trim().replace(/\\/g, "/");
|
|
18693
|
-
if (
|
|
18694
|
-
const rel =
|
|
18949
|
+
if (path30.posix.isAbsolute(slashed) || path30.win32.isAbsolute(slashed)) return null;
|
|
18950
|
+
const rel = path30.posix.normalize(slashed).replace(/\/+$/, "");
|
|
18695
18951
|
return rel === "" || rel === "." || rel === ".." || rel.startsWith("../") ? null : rel;
|
|
18696
18952
|
}
|
|
18697
18953
|
function childDirs(dir) {
|
|
@@ -18702,14 +18958,14 @@ function childDirs(dir) {
|
|
|
18702
18958
|
}
|
|
18703
18959
|
}
|
|
18704
18960
|
function groupPaths(workspaceRoot, listed3) {
|
|
18705
|
-
if (listed3.length === 0) return childDirs(workspaceRoot).filter((name) => hasGitEntry(
|
|
18706
|
-
const paths = listed3.map(groupPathOf).filter((rel) => rel !== null && hasGitEntry(
|
|
18961
|
+
if (listed3.length === 0) return childDirs(workspaceRoot).filter((name) => hasGitEntry(path30.join(workspaceRoot, name)));
|
|
18962
|
+
const paths = listed3.map(groupPathOf).filter((rel) => rel !== null && hasGitEntry(path30.join(workspaceRoot, rel)));
|
|
18707
18963
|
return [...new Set(paths)].sort();
|
|
18708
18964
|
}
|
|
18709
18965
|
function workspaceRepoGroup(workspaceRoot, listed3) {
|
|
18710
|
-
for (let dir =
|
|
18966
|
+
for (let dir = path30.resolve(workspaceRoot); ; dir = path30.dirname(dir)) {
|
|
18711
18967
|
if (hasGitEntry(dir)) return [SELF_REPO];
|
|
18712
|
-
if (
|
|
18968
|
+
if (path30.dirname(dir) === dir) break;
|
|
18713
18969
|
}
|
|
18714
18970
|
return groupPaths(workspaceRoot, listed3);
|
|
18715
18971
|
}
|
|
@@ -18790,8 +19046,8 @@ function copyDirSync(src, dest) {
|
|
|
18790
19046
|
fs11.mkdirSync(dest, { recursive: true });
|
|
18791
19047
|
const entries = fs11.readdirSync(src, { withFileTypes: true });
|
|
18792
19048
|
for (const entry of entries) {
|
|
18793
|
-
const srcPath =
|
|
18794
|
-
const destPath =
|
|
19049
|
+
const srcPath = path31.join(src, entry.name);
|
|
19050
|
+
const destPath = path31.join(dest, entry.name);
|
|
18795
19051
|
if (entry.isDirectory()) copyDirSync(srcPath, destPath);
|
|
18796
19052
|
else fs11.copyFileSync(srcPath, destPath);
|
|
18797
19053
|
}
|
|
@@ -18815,7 +19071,7 @@ function readDir(dir, source) {
|
|
|
18815
19071
|
if (!fs11.existsSync(dir)) return { skills, invalid };
|
|
18816
19072
|
for (const entry of fs11.readdirSync(dir, { withFileTypes: true })) {
|
|
18817
19073
|
if (!entry.isDirectory()) continue;
|
|
18818
|
-
const skillFile =
|
|
19074
|
+
const skillFile = path31.join(dir, entry.name, "SKILL.md");
|
|
18819
19075
|
if (!fs11.existsSync(skillFile)) continue;
|
|
18820
19076
|
if (!isSkillName(entry.name)) {
|
|
18821
19077
|
invalid.push({ name: entry.name, path: skillFile, source, reason: INVALID_SKILL_NAME_REASON });
|
|
@@ -18834,15 +19090,15 @@ function skillFileIn(dir, name) {
|
|
|
18834
19090
|
return void 0;
|
|
18835
19091
|
}
|
|
18836
19092
|
if (!entries.some((entry) => entry.isDirectory() && entry.name === name)) return void 0;
|
|
18837
|
-
const skillFile =
|
|
19093
|
+
const skillFile = path31.join(dir, name, "SKILL.md");
|
|
18838
19094
|
return fs11.existsSync(skillFile) ? skillFile : void 0;
|
|
18839
19095
|
}
|
|
18840
19096
|
function skillsDirOf(root) {
|
|
18841
|
-
return
|
|
19097
|
+
return path31.join(root, STATE_DIR, "skills");
|
|
18842
19098
|
}
|
|
18843
19099
|
function workspaceSkillRoots(workspaceRoot, repos, checkout = workspaceRoot) {
|
|
18844
19100
|
if (repos.length === 0 || repos.includes(SELF_REPO)) return [checkout];
|
|
18845
|
-
return [workspaceRoot, ...repos.map((repo) =>
|
|
19101
|
+
return [workspaceRoot, ...repos.map((repo) => path31.join(checkout, repo))];
|
|
18846
19102
|
}
|
|
18847
19103
|
var SkillsService = class _SkillsService {
|
|
18848
19104
|
workspaceRoots;
|
|
@@ -18865,11 +19121,11 @@ var SkillsService = class _SkillsService {
|
|
|
18865
19121
|
return this.workspaceRoots.map(skillsDirOf);
|
|
18866
19122
|
}
|
|
18867
19123
|
globalDir() {
|
|
18868
|
-
return
|
|
19124
|
+
return path31.join(globalDataDir(), "skills");
|
|
18869
19125
|
}
|
|
18870
19126
|
readSeedManifest() {
|
|
18871
19127
|
try {
|
|
18872
|
-
const parsed = JSON.parse(fs11.readFileSync(
|
|
19128
|
+
const parsed = JSON.parse(fs11.readFileSync(path31.join(this.globalDir(), SEED_MANIFEST), "utf8"));
|
|
18873
19129
|
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
18874
19130
|
return Object.fromEntries(
|
|
18875
19131
|
Object.entries(parsed).filter((entry) => typeof entry[1] === "string")
|
|
@@ -18885,7 +19141,7 @@ var SkillsService = class _SkillsService {
|
|
|
18885
19141
|
manifest[name] = hash;
|
|
18886
19142
|
try {
|
|
18887
19143
|
fs11.mkdirSync(this.globalDir(), { recursive: true });
|
|
18888
|
-
fs11.writeFileSync(
|
|
19144
|
+
fs11.writeFileSync(path31.join(this.globalDir(), SEED_MANIFEST), JSON.stringify(manifest, null, 2));
|
|
18889
19145
|
} catch (err) {
|
|
18890
19146
|
console.warn(`Could not record seeded skill ${name}: ${String(err)}`);
|
|
18891
19147
|
}
|
|
@@ -18896,11 +19152,11 @@ var SkillsService = class _SkillsService {
|
|
|
18896
19152
|
return this.readSeedManifest()[name] === currentHash || (PRIOR_BUILTIN_HASHES[name] ?? []).includes(currentHash);
|
|
18897
19153
|
}
|
|
18898
19154
|
seed(name) {
|
|
18899
|
-
const dest =
|
|
19155
|
+
const dest = path31.join(this.globalDir(), name);
|
|
18900
19156
|
const exists = fs11.existsSync(dest);
|
|
18901
19157
|
let srcDir;
|
|
18902
19158
|
try {
|
|
18903
|
-
srcDir =
|
|
19159
|
+
srcDir = path31.join(builtinSkillsDir(), name);
|
|
18904
19160
|
} catch (err) {
|
|
18905
19161
|
if (!exists) console.warn(`Could not locate built-in skills dir: ${String(err)}`);
|
|
18906
19162
|
return false;
|
|
@@ -18909,9 +19165,9 @@ var SkillsService = class _SkillsService {
|
|
|
18909
19165
|
if (!exists) console.warn(`Built-in skill not found in package: ${name}`);
|
|
18910
19166
|
return false;
|
|
18911
19167
|
}
|
|
18912
|
-
const srcHash = fileHash(
|
|
19168
|
+
const srcHash = fileHash(path31.join(srcDir, "SKILL.md"));
|
|
18913
19169
|
if (exists) {
|
|
18914
|
-
const currentHash = fileHash(
|
|
19170
|
+
const currentHash = fileHash(path31.join(dest, "SKILL.md"));
|
|
18915
19171
|
if (currentHash !== void 0 && currentHash === srcHash) {
|
|
18916
19172
|
this.recordSeed(name, currentHash);
|
|
18917
19173
|
return false;
|
|
@@ -18955,7 +19211,7 @@ var SkillsService = class _SkillsService {
|
|
|
18955
19211
|
if (entries.length !== 1 || entries[0].name !== "SKILL.md" || !entries[0].isFile()) return false;
|
|
18956
19212
|
let raw;
|
|
18957
19213
|
try {
|
|
18958
|
-
raw = readSkillText(
|
|
19214
|
+
raw = readSkillText(path31.join(dir, "SKILL.md"));
|
|
18959
19215
|
} catch {
|
|
18960
19216
|
return false;
|
|
18961
19217
|
}
|
|
@@ -18963,7 +19219,7 @@ var SkillsService = class _SkillsService {
|
|
|
18963
19219
|
}
|
|
18964
19220
|
pruneRetired() {
|
|
18965
19221
|
for (const name of RETIRED_BUILTIN_SKILL_NAMES) {
|
|
18966
|
-
const dest =
|
|
19222
|
+
const dest = path31.join(this.globalDir(), name);
|
|
18967
19223
|
if (!fs11.existsSync(dest)) continue;
|
|
18968
19224
|
if (!this.isUnmodifiedRetiredSeed(dest, name)) continue;
|
|
18969
19225
|
try {
|
|
@@ -19101,12 +19357,12 @@ var runGit = execFileWithTimeout(1e4);
|
|
|
19101
19357
|
async function committedInHead(files) {
|
|
19102
19358
|
const byDir = /* @__PURE__ */ new Map();
|
|
19103
19359
|
for (const file of new Set(files)) {
|
|
19104
|
-
const dir =
|
|
19360
|
+
const dir = path32.dirname(path32.dirname(file));
|
|
19105
19361
|
byDir.set(dir, [...byDir.get(dir) ?? [], file]);
|
|
19106
19362
|
}
|
|
19107
19363
|
const result = /* @__PURE__ */ new Map();
|
|
19108
19364
|
await Promise.all([...byDir].map(async ([dir, inDir]) => {
|
|
19109
|
-
const relative17 = (file) =>
|
|
19365
|
+
const relative17 = (file) => path32.relative(dir, file).split(path32.sep).join("/");
|
|
19110
19366
|
try {
|
|
19111
19367
|
const { stdout } = await runGit("git", ["ls-tree", "--name-only", "HEAD", "--", ...inDir.map(relative17)], { cwd: dir, env: cleanEnv() });
|
|
19112
19368
|
const listed3 = new Set(stdout.split("\n").map((line) => line.trim()).filter(Boolean));
|
|
@@ -19126,9 +19382,9 @@ function plannedSkillLookup(skillsAt, workspaceRoot, layout) {
|
|
|
19126
19382
|
searchedDirs: () => lookup2.searchedDirs(),
|
|
19127
19383
|
listSkills: () => lookup2.listSkills(),
|
|
19128
19384
|
uncommitted: async (skills) => {
|
|
19129
|
-
const checked = skills.filter((skill) => skill.source === "workspace" && !(readInPlace &&
|
|
19385
|
+
const checked = skills.filter((skill) => skill.source === "workspace" && !(readInPlace && path32.dirname(path32.dirname(skill.path)) === readInPlace));
|
|
19130
19386
|
const committed = await committedInHead(checked.map((skill) => skill.path));
|
|
19131
|
-
return new Map(checked.filter((skill) => committed.get(skill.path) === false).map((skill) => [skill.path,
|
|
19387
|
+
return new Map(checked.filter((skill) => committed.get(skill.path) === false).map((skill) => [skill.path, path32.relative(workspaceRoot, path32.dirname(skill.path)).split(path32.sep).join("/")]));
|
|
19132
19388
|
}
|
|
19133
19389
|
};
|
|
19134
19390
|
}
|
|
@@ -19141,13 +19397,13 @@ function checkPlanSkills(tasks, lookup2) {
|
|
|
19141
19397
|
function checkTaskSkillsEdit(task2, skills, lookup2) {
|
|
19142
19398
|
return check([{ taskId: task2.id, owner: `Task "${task2.title}"`, ...attached(skills) }], lookup2);
|
|
19143
19399
|
}
|
|
19144
|
-
function subtaskEntries(op, spec,
|
|
19400
|
+
function subtaskEntries(op, spec, path47 = []) {
|
|
19145
19401
|
const subtasks = spec?.subtasks;
|
|
19146
19402
|
if (!Array.isArray(subtasks)) return [];
|
|
19147
19403
|
return subtasks.flatMap((sub, i) => {
|
|
19148
19404
|
if (typeof sub !== "object" || sub === null) return [];
|
|
19149
19405
|
const child = sub;
|
|
19150
|
-
const at = [...
|
|
19406
|
+
const at = [...path47, typeof child.title === "string" && child.title.trim() ? `"${child.title}"` : `#${i + 1}`];
|
|
19151
19407
|
return [{ op, owner: `subtask ${at.join(" > ")}`, ...attached(child.skills) }, ...subtaskEntries(op, child, at)];
|
|
19152
19408
|
});
|
|
19153
19409
|
}
|
|
@@ -22989,7 +23245,7 @@ var RUN_SETTING_KEYS = Object.keys(RUN_SETTING_CHECKS);
|
|
|
22989
23245
|
function getSettingsPath() {
|
|
22990
23246
|
const override = process.env.ORDEWELL_SETTINGS_PATH;
|
|
22991
23247
|
if (override && override.trim()) return override.trim();
|
|
22992
|
-
return
|
|
23248
|
+
return path33.join(globalDataDir(), "settings.json");
|
|
22993
23249
|
}
|
|
22994
23250
|
function readPlannerModels(raw) {
|
|
22995
23251
|
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return void 0;
|
|
@@ -23069,8 +23325,8 @@ var SettingsService = class {
|
|
|
23069
23325
|
* a watch on the old one pointing at nothing.
|
|
23070
23326
|
*/
|
|
23071
23327
|
watch(listener) {
|
|
23072
|
-
const dir =
|
|
23073
|
-
const name =
|
|
23328
|
+
const dir = path33.dirname(this.filePath);
|
|
23329
|
+
const name = path33.basename(this.filePath);
|
|
23074
23330
|
let seen = this.fileStamp();
|
|
23075
23331
|
let timer;
|
|
23076
23332
|
let dead = false;
|
|
@@ -23079,7 +23335,7 @@ var SettingsService = class {
|
|
|
23079
23335
|
fs12.mkdirSync(dir, { recursive: true });
|
|
23080
23336
|
watcher = fs12.watch(dir, (_event, changed) => {
|
|
23081
23337
|
if (dead) return;
|
|
23082
|
-
if (changed && changed !== name && changed !==
|
|
23338
|
+
if (changed && changed !== name && changed !== path33.basename(dir)) return;
|
|
23083
23339
|
clearTimeout(timer);
|
|
23084
23340
|
timer = setTimeout(() => {
|
|
23085
23341
|
const stamp2 = this.fileStamp();
|
|
@@ -23312,7 +23568,7 @@ var SettingsService = class {
|
|
|
23312
23568
|
if (this.unreadable) {
|
|
23313
23569
|
throw new Error(`Cannot save settings: ${this.filePath} could not be read, so it will not be overwritten. Fix or remove it and try again.`);
|
|
23314
23570
|
}
|
|
23315
|
-
const dir =
|
|
23571
|
+
const dir = path33.dirname(this.filePath);
|
|
23316
23572
|
if (!fs12.existsSync(dir)) {
|
|
23317
23573
|
fs12.mkdirSync(dir, { recursive: true });
|
|
23318
23574
|
}
|
|
@@ -23756,7 +24012,7 @@ var BaseConfig = class {
|
|
|
23756
24012
|
const { model, effort, override = "" } = configured;
|
|
23757
24013
|
const named3 = isPlannerBackend(configured.setting ?? "") || isPlannerBackend(process.env.AI_PROVIDER ?? "");
|
|
23758
24014
|
const pickedElsewhere = configured.pickedFor !== void 0 && configured.pickedFor !== provider;
|
|
23759
|
-
const kept = configured.pickedFor === provider || !pickedElsewhere && (isApiProvider(provider) || named3 && !isForeignModel(provider, model, this.agentCatalog(provider)));
|
|
24015
|
+
const kept = configured.pickedFor === provider || !pickedElsewhere && (isApiProvider(provider) || named3 && !isForeignModel(provider, model, () => this.agentCatalog(provider)));
|
|
23760
24016
|
if (kept) return { model: override || model, effort, source: override ? "request" : "configured" };
|
|
23761
24017
|
const own = pickedElsewhere || named3 ? rememberedOrDefault(provider) : detectedAgentChoice(provider, configured);
|
|
23762
24018
|
return !override || override === own.model ? own : { model: override, effort: "", source: "request" };
|
|
@@ -23765,7 +24021,8 @@ var BaseConfig = class {
|
|
|
23765
24021
|
* The model ids a coding agent was last discovered serving, empty while
|
|
23766
24022
|
* discovery is cold. A host that holds a runner catalog supplies it: it is
|
|
23767
24023
|
* the only evidence that tells OpenCode's `provider/model` ids from an
|
|
23768
|
-
* OpenRouter slug.
|
|
24024
|
+
* OpenRouter slug. Asked only when a named agent's configured model is
|
|
24025
|
+
* neither its alias nor its remembered pick.
|
|
23769
24026
|
*/
|
|
23770
24027
|
agentCatalog(_provider) {
|
|
23771
24028
|
return [];
|
|
@@ -23790,8 +24047,10 @@ function isVendorModelFor(agent, model) {
|
|
|
23790
24047
|
return !discoveryOf(agent)?.providerQualifiedIds && SLUG_SHAPE.test(model);
|
|
23791
24048
|
}
|
|
23792
24049
|
function isForeignModel(agent, model, catalog) {
|
|
23793
|
-
if (!model || isStableAlias(agent, model) ||
|
|
23794
|
-
|
|
24050
|
+
if (!model || isStableAlias(agent, model) || rememberedChoice(agent)?.model === model) return false;
|
|
24051
|
+
const listed3 = catalog();
|
|
24052
|
+
if (listed3.includes(model)) return false;
|
|
24053
|
+
return isVendorModelFor(agent, model) || listed3.length > 0 && SLUG_SHAPE.test(model);
|
|
23795
24054
|
}
|
|
23796
24055
|
function rememberedChoice(provider) {
|
|
23797
24056
|
const entry = new SettingsService().getPlannerModel(provider);
|
|
@@ -23826,11 +24085,11 @@ var EnvConfig = class extends BaseConfig {
|
|
|
23826
24085
|
// src/services/taskDiffRead.ts
|
|
23827
24086
|
import { createHash as createHash3 } from "crypto";
|
|
23828
24087
|
import * as fs13 from "fs";
|
|
23829
|
-
import * as
|
|
24088
|
+
import * as path34 from "path";
|
|
23830
24089
|
import { devNull } from "os";
|
|
23831
24090
|
var TASK_DIFF_UNAVAILABLE = "Task diff is not available for this run: no task-specific changes were recorded (shared runs cannot attribute workspace changes to one task).";
|
|
23832
24091
|
function allowedPath(file) {
|
|
23833
|
-
return file.length > 0 && !
|
|
24092
|
+
return file.length > 0 && !path34.isAbsolute(file) && !/^[A-Za-z]:/.test(file) && !file.split(/[\\/]/).some((part) => part === ".." || part === ".git" || part === ".ordewell");
|
|
23834
24093
|
}
|
|
23835
24094
|
function stats(raw) {
|
|
23836
24095
|
return raw.split("\0").flatMap((row) => {
|
|
@@ -23839,8 +24098,8 @@ function stats(raw) {
|
|
|
23839
24098
|
});
|
|
23840
24099
|
}
|
|
23841
24100
|
function inside(root, target) {
|
|
23842
|
-
const rel =
|
|
23843
|
-
return rel === "" || !rel.startsWith(`..${
|
|
24101
|
+
const rel = path34.relative(root, target);
|
|
24102
|
+
return rel === "" || !rel.startsWith(`..${path34.sep}`) && rel !== ".." && !path34.isAbsolute(rel);
|
|
23844
24103
|
}
|
|
23845
24104
|
function unquoted(quoted4) {
|
|
23846
24105
|
const bytes = [];
|
|
@@ -23888,10 +24147,10 @@ function patchFiles(patch) {
|
|
|
23888
24147
|
const sections = patch.toString("utf8").split(/(?<![^\n])(?=diff --git )/).filter((section) => section.startsWith("diff --git "));
|
|
23889
24148
|
const files = /* @__PURE__ */ new Map();
|
|
23890
24149
|
for (const section of sections) {
|
|
23891
|
-
const
|
|
24150
|
+
const path47 = sectionPath(section);
|
|
23892
24151
|
const counts = sectionStats(section);
|
|
23893
|
-
const earlier = files.get(
|
|
23894
|
-
files.set(
|
|
24152
|
+
const earlier = files.get(path47);
|
|
24153
|
+
files.set(path47, earlier ? { path: path47, section: earlier.section + section, added: summed(earlier.added, counts.added), removed: summed(earlier.removed, counts.removed) } : { path: path47, section, ...counts });
|
|
23895
24154
|
}
|
|
23896
24155
|
return [...files.values()];
|
|
23897
24156
|
}
|
|
@@ -23961,11 +24220,11 @@ async function readTaskDiff(invoker, run, taskId, options) {
|
|
|
23961
24220
|
files.push({ ...file, repo: repo.path, patch: () => git(reader, cwd, [...diffArgs, "--", `:(top,literal)${prefix}${file.path}`]) });
|
|
23962
24221
|
}
|
|
23963
24222
|
if (live2) {
|
|
23964
|
-
const workspace =
|
|
24223
|
+
const workspace = path34.join(entry.worktree, prefix);
|
|
23965
24224
|
const others = await git(reader, workspace, [...common, "ls-files", "--others", "--exclude-standard", "-z", "--", "."]);
|
|
23966
24225
|
for (const file of others.split("\0").filter((f) => allowed(f))) {
|
|
23967
|
-
const full =
|
|
23968
|
-
if (!inside(fs13.realpathSync(workspace), fs13.realpathSync(
|
|
24226
|
+
const full = path34.join(workspace, file);
|
|
24227
|
+
if (!inside(fs13.realpathSync(workspace), fs13.realpathSync(path34.dirname(full)))) continue;
|
|
23969
24228
|
const untracked = async (numstat) => {
|
|
23970
24229
|
const result = await tryGit(reader, workspace, [...common, "diff", "--no-index", "--no-renames", "--no-ext-diff", "--no-textconv", ...numstat ? ["--numstat", "-z"] : [], "--", devNull, file]);
|
|
23971
24230
|
if (!result.ok && result.code !== 1) throw new Error(result.stderr.trim() || "Could not read an untracked file diff.");
|
|
@@ -23995,11 +24254,11 @@ ${await file.patch()}`);
|
|
|
23995
24254
|
import { randomBytes as randomBytes6 } from "crypto";
|
|
23996
24255
|
import { spawnSync } from "child_process";
|
|
23997
24256
|
import * as fs15 from "fs";
|
|
23998
|
-
import * as
|
|
24257
|
+
import * as path36 from "path";
|
|
23999
24258
|
|
|
24000
24259
|
// src/utils/prdStore.ts
|
|
24001
24260
|
import * as fs14 from "fs";
|
|
24002
|
-
import * as
|
|
24261
|
+
import * as path35 from "path";
|
|
24003
24262
|
var PRD_BLOCK_RE = /<!--\s*ORDEWELL_PRD_START\s+slug="([^"]+)"\s*-->\s*([\s\S]*?)\s*<!--\s*ORDEWELL_PRD_END\s*-->/;
|
|
24004
24263
|
function extractPrdBlock(text4) {
|
|
24005
24264
|
const match = PRD_BLOCK_RE.exec(text4);
|
|
@@ -24013,9 +24272,9 @@ function sanitizeSlug(raw) {
|
|
|
24013
24272
|
return raw.toLowerCase().replace(/[^a-z0-9-_]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 80);
|
|
24014
24273
|
}
|
|
24015
24274
|
function savePrdMarkdown(workspace, slug, markdown) {
|
|
24016
|
-
const dir =
|
|
24275
|
+
const dir = path35.join(workspace, ".scratch", sanitizeSlug(slug));
|
|
24017
24276
|
fs14.mkdirSync(dir, { recursive: true });
|
|
24018
|
-
const file =
|
|
24277
|
+
const file = path35.join(dir, "PRD.md");
|
|
24019
24278
|
fs14.writeFileSync(file, markdown.endsWith("\n") ? markdown : markdown + "\n", "utf8");
|
|
24020
24279
|
return file;
|
|
24021
24280
|
}
|
|
@@ -24047,12 +24306,12 @@ var NESTED_REPO_DEPTH = 2;
|
|
|
24047
24306
|
var GITLINK_MODE = "160000";
|
|
24048
24307
|
var READ_ONLY = ["--no-optional-locks", "-c", "core.fsmonitor=false"];
|
|
24049
24308
|
function isInside(parent, child) {
|
|
24050
|
-
const rel =
|
|
24051
|
-
return rel !== "" && !rel.startsWith("..") && !
|
|
24309
|
+
const rel = path36.relative(parent, child);
|
|
24310
|
+
return rel !== "" && !rel.startsWith("..") && !path36.isAbsolute(rel);
|
|
24052
24311
|
}
|
|
24053
24312
|
function resolvesAsNamed(base, p) {
|
|
24054
24313
|
try {
|
|
24055
|
-
return fs15.realpathSync(p) ===
|
|
24314
|
+
return fs15.realpathSync(p) === path36.join(fs15.realpathSync(base), path36.relative(base, p));
|
|
24056
24315
|
} catch {
|
|
24057
24316
|
return false;
|
|
24058
24317
|
}
|
|
@@ -24107,7 +24366,7 @@ function sameDir(a, b) {
|
|
|
24107
24366
|
try {
|
|
24108
24367
|
return fs15.realpathSync.native(p);
|
|
24109
24368
|
} catch {
|
|
24110
|
-
return
|
|
24369
|
+
return path36.resolve(p);
|
|
24111
24370
|
}
|
|
24112
24371
|
};
|
|
24113
24372
|
return real(a) === real(b);
|
|
@@ -24129,7 +24388,7 @@ function clearImmutableFlags(platform, dir) {
|
|
|
24129
24388
|
spawnSync("/usr/bin/chflags", ["-R", "nouchg", dir], { stdio: "ignore" });
|
|
24130
24389
|
}
|
|
24131
24390
|
function leftoverLinks(dir) {
|
|
24132
|
-
const named3 =
|
|
24391
|
+
const named3 = listDir2(dir).filter((name) => LINKED_ARTIFACTS.has(name) || isEnvFile(name) || isLink(path36.join(dir, name)));
|
|
24133
24392
|
return [.../* @__PURE__ */ new Set([...named3, ...installDirs(dir)])];
|
|
24134
24393
|
}
|
|
24135
24394
|
function isLink(target) {
|
|
@@ -24202,7 +24461,7 @@ var GitWorktreeIsolation = class {
|
|
|
24202
24461
|
const head = await this.headOf(root);
|
|
24203
24462
|
const repo = { path: repoPath, head };
|
|
24204
24463
|
if (scan.toplevel !== void 0) {
|
|
24205
|
-
const top =
|
|
24464
|
+
const top = path36.relative(workspaceRoot, scan.toplevel).split(path36.sep).join("/");
|
|
24206
24465
|
if (top !== "") repo.top = top;
|
|
24207
24466
|
}
|
|
24208
24467
|
if (enabled && (head.kind === "branch" || head.kind === "detached")) {
|
|
@@ -24259,9 +24518,9 @@ var GitWorktreeIsolation = class {
|
|
|
24259
24518
|
async nestedRepos(workspaceRoot, toplevel) {
|
|
24260
24519
|
const found = [];
|
|
24261
24520
|
const walk = (rel, depth) => {
|
|
24262
|
-
for (const name of childDirs(
|
|
24521
|
+
for (const name of childDirs(path36.join(workspaceRoot, rel))) {
|
|
24263
24522
|
const child = rel ? `${rel}/${name}` : name;
|
|
24264
|
-
if (hasGitEntry(
|
|
24523
|
+
if (hasGitEntry(path36.join(workspaceRoot, child))) found.push(child);
|
|
24265
24524
|
else if (depth < NESTED_REPO_DEPTH) walk(child, depth + 1);
|
|
24266
24525
|
}
|
|
24267
24526
|
};
|
|
@@ -24279,7 +24538,7 @@ var GitWorktreeIsolation = class {
|
|
|
24279
24538
|
const [meta, file] = entry.split(" ");
|
|
24280
24539
|
if (file !== void 0 && meta.startsWith(`${GITLINK_MODE} `)) paths.add(file);
|
|
24281
24540
|
}
|
|
24282
|
-
const declared = await this.tryGit(workspaceRoot, ["config", "-z", "-f",
|
|
24541
|
+
const declared = await this.tryGit(workspaceRoot, ["config", "-z", "-f", path36.join(toplevel, ".gitmodules"), "--get-regexp", "^submodule\\..*\\.path$"]);
|
|
24283
24542
|
for (const entry of declared.stdout.split("\0")) {
|
|
24284
24543
|
const value = entry.split("\n")[1];
|
|
24285
24544
|
if (value) paths.add(value.replace(/\/+$/, ""));
|
|
@@ -24337,7 +24596,7 @@ var GitWorktreeIsolation = class {
|
|
|
24337
24596
|
async acceptsWorktree(run, repo) {
|
|
24338
24597
|
const dir = this.integrationDir(run, repo);
|
|
24339
24598
|
ensureStateDirIgnored(run.workspaceRoot);
|
|
24340
|
-
fs15.mkdirSync(
|
|
24599
|
+
fs15.mkdirSync(path36.dirname(dir), { recursive: true });
|
|
24341
24600
|
const added = await this.tryGit(repo.root, ["worktree", "add", "-q", "--no-checkout", dir, repo.integrationBranch]);
|
|
24342
24601
|
await this.removeWorktreeDir(run, repo, dir, []);
|
|
24343
24602
|
return added.ok;
|
|
@@ -24352,12 +24611,12 @@ var GitWorktreeIsolation = class {
|
|
|
24352
24611
|
if (isolated.includes(SELF_REPO)) return [];
|
|
24353
24612
|
const shared2 = [];
|
|
24354
24613
|
const walk = (rel) => {
|
|
24355
|
-
for (const name of
|
|
24614
|
+
for (const name of listDir2(path36.join(workspaceRoot, rel))) {
|
|
24356
24615
|
if (!rel && (name === STATE_DIR || name === ".git")) continue;
|
|
24357
24616
|
const child = rel ? `${rel}/${name}` : name;
|
|
24358
24617
|
if (isolated.includes(child)) continue;
|
|
24359
24618
|
if (isolated.some((p) => p.startsWith(`${child}/`))) walk(child);
|
|
24360
|
-
else if (resolves(
|
|
24619
|
+
else if (resolves(path36.join(workspaceRoot, child))) shared2.push(child);
|
|
24361
24620
|
}
|
|
24362
24621
|
};
|
|
24363
24622
|
walk("");
|
|
@@ -24397,9 +24656,9 @@ var GitWorktreeIsolation = class {
|
|
|
24397
24656
|
try {
|
|
24398
24657
|
ensureStateDirIgnored(run.workspaceRoot);
|
|
24399
24658
|
for (const repo of run.repos) {
|
|
24400
|
-
const worktree =
|
|
24659
|
+
const worktree = path36.join(dir, repo.path);
|
|
24401
24660
|
const tip = (await this.git(repo.root, ["rev-parse", repo.integrationBranch])).trim();
|
|
24402
|
-
fs15.mkdirSync(
|
|
24661
|
+
fs15.mkdirSync(path36.dirname(worktree), { recursive: true });
|
|
24403
24662
|
await this.git(repo.root, ["worktree", "add", "-q", "-B", branch, worktree, tip]);
|
|
24404
24663
|
record.repos[repo.path] = { worktree, linked: [], baseRef: tip };
|
|
24405
24664
|
}
|
|
@@ -24424,13 +24683,13 @@ var GitWorktreeIsolation = class {
|
|
|
24424
24683
|
setupFailed.push({ repo: repo.path, detail: err.detail });
|
|
24425
24684
|
}
|
|
24426
24685
|
entry.linked = boot.linked;
|
|
24427
|
-
copied.push(...boot.copied.map((name) =>
|
|
24686
|
+
copied.push(...boot.copied.map((name) => path36.posix.join(repo.path, name)));
|
|
24428
24687
|
}
|
|
24429
24688
|
for (const rel of run.shared) {
|
|
24430
|
-
const source =
|
|
24431
|
-
const target =
|
|
24689
|
+
const source = path36.join(run.workspaceRoot, rel);
|
|
24690
|
+
const target = path36.join(cwd, rel);
|
|
24432
24691
|
if (!resolves(source) || lexists(target)) continue;
|
|
24433
|
-
fs15.mkdirSync(
|
|
24692
|
+
fs15.mkdirSync(path36.dirname(target), { recursive: true });
|
|
24434
24693
|
if (linkPath(source, target, this.platform) === "copy") copied.push(rel);
|
|
24435
24694
|
const entry = self ? record.repos[SELF_REPO] : void 0;
|
|
24436
24695
|
if (entry) entry.linked.push(rel);
|
|
@@ -24462,7 +24721,7 @@ var GitWorktreeIsolation = class {
|
|
|
24462
24721
|
*/
|
|
24463
24722
|
async clearLeftovers(run, task2, names) {
|
|
24464
24723
|
const removal = noRemoval();
|
|
24465
|
-
const holder = Object.values(run.tasks).find((r) => r.taskId !== task2.id && (r.branch === names.branch ||
|
|
24724
|
+
const holder = Object.values(run.tasks).find((r) => r.taskId !== task2.id && (r.branch === names.branch || path36.resolve(r.workspace) === path36.resolve(names.dir) || isInside(r.workspace, names.dir)));
|
|
24466
24725
|
if (holder) throw new Error(`Its worktree path or branch is held by the record of task ${holder.order} (${holder.title}); Ordewell will not remove it`);
|
|
24467
24726
|
const found = [];
|
|
24468
24727
|
for (const repo of run.repos) {
|
|
@@ -24472,14 +24731,14 @@ var GitWorktreeIsolation = class {
|
|
|
24472
24731
|
worktree: /^worktree (.+)$/m.exec(block)?.[1],
|
|
24473
24732
|
branch: /^branch refs\/heads\/(.+)$/m.exec(block)?.[1]
|
|
24474
24733
|
}));
|
|
24475
|
-
const at = /* @__PURE__ */ new Set([
|
|
24734
|
+
const at = /* @__PURE__ */ new Set([path36.join(names.dir, repo.path)]);
|
|
24476
24735
|
for (const entry of registered) {
|
|
24477
|
-
if (entry.worktree && entry.branch === names.branch && isInside(this.runRoot(run),
|
|
24736
|
+
if (entry.worktree && entry.branch === names.branch && isInside(this.runRoot(run), path36.resolve(entry.worktree))) at.add(entry.worktree);
|
|
24478
24737
|
}
|
|
24479
|
-
const base = { branch: names.branch, name:
|
|
24738
|
+
const base = { branch: names.branch, name: path36.basename(names.dir), title: task2.title };
|
|
24480
24739
|
for (const worktree of at) {
|
|
24481
24740
|
if (!fs15.existsSync(worktree)) continue;
|
|
24482
|
-
const linked = leftoverLinks(
|
|
24741
|
+
const linked = leftoverLinks(path36.join(worktree, await this.prefixOf(repo)));
|
|
24483
24742
|
found.push({ repo, worktree, target: { ...base, worktree, linked } });
|
|
24484
24743
|
}
|
|
24485
24744
|
found.push({ repo, worktree: names.dir, target: { ...base, linked: [] } });
|
|
@@ -24572,7 +24831,7 @@ var GitWorktreeIsolation = class {
|
|
|
24572
24831
|
*/
|
|
24573
24832
|
async inWorkspacePlace(repo, worktree) {
|
|
24574
24833
|
const prefix = await this.prefixOf(repo);
|
|
24575
|
-
return prefix ?
|
|
24834
|
+
return prefix ? path36.join(worktree, prefix) : worktree;
|
|
24576
24835
|
}
|
|
24577
24836
|
async prefixOf(repo) {
|
|
24578
24837
|
return (await this.git(repo.root, ["rev-parse", "--show-prefix"])).trim();
|
|
@@ -25102,15 +25361,15 @@ var GitWorktreeIsolation = class {
|
|
|
25102
25361
|
return landingMessage(kind, from, this.deps.plannedWith?.() ?? false);
|
|
25103
25362
|
}
|
|
25104
25363
|
integrationDir(run, repo) {
|
|
25105
|
-
return
|
|
25364
|
+
return path36.join(this.runRoot(run), INTEGRATION_DIR, repo.path);
|
|
25106
25365
|
}
|
|
25107
25366
|
async ensureIntegrationWorktree(run, repo) {
|
|
25108
25367
|
return this.admin(run.workspaceRoot, async () => {
|
|
25109
25368
|
const dir = this.integrationDir(run, repo);
|
|
25110
|
-
if (fs15.existsSync(
|
|
25369
|
+
if (fs15.existsSync(path36.join(dir, ".git"))) return dir;
|
|
25111
25370
|
await this.tryGit(repo.root, ["worktree", "prune"]);
|
|
25112
25371
|
ensureStateDirIgnored(run.workspaceRoot);
|
|
25113
|
-
fs15.mkdirSync(
|
|
25372
|
+
fs15.mkdirSync(path36.dirname(dir), { recursive: true });
|
|
25114
25373
|
await this.git(repo.root, ["worktree", "add", "-q", dir, repo.integrationBranch]);
|
|
25115
25374
|
return dir;
|
|
25116
25375
|
});
|
|
@@ -25133,7 +25392,7 @@ var GitWorktreeIsolation = class {
|
|
|
25133
25392
|
const removal = noRemoval();
|
|
25134
25393
|
for (const repo of opts.preserve === false || record.readOnly ? [] : run.repos) {
|
|
25135
25394
|
const entry = record.repos[repo.path];
|
|
25136
|
-
const target = { worktree: entry?.worktree, linked: entry?.linked ?? [], branch: record.branch, name:
|
|
25395
|
+
const target = { worktree: entry?.worktree, linked: entry?.linked ?? [], branch: record.branch, name: path36.basename(record.workspace), title: record.title, commit: record.commit };
|
|
25137
25396
|
try {
|
|
25138
25397
|
const kept = await this.preserve(run, repo, target, opts.intent);
|
|
25139
25398
|
if (kept) removal.preserved.push({ task: task2, ...kept });
|
|
@@ -25214,7 +25473,7 @@ var GitWorktreeIsolation = class {
|
|
|
25214
25473
|
return;
|
|
25215
25474
|
}
|
|
25216
25475
|
for (const entry of entries) {
|
|
25217
|
-
const target =
|
|
25476
|
+
const target = path36.join(dir, entry.name);
|
|
25218
25477
|
if (entry.isSymbolicLink()) this.unlinkIfLink(target);
|
|
25219
25478
|
else if (entry.isDirectory()) this.unlinkLinksUnder(target);
|
|
25220
25479
|
}
|
|
@@ -25236,20 +25495,20 @@ var GitWorktreeIsolation = class {
|
|
|
25236
25495
|
return false;
|
|
25237
25496
|
}
|
|
25238
25497
|
};
|
|
25239
|
-
const owned = new Set(Object.values(run.tasks).map((r) =>
|
|
25498
|
+
const owned = new Set(Object.values(run.tasks).map((r) => path36.resolve(r.workspace)));
|
|
25240
25499
|
const root = this.runRoot(run);
|
|
25241
25500
|
if (fs15.existsSync(root)) {
|
|
25242
25501
|
for (const entry of fs15.readdirSync(root)) {
|
|
25243
|
-
const dir =
|
|
25244
|
-
if (entry === INTEGRATION_DIR || owned.has(
|
|
25502
|
+
const dir = path36.join(root, entry);
|
|
25503
|
+
if (entry === INTEGRATION_DIR || owned.has(path36.resolve(dir))) continue;
|
|
25245
25504
|
let kept = true;
|
|
25246
25505
|
for (const repo of run.repos) {
|
|
25247
|
-
const worktree =
|
|
25248
|
-
const linked = leftoverLinks(
|
|
25506
|
+
const worktree = path36.join(dir, repo.path);
|
|
25507
|
+
const linked = leftoverLinks(path36.join(worktree, await this.prefixOf(repo)));
|
|
25249
25508
|
if (!await keep2(repo, { worktree, linked, branch: null, name: entry, title: entry }, worktree)) kept = false;
|
|
25250
25509
|
}
|
|
25251
25510
|
if (!kept) continue;
|
|
25252
|
-
for (const repo of run.repos) await this.removeWorktreeDir(run, repo,
|
|
25511
|
+
for (const repo of run.repos) await this.removeWorktreeDir(run, repo, path36.join(dir, repo.path), []);
|
|
25253
25512
|
this.removeTaskWorkspace(run, dir);
|
|
25254
25513
|
}
|
|
25255
25514
|
}
|
|
@@ -25267,13 +25526,13 @@ var GitWorktreeIsolation = class {
|
|
|
25267
25526
|
}
|
|
25268
25527
|
async removeWorktreeDir(run, repo, dir, linked) {
|
|
25269
25528
|
for (const name of /* @__PURE__ */ new Set([...linked, ...installDirs(dir)])) {
|
|
25270
|
-
const target =
|
|
25271
|
-
if (!resolvesAsNamed(this.runRoot(run),
|
|
25529
|
+
const target = path36.join(dir, name);
|
|
25530
|
+
if (!resolvesAsNamed(this.runRoot(run), path36.dirname(target))) continue;
|
|
25272
25531
|
this.unlinkIfLink(target);
|
|
25273
25532
|
this.unlinkLinksUnder(target);
|
|
25274
25533
|
}
|
|
25275
25534
|
for (const name of resolvesAsNamed(this.runRoot(run), dir) ? fs15.readdirSync(dir) : []) {
|
|
25276
|
-
if (LINKED_ARTIFACTS.has(name) || isEnvFile(name)) this.unlinkIfLink(
|
|
25535
|
+
if (LINKED_ARTIFACTS.has(name) || isEnvFile(name)) this.unlinkIfLink(path36.join(dir, name));
|
|
25277
25536
|
}
|
|
25278
25537
|
await this.tryGit(repo.root, ["worktree", "remove", "--force", dir]);
|
|
25279
25538
|
if (fs15.existsSync(dir) && isInside(this.runRoot(run), dir)) {
|
|
@@ -25281,7 +25540,7 @@ var GitWorktreeIsolation = class {
|
|
|
25281
25540
|
fs15.rmSync(dir, { recursive: true, force: true });
|
|
25282
25541
|
}
|
|
25283
25542
|
await this.tryGit(repo.root, ["worktree", "prune"]);
|
|
25284
|
-
for (let parent =
|
|
25543
|
+
for (let parent = path36.dirname(dir); isInside(this.runRoot(run), parent); parent = path36.dirname(parent)) this.removeIfEmpty(parent);
|
|
25285
25544
|
}
|
|
25286
25545
|
unlinkIfLink(target) {
|
|
25287
25546
|
try {
|
|
@@ -25296,14 +25555,14 @@ var GitWorktreeIsolation = class {
|
|
|
25296
25555
|
}
|
|
25297
25556
|
}
|
|
25298
25557
|
runRoot(run) {
|
|
25299
|
-
return
|
|
25558
|
+
return path36.join(run.workspaceRoot, STATE_DIR, "worktrees", run.id);
|
|
25300
25559
|
}
|
|
25301
25560
|
namesFor(run, task2) {
|
|
25302
25561
|
const slug = sanitizeSlug(task2.title).slice(0, 40).replace(/-+$/, "") || "task";
|
|
25303
25562
|
let name = `${task2.order}-${slug}`;
|
|
25304
25563
|
const clash = Object.values(run.tasks).some((r) => r.taskId !== task2.id && r.branch === `ordewell/${run.id}/${name}`);
|
|
25305
25564
|
if (clash) name = `${name}-${sanitizeSlug(task2.id) || "x"}`;
|
|
25306
|
-
return { branch: `ordewell/${run.id}/${name}`, dir:
|
|
25565
|
+
return { branch: `ordewell/${run.id}/${name}`, dir: path36.join(this.runRoot(run), name) };
|
|
25307
25566
|
}
|
|
25308
25567
|
async unmergedPaths(cwd) {
|
|
25309
25568
|
const listed3 = await this.tryGit(cwd, ["diff", "--name-only", "-z", "--diff-filter=U"]);
|
|
@@ -25317,7 +25576,7 @@ var GitWorktreeIsolation = class {
|
|
|
25317
25576
|
async rebaseInProgress(cwd) {
|
|
25318
25577
|
for (const dir of ["rebase-merge", "rebase-apply"]) {
|
|
25319
25578
|
const found = await this.tryGit(cwd, ["rev-parse", "--git-path", dir]);
|
|
25320
|
-
if (found.ok && fs15.existsSync(
|
|
25579
|
+
if (found.ok && fs15.existsSync(path36.resolve(cwd, found.stdout.trim()))) return true;
|
|
25321
25580
|
}
|
|
25322
25581
|
return false;
|
|
25323
25582
|
}
|
|
@@ -29147,15 +29406,21 @@ var Landing = class {
|
|
|
29147
29406
|
* has landed. Its branch is on the integration branch by then, so it merges
|
|
29148
29407
|
* clean — and one the resolver did not really bring along conflicts again
|
|
29149
29408
|
* instead of being taken at its word. Null when there is nothing to land.
|
|
29409
|
+
* The task is named before its landing settles, so the caller can hold
|
|
29410
|
+
* its worktree for as long as the merge is in it; one the caller says is
|
|
29411
|
+
* `leaving` the plan is not landed at all.
|
|
29150
29412
|
*/
|
|
29151
|
-
|
|
29413
|
+
landResolved(resolverId, leaving = () => false) {
|
|
29152
29414
|
const conflictedId = this.runs.takeResolver(resolverId);
|
|
29153
|
-
if (!conflictedId) return null;
|
|
29415
|
+
if (!conflictedId || leaving(conflictedId)) return null;
|
|
29154
29416
|
const task2 = this.tasks.get(conflictedId);
|
|
29155
29417
|
if (!task2 || this.runs.current?.tasks[conflictedId]?.status !== "conflict") return null;
|
|
29418
|
+
return { task: task2, outcome: this.landConflicted(task2) };
|
|
29419
|
+
}
|
|
29420
|
+
async landConflicted(task2) {
|
|
29156
29421
|
const landing2 = await this.runs.integrate(task2);
|
|
29157
|
-
if (landing2 !== "merged") return
|
|
29158
|
-
return {
|
|
29422
|
+
if (landing2 !== "merged") return this.unmerged(task2, landing2);
|
|
29423
|
+
return { kind: "landed", messages: [{ level: "info", text: `Task "${task2.title}" landed through its conflict resolution.` }] };
|
|
29159
29424
|
}
|
|
29160
29425
|
/** The repair a conflicted task is owed next; null when repair is off, used up, or there is no isolated run to repair it in. */
|
|
29161
29426
|
nextRepair(taskId) {
|
|
@@ -29500,7 +29765,7 @@ function heldFrom(state) {
|
|
|
29500
29765
|
}
|
|
29501
29766
|
|
|
29502
29767
|
// src/services/Attempts.ts
|
|
29503
|
-
import * as
|
|
29768
|
+
import * as path39 from "path";
|
|
29504
29769
|
|
|
29505
29770
|
// src/services/VerdictEngine.ts
|
|
29506
29771
|
var IDLE_TIMEOUT_MS = 10 * 6e4;
|
|
@@ -29608,6 +29873,18 @@ var VerdictEngine = class {
|
|
|
29608
29873
|
this.resumeIdle(taskId);
|
|
29609
29874
|
this.toolCheckpoints.get(taskId)?.({ kind: "rejected", reason });
|
|
29610
29875
|
}
|
|
29876
|
+
/**
|
|
29877
|
+
* An attempt's runner is being started, with no session to watch yet. Its
|
|
29878
|
+
* silence counts from here, so one whose start never resolves — stuck
|
|
29879
|
+
* launching, or waiting for its tools to attach — is flagged quiet as well.
|
|
29880
|
+
* {@link watch} starts the count again once the runner is up.
|
|
29881
|
+
*/
|
|
29882
|
+
starting(taskId) {
|
|
29883
|
+
const gen = this.bumpGeneration(taskId);
|
|
29884
|
+
this.idlePaused.delete(taskId);
|
|
29885
|
+
this.openApprovals.delete(taskId);
|
|
29886
|
+
this.touchIdle(taskId, gen);
|
|
29887
|
+
}
|
|
29611
29888
|
/**
|
|
29612
29889
|
* Attach to a spawned session. The runner's `task_complete` call on the
|
|
29613
29890
|
* attempt's token is the only completion evidence (ADR-0022, V2); a session
|
|
@@ -29618,6 +29895,9 @@ var VerdictEngine = class {
|
|
|
29618
29895
|
*/
|
|
29619
29896
|
watch(task2, session) {
|
|
29620
29897
|
const gen = this.bumpGeneration(task2.id);
|
|
29898
|
+
this.idlePaused.delete(task2.id);
|
|
29899
|
+
this.openApprovals.delete(task2.id);
|
|
29900
|
+
this.touchIdle(task2.id, gen);
|
|
29621
29901
|
session.onOutput(() => {
|
|
29622
29902
|
if (this.generations.get(task2.id) === gen) this.touchIdle(task2.id, gen);
|
|
29623
29903
|
});
|
|
@@ -29876,7 +30156,7 @@ function reportedVerdict(report) {
|
|
|
29876
30156
|
// src/services/ScriptRunner.ts
|
|
29877
30157
|
import { spawn as nodeSpawn2 } from "child_process";
|
|
29878
30158
|
import * as fs16 from "fs";
|
|
29879
|
-
import * as
|
|
30159
|
+
import * as path37 from "path";
|
|
29880
30160
|
import { EventEmitter as EventEmitter2 } from "events";
|
|
29881
30161
|
import { StringDecoder as StringDecoder2 } from "string_decoder";
|
|
29882
30162
|
|
|
@@ -30017,7 +30297,7 @@ var ScriptSession = class extends AbstractRunnerSession {
|
|
|
30017
30297
|
async launchOf(step) {
|
|
30018
30298
|
if ("argv" in step) {
|
|
30019
30299
|
const [named3, ...args] = step.argv;
|
|
30020
|
-
const program = this.setup.platform === "win32" &&
|
|
30300
|
+
const program = this.setup.platform === "win32" && path37.win32.basename(named3) !== named3 && !path37.win32.isAbsolute(named3) ? path37.win32.resolve(this.setup.cwd, named3) : named3;
|
|
30021
30301
|
const plan = await planDirectLaunch(program, args, this.setup.launch);
|
|
30022
30302
|
return { file: plan.file, args: plan.args, shell: false, ...plan.verbatim ? { verbatim: true } : {} };
|
|
30023
30303
|
}
|
|
@@ -30252,8 +30532,8 @@ function passed(status) {
|
|
|
30252
30532
|
// src/services/PatchCapture.ts
|
|
30253
30533
|
import { createHash as createHash4, randomBytes as randomBytes7 } from "crypto";
|
|
30254
30534
|
import * as fs17 from "fs";
|
|
30255
|
-
import * as
|
|
30256
|
-
import * as
|
|
30535
|
+
import * as os10 from "os";
|
|
30536
|
+
import * as path38 from "path";
|
|
30257
30537
|
|
|
30258
30538
|
// src/services/readTask.ts
|
|
30259
30539
|
function readTask2(args, readerId, plan, log) {
|
|
@@ -30520,7 +30800,7 @@ function linkInPatch(patch) {
|
|
|
30520
30800
|
function readOwn(scratch, name, max) {
|
|
30521
30801
|
let fd;
|
|
30522
30802
|
try {
|
|
30523
|
-
fd = fs17.openSync(
|
|
30803
|
+
fd = fs17.openSync(path38.join(scratch, name), fs17.constants.O_RDONLY | (fs17.constants.O_NOFOLLOW ?? 0));
|
|
30524
30804
|
} catch (err) {
|
|
30525
30805
|
throw new Refusal(`the capture left no ${name.split(".").pop()} Ordewell could read (${err.code ?? String(err)})`);
|
|
30526
30806
|
}
|
|
@@ -30558,7 +30838,7 @@ async function captureHeld(runs, opts, signal) {
|
|
|
30558
30838
|
const [worktree] = runs.worktrees;
|
|
30559
30839
|
const scratch = runs.scratch;
|
|
30560
30840
|
const tag = `ordewell-patch-${randomBytes7(8).toString("hex")}`;
|
|
30561
|
-
const stem =
|
|
30841
|
+
const stem = path38.join(scratch, tag);
|
|
30562
30842
|
const own = (suffix, max = LISTING_BYTES) => readOwn(scratch, `${tag}.${suffix}`, max);
|
|
30563
30843
|
try {
|
|
30564
30844
|
const stage = await runs.run(stageScript(stem, opts.linked ?? []), worktree);
|
|
@@ -30650,8 +30930,8 @@ async function indexEntries(invoker, worktree) {
|
|
|
30650
30930
|
return entries;
|
|
30651
30931
|
}
|
|
30652
30932
|
async function trialApply(invoker, worktree, patchFile, scratch) {
|
|
30653
|
-
const index =
|
|
30654
|
-
const copy =
|
|
30933
|
+
const index = path38.resolve(worktree, (await git(invoker, worktree, [...APPLY_GIT, "rev-parse", "--git-path", "index"])).trim());
|
|
30934
|
+
const copy = path38.join(scratch, "index");
|
|
30655
30935
|
fs17.copyFileSync(index, copy);
|
|
30656
30936
|
const trial = { ...invoker, exec: (file, args, opts) => invoker.exec(file, args, { ...opts, env: { ...opts.env, GIT_INDEX_FILE: copy } }) };
|
|
30657
30937
|
const tried = await tryGit(trial, worktree, [...APPLY_GIT, "apply", "--cached", "--3way", "--whitespace=nowarn", patchFile]);
|
|
@@ -30671,7 +30951,7 @@ function gitRefused(lead, stderr) {
|
|
|
30671
30951
|
function onDisk3(worktree, names) {
|
|
30672
30952
|
return names.map((name) => {
|
|
30673
30953
|
try {
|
|
30674
|
-
const stat = fs17.lstatSync(
|
|
30954
|
+
const stat = fs17.lstatSync(path38.join(worktree, name), { bigint: true, throwIfNoEntry: false });
|
|
30675
30955
|
return stat ? `${stat.mode} ${stat.ino} ${stat.size} ${stat.mtimeNs} ${stat.ctimeNs}` : "none";
|
|
30676
30956
|
} catch (err) {
|
|
30677
30957
|
return err.code ?? "unreadable";
|
|
@@ -30711,7 +30991,7 @@ function leavesWorktree(worktree, names, linked, held2, left) {
|
|
|
30711
30991
|
if (i < parts.length && held2.get(at)?.some((entry) => entry.mode === SUBMODULE)) {
|
|
30712
30992
|
return `the patch writes ${name}, under ${at}, a submodule, whose files are another repository's.`;
|
|
30713
30993
|
}
|
|
30714
|
-
const full =
|
|
30994
|
+
const full = path38.join(worktree, ...parts.slice(0, i));
|
|
30715
30995
|
const stat = fs17.lstatSync(full, { throwIfNoEntry: false });
|
|
30716
30996
|
if (!stat) break;
|
|
30717
30997
|
const last = i === parts.length;
|
|
@@ -30739,7 +31019,7 @@ async function applyPatch(invoker, worktree, patch, saved, opts = {}) {
|
|
|
30739
31019
|
if (link !== void 0) {
|
|
30740
31020
|
return refused3(`the patch holds a link or a submodule (${JSON.stringify(link.slice(0, 80))}), which a saved patch never does, so it is not one that was saved.`);
|
|
30741
31021
|
}
|
|
30742
|
-
const dir = fs17.mkdtempSync(
|
|
31022
|
+
const dir = fs17.mkdtempSync(path38.join(os10.tmpdir(), "ordewell-apply-"));
|
|
30743
31023
|
try {
|
|
30744
31024
|
const toplevel = (await git(invoker, worktree, [...APPLY_GIT, "rev-parse", "--show-toplevel"])).trim();
|
|
30745
31025
|
if (fs17.realpathSync(toplevel) !== fs17.realpathSync(worktree)) return refused3(`${worktree} is not the root of a worktree of its own.`);
|
|
@@ -30749,7 +31029,7 @@ async function applyPatch(invoker, worktree, patch, saved, opts = {}) {
|
|
|
30749
31029
|
}
|
|
30750
31030
|
const before = await unmergedPaths(invoker, worktree);
|
|
30751
31031
|
if (before.length > 0) return refused3(`your worktree still has unresolved conflicts (${listed(before.slice(0, 10))}${before.length > 10 ? `, +${before.length - 10} more` : ""}). Resolve them and git add them, or undo them, then apply the patch.`);
|
|
30752
|
-
const file =
|
|
31032
|
+
const file = path38.join(dir, "saved.patch");
|
|
30753
31033
|
fs17.writeFileSync(file, patch, { flag: "wx", mode: 384 });
|
|
30754
31034
|
const held2 = await indexEntries(invoker, worktree);
|
|
30755
31035
|
const named3 = await pathsNamed(invoker, worktree, file);
|
|
@@ -30985,6 +31265,10 @@ var Attempts = class {
|
|
|
30985
31265
|
receipt(taskId) {
|
|
30986
31266
|
return this.live.get(taskId)?.receipt ?? null;
|
|
30987
31267
|
}
|
|
31268
|
+
/** Which spawn of the live attempt's runner waits for Ordewell's tools to connect; null when none does. */
|
|
31269
|
+
attaching(taskId) {
|
|
31270
|
+
return this.live.get(taskId)?.attaching ?? null;
|
|
31271
|
+
}
|
|
30988
31272
|
/** The kind a task's next attempt starts as, short of a repair or a continue: what is weighed before one exists. */
|
|
30989
31273
|
nextKind(taskId) {
|
|
30990
31274
|
const task2 = this.deps.store.get(taskId);
|
|
@@ -31025,7 +31309,9 @@ var Attempts = class {
|
|
|
31025
31309
|
shell: null,
|
|
31026
31310
|
web: null,
|
|
31027
31311
|
patch: null,
|
|
31028
|
-
receipt: null
|
|
31312
|
+
receipt: null,
|
|
31313
|
+
attaching: null,
|
|
31314
|
+
spawning: new AbortController()
|
|
31029
31315
|
};
|
|
31030
31316
|
this.spawnCounts.set(task2.id, attempt.attempt);
|
|
31031
31317
|
this.live.set(task2.id, attempt);
|
|
@@ -31040,7 +31326,8 @@ var Attempts = class {
|
|
|
31040
31326
|
/**
|
|
31041
31327
|
* The one way an attempt ends. Dropping the record is itself what
|
|
31042
31328
|
* invalidates a spawn still in flight ({@link run} kills the session it
|
|
31043
|
-
* receives for an attempt that is no longer current)
|
|
31329
|
+
* receives for an attempt that is no longer current), and its cancel takes
|
|
31330
|
+
* back a runner still starting meanwhile.
|
|
31044
31331
|
*
|
|
31045
31332
|
* A user interruption also bumps the verifier's generation *before* stopping
|
|
31046
31333
|
* the runner: a runner may fire onExit synchronously from
|
|
@@ -31056,6 +31343,7 @@ var Attempts = class {
|
|
|
31056
31343
|
const session = attempt?.session ?? null;
|
|
31057
31344
|
const nativeSessionId = session?.nativeSessionId() ?? null;
|
|
31058
31345
|
if (attempt) {
|
|
31346
|
+
attempt.spawning.abort();
|
|
31059
31347
|
store.endAttemptUsage(taskId, attempt.attempt, (/* @__PURE__ */ new Date()).toISOString(), session?.sessionCost?.() ?? void 0, nativeSessionId ?? void 0);
|
|
31060
31348
|
this.deps.output.detach(taskId);
|
|
31061
31349
|
attempt.shell?.end();
|
|
@@ -31232,12 +31520,14 @@ var Attempts = class {
|
|
|
31232
31520
|
this.abandonSpawn(task2, attempt);
|
|
31233
31521
|
return false;
|
|
31234
31522
|
}
|
|
31523
|
+
this.verifier.starting(task2.id);
|
|
31235
31524
|
const session = attempt.script ? await this.spawnScript(task2, attempt, cwd) : await this.spawnRunner(task2, attempt, cwd, worktree ? workspace : void 0, previousWork);
|
|
31236
31525
|
if (this.live.get(task2.id) !== attempt) {
|
|
31237
31526
|
this.abandonSpawn(task2, attempt, session);
|
|
31238
31527
|
return false;
|
|
31239
31528
|
}
|
|
31240
31529
|
attempt.phase = "running";
|
|
31530
|
+
attempt.attaching = null;
|
|
31241
31531
|
attempt.session = session;
|
|
31242
31532
|
store.setTaskRunnerSessionId(task2.id, void 0);
|
|
31243
31533
|
store.clearCarriedFrom(task2.id);
|
|
@@ -31383,7 +31673,13 @@ var Attempts = class {
|
|
|
31383
31673
|
...web ? { webReads: web } : {},
|
|
31384
31674
|
applyPatch: (args) => this.applyPatch(task2, attempt, args),
|
|
31385
31675
|
workspaceRoot: this.deps.workspaceRoot(),
|
|
31386
|
-
onNotice: (message) => this.host.tell("warn", `Task "${task2.title}": ${message}`)
|
|
31676
|
+
onNotice: (message) => this.host.tell("warn", `Task "${task2.title}": ${message}`),
|
|
31677
|
+
cancelled: attempt.spawning.signal,
|
|
31678
|
+
onAttaching: (spawn) => {
|
|
31679
|
+
if (this.live.get(task2.id) !== attempt) return;
|
|
31680
|
+
attempt.attaching = spawn;
|
|
31681
|
+
this.host.emit("onTaskChanged");
|
|
31682
|
+
}
|
|
31387
31683
|
});
|
|
31388
31684
|
}
|
|
31389
31685
|
/**
|
|
@@ -31436,9 +31732,9 @@ var Attempts = class {
|
|
|
31436
31732
|
const repos = Object.values(this.deps.runs.openRecord(taskId)?.repos ?? {});
|
|
31437
31733
|
if (repos.length !== 1 || cwd === null) return [];
|
|
31438
31734
|
const [{ worktree, linked }] = repos;
|
|
31439
|
-
const prefix =
|
|
31440
|
-
if (prefix.startsWith("..") ||
|
|
31441
|
-
return linked.map((name) => (prefix ? `${prefix}/${name}` : name).split(
|
|
31735
|
+
const prefix = path39.relative(worktree, cwd).split(path39.sep).join("/");
|
|
31736
|
+
if (prefix.startsWith("..") || path39.isAbsolute(prefix)) return [];
|
|
31737
|
+
return linked.map((name) => (prefix ? `${prefix}/${name}` : name).split(path39.sep).join("/"));
|
|
31442
31738
|
}
|
|
31443
31739
|
/**
|
|
31444
31740
|
* A script task's start (ADR-0035): its body as the plan holds it now, run
|
|
@@ -31476,7 +31772,7 @@ var Attempts = class {
|
|
|
31476
31772
|
this.deps.notifications.warn(message);
|
|
31477
31773
|
};
|
|
31478
31774
|
if (resolved.blockedEnvrc) {
|
|
31479
|
-
warn(`blocked:${resolved.blockedEnvrc}`, `direnv has blocked ${resolved.blockedEnvrc}, so tasks start without its variables. Run \`direnv allow\` in ${
|
|
31775
|
+
warn(`blocked:${resolved.blockedEnvrc}`, `direnv has blocked ${resolved.blockedEnvrc}, so tasks start without its variables. Run \`direnv allow\` in ${path39.dirname(resolved.blockedEnvrc)} to use them.`);
|
|
31480
31776
|
}
|
|
31481
31777
|
if (resolved.trackedEnvFile) {
|
|
31482
31778
|
warn(`tracked:${resolved.trackedEnvFile}`, `Ignored ${resolved.trackedEnvFile}: git tracks it, and a committed file must not choose the environment agents run in. Untrack it to use it.`);
|
|
@@ -31502,7 +31798,7 @@ var Attempts = class {
|
|
|
31502
31798
|
uncommittedSkillFolder(name) {
|
|
31503
31799
|
const root = this.deps.workspaceRoot();
|
|
31504
31800
|
const skill = this.deps.skillsAt(this.skillRoots(root)).findSkill(name);
|
|
31505
|
-
return skill?.source === "workspace" ?
|
|
31801
|
+
return skill?.source === "workspace" ? path39.relative(root, path39.dirname(skill.path)).split(path39.sep).join("/") : void 0;
|
|
31506
31802
|
}
|
|
31507
31803
|
/**
|
|
31508
31804
|
* The roots whose `.ordewell/skills/` an attempt at `cwd` reads: in a repo
|
|
@@ -31871,12 +32167,12 @@ function ancestors(task2, store) {
|
|
|
31871
32167
|
|
|
31872
32168
|
// src/services/sessionLease.ts
|
|
31873
32169
|
import * as fs19 from "fs";
|
|
31874
|
-
import * as
|
|
32170
|
+
import * as path41 from "path";
|
|
31875
32171
|
import { randomUUID as randomUUID6 } from "crypto";
|
|
31876
32172
|
|
|
31877
32173
|
// src/utils/sessionStore.ts
|
|
31878
32174
|
import * as fs18 from "fs";
|
|
31879
|
-
import * as
|
|
32175
|
+
import * as path40 from "path";
|
|
31880
32176
|
|
|
31881
32177
|
// src/utils/sessionId.ts
|
|
31882
32178
|
import { randomBytes as randomBytes8 } from "crypto";
|
|
@@ -31887,13 +32183,13 @@ function mintSessionId() {
|
|
|
31887
32183
|
// src/utils/sessionStore.ts
|
|
31888
32184
|
var SESSIONS_SUBDIR = "sessions";
|
|
31889
32185
|
function getSessionsDir(baseDir) {
|
|
31890
|
-
return
|
|
32186
|
+
return path40.join(getStateDir(baseDir), SESSIONS_SUBDIR);
|
|
31891
32187
|
}
|
|
31892
32188
|
function idSegment(id2) {
|
|
31893
32189
|
return /^[A-Za-z0-9_-][A-Za-z0-9._-]{0,127}$/.test(id2) ? id2 : `~${Buffer.from(id2, "utf-8").toString("hex")}`;
|
|
31894
32190
|
}
|
|
31895
32191
|
function sessionDataDir(sessionId, baseDir) {
|
|
31896
|
-
return
|
|
32192
|
+
return path40.join(getSessionsDir(baseDir), idSegment(sessionId));
|
|
31897
32193
|
}
|
|
31898
32194
|
var REJECTION_MESSAGE = "This session was created with an older version of Ordewell. Please re-plan.";
|
|
31899
32195
|
function isSessionMeta(value) {
|
|
@@ -31920,8 +32216,8 @@ function legacySessionFilename(session) {
|
|
|
31920
32216
|
return `${filenameStem(session)}_${session.id.slice(0, 8)}.json`;
|
|
31921
32217
|
}
|
|
31922
32218
|
function dropLegacyFile(sessionsDir, meta) {
|
|
31923
|
-
const legacy =
|
|
31924
|
-
if (legacy ===
|
|
32219
|
+
const legacy = path40.join(sessionsDir, legacySessionFilename(meta));
|
|
32220
|
+
if (legacy === path40.join(sessionsDir, sessionFilename(meta)) || !fs18.existsSync(legacy)) return;
|
|
31925
32221
|
try {
|
|
31926
32222
|
const held2 = JSON.parse(fs18.readFileSync(legacy, "utf-8")).meta?.id;
|
|
31927
32223
|
if (held2 === meta.id) fs18.unlinkSync(legacy);
|
|
@@ -31948,7 +32244,7 @@ function saveSession(plan, goal, baseDir, id2) {
|
|
|
31948
32244
|
};
|
|
31949
32245
|
dropRetiredPlanFields(session.plan);
|
|
31950
32246
|
const filename = sessionFilename(meta);
|
|
31951
|
-
const filePath =
|
|
32247
|
+
const filePath = path40.join(sessionsDir, filename);
|
|
31952
32248
|
writeFileAtomic(filePath, JSON.stringify(session, null, 2));
|
|
31953
32249
|
dropLegacyFile(sessionsDir, meta);
|
|
31954
32250
|
return meta;
|
|
@@ -31960,7 +32256,7 @@ function listSessions(baseDir, logger = defaultLogger) {
|
|
|
31960
32256
|
return files.map((f) => {
|
|
31961
32257
|
let parsed;
|
|
31962
32258
|
try {
|
|
31963
|
-
parsed = JSON.parse(fs18.readFileSync(
|
|
32259
|
+
parsed = JSON.parse(fs18.readFileSync(path40.join(sessionsDir, f), "utf-8"));
|
|
31964
32260
|
} catch (err) {
|
|
31965
32261
|
if (err instanceof SyntaxError) return moveAside(sessionsDir, f, "is not valid JSON", logger);
|
|
31966
32262
|
logger.warn("sessionStore", `failed to read session file ${f}; skipping`, err);
|
|
@@ -31981,7 +32277,7 @@ function listSessions(baseDir, logger = defaultLogger) {
|
|
|
31981
32277
|
function moveAside(sessionsDir, file, reason, logger) {
|
|
31982
32278
|
const aside = `${file}.${Date.now()}.corrupt`;
|
|
31983
32279
|
try {
|
|
31984
|
-
fs18.renameSync(
|
|
32280
|
+
fs18.renameSync(path40.join(sessionsDir, file), path40.join(sessionsDir, aside));
|
|
31985
32281
|
logger.warn("sessionStore", `session file ${file} ${reason}; moved aside to ${aside} rather than overwritten`);
|
|
31986
32282
|
} catch {
|
|
31987
32283
|
logger.warn("sessionStore", `failed to read session file ${file}; skipping`);
|
|
@@ -31992,7 +32288,7 @@ function findSessionFile(sessionsDir, sessionId, logger) {
|
|
|
31992
32288
|
const files = fs18.readdirSync(sessionsDir).filter((f) => f.endsWith(".json"));
|
|
31993
32289
|
for (const f of files) {
|
|
31994
32290
|
try {
|
|
31995
|
-
const raw = fs18.readFileSync(
|
|
32291
|
+
const raw = fs18.readFileSync(path40.join(sessionsDir, f), "utf-8");
|
|
31996
32292
|
const session = JSON.parse(raw);
|
|
31997
32293
|
if (session.meta.id === sessionId) return f;
|
|
31998
32294
|
} catch (err) {
|
|
@@ -32007,7 +32303,7 @@ function loadSession(sessionId, baseDir, logger = defaultLogger) {
|
|
|
32007
32303
|
try {
|
|
32008
32304
|
const file = findSessionFile(sessionsDir, sessionId, logger);
|
|
32009
32305
|
if (file) {
|
|
32010
|
-
const raw = fs18.readFileSync(
|
|
32306
|
+
const raw = fs18.readFileSync(path40.join(sessionsDir, file), "utf-8");
|
|
32011
32307
|
const parsed = JSON.parse(raw);
|
|
32012
32308
|
if (hasScalarRunner(parsed)) {
|
|
32013
32309
|
logger.warn("sessionStore", `session file ${file}: ${REJECTION_MESSAGE}`);
|
|
@@ -32049,7 +32345,7 @@ function deleteSession(sessionId, baseDir, logger = defaultLogger) {
|
|
|
32049
32345
|
try {
|
|
32050
32346
|
const file = findSessionFile(sessionsDir, sessionId, logger);
|
|
32051
32347
|
if (file) {
|
|
32052
|
-
fs18.unlinkSync(
|
|
32348
|
+
fs18.unlinkSync(path40.join(sessionsDir, file));
|
|
32053
32349
|
return true;
|
|
32054
32350
|
}
|
|
32055
32351
|
} catch (err) {
|
|
@@ -32102,7 +32398,7 @@ var UNLEASED = {
|
|
|
32102
32398
|
};
|
|
32103
32399
|
var hostsHere = /* @__PURE__ */ new Set();
|
|
32104
32400
|
function leaseFile(sessionId, workspace) {
|
|
32105
|
-
return
|
|
32401
|
+
return path41.join(sessionDataDir(sessionId, workspace), LEASE_FILE);
|
|
32106
32402
|
}
|
|
32107
32403
|
function isRecord6(value) {
|
|
32108
32404
|
if (typeof value !== "object" || value === null) return false;
|
|
@@ -32191,7 +32487,7 @@ var LeaseHost = class {
|
|
|
32191
32487
|
*/
|
|
32192
32488
|
acquire(file, workspace, holder) {
|
|
32193
32489
|
ensureStateDirIgnored(workspace);
|
|
32194
|
-
ensurePrivateDir(
|
|
32490
|
+
ensurePrivateDir(path41.dirname(file));
|
|
32195
32491
|
const outcome = withFileLock(file, () => {
|
|
32196
32492
|
const current = readRecord(file);
|
|
32197
32493
|
const taken = current !== null && current.hostId !== this.id ? current : null;
|
|
@@ -32246,7 +32542,7 @@ var LeaseHost = class {
|
|
|
32246
32542
|
withFileLock(file, () => {
|
|
32247
32543
|
if (readRecord(file)?.hostId === this.id) fs19.rmSync(file, { force: true });
|
|
32248
32544
|
}, "release a session lease");
|
|
32249
|
-
fs19.rmdirSync(
|
|
32545
|
+
fs19.rmdirSync(path41.dirname(file));
|
|
32250
32546
|
} catch {
|
|
32251
32547
|
}
|
|
32252
32548
|
}
|
|
@@ -32439,10 +32735,14 @@ var TaskOrchestrator = class _TaskOrchestrator {
|
|
|
32439
32735
|
/** Conflict repairs owed with every slot taken and no scheduler to claim them: a freed slot starts them. */
|
|
32440
32736
|
owedRepairs = /* @__PURE__ */ new Set();
|
|
32441
32737
|
/**
|
|
32442
|
-
* Tasks Mark complete is landing after it ended their attempt
|
|
32443
|
-
*
|
|
32738
|
+
* Tasks Mark complete is landing after it ended their attempt, or that
|
|
32739
|
+
* their conflict resolver is landing, and that landing: in progress with
|
|
32740
|
+
* no attempt for as long as it takes, and not orphans for it. Both can be
|
|
32741
|
+
* landing one task at once, so each holds it for itself (see {@link holdWhileLanding}).
|
|
32444
32742
|
*/
|
|
32445
|
-
completing = /* @__PURE__ */ new
|
|
32743
|
+
completing = /* @__PURE__ */ new Map();
|
|
32744
|
+
/** Tasks {@link releaseTask} is tearing down once their landings have settled: none may begin another. */
|
|
32745
|
+
releasing = /* @__PURE__ */ new Set();
|
|
32446
32746
|
/** Tasks paused on a usage limit, the run's hold on them, and each runner's wait for its reset (ADR-0018, U1). */
|
|
32447
32747
|
limitPauses;
|
|
32448
32748
|
/**
|
|
@@ -32578,6 +32878,10 @@ var TaskOrchestrator = class _TaskOrchestrator {
|
|
|
32578
32878
|
getCompletionReceipt(taskId) {
|
|
32579
32879
|
return this.attempts.receipt(taskId);
|
|
32580
32880
|
}
|
|
32881
|
+
/** Which spawn of a live attempt's runner waits for Ordewell's tools to connect (ADR-0025); null when none does. */
|
|
32882
|
+
getToolsAttaching(taskId) {
|
|
32883
|
+
return this.attempts.attaching(taskId);
|
|
32884
|
+
}
|
|
32581
32885
|
/** Recent clean output of a task's latest attempt, running or ended; null if it never ran. */
|
|
32582
32886
|
getLiveOutput(taskId, opts) {
|
|
32583
32887
|
return this.attempts.liveTail(taskId, opts);
|
|
@@ -32742,12 +33046,13 @@ var TaskOrchestrator = class _TaskOrchestrator {
|
|
|
32742
33046
|
/**
|
|
32743
33047
|
* Something can still start that nobody has to ask for again: a start
|
|
32744
33048
|
* admitted and not yet claimed, a force start queued behind its
|
|
32745
|
-
* dependencies, a
|
|
32746
|
-
*
|
|
32747
|
-
* replay. A Stop is owed to
|
|
33049
|
+
* dependencies, a conflict repair owed a slot or the end of a start hold,
|
|
33050
|
+
* a task that resumes on its own at its usage limit's reset, or a start a
|
|
33051
|
+
* dirty checkout parked for Stash and continue to replay. A Stop is owed to
|
|
33052
|
+
* it, whether or not anything runs.
|
|
32748
33053
|
*/
|
|
32749
33054
|
get startsPending() {
|
|
32750
|
-
return this.admissions > 0 || this.queuedStarts.size > 0 || this.runs.blocked || this.store.allTasks.some(waitsForReset);
|
|
33055
|
+
return this.admissions > 0 || this.queuedStarts.size > 0 || this.owedRepairs.size > 0 || this.runs.blocked || this.store.allTasks.some(waitsForReset);
|
|
32751
33056
|
}
|
|
32752
33057
|
/**
|
|
32753
33058
|
* The plan's activity as one word for every surface: a start on its way,
|
|
@@ -33248,6 +33553,7 @@ var TaskOrchestrator = class _TaskOrchestrator {
|
|
|
33248
33553
|
* Work that did not land waits on the user — it never fails the task or
|
|
33249
33554
|
* blocks its dependents — unless its conflict is owed a repair, which takes
|
|
33250
33555
|
* the slot the ending attempt freed and never one more than the run allows.
|
|
33556
|
+
* A planner turn's start hold holds it as it holds any new spawn (ADR-0029).
|
|
33251
33557
|
* The repair follows on from the start whose work it repairs: a Stop since
|
|
33252
33558
|
* that was admitted withdraws it.
|
|
33253
33559
|
*/
|
|
@@ -33255,20 +33561,22 @@ var TaskOrchestrator = class _TaskOrchestrator {
|
|
|
33255
33561
|
this.store.markAwaitingUser(task2.id, "conflict");
|
|
33256
33562
|
this.say(outcome.messages);
|
|
33257
33563
|
if (outcome.kind !== "repair-needed") return;
|
|
33258
|
-
|
|
33564
|
+
const waitsFor = this.startHolds > 0 ? "the planner's turn ends" : this.slotsFull() ? "a slot is free" : null;
|
|
33565
|
+
if (!waitsFor) {
|
|
33259
33566
|
const claimed = this.claim(task2.id, admission);
|
|
33260
33567
|
if ("refused" in claimed) this.tell("warn", `The conflict of task "${task2.title}" is not repaired: ${claimed.refused}`);
|
|
33261
33568
|
else this.unawaited(claimed.spawned, `Could not start the repair of task "${task2.title}"`);
|
|
33262
33569
|
} else {
|
|
33263
33570
|
this.store.markPending(task2.id);
|
|
33264
33571
|
if (!this.isScheduling) this.owedRepairs.add(task2.id);
|
|
33265
|
-
this.tell("info", `Task "${task2.title}" is repaired once
|
|
33572
|
+
this.tell("info", `Task "${task2.title}" is repaired once ${waitsFor}.`);
|
|
33266
33573
|
}
|
|
33267
33574
|
}
|
|
33268
|
-
/** Start the repairs {@link settleUnlanded} left owed, as slots allow. */
|
|
33575
|
+
/** Start the repairs {@link settleUnlanded} left owed, as slots and any start hold allow. */
|
|
33269
33576
|
startOwedRepairs(admission) {
|
|
33577
|
+
if (this.startHolds > 0) return;
|
|
33270
33578
|
for (const id2 of [...this.owedRepairs]) {
|
|
33271
|
-
if (this.
|
|
33579
|
+
if (this.slotsFull()) return;
|
|
33272
33580
|
this.owedRepairs.delete(id2);
|
|
33273
33581
|
const task2 = this.store.get(id2);
|
|
33274
33582
|
if (!task2 || task2.status !== "pending" || this.attempts.has(id2) || !this.landing.nextRepair(id2)) continue;
|
|
@@ -33278,13 +33586,31 @@ var TaskOrchestrator = class _TaskOrchestrator {
|
|
|
33278
33586
|
}
|
|
33279
33587
|
}
|
|
33280
33588
|
async landResolved(resolverId, admission) {
|
|
33281
|
-
const resolved =
|
|
33589
|
+
const resolved = this.landing.landResolved(resolverId, (id2) => this.releasing.has(id2));
|
|
33282
33590
|
if (!resolved) return;
|
|
33283
|
-
const { task: task2
|
|
33591
|
+
const { task: task2 } = resolved;
|
|
33592
|
+
const outcome = await this.holdWhileLanding(task2.id, resolved.outcome);
|
|
33593
|
+
if (this.store.get(task2.id)?.status === "completed") return;
|
|
33284
33594
|
await this.applyLanding(task2, outcome, admission, () => {
|
|
33285
33595
|
if (task2.verdict) this.logAndArchive(task2, task2.verdict);
|
|
33286
33596
|
});
|
|
33287
33597
|
}
|
|
33598
|
+
/**
|
|
33599
|
+
* Hold a task for as long as `landing` is merging its worktree. Its end lets
|
|
33600
|
+
* go of its own hold only: another landing of the task begun meanwhile
|
|
33601
|
+
* still holds it.
|
|
33602
|
+
*/
|
|
33603
|
+
async holdWhileLanding(taskId, landing2) {
|
|
33604
|
+
const holds2 = this.completing.get(taskId) ?? /* @__PURE__ */ new Set();
|
|
33605
|
+
holds2.add(landing2);
|
|
33606
|
+
this.completing.set(taskId, holds2);
|
|
33607
|
+
try {
|
|
33608
|
+
return await landing2;
|
|
33609
|
+
} finally {
|
|
33610
|
+
holds2.delete(landing2);
|
|
33611
|
+
if (holds2.size === 0 && this.completing.get(taskId) === holds2) this.completing.delete(taskId);
|
|
33612
|
+
}
|
|
33613
|
+
}
|
|
33288
33614
|
getReadyTasks() {
|
|
33289
33615
|
return this.readiness().ready;
|
|
33290
33616
|
}
|
|
@@ -33392,17 +33718,27 @@ var TaskOrchestrator = class _TaskOrchestrator {
|
|
|
33392
33718
|
* as {@link cancelTask} does, but its worktree goes: no task is left to land
|
|
33393
33719
|
* it into. A spawn still in flight just loses its attempt,
|
|
33394
33720
|
* which is what makes {@link Attempts} kill the session it is about to
|
|
33395
|
-
* receive.
|
|
33396
|
-
*
|
|
33721
|
+
* receive. Every landing of it in flight — Mark complete's, its resolver's —
|
|
33722
|
+
* settles first, so its worktree is not torn down under a merge, and none
|
|
33723
|
+
* begins while it goes; a repair a landing claims is cancelled as any
|
|
33724
|
+
* running attempt is. The id's cross-attempt bookkeeping
|
|
33725
|
+
* goes too — a hold or retry count kept for a task that no longer exists
|
|
33726
|
+
* would be inherited by nothing.
|
|
33397
33727
|
*/
|
|
33398
33728
|
async releaseTask(taskId) {
|
|
33399
33729
|
this.limitPauses.cancel(taskId);
|
|
33400
|
-
|
|
33401
|
-
|
|
33402
|
-
|
|
33403
|
-
this.attempts.
|
|
33404
|
-
if (
|
|
33405
|
-
|
|
33730
|
+
for (let holds2 = this.completing.get(taskId); holds2; holds2 = this.completing.get(taskId)) await Promise.allSettled(holds2);
|
|
33731
|
+
this.releasing.add(taskId);
|
|
33732
|
+
try {
|
|
33733
|
+
const phase = this.attempts.phaseOf(taskId);
|
|
33734
|
+
if (phase === "running" || phase === "integrating") await this.cancelAttempt(taskId, { keep: false });
|
|
33735
|
+
else {
|
|
33736
|
+
this.attempts.end(taskId, "release");
|
|
33737
|
+
if (this.store.get(taskId)?.status === "in_progress") this.store.markPending(taskId);
|
|
33738
|
+
await this.runs.release(taskId, { keep: false });
|
|
33739
|
+
}
|
|
33740
|
+
} finally {
|
|
33741
|
+
this.releasing.delete(taskId);
|
|
33406
33742
|
}
|
|
33407
33743
|
this.onHold.delete(taskId);
|
|
33408
33744
|
this.retryCounts.delete(taskId);
|
|
@@ -33411,17 +33747,12 @@ var TaskOrchestrator = class _TaskOrchestrator {
|
|
|
33411
33747
|
async markTaskComplete(taskId) {
|
|
33412
33748
|
const task2 = this.store.get(taskId);
|
|
33413
33749
|
if (!task2 || task2.status === "completed") return;
|
|
33414
|
-
if (this.completing.has(taskId)) return;
|
|
33750
|
+
if (this.completing.has(taskId) || this.releasing.has(taskId)) return;
|
|
33415
33751
|
const admission = this.admittedNow();
|
|
33416
33752
|
const ended = this.attempts.end(taskId, "complete");
|
|
33417
33753
|
const verdict = this.attempts.markComplete(task2);
|
|
33418
|
-
this.
|
|
33419
|
-
|
|
33420
|
-
try {
|
|
33421
|
-
landing2 = await (ended?.integration ?? this.landing.landVouched(task2));
|
|
33422
|
-
} finally {
|
|
33423
|
-
this.completing.delete(taskId);
|
|
33424
|
-
}
|
|
33754
|
+
const landed2 = ended?.integration ?? this.landing.landVouched(task2);
|
|
33755
|
+
const landing2 = await this.holdWhileLanding(taskId, landed2);
|
|
33425
33756
|
this.store.setTaskVerdict(taskId, verdict);
|
|
33426
33757
|
const handoff = task2.type === "user" ? userTaskHandoff(task2.userSteps) : void 0;
|
|
33427
33758
|
this.attempts.handOver(taskId, summarizeOutput(verdict.reason, ""), handoff);
|
|
@@ -33710,7 +34041,7 @@ ${now.message}` : LIMIT_RESET_MESSAGE;
|
|
|
33710
34041
|
if (!await this.openForAdmitted(taskId, origin, resume, admission)) return;
|
|
33711
34042
|
if (this.attempts.has(taskId)) return;
|
|
33712
34043
|
this.refuseOpsDuringMerge(taskId, forced);
|
|
33713
|
-
if (forced.queued && this.holdsQueued(taskId, forced)) {
|
|
34044
|
+
if (forced.queued && (this.holdsQueued(taskId, forced) || this.slotsFull())) {
|
|
33714
34045
|
this.queuedStarts.set(taskId, { pastGate: forced.pastGate });
|
|
33715
34046
|
return;
|
|
33716
34047
|
}
|
|
@@ -33769,8 +34100,9 @@ ${now.message}` : LIMIT_RESET_MESSAGE;
|
|
|
33769
34100
|
this.queuedStarts.delete(id2);
|
|
33770
34101
|
continue;
|
|
33771
34102
|
}
|
|
34103
|
+
if (this.slotsFull()) continue;
|
|
33772
34104
|
const waits = this.startWaits(task2, task2.research ? void 0 : { queued: true, pastGate: queued.pastGate });
|
|
33773
|
-
if (task2.research &&
|
|
34105
|
+
if (task2.research && waits.length > 0) continue;
|
|
33774
34106
|
if (waits.some((w) => QUEUED_WAITS.has(w.kind))) continue;
|
|
33775
34107
|
if (this.leftToScheduler(task2) && queued.pastGate.length === 0) {
|
|
33776
34108
|
this.queuedStarts.delete(id2);
|
|
@@ -33799,6 +34131,9 @@ ${now.message}` : LIMIT_RESET_MESSAGE;
|
|
|
33799
34131
|
const task2 = this.store.get(taskId);
|
|
33800
34132
|
return !!task2 && this.startWaits(task2, forced).some((w) => QUEUED_WAITS.has(w.kind) || w.kind === "dependencies" || w.kind === "merge-gate");
|
|
33801
34133
|
}
|
|
34134
|
+
slotsFull() {
|
|
34135
|
+
return this.attempts.size >= this.config.maxParallelSessions;
|
|
34136
|
+
}
|
|
33802
34137
|
sayQueuedAtGate(task2, queued, unmerged) {
|
|
33803
34138
|
const titles = unmerged.map((id2) => `"${this.store.get(id2)?.title ?? id2}"`).join(", ");
|
|
33804
34139
|
const notice = `Task "${task2.title}" waits for Merge all: the work of ${titles} is not merged into your branch yet. Merge all to start it, or force start it again to start it without that work.`;
|
|
@@ -33899,7 +34234,7 @@ ${now.message}` : LIMIT_RESET_MESSAGE;
|
|
|
33899
34234
|
*/
|
|
33900
34235
|
async settleRun(after) {
|
|
33901
34236
|
if (!this.store.isFinished()) this.transition({ type: "reopen" });
|
|
33902
|
-
if (this.attempts.size > 0) return;
|
|
34237
|
+
if (this.attempts.size > 0 || this.owedRepairs.size > 0) return;
|
|
33903
34238
|
if (this.runState.kind === "held") return;
|
|
33904
34239
|
if (this.runState.kind !== "scheduling") {
|
|
33905
34240
|
if (after === "tick") {
|
|
@@ -34193,21 +34528,45 @@ var Planner = class {
|
|
|
34193
34528
|
};
|
|
34194
34529
|
|
|
34195
34530
|
// src/services/ModelResolver.ts
|
|
34196
|
-
var ModelResolver = class {
|
|
34531
|
+
var ModelResolver = class _ModelResolver {
|
|
34197
34532
|
constructor(registry, config, deps = {}) {
|
|
34198
34533
|
this.registry = registry;
|
|
34199
34534
|
this.config = config;
|
|
34200
|
-
this.discovery = new ModelDiscovery(registry, deps.execImpl, deps.fetchImpl);
|
|
34535
|
+
this.discovery = new ModelDiscovery(registry, deps.execImpl, deps.fetchImpl, void 0, void 0, void 0, deps.env);
|
|
34201
34536
|
this.fetchImpl = deps.fetchImpl;
|
|
34537
|
+
this.execImpl = deps.execImpl;
|
|
34538
|
+
this.shared = { vendor: { options: null, load: null, errors: {}, generation: 0 }, root: this, views: /* @__PURE__ */ new Map() };
|
|
34202
34539
|
}
|
|
34203
34540
|
registry;
|
|
34204
34541
|
config;
|
|
34205
34542
|
discovery;
|
|
34206
34543
|
fetchImpl;
|
|
34207
|
-
|
|
34208
|
-
|
|
34209
|
-
|
|
34210
|
-
|
|
34544
|
+
execImpl;
|
|
34545
|
+
shared;
|
|
34546
|
+
get vendor() {
|
|
34547
|
+
return this.shared.vendor;
|
|
34548
|
+
}
|
|
34549
|
+
/**
|
|
34550
|
+
* This resolver for runners launched with another environment, a
|
|
34551
|
+
* workspace's own: their catalogs are discovered and cached under `env`, as
|
|
34552
|
+
* `CLAUDE_CONFIG_DIR` there can name another account, while the vendor
|
|
34553
|
+
* catalogs stay this resolver's, fetched once. One view per `key`, made on
|
|
34554
|
+
* its first ask: a later ask with the same key gets that view, whatever
|
|
34555
|
+
* `env` it passes.
|
|
34556
|
+
*/
|
|
34557
|
+
withRunnerEnv(key, env) {
|
|
34558
|
+
let view = this.shared.views.get(key);
|
|
34559
|
+
if (!view) {
|
|
34560
|
+
view = new _ModelResolver(this.registry, this.config, { fetchImpl: this.fetchImpl, execImpl: this.execImpl, env });
|
|
34561
|
+
view.shared = this.shared;
|
|
34562
|
+
this.shared.views.set(key, view);
|
|
34563
|
+
}
|
|
34564
|
+
return view;
|
|
34565
|
+
}
|
|
34566
|
+
/** Every runner-environment catalog this resolver's family holds: its root's and each view's. */
|
|
34567
|
+
runnerCatalogs() {
|
|
34568
|
+
return [this.shared.root, ...this.shared.views.values()].map((resolver) => resolver.discovery);
|
|
34569
|
+
}
|
|
34211
34570
|
static builtinOptions() {
|
|
34212
34571
|
return toOrchestratorOptions({}, ORCHESTRATOR_SHORTCUTS);
|
|
34213
34572
|
}
|
|
@@ -34223,16 +34582,16 @@ var ModelResolver = class {
|
|
|
34223
34582
|
return Object.fromEntries(discovered);
|
|
34224
34583
|
}
|
|
34225
34584
|
async pickerOptions() {
|
|
34226
|
-
if (this.
|
|
34227
|
-
if (!this.
|
|
34585
|
+
if (this.vendor.options) return this.vendor.options;
|
|
34586
|
+
if (!this.vendor.load) {
|
|
34228
34587
|
const load = this.loadPickerOptions();
|
|
34229
|
-
this.
|
|
34588
|
+
this.vendor.load = load;
|
|
34230
34589
|
void load.finally(() => {
|
|
34231
|
-
if (this.
|
|
34590
|
+
if (this.vendor.load === load) this.vendor.load = null;
|
|
34232
34591
|
}).catch(() => {
|
|
34233
34592
|
});
|
|
34234
34593
|
}
|
|
34235
|
-
return this.
|
|
34594
|
+
return this.vendor.load;
|
|
34236
34595
|
}
|
|
34237
34596
|
/**
|
|
34238
34597
|
* Start loading the vendor catalogs without waiting for them, unless they
|
|
@@ -34240,20 +34599,25 @@ var ModelResolver = class {
|
|
|
34240
34599
|
* known yet reads it from them on a later call, never waiting on the fetch.
|
|
34241
34600
|
*/
|
|
34242
34601
|
warmPickerOptions() {
|
|
34243
|
-
if (this.
|
|
34602
|
+
if (this.vendor.options || this.vendor.load) return;
|
|
34244
34603
|
this.pickerOptions().catch(() => {
|
|
34245
34604
|
});
|
|
34246
34605
|
}
|
|
34247
34606
|
async loadPickerOptions() {
|
|
34607
|
+
const vendor = this.vendor;
|
|
34608
|
+
const generation2 = vendor.generation;
|
|
34248
34609
|
const { apiKeys, baseUrls } = collectProviderCredentials(this.config);
|
|
34249
34610
|
const { models, errors } = await fetchAllProviderModels({
|
|
34250
34611
|
apiKeys,
|
|
34251
34612
|
baseUrls,
|
|
34252
34613
|
fetchImpl: this.fetchImpl
|
|
34253
34614
|
});
|
|
34254
|
-
|
|
34255
|
-
|
|
34256
|
-
|
|
34615
|
+
const options = toOrchestratorOptions(models, ORCHESTRATOR_SHORTCUTS);
|
|
34616
|
+
if (vendor.generation === generation2) {
|
|
34617
|
+
vendor.errors = errors;
|
|
34618
|
+
vendor.options = options;
|
|
34619
|
+
}
|
|
34620
|
+
return options;
|
|
34257
34621
|
}
|
|
34258
34622
|
/**
|
|
34259
34623
|
* Per-provider failures from the most recent `pickerOptions()` fetch, keyed
|
|
@@ -34262,7 +34626,7 @@ var ModelResolver = class {
|
|
|
34262
34626
|
* key/endpoint is set but whose catalog could not be reached.
|
|
34263
34627
|
*/
|
|
34264
34628
|
getDiscoveryErrors() {
|
|
34265
|
-
return this.
|
|
34629
|
+
return this.vendor.errors;
|
|
34266
34630
|
}
|
|
34267
34631
|
async refresh() {
|
|
34268
34632
|
const options = await this.pickerOptions();
|
|
@@ -34273,8 +34637,25 @@ var ModelResolver = class {
|
|
|
34273
34637
|
this.config.setProviderModelLists(lists);
|
|
34274
34638
|
return lists;
|
|
34275
34639
|
}
|
|
34640
|
+
/** Forget every runner catalog, each workspace's included; the vendor catalogs stay. */
|
|
34276
34641
|
refreshRunnerModels() {
|
|
34277
|
-
this.discovery.clear();
|
|
34642
|
+
for (const discovery of this.runnerCatalogs()) discovery.clear();
|
|
34643
|
+
}
|
|
34644
|
+
/**
|
|
34645
|
+
* The models a pick for `provider` is judged against: a harness planner's
|
|
34646
|
+
* own runner catalog, or the vendor picker catalog (ADR-0009). Discovered
|
|
34647
|
+
* if not held. Never throws: a catalog that cannot be had is empty.
|
|
34648
|
+
*/
|
|
34649
|
+
async plannerCatalog(provider) {
|
|
34650
|
+
const runner = runnerForProvider(provider);
|
|
34651
|
+
if (runner) return candidatesOf((await this.modelsForRunners([runner]))[runner] ?? []);
|
|
34652
|
+
return (await this.pickerOptions().catch(() => [])).map((o) => ({ id: o.id }));
|
|
34653
|
+
}
|
|
34654
|
+
/** {@link plannerCatalog} from what is already held, so a cold catalog is empty; never discovers. */
|
|
34655
|
+
cachedPlannerCatalog(provider) {
|
|
34656
|
+
const runner = runnerForProvider(provider);
|
|
34657
|
+
if (runner) return candidatesOf(this.getCachedRunnerModels(runner));
|
|
34658
|
+
return this.getCachedPickerOptions().map((o) => ({ id: o.id }));
|
|
34278
34659
|
}
|
|
34279
34660
|
/**
|
|
34280
34661
|
* Synchronous reads of whatever the last `modelsForRunners`/`pickerOptions`
|
|
@@ -34285,7 +34666,7 @@ var ModelResolver = class {
|
|
|
34285
34666
|
return this.discovery.getCached(runner) ?? [];
|
|
34286
34667
|
}
|
|
34287
34668
|
getCachedPickerOptions() {
|
|
34288
|
-
return this.
|
|
34669
|
+
return this.vendor.options ?? [];
|
|
34289
34670
|
}
|
|
34290
34671
|
/**
|
|
34291
34672
|
* A model's context window from whatever catalog is already cached (#49),
|
|
@@ -34298,7 +34679,7 @@ var ModelResolver = class {
|
|
|
34298
34679
|
*/
|
|
34299
34680
|
contextWindowFor(source, model) {
|
|
34300
34681
|
if (!model) return void 0;
|
|
34301
|
-
const window2 = isApiProvider(source) ? this.
|
|
34682
|
+
const window2 = isApiProvider(source) ? this.vendor.options?.find((o) => o.apiProvider === source && o.id === prefixModelId(source, stripModelPrefix(model, source)))?.contextWindow : this.discovery.getCached(source)?.find((m) => m.modelId === model)?.contextWindow;
|
|
34302
34683
|
return window2 && window2 > 0 ? window2 : void 0;
|
|
34303
34684
|
}
|
|
34304
34685
|
/**
|
|
@@ -34308,14 +34689,16 @@ var ModelResolver = class {
|
|
|
34308
34689
|
*/
|
|
34309
34690
|
maxOutputTokensFor(source, model) {
|
|
34310
34691
|
if (!model || !isApiProvider(source)) return void 0;
|
|
34311
|
-
const cap = this.
|
|
34692
|
+
const cap = this.vendor.options?.find((o) => o.apiProvider === source && o.id === prefixModelId(source, stripModelPrefix(model, source)))?.maxOutputTokens;
|
|
34312
34693
|
return cap && cap > 0 ? cap : void 0;
|
|
34313
34694
|
}
|
|
34695
|
+
/** Forget every catalog: the vendor ones, and each runner environment's. */
|
|
34314
34696
|
invalidate() {
|
|
34315
|
-
this.
|
|
34316
|
-
this.
|
|
34317
|
-
this.
|
|
34318
|
-
this.
|
|
34697
|
+
this.refreshRunnerModels();
|
|
34698
|
+
this.vendor.generation++;
|
|
34699
|
+
this.vendor.options = null;
|
|
34700
|
+
this.vendor.load = null;
|
|
34701
|
+
this.vendor.errors = {};
|
|
34319
34702
|
if (!this.fetchImpl) {
|
|
34320
34703
|
for (const provider of PROVIDER_PRIORITY) {
|
|
34321
34704
|
const baseUrl = this.config.getProviderBaseUrl(provider);
|
|
@@ -34325,6 +34708,9 @@ var ModelResolver = class {
|
|
|
34325
34708
|
}
|
|
34326
34709
|
}
|
|
34327
34710
|
};
|
|
34711
|
+
function candidatesOf(models) {
|
|
34712
|
+
return models.map((m) => ({ id: m.modelId, variants: m.variants }));
|
|
34713
|
+
}
|
|
34328
34714
|
|
|
34329
34715
|
// src/services/RunnerInstallation.ts
|
|
34330
34716
|
import { exec as exec4 } from "child_process";
|
|
@@ -35034,7 +35420,7 @@ function isoInstant(value) {
|
|
|
35034
35420
|
}
|
|
35035
35421
|
|
|
35036
35422
|
// src/services/harness/openCodeGoLimits.ts
|
|
35037
|
-
import { homedir as
|
|
35423
|
+
import { homedir as homedir9 } from "os";
|
|
35038
35424
|
|
|
35039
35425
|
// src/services/harness/openCodeCredentials.ts
|
|
35040
35426
|
import { createHash as createHash5 } from "crypto";
|
|
@@ -35102,12 +35488,12 @@ function openCodeStorePaths(env, home) {
|
|
|
35102
35488
|
return { db: file === ":memory:" ? null : resolve13(data, file), auth: join38(data, "auth.json") };
|
|
35103
35489
|
}
|
|
35104
35490
|
function openCodeGoLoginReader(deps = {}) {
|
|
35105
|
-
const
|
|
35491
|
+
const homedir10 = deps.homedir ?? osHomedir;
|
|
35106
35492
|
let last;
|
|
35107
35493
|
return {
|
|
35108
35494
|
async read(env, signal) {
|
|
35109
35495
|
if (signal.aborted) return { kind: "unreadable" };
|
|
35110
|
-
const paths = openCodeStorePaths(env,
|
|
35496
|
+
const paths = openCodeStorePaths(env, homedir10());
|
|
35111
35497
|
const [db, wal, auth] = await Promise.all([
|
|
35112
35498
|
paths.db ? stamp(paths.db) : null,
|
|
35113
35499
|
paths.db ? stamp(`${paths.db}-wal`) : null,
|
|
@@ -35161,8 +35547,8 @@ function v2Login(db, row) {
|
|
|
35161
35547
|
login: { route: "console", owner: digest("console", db, row.id, account, orgID ?? ""), access: value.access, ...orgID ? { orgID } : {}, expires: value.expires }
|
|
35162
35548
|
};
|
|
35163
35549
|
}
|
|
35164
|
-
async function legacyLogin(
|
|
35165
|
-
const content = env.OPENCODE_AUTH_CONTENT ?? await readSmall(
|
|
35550
|
+
async function legacyLogin(path47, env) {
|
|
35551
|
+
const content = env.OPENCODE_AUTH_CONTENT ?? await readSmall(path47);
|
|
35166
35552
|
if (content === void 0) return env[ENV_KEY] ? { kind: "unsupported" } : { kind: "none" };
|
|
35167
35553
|
if (content === null) return { kind: "unreadable" };
|
|
35168
35554
|
const auth = parseJson(content);
|
|
@@ -35171,15 +35557,15 @@ async function legacyLogin(path46, env) {
|
|
|
35171
35557
|
if (entry === void 0) return env[ENV_KEY] ? { kind: "unsupported" } : { kind: "none" };
|
|
35172
35558
|
if (!isRecord9(entry) || entry.type !== "api") return { kind: "unsupported" };
|
|
35173
35559
|
if (typeof entry.key !== "string" || entry.key.length === 0) return { kind: "unreadable" };
|
|
35174
|
-
return { kind: "login", login: { route: "key", owner: digest("v1",
|
|
35560
|
+
return { kind: "login", login: { route: "key", owner: digest("v1", path47, entry.key), key: entry.key } };
|
|
35175
35561
|
}
|
|
35176
|
-
function queryStore(
|
|
35562
|
+
function queryStore(path47, signal, deps) {
|
|
35177
35563
|
return new Promise((settle) => {
|
|
35178
35564
|
let worker;
|
|
35179
35565
|
try {
|
|
35180
35566
|
worker = (deps.spawnWorker ?? ((source, options) => new Worker(source, options)))(WORKER_SOURCE, {
|
|
35181
35567
|
eval: true,
|
|
35182
|
-
workerData: { path:
|
|
35568
|
+
workerData: { path: path47, integrations: GO_INTEGRATIONS, busyMs: BUSY_MS, maxValueBytes: MAX_VALUE_BYTES },
|
|
35183
35569
|
execArgv: deps.workerExecArgv ?? [],
|
|
35184
35570
|
resourceLimits: { maxOldGenerationSizeMb: 64 }
|
|
35185
35571
|
});
|
|
@@ -35206,19 +35592,19 @@ function queryStore(path46, signal, deps) {
|
|
|
35206
35592
|
worker.once("exit", aborted);
|
|
35207
35593
|
});
|
|
35208
35594
|
}
|
|
35209
|
-
async function stamp(
|
|
35595
|
+
async function stamp(path47) {
|
|
35210
35596
|
try {
|
|
35211
|
-
const stat = await fs20.stat(
|
|
35597
|
+
const stat = await fs20.stat(path47);
|
|
35212
35598
|
return [stat.ino, stat.size, stat.mtimeMs];
|
|
35213
35599
|
} catch {
|
|
35214
35600
|
return null;
|
|
35215
35601
|
}
|
|
35216
35602
|
}
|
|
35217
|
-
async function readSmall(
|
|
35603
|
+
async function readSmall(path47) {
|
|
35218
35604
|
try {
|
|
35219
|
-
const stat = await fs20.stat(
|
|
35605
|
+
const stat = await fs20.stat(path47);
|
|
35220
35606
|
if (!stat.isFile() || stat.size > MAX_AUTH_FILE_BYTES) return null;
|
|
35221
|
-
return await fs20.readFile(
|
|
35607
|
+
return await fs20.readFile(path47, "utf8");
|
|
35222
35608
|
} catch (err) {
|
|
35223
35609
|
return err.code === "ENOENT" ? void 0 : null;
|
|
35224
35610
|
}
|
|
@@ -35251,7 +35637,7 @@ function openCodeGoAccountLimitCollector(options) {
|
|
|
35251
35637
|
label: "OpenCode Go",
|
|
35252
35638
|
provider: "opencode-go",
|
|
35253
35639
|
scope: () => {
|
|
35254
|
-
const paths = openCodeStorePaths(env(), (deps.reader?.homedir ??
|
|
35640
|
+
const paths = openCodeStorePaths(env(), (deps.reader?.homedir ?? homedir9)());
|
|
35255
35641
|
return ["opencode", paths.db ?? ":memory:", paths.auth].join("\0");
|
|
35256
35642
|
},
|
|
35257
35643
|
acquire: async (signal) => {
|
|
@@ -36347,20 +36733,20 @@ function createAiService(config, deps) {
|
|
|
36347
36733
|
}
|
|
36348
36734
|
|
|
36349
36735
|
// src/services/createSession.ts
|
|
36350
|
-
import * as
|
|
36736
|
+
import * as path44 from "path";
|
|
36351
36737
|
|
|
36352
36738
|
// src/services/handoffReferences.ts
|
|
36353
36739
|
import * as fs21 from "fs";
|
|
36354
|
-
import * as
|
|
36740
|
+
import * as path42 from "path";
|
|
36355
36741
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
36356
36742
|
function resolveHandoffReferences(handoff, workspace) {
|
|
36357
36743
|
const checked = /* @__PURE__ */ new Map();
|
|
36358
36744
|
const contained = (candidate) => {
|
|
36359
36745
|
try {
|
|
36360
36746
|
const root = fs21.realpathSync(workspace);
|
|
36361
|
-
const resolved = fs21.realpathSync(
|
|
36362
|
-
const relative17 =
|
|
36363
|
-
return relative17 !== ".." && !relative17.startsWith(`..${
|
|
36747
|
+
const resolved = fs21.realpathSync(path42.resolve(root, candidate));
|
|
36748
|
+
const relative17 = path42.relative(root, resolved);
|
|
36749
|
+
return relative17 !== ".." && !relative17.startsWith(`..${path42.sep}`) && !path42.isAbsolute(relative17) ? resolved : void 0;
|
|
36364
36750
|
} catch {
|
|
36365
36751
|
return void 0;
|
|
36366
36752
|
}
|
|
@@ -36381,7 +36767,7 @@ function resolveHandoffReferences(handoff, workspace) {
|
|
|
36381
36767
|
}
|
|
36382
36768
|
} else if (/^[0-9a-fA-F]{4,64}$/.test(ref.sha)) {
|
|
36383
36769
|
const repo = contained(ref.repo ?? ".");
|
|
36384
|
-
if (repo && fs21.existsSync(
|
|
36770
|
+
if (repo && fs21.existsSync(path42.join(repo, ".git"))) {
|
|
36385
36771
|
const result2 = spawnSync2("git", ["--no-optional-locks", "--no-replace-objects", "cat-file", "-t", ref.sha], {
|
|
36386
36772
|
cwd: repo,
|
|
36387
36773
|
encoding: "utf8",
|
|
@@ -37733,7 +38119,8 @@ var SessionEventRelay = class {
|
|
|
37733
38119
|
this.checkpointQuestion(t.id),
|
|
37734
38120
|
this.waitsForApproval(t.id),
|
|
37735
38121
|
this.orchestrator.holdReason(t.id),
|
|
37736
|
-
this.orchestrator.getCompletionReceipt(t.id)
|
|
38122
|
+
this.orchestrator.getCompletionReceipt(t.id),
|
|
38123
|
+
this.orchestrator.getToolsAttaching(t.id)
|
|
37737
38124
|
)),
|
|
37738
38125
|
...runMerge ? { runMerge } : {},
|
|
37739
38126
|
activity: this.orchestrator.activity
|
|
@@ -37855,10 +38242,10 @@ var SessionEventRelay = class {
|
|
|
37855
38242
|
|
|
37856
38243
|
// src/utils/taskLogStore.ts
|
|
37857
38244
|
import * as fs22 from "fs";
|
|
37858
|
-
import * as
|
|
38245
|
+
import * as path43 from "path";
|
|
37859
38246
|
var EXTENSION = ".jsonl";
|
|
37860
38247
|
function taskDir({ baseDir, sessionId }, taskId) {
|
|
37861
|
-
return
|
|
38248
|
+
return path43.join(sessionDataDir(sessionId, baseDir), "tasks", idSegment(taskId));
|
|
37862
38249
|
}
|
|
37863
38250
|
function listTaskLogAttempts(location, taskId) {
|
|
37864
38251
|
let names;
|
|
@@ -37876,7 +38263,7 @@ function openTaskLog(location, taskId, attempt) {
|
|
|
37876
38263
|
const next = (listTaskLogAttempts(location, taskId).pop() ?? 0) + 1;
|
|
37877
38264
|
let number = attempt !== void 0 && Number.isInteger(attempt) && attempt >= 1 ? attempt : next;
|
|
37878
38265
|
for (; ; ) {
|
|
37879
|
-
const file =
|
|
38266
|
+
const file = path43.join(dir, `${number}${EXTENSION}`);
|
|
37880
38267
|
try {
|
|
37881
38268
|
fs22.writeFileSync(file, "", { flag: "wx" });
|
|
37882
38269
|
} catch (err) {
|
|
@@ -37900,15 +38287,15 @@ function prepareTaskOutputFile(location, taskId, attempt) {
|
|
|
37900
38287
|
ensureStateDirIgnored(location.baseDir);
|
|
37901
38288
|
const dir = taskDir(location, taskId);
|
|
37902
38289
|
fs22.mkdirSync(dir, { recursive: true });
|
|
37903
|
-
return
|
|
38290
|
+
return path43.join(dir, `${attempt}.output.txt`);
|
|
37904
38291
|
}
|
|
37905
38292
|
function taskPatchFile(location, taskId, attempt) {
|
|
37906
|
-
return
|
|
38293
|
+
return path43.join(taskDir(location, taskId), `${attempt}.patch`);
|
|
37907
38294
|
}
|
|
37908
38295
|
function saveTaskPatch(location, taskId, attempt, patch) {
|
|
37909
38296
|
ensureStateDirIgnored(location.baseDir);
|
|
37910
38297
|
const file = taskPatchFile(location, taskId, attempt);
|
|
37911
|
-
fs22.mkdirSync(
|
|
38298
|
+
fs22.mkdirSync(path43.dirname(file), { recursive: true });
|
|
37912
38299
|
const partial = `${file}.${process.pid}-${Date.now()}.partial`;
|
|
37913
38300
|
fs22.writeFileSync(partial, patch, { flag: "wx" });
|
|
37914
38301
|
fs22.renameSync(partial, file);
|
|
@@ -37929,7 +38316,7 @@ function readTaskLog(location, taskId, attempt) {
|
|
|
37929
38316
|
if (!Number.isInteger(attempt) || attempt < 1) return [];
|
|
37930
38317
|
let raw;
|
|
37931
38318
|
try {
|
|
37932
|
-
raw = fs22.readFileSync(
|
|
38319
|
+
raw = fs22.readFileSync(path43.join(taskDir(location, taskId), `${attempt}${EXTENSION}`), "utf-8");
|
|
37933
38320
|
} catch {
|
|
37934
38321
|
return [];
|
|
37935
38322
|
}
|
|
@@ -39958,7 +40345,7 @@ function createSession(deps) {
|
|
|
39958
40345
|
...deps.openTaskLog ? {} : {
|
|
39959
40346
|
keepPatch: (taskId, attempt, patch) => {
|
|
39960
40347
|
const location = session.taskLogLocation;
|
|
39961
|
-
return
|
|
40348
|
+
return path44.relative(location.baseDir, saveTaskPatch(location, taskId, attempt, patch)).split(path44.sep).join("/");
|
|
39962
40349
|
}
|
|
39963
40350
|
},
|
|
39964
40351
|
recordHandoff: (sessionId, taskId, handoff) => taskLogs.note(sessionId, taskId, { type: "handoff", handoff }),
|
|
@@ -42087,6 +42474,7 @@ var StructuredSession = class extends AbstractRunnerSession {
|
|
|
42087
42474
|
*/
|
|
42088
42475
|
async startAttached(opts) {
|
|
42089
42476
|
const { server: server4, scope, shell, savePatch, webReads } = this.launch.tools;
|
|
42477
|
+
this.launch.onAttaching?.(1);
|
|
42090
42478
|
const spawned = await spawnAttached({
|
|
42091
42479
|
runner: this.launch.runner,
|
|
42092
42480
|
create: () => this.launch.createAdapter(this.launch.runner, this.launch.deps),
|
|
@@ -42123,7 +42511,10 @@ var StructuredSession = class extends AbstractRunnerSession {
|
|
|
42123
42511
|
},
|
|
42124
42512
|
cancelled: this.ended.signal,
|
|
42125
42513
|
startupExit: () => this.startupExit,
|
|
42126
|
-
onRespawn: (failure2) =>
|
|
42514
|
+
onRespawn: (failure2) => {
|
|
42515
|
+
this.launch.onNotice?.(`${this.launch.runner} started without Ordewell's tools (${failure2}); respawning once.`);
|
|
42516
|
+
this.launch.onAttaching?.(2);
|
|
42517
|
+
},
|
|
42127
42518
|
failed: (first, second) => `Could not start ${this.launch.runner} with Ordewell's tools, so the task was not sent to it. First spawn: ${first}. Respawn: ${second}.`
|
|
42128
42519
|
});
|
|
42129
42520
|
if (!spawned) return;
|
|
@@ -42706,16 +43097,22 @@ var StructuredRunner = class extends AbstractRunner {
|
|
|
42706
43097
|
checkHandoff: opts.checkHandoff ?? ((completion) => checkHandoff(completion, opts.taskId, [])),
|
|
42707
43098
|
...opts.readTask ? { readTask: opts.readTask } : {},
|
|
42708
43099
|
...opts.applyPatch ? { applyPatch: opts.applyPatch } : {},
|
|
42709
|
-
onNotice: opts.onNotice
|
|
43100
|
+
onNotice: opts.onNotice,
|
|
43101
|
+
onAttaching: opts.onAttaching
|
|
42710
43102
|
});
|
|
42711
43103
|
console.error(`[structured] Starting ${opts.runner} [${opts.mode ?? "default"}] (${opts.modelId || "default"}) for task ${opts.taskId.slice(0, 8)}`);
|
|
42712
43104
|
this.registerSession(id2, session);
|
|
43105
|
+
const cancel = () => void session.stop();
|
|
43106
|
+
opts.cancelled?.addEventListener("abort", cancel, { once: true });
|
|
42713
43107
|
try {
|
|
43108
|
+
if (opts.cancelled?.aborted) cancel();
|
|
42714
43109
|
await session.start(opts.prompt);
|
|
42715
43110
|
} catch (err) {
|
|
42716
43111
|
session.kill();
|
|
42717
43112
|
this.sessions.delete(id2);
|
|
42718
43113
|
throw err;
|
|
43114
|
+
} finally {
|
|
43115
|
+
opts.cancelled?.removeEventListener("abort", cancel);
|
|
42719
43116
|
}
|
|
42720
43117
|
return session;
|
|
42721
43118
|
}
|
|
@@ -42789,7 +43186,7 @@ var RunnerRegistry = class {
|
|
|
42789
43186
|
|
|
42790
43187
|
// src/plugins/removedPluginNotice.ts
|
|
42791
43188
|
import * as fs23 from "fs";
|
|
42792
|
-
import * as
|
|
43189
|
+
import * as path45 from "path";
|
|
42793
43190
|
function isManifestFile(file) {
|
|
42794
43191
|
try {
|
|
42795
43192
|
return fs23.statSync(file).isFile();
|
|
@@ -42798,10 +43195,10 @@ function isManifestFile(file) {
|
|
|
42798
43195
|
}
|
|
42799
43196
|
}
|
|
42800
43197
|
function removedPluginNotice(dataDir = globalDataDir()) {
|
|
42801
|
-
const directory =
|
|
43198
|
+
const directory = path45.join(dataDir, "plugins");
|
|
42802
43199
|
try {
|
|
42803
|
-
const hasManifest = isManifestFile(
|
|
42804
|
-
(entry) => (entry.isDirectory() || entry.isSymbolicLink()) && isManifestFile(
|
|
43200
|
+
const hasManifest = isManifestFile(path45.join(directory, "manifest.json")) || fs23.readdirSync(directory, { withFileTypes: true }).some(
|
|
43201
|
+
(entry) => (entry.isDirectory() || entry.isSymbolicLink()) && isManifestFile(path45.join(directory, entry.name, "manifest.json"))
|
|
42805
43202
|
);
|
|
42806
43203
|
if (hasManifest) return "Plugin runners were removed; manifests in ~/.ordewell/plugins/ are ignored. See the CHANGELOG.";
|
|
42807
43204
|
} catch {
|
|
@@ -42812,15 +43209,15 @@ function removedPluginNotice(dataDir = globalDataDir()) {
|
|
|
42812
43209
|
|
|
42813
43210
|
// src/utils/stateStore.ts
|
|
42814
43211
|
import * as fs24 from "fs";
|
|
42815
|
-
import * as
|
|
43212
|
+
import * as path46 from "path";
|
|
42816
43213
|
var STATE_FILE2 = "state.json";
|
|
42817
43214
|
function getStatePath(baseDir) {
|
|
42818
|
-
return
|
|
43215
|
+
return path46.join(getStateDir(baseDir), STATE_FILE2);
|
|
42819
43216
|
}
|
|
42820
43217
|
var SESSION_ID_KEY = "sessionId";
|
|
42821
43218
|
function saveState(plan, baseDir, sessionId) {
|
|
42822
43219
|
const statePath = getStatePath(baseDir ?? process.cwd());
|
|
42823
|
-
ensureDir(
|
|
43220
|
+
ensureDir(path46.dirname(statePath));
|
|
42824
43221
|
plan.lastUpdated = (/* @__PURE__ */ new Date()).toISOString();
|
|
42825
43222
|
const file = sessionId ? { ...plan, [SESSION_ID_KEY]: sessionId } : plan;
|
|
42826
43223
|
dropRetiredPlanFields(file);
|
|
@@ -43106,6 +43503,7 @@ export {
|
|
|
43106
43503
|
assignedRunnerOf,
|
|
43107
43504
|
assignmentIssue,
|
|
43108
43505
|
assignmentIssueText,
|
|
43506
|
+
attachingLabel,
|
|
43109
43507
|
augmentedPath,
|
|
43110
43508
|
automergeSummary,
|
|
43111
43509
|
autonomyLevelLabel,
|