@sleds/cli 0.1.43 → 0.1.45
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/sleds.mjs +1086 -444
- package/package.json +1 -1
package/dist/sleds.mjs
CHANGED
|
@@ -21,6 +21,9 @@ var p = (input, output, cacheWrite, cacheWrite1h, cacheRead) => ({ input, output
|
|
|
21
21
|
}), OPENAI_PRICES, byLength = (t) => Object.keys(t).sort((a, b) => b.length - a.length), ANTHROPIC_KEYS, OPENAI_KEYS;
|
|
22
22
|
var init_pricing = __esm(() => {
|
|
23
23
|
ANTHROPIC_PRICES = {
|
|
24
|
+
"claude-opus-5-5": p(4, 20, 5, 8, 0.2),
|
|
25
|
+
"claude-sonnet-5-5": p(2, 10, 2.5, 4, 0.1),
|
|
26
|
+
"claude-haiku-5-5": p(0.1, 0.5, 0.125, 0.2, 0.01),
|
|
24
27
|
"claude-fable-5-1": p(10, 50, 12.5, 20, 0.25),
|
|
25
28
|
"claude-mythos-5-1": p(10, 50, 12.5, 20, 0.25),
|
|
26
29
|
"claude-fable-5": p(10, 50, 12.5, 20, 1),
|
|
@@ -794,10 +797,17 @@ var init_verbs = __esm(() => {
|
|
|
794
797
|
{
|
|
795
798
|
id: "update",
|
|
796
799
|
group: "meter",
|
|
797
|
-
want: "Change an item — its title, why, done, description, due day, what it sits under, or what it touches",
|
|
798
|
-
mcp: "update {project, ask?, why?, done?, smallest?, touches?, demand?, description?, due?, parent?, repos?, sensitivity?}",
|
|
800
|
+
want: "Change an item — its title, why, done, worth, description, due day, what it sits under, or what it touches",
|
|
801
|
+
mcp: "update {project, ask?, why?, done?, smallest?, touches?, demand?, worth?, carry?, description?, due?, parent?, repos?, sensitivity?}",
|
|
799
802
|
app: "Work › the item › click a field",
|
|
800
|
-
how: "The filer, an approver or an admin changes an item from chat or by clicking the field in the app; every change says who and through what (via Claude · Cowork, via ChatGPT, in the app) and the record keeps before and after. A change to the ask, done or smallest marks the band stale with a Re-size at hand — never re-sized silently. A mirrored ticket is the tracker's word and is not changed here."
|
|
803
|
+
how: "The filer, an approver or an admin changes an item from chat or by clicking the field in the app; every change says who and through what (via Claude · Cowork, via ChatGPT, in the app) and the record keeps before and after. A change to the ask, done or smallest marks the band stale with a Re-size at hand — never re-sized silently. Worth is the person's word in their words ('high · Acme renewal', '$5k–20k'; null clears it); on an epic, `carry: true` puts it on each child that has none, each child's record signed the same way. A mirrored ticket is the tracker's word and is not changed here."
|
|
804
|
+
},
|
|
805
|
+
{
|
|
806
|
+
id: "reopen",
|
|
807
|
+
group: "meter",
|
|
808
|
+
want: "Put back an item that landed by mistake",
|
|
809
|
+
mcp: "reopen {project, reason}",
|
|
810
|
+
how: "SL-1819. For an item a skipped node or a wrong actual landed. It goes back on the sled it was on — or to ready on no sled, when that sled is gone from the org — the false actual is cleared and the band stands. A reopen always says why; the why goes on the item's record. The filer, an approver or an admin. Only a landed item reopens."
|
|
801
811
|
},
|
|
802
812
|
{
|
|
803
813
|
id: "archive",
|
|
@@ -1085,7 +1095,19 @@ var init_verbs = __esm(() => {
|
|
|
1085
1095
|
|
|
1086
1096
|
// ../../packages/shared/src/intake/forms.ts
|
|
1087
1097
|
function norm(label) {
|
|
1088
|
-
return label.replace(/[*_`~]/g, "").replace(/\(s\)/gi, "s").replace(/[:?]+\s*$/, "").replace(/\s+/g, " ").trim().toLowerCase();
|
|
1098
|
+
return label.replace(/[*_`~]/g, "").replace(/\(s\)/gi, "s").replace(/[:?]+\s*$/, "").replace(/,\s*including\b.*$/i, "").replace(/\s+/g, " ").trim().toLowerCase();
|
|
1099
|
+
}
|
|
1100
|
+
function plain(s) {
|
|
1101
|
+
return s.replace(/[*_`~]+/g, "").replace(/\s+/g, " ").trim();
|
|
1102
|
+
}
|
|
1103
|
+
function titleOf(s, max = 80) {
|
|
1104
|
+
const t = plain(s);
|
|
1105
|
+
const first = /^(.+?[.!?])(\s|$)/.exec(t)?.[1] ?? t;
|
|
1106
|
+
if (first.length <= max)
|
|
1107
|
+
return first;
|
|
1108
|
+
const cut = first.slice(0, max);
|
|
1109
|
+
const sp = cut.lastIndexOf(" ");
|
|
1110
|
+
return `${(sp > max / 2 ? cut.slice(0, sp) : cut).replace(/[,;:\s]+$/, "")}…`;
|
|
1089
1111
|
}
|
|
1090
1112
|
function readForm(text) {
|
|
1091
1113
|
const lines = text.split(/\r?\n/);
|
|
@@ -1097,67 +1119,84 @@ function readForm(text) {
|
|
|
1097
1119
|
const line = raw.trim();
|
|
1098
1120
|
if (!line)
|
|
1099
1121
|
continue;
|
|
1122
|
+
const bold = /^\*[^*]{2,80}\*:?$/.test(line);
|
|
1123
|
+
const whole = line.length <= 80 ? slotOf(def, line) : null;
|
|
1100
1124
|
const inline = /^\**([^:*]{2,60}?)\**\s*:\s*(.+)$/.exec(line);
|
|
1101
|
-
const
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
if (slotWhole) {
|
|
1105
|
-
cur = { label: line.replace(/[*_]/g, "").replace(/:\s*$/, ""), slot: slotWhole, value: [] };
|
|
1125
|
+
const slotInline = inline ? slotOf(def, inline[1] ?? "") : null;
|
|
1126
|
+
if (whole && (bold || !inline)) {
|
|
1127
|
+
cur = { label: plain(norm(line)), slot: whole, value: [] };
|
|
1106
1128
|
found.push(cur);
|
|
1107
1129
|
known++;
|
|
1108
1130
|
} else if (slotInline && inline) {
|
|
1109
|
-
cur = { label: (inline[1] ?? "")
|
|
1131
|
+
cur = { label: plain(norm(inline[1] ?? "")), slot: slotInline, value: [inline[2] ?? ""] };
|
|
1110
1132
|
found.push(cur);
|
|
1111
1133
|
known++;
|
|
1112
|
-
} else if (
|
|
1113
|
-
cur = { label: line.replace(
|
|
1134
|
+
} else if (bold && cur) {
|
|
1135
|
+
cur = { label: plain(line.replace(/:$/, "")), slot: null, value: [] };
|
|
1114
1136
|
found.push(cur);
|
|
1115
1137
|
} else if (cur)
|
|
1116
1138
|
cur.value.push(line);
|
|
1117
1139
|
}
|
|
1118
1140
|
if (known < def.min)
|
|
1119
1141
|
continue;
|
|
1120
|
-
const val = (f) => f.value.join(" ")
|
|
1121
|
-
const
|
|
1122
|
-
const ask =
|
|
1123
|
-
const
|
|
1124
|
-
const
|
|
1125
|
-
const
|
|
1126
|
-
const
|
|
1127
|
-
const
|
|
1128
|
-
const
|
|
1129
|
-
const
|
|
1142
|
+
const val = (f) => plain(f.value.join(" "));
|
|
1143
|
+
const one = (slot) => found.find((f) => f.slot === slot && val(f));
|
|
1144
|
+
const ask = one("ask");
|
|
1145
|
+
const users = one("users");
|
|
1146
|
+
const sid = one("sid");
|
|
1147
|
+
const loc = one("location");
|
|
1148
|
+
const clients = one("clients");
|
|
1149
|
+
const rev = one("revenue");
|
|
1150
|
+
const n = clients ? val(clients) : "";
|
|
1151
|
+
const clientsText = n ? /^\d+$/.test(n) ? `${n} client${n === "1" ? "" : "s"} affected` : `clients affected: ${n}` : "";
|
|
1152
|
+
const r = rev ? val(rev) : "";
|
|
1153
|
+
const revenue = YES.test(r) ? true : NO.test(r) ? false : undefined;
|
|
1154
|
+
const revText = revenue === true ? "Impacting revenue" : revenue === false ? "No revenue impact" : "";
|
|
1155
|
+
const unknown = found.filter((f) => f.slot === null && val(f)).map((f) => `${f.label}: ${val(f)}`);
|
|
1156
|
+
const who = [
|
|
1157
|
+
users ? `Users: ${val(users)}` : "",
|
|
1158
|
+
sid ? `SID: ${val(sid)}` : "",
|
|
1159
|
+
loc ? `Location ${val(loc)}` : "",
|
|
1160
|
+
...unknown
|
|
1161
|
+
].filter(Boolean);
|
|
1162
|
+
const touches = who.join(" · ");
|
|
1163
|
+
const demand = [clientsText, revText].filter(Boolean).join(" · ");
|
|
1164
|
+
const details = [...who, clientsText, revText].filter(Boolean).join(" · ");
|
|
1130
1165
|
return {
|
|
1131
1166
|
form: def.name,
|
|
1132
|
-
...ask ? { ask } : {},
|
|
1133
|
-
...
|
|
1167
|
+
...ask ? { ask: val(ask) } : {},
|
|
1168
|
+
...details ? { details } : {},
|
|
1134
1169
|
...touches ? { touches } : {},
|
|
1135
1170
|
...demand ? { demand } : {},
|
|
1136
|
-
...
|
|
1171
|
+
...revenue !== undefined ? { revenue } : {}
|
|
1137
1172
|
};
|
|
1138
1173
|
}
|
|
1139
1174
|
return null;
|
|
1140
1175
|
}
|
|
1141
|
-
var FORMS, YES
|
|
1176
|
+
var FORMS, YES, NO, slotOf = (def, label) => {
|
|
1177
|
+
const n = norm(label);
|
|
1178
|
+
for (const [re, slot] of def.labels)
|
|
1179
|
+
if (re.test(n))
|
|
1180
|
+
return slot;
|
|
1181
|
+
return null;
|
|
1182
|
+
};
|
|
1142
1183
|
var init_forms = __esm(() => {
|
|
1143
1184
|
FORMS = [
|
|
1144
1185
|
{
|
|
1145
1186
|
name: "Create Tech Support Ticket",
|
|
1146
|
-
labels:
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
"number of clients affected": "demand",
|
|
1155
|
-
"is the issue currently impacting revenue": "revenue"
|
|
1156
|
-
},
|
|
1187
|
+
labels: [
|
|
1188
|
+
[/^users? experiencing\b/, "users"],
|
|
1189
|
+
[/^sids?\b/, "sid"],
|
|
1190
|
+
[/^location ids?\b/, "location"],
|
|
1191
|
+
[/^issue description\b/, "ask"],
|
|
1192
|
+
[/^(?:number of )?clients? affected\b|^number of clients\b/, "clients"],
|
|
1193
|
+
[/\bimpacting revenue\b|\brevenue impact\b/, "revenue"]
|
|
1194
|
+
],
|
|
1157
1195
|
min: 3
|
|
1158
1196
|
}
|
|
1159
1197
|
];
|
|
1160
1198
|
YES = /^(yes|y|yep|true|definitely|it is)\b/i;
|
|
1199
|
+
NO = /^(no|n|nope|false|not)\b/i;
|
|
1161
1200
|
});
|
|
1162
1201
|
|
|
1163
1202
|
// ../../packages/shared/src/intake/brief.ts
|
|
@@ -1235,13 +1274,14 @@ function draftBrief(input) {
|
|
|
1235
1274
|
const form = readForm(text);
|
|
1236
1275
|
const parsed = sections(text);
|
|
1237
1276
|
const preamble = parsed.preamble;
|
|
1238
|
-
const byKey = form ? {
|
|
1239
|
-
const
|
|
1277
|
+
const byKey = form ? { touches: form.touches, demand: form.demand } : parsed.byKey;
|
|
1278
|
+
const fromText = form?.ask ? form.ask : askFrom(text, preamble);
|
|
1279
|
+
const askText = a.ask?.trim() || (fromText ? titleOf(fromText) : undefined);
|
|
1240
1280
|
if (!askText)
|
|
1241
1281
|
return null;
|
|
1242
1282
|
const ask = a.ask?.trim() ? { text: clip(a.ask, MAX_ASK), from: "filer" } : { text: clip(askText, MAX_ASK), from: "artifact" };
|
|
1243
1283
|
const pick = (k) => field(a[k], "filer") ?? field(byKey[k], "artifact");
|
|
1244
|
-
const worth = worthFromAnswer(a.worth) ?? parseWorth(text) ??
|
|
1284
|
+
const worth = worthFromAnswer(a.worth) ?? parseWorth(text) ?? undefined;
|
|
1245
1285
|
const sources = [...new Set((input.links ?? []).map((l) => l.trim()).filter(Boolean))].slice(0, MAX_SOURCES);
|
|
1246
1286
|
return {
|
|
1247
1287
|
ask,
|
|
@@ -1370,11 +1410,11 @@ var init_brief = __esm(() => {
|
|
|
1370
1410
|
init_worth();
|
|
1371
1411
|
init_forms();
|
|
1372
1412
|
QUESTIONS = {
|
|
1373
|
-
why: "Who is
|
|
1374
|
-
done: "
|
|
1375
|
-
smallest: "What
|
|
1376
|
-
worth: "
|
|
1377
|
-
touches: "
|
|
1413
|
+
why: "Who is this for, and why does it matter now?",
|
|
1414
|
+
done: "How will we know it's done?",
|
|
1415
|
+
smallest: "What's the smallest version that would still help?",
|
|
1416
|
+
worth: "How important is this: high, medium, or low? If a specific customer is waiting on it, tell me who.",
|
|
1417
|
+
touches: "Which part of the product is this about?",
|
|
1378
1418
|
demand: "How many people have asked for this, or how often does it come up?"
|
|
1379
1419
|
};
|
|
1380
1420
|
REQUIRED_TO_FILE = ["why", "done"];
|
|
@@ -1635,7 +1675,8 @@ function intakePlan(g) {
|
|
|
1635
1675
|
card: g.card ? [...g.card] : base.card,
|
|
1636
1676
|
limits: [...g.limits ?? []],
|
|
1637
1677
|
followed: [...g.followed ?? []],
|
|
1638
|
-
text: g.text ?? ""
|
|
1678
|
+
text: g.text ?? "",
|
|
1679
|
+
...g.references ? { references: g.references } : {}
|
|
1639
1680
|
};
|
|
1640
1681
|
}
|
|
1641
1682
|
function planOpen(b, plan, extra = {}) {
|
|
@@ -1667,11 +1708,14 @@ var init_intake = __esm(() => {
|
|
|
1667
1708
|
ORDER = ["why", "done", "smallest", "worth", "touches", "demand"];
|
|
1668
1709
|
DEFAULT_CARD = CARD_FIELDS.filter((f) => f !== "product map details");
|
|
1669
1710
|
FIELD_WORDS = [
|
|
1670
|
-
["why", /\bwho is it for\b|\bwhy now\b|\bwho (?:hits|needs|asked)\b|\bwhy\b/i],
|
|
1671
|
-
["done", /\bdone\b|\blook like\b|\bsuccess\b|\bacceptance\b/i],
|
|
1711
|
+
["why", /\bwho is (?:it|this) for\b|\bwhy now\b|\bwho (?:hits|needs|asked)\b|\bwhy\b/i],
|
|
1712
|
+
["done", /\bdone\b|\blook like\b|\bsuccess\b|\bacceptance\b|\bis fixed\b|\bit'?s fixed\b/i],
|
|
1672
1713
|
["smallest", /\bsmallest\b|\bminimum\b|\bmvp\b|\bsmaller version\b/i],
|
|
1673
|
-
["worth", /\bworth\b|\bvalue to\b|\bthe business\b|\brevenue\b/i],
|
|
1674
|
-
[
|
|
1714
|
+
["worth", /\bworth\b|\bvalue to\b|\bthe business\b|\brevenue\b|\bhow important\b/i],
|
|
1715
|
+
[
|
|
1716
|
+
"touches",
|
|
1717
|
+
/\btouch(?:es)?\b|\bwhich (?:product|system|area)s?\b|\bwhere in the product\b|\bwhich part of the product\b/i
|
|
1718
|
+
],
|
|
1675
1719
|
["demand", /\bhow many\b|\bhow often\b|\bcome up\b|\basked for this\b/i]
|
|
1676
1720
|
];
|
|
1677
1721
|
});
|
|
@@ -2042,7 +2086,7 @@ ${state}`)}, fillcolor="${fill}", color="${border}"${style}];`);
|
|
|
2042
2086
|
`)}
|
|
2043
2087
|
`;
|
|
2044
2088
|
}
|
|
2045
|
-
var ROUTE_APPROACH_MAX = 2000, PLAN_APPROACH_MAX = 1200, PLAN_LIST_MAX = 8, PLAN_LINE_MAX = 240, CONTROL, MAX_NODES = 24, DEPTH_WARN = 4, norm2 = (s) => s.trim().toUpperCase(), AREA_ROOTS, NOT_AREAS, sameRepo = (a, b) => a.length === 0 || b.length === 0 || a.some((r) => b.includes(r)), DOT_COLORS, ROUTE_DRAFT_RULE = "Draft the route: PR-sized nodes, file-disjoint where they run in parallel, each with a title, the files or areas it touches, the repo, and the nodes it waits on (by key: A, B, C…). Prefer wide waves over chains deeper than four. Make the tough calls once, so each node's session follows them: an `approach` paragraph for the whole route, and on each node a `plan` — its approach, what it `provides` that later nodes rely on (names, shapes, files), what it `expects` from the nodes it waits on, and the `doneChecks` that say it landed. Show the graph and the plans as text and ask for a yes; file nothing before it. Then `route {q, approach, nodes: [{key, title, touches, repos, blockedBy, plan: {approach, provides, expects, doneChecks}}], confirm: true}`.";
|
|
2089
|
+
var ROUTE_APPROACH_MAX = 2000, PLAN_APPROACH_MAX = 1200, PLAN_LIST_MAX = 8, PLAN_LINE_MAX = 240, CONTROL, MAX_NODES = 24, DEPTH_WARN = 4, norm2 = (s) => s.trim().toUpperCase(), AREA_ROOTS, NOT_AREAS, sameRepo = (a, b) => a.length === 0 || b.length === 0 || a.some((r) => b.includes(r)), DOT_COLORS, ROUTE_DRAFT_RULE = "Draft the route: PR-sized nodes, file-disjoint where they run in parallel, each with a title, the files or areas it touches, the repo, and the nodes it waits on (by key: A, B, C…). Prefer wide waves over chains deeper than four. Make the tough calls once, so each node's session follows them: an `approach` paragraph for the whole route, and on each node a `plan` — its approach, what it `provides` that later nodes rely on (names, shapes, files), what it `expects` from the nodes it waits on, and the `doneChecks` that say it landed. Show the graph and the plans as text and ask for a yes; file nothing before it. An item with children takes no nodes: its children are the work, and their order lives in their blockedBy. Then `route {q, approach, nodes: [{key, title, touches, repos, blockedBy, plan: {approach, provides, expects, doneChecks}}], confirm: true}`.";
|
|
2046
2090
|
var init_route = __esm(() => {
|
|
2047
2091
|
CONTROL = /[\u0000-\u0008\u000b-\u001f\u007f-\u009f]/g;
|
|
2048
2092
|
AREA_ROOTS = new Set(["apps", "packages", "services", "libs", "crates", "modules"]);
|
|
@@ -2114,6 +2158,7 @@ var init_annotations = __esm(() => {
|
|
|
2114
2158
|
finish: add("Land the work"),
|
|
2115
2159
|
buddy: add("Pair with a buddy"),
|
|
2116
2160
|
pickup: add("Pick up a hand-off"),
|
|
2161
|
+
reopen: add("Reopen an item", true),
|
|
2117
2162
|
update: change("Change an item", true),
|
|
2118
2163
|
archive: change("Archive an item", true),
|
|
2119
2164
|
withdraw: change("Withdraw a hand-off", true)
|
|
@@ -3154,6 +3199,13 @@ async function updateTool(ctx, args) {
|
|
|
3154
3199
|
patch.repos = args.repos.split(/[,\s]+/).filter(Boolean);
|
|
3155
3200
|
if (Array.isArray(args.relations))
|
|
3156
3201
|
patch.relations = args.relations.filter(isObject4).map((r) => ({ key: str5(r.key)?.trim() ?? "", how: str5(r.how) ?? "relates" })).filter((r) => r.key && /^(blockedBy|blocks|relates|duplicates)$/.test(r.how));
|
|
3202
|
+
if (args.worth === null)
|
|
3203
|
+
patch.worth = null;
|
|
3204
|
+
else if (typeof args.worth === "string" && args.worth.trim()) {
|
|
3205
|
+
patch.worth = args.worth.trim();
|
|
3206
|
+
if (args.carry === true)
|
|
3207
|
+
patch.carry = true;
|
|
3208
|
+
}
|
|
3157
3209
|
if (args.sensitivity === null)
|
|
3158
3210
|
patch.sensitivity = null;
|
|
3159
3211
|
else if (typeof args.sensitivity === "string" && /^(public|internal|restricted)$/.test(args.sensitivity)) {
|
|
@@ -3163,7 +3215,7 @@ async function updateTool(ctx, args) {
|
|
|
3163
3215
|
}
|
|
3164
3216
|
if (Object.keys(patch).length === 0)
|
|
3165
3217
|
return {
|
|
3166
|
-
text: "Nothing to change. Pass the fields that change: ask, why, done, smallest, touches, demand, description, due, parent, repos, relations.",
|
|
3218
|
+
text: "Nothing to change. Pass the fields that change: ask, why, done, smallest, touches, demand, worth, description, due, parent, repos, relations.",
|
|
3167
3219
|
isError: true
|
|
3168
3220
|
};
|
|
3169
3221
|
const res = await postSaying(ctx, "/proxy/item/update", { project, ...patch }, "Could not update the item.");
|
|
@@ -3171,13 +3223,48 @@ async function updateTool(ctx, args) {
|
|
|
3171
3223
|
return res.fail;
|
|
3172
3224
|
const changed = Array.isArray(res.json.changed) ? res.json.changed.filter((x) => typeof x === "string") : [];
|
|
3173
3225
|
const key = str5(res.json.key) ?? project;
|
|
3226
|
+
const carried = carriedLine(res.json.carried);
|
|
3174
3227
|
if (changed.length === 0)
|
|
3175
|
-
return { text: `${key}: nothing differed from what is on the item
|
|
3228
|
+
return { text: `${key}: nothing differed from what is on the item.${carried}` };
|
|
3176
3229
|
const stale = res.json.sizeStale === true;
|
|
3177
3230
|
return {
|
|
3178
|
-
text: `${key} updated: ${changed.join(", ")}.${stale ? " The band was sized before this change — say so, and offer to re-size (estimate with the same source)." : ""}`
|
|
3231
|
+
text: `${key} updated: ${changed.join(", ")}.${carried}${stale ? " The band was sized before this change — say so, and offer to re-size (estimate with the same source)." : ""}`
|
|
3232
|
+
};
|
|
3233
|
+
}
|
|
3234
|
+
async function reopenTool(ctx, args) {
|
|
3235
|
+
if (!ctx.client)
|
|
3236
|
+
return NOT_SET_UP;
|
|
3237
|
+
const project = str5(args.project)?.trim();
|
|
3238
|
+
if (!project)
|
|
3239
|
+
return { text: "Which item? Pass project: its key (QW-142).", isError: true };
|
|
3240
|
+
const reason = str5(args.reason)?.trim();
|
|
3241
|
+
if (!reason)
|
|
3242
|
+
return {
|
|
3243
|
+
text: "Say why it is reopened — pass reason, one line. It goes on the item's record.",
|
|
3244
|
+
isError: true
|
|
3245
|
+
};
|
|
3246
|
+
const res = await postSaying(ctx, "/proxy/reopen", { project, reason }, "Could not reopen the item.");
|
|
3247
|
+
if ("fail" in res)
|
|
3248
|
+
return res.fail;
|
|
3249
|
+
const key = str5(res.json.key) ?? project;
|
|
3250
|
+
const sled = str5(res.json.sled);
|
|
3251
|
+
return {
|
|
3252
|
+
text: `Reopened ${key} — ${sled ? `back on ${sled}'s sled` : "back to ready, on no sled"}. The false actual is cleared; the band stands.`
|
|
3179
3253
|
};
|
|
3180
3254
|
}
|
|
3255
|
+
function carriedLine(c) {
|
|
3256
|
+
if (!isObject4(c))
|
|
3257
|
+
return "";
|
|
3258
|
+
const set = Array.isArray(c.set) ? c.set.filter((x) => typeof x === "string") : [];
|
|
3259
|
+
const valued = num3(c.valued) ?? 0;
|
|
3260
|
+
const skipped = num3(c.skipped) ?? 0;
|
|
3261
|
+
const parts = [
|
|
3262
|
+
set.length ? `worth set on ${set.length} ${set.length === 1 ? "child" : "children"} (${set.join(", ")})` : "no child took it",
|
|
3263
|
+
...valued ? [`${valued} already had a worth and kept it`] : [],
|
|
3264
|
+
...skipped ? [`${skipped} you may not change (a mirror, or not yours) — left as they were`] : []
|
|
3265
|
+
];
|
|
3266
|
+
return ` ${parts.join("; ").replace(/^./, (ch) => ch.toUpperCase())}.`;
|
|
3267
|
+
}
|
|
3181
3268
|
async function archiveTool(ctx, args) {
|
|
3182
3269
|
if (!ctx.client)
|
|
3183
3270
|
return NOT_SET_UP;
|
|
@@ -3473,9 +3560,21 @@ async function itemTool(ctx, args) {
|
|
|
3473
3560
|
text: `Nothing on the sled answers to "${q}". Try the key (QW-142) or a word from the title with status.`,
|
|
3474
3561
|
isError: true
|
|
3475
3562
|
};
|
|
3476
|
-
const md = str5(r.markdown) ?? "";
|
|
3477
3563
|
if (asFile)
|
|
3478
|
-
return { text:
|
|
3564
|
+
return { text: str5(r.markdown) ?? "", structured: { key: str5(r.key) ?? q, export: true } };
|
|
3565
|
+
const envs = isObject4(r.whereLive) && Array.isArray(r.whereLive.envs) ? r.whereLive.envs : [];
|
|
3566
|
+
const live = envs.filter(isObject4).filter((e) => {
|
|
3567
|
+
const s = isObject4(e.state) ? e.state : null;
|
|
3568
|
+
return !(s && (s.kind === "no_code" || s.state === "no_code"));
|
|
3569
|
+
}).map((e) => `- ${str5(e.name) ?? str5(e.slug) ?? "?"}: ${str5(e.words) ?? ""}`);
|
|
3570
|
+
const body = str5(r.markdown) ?? "";
|
|
3571
|
+
const section = live.length ? `## Where it's live
|
|
3572
|
+
${live.join(`
|
|
3573
|
+
`)}
|
|
3574
|
+
|
|
3575
|
+
` : "";
|
|
3576
|
+
const title = body.match(/^# [^\n]*\n+/)?.[0] ?? "";
|
|
3577
|
+
const md = `${title}${section}${body.slice(title.length)}`;
|
|
3479
3578
|
const readiness = isObject4(r.readiness) ? r.readiness : null;
|
|
3480
3579
|
const line = readiness ? str5(readiness.line) : undefined;
|
|
3481
3580
|
const state = readiness ? str5(readiness.state) : undefined;
|
|
@@ -4216,6 +4315,8 @@ async function callTool(name, args, ctx) {
|
|
|
4216
4315
|
return attachTool(ctx, args);
|
|
4217
4316
|
case "update":
|
|
4218
4317
|
return updateTool(ctx, args);
|
|
4318
|
+
case "reopen":
|
|
4319
|
+
return reopenTool(ctx, args);
|
|
4219
4320
|
case "archive":
|
|
4220
4321
|
return archiveTool(ctx, args);
|
|
4221
4322
|
case "comment":
|
|
@@ -4621,7 +4722,7 @@ var init_server = __esm(() => {
|
|
|
4621
4722
|
},
|
|
4622
4723
|
{
|
|
4623
4724
|
name: "route",
|
|
4624
|
-
description: "The route — an item's internal order as PR-sized nodes with blockers (spec 26). With only `q`: the route as filed (each node's state, what it waits on, the ready set, where each run can be watched), or, when none is filed, the pack and the drafting rule so you draft one. Drafting: PR-sized nodes, file-disjoint where they run in parallel, each with a title, the files or areas it touches, the repo, and the nodes it waits on by key (A, B, C…); prefer wide waves over chains deeper than four. Make the tough calls once: an `approach` for the route and a `plan` on each node (its approach, what it provides to later nodes, what it expects from the nodes it waits on, its done-checks), so each node's session follows them instead of re-planning. Show the graph and the plans as text and ask for a yes; file nothing before it. Then call again with `nodes` and `confirm: true`. Re-drafting shows what changed; a node with a run or a PR is never removed. Ready is computed from landed blockers, never set. With nobody to ask (a drafting session an engine started, spec 26 §5.1): call with `nodes` and `propose: true` instead — the route is held on the item for a person's yes and nothing is filed; then stop. A read of an item with a proposal shows it as drafted, not filed.",
|
|
4725
|
+
description: "The route — an item's internal order as PR-sized nodes with blockers (spec 26). With only `q`: the route as filed (each node's state, what it waits on, the ready set, where each run can be watched), or, when none is filed, the pack and the drafting rule so you draft one. Drafting: PR-sized nodes, file-disjoint where they run in parallel, each with a title, the files or areas it touches, the repo, and the nodes it waits on by key (A, B, C…); prefer wide waves over chains deeper than four. Make the tough calls once: an `approach` for the route and a `plan` on each node (its approach, what it provides to later nodes, what it expects from the nodes it waits on, its done-checks), so each node's session follows them instead of re-planning. Show the graph and the plans as text and ask for a yes; file nothing before it. Then call again with `nodes` and `confirm: true`. An item with children is refused nodes — its children are the work: set their order with blockedBy on them. The item lands only when every child has landed. Re-drafting shows what changed; a node with a run or a PR is never removed. Ready is computed from landed blockers, never set. With nobody to ask (a drafting session an engine started, spec 26 §5.1): call with `nodes` and `propose: true` instead — the route is held on the item for a person's yes and nothing is filed; then stop. A read of an item with a proposal shows it as drafted, not filed.",
|
|
4625
4726
|
inputSchema: {
|
|
4626
4727
|
type: "object",
|
|
4627
4728
|
properties: {
|
|
@@ -4854,7 +4955,7 @@ var init_server = __esm(() => {
|
|
|
4854
4955
|
},
|
|
4855
4956
|
{
|
|
4856
4957
|
name: "update",
|
|
4857
|
-
description: "Change an item on the sled — its title (`ask`), why, done, smallest, touches, demand, its description (markdown: the prose the brief does not hold), its due day, which item it sits under, the repo(s) it lands in, or its sensitivity (public · internal · restricted). Pass the item's key and only the fields that change. The filer, an approver or an admin may; a mirrored ticket cannot be changed here. A change to the ask, done or smallest marks the band stale — say so, and offer to re-size. Everything you write is signed as coming through you.",
|
|
4958
|
+
description: "Change an item on the sled — its title (`ask`), why, done, smallest, touches, demand, its worth, its description (markdown: the prose the brief does not hold), its due day, which item it sits under, the repo(s) it lands in, or its sensitivity (public · internal · restricted). Pass the item's key and only the fields that change. Worth is the person's word — never guess it; on an epic, `carry: true` puts it on the children that have none. The filer, an approver or an admin may; a mirrored ticket cannot be changed here. A change to the ask, done or smallest marks the band stale — say so, and offer to re-size. Everything you write is signed as coming through you.",
|
|
4858
4959
|
inputSchema: {
|
|
4859
4960
|
type: "object",
|
|
4860
4961
|
properties: {
|
|
@@ -4865,6 +4966,14 @@ var init_server = __esm(() => {
|
|
|
4865
4966
|
smallest: { type: "string" },
|
|
4866
4967
|
touches: { type: "string" },
|
|
4867
4968
|
demand: { type: "string" },
|
|
4969
|
+
worth: {
|
|
4970
|
+
type: ["string", "null"],
|
|
4971
|
+
description: "What the person said it is worth: a level (low · medium · high · critical), dollars, a customer — e.g. 'high · Acme renewal', '$5k–20k'. Null clears it."
|
|
4972
|
+
},
|
|
4973
|
+
carry: {
|
|
4974
|
+
type: "boolean",
|
|
4975
|
+
description: "With worth: put it on this item's children that have no worth yet, each on its own record. A child with a worth keeps it."
|
|
4976
|
+
},
|
|
4868
4977
|
description: {
|
|
4869
4978
|
type: "string",
|
|
4870
4979
|
description: "Markdown. Links and images inline. Empty clears it."
|
|
@@ -4901,6 +5010,21 @@ var init_server = __esm(() => {
|
|
|
4901
5010
|
required: ["project"]
|
|
4902
5011
|
}
|
|
4903
5012
|
},
|
|
5013
|
+
{
|
|
5014
|
+
name: "reopen",
|
|
5015
|
+
description: "Put back an item that landed by mistake — a skipped node closed its route, or an actual was sent against the wrong item. It goes back on the sled it was on (or to ready, when that sled is gone), the false actual is cleared and the band stands. Pass the item's key and why: a reopen always says why, and the why goes on the item's record. The filer, an approver or an admin may. Only a landed item can be reopened.",
|
|
5016
|
+
inputSchema: {
|
|
5017
|
+
type: "object",
|
|
5018
|
+
properties: {
|
|
5019
|
+
project: { type: "string", description: "The item's key — QW-142 — or its project key." },
|
|
5020
|
+
reason: {
|
|
5021
|
+
type: "string",
|
|
5022
|
+
description: "Why it is reopened, one line — e.g. 'a skipped node landed it; six children are open'."
|
|
5023
|
+
}
|
|
5024
|
+
},
|
|
5025
|
+
required: ["project", "reason"]
|
|
5026
|
+
}
|
|
5027
|
+
},
|
|
4904
5028
|
{
|
|
4905
5029
|
name: "item",
|
|
4906
5030
|
description: "Read one item whole, as one markdown document — the pack: everything an engineer or an agent needs to finish the work from this document alone. Eight parts: the ask, where the code is, how to run it (the repo's setup profile), how to know it is done (the gate), how to ship it (the playbook), what the org expects, what is known already (the Field Book — notes from earlier runs on this item, its feature and what landed beside it, the questions, the thread), and the rules of the road. Carries its readiness and whether it is packable. Pass the item's key (QW-142) or its title. Call this before building from an item, and before `check`.",
|
|
@@ -5323,9 +5447,33 @@ var init_outcome = __esm(() => {
|
|
|
5323
5447
|
});
|
|
5324
5448
|
|
|
5325
5449
|
// ../../packages/shared/src/slack/mention.ts
|
|
5450
|
+
var MENTION_HELP;
|
|
5326
5451
|
var init_mention = __esm(() => {
|
|
5327
5452
|
init_intake();
|
|
5328
5453
|
init_brief();
|
|
5454
|
+
MENTION_HELP = [
|
|
5455
|
+
"Here's what I can do:",
|
|
5456
|
+
"• *@SLEDS file this*: turn the message above into a request for engineering",
|
|
5457
|
+
"• *@SLEDS status*: see where something stands",
|
|
5458
|
+
"You can also ask me to *watch* or *mute* a thread, or *run QW-123*."
|
|
5459
|
+
].join(`
|
|
5460
|
+
`);
|
|
5461
|
+
});
|
|
5462
|
+
|
|
5463
|
+
// ../../packages/shared/src/slack/copy.ts
|
|
5464
|
+
var init_copy = __esm(() => {
|
|
5465
|
+
init_intake();
|
|
5466
|
+
init_forms();
|
|
5467
|
+
init_mention();
|
|
5468
|
+
init_mention();
|
|
5469
|
+
});
|
|
5470
|
+
|
|
5471
|
+
// ../../packages/shared/src/slack/agent.ts
|
|
5472
|
+
var init_agent = __esm(() => {
|
|
5473
|
+
init_intake();
|
|
5474
|
+
init_brief();
|
|
5475
|
+
init_copy();
|
|
5476
|
+
init_mention();
|
|
5329
5477
|
});
|
|
5330
5478
|
|
|
5331
5479
|
// ../../packages/shared/src/item/productHits.ts
|
|
@@ -5627,6 +5775,24 @@ var init_fieldMap = __esm(() => {
|
|
|
5627
5775
|
var SEPT_30 = "/whats-new/2026-09-30", WHATS_NEW;
|
|
5628
5776
|
var init_whatsNew = __esm(() => {
|
|
5629
5777
|
WHATS_NEW = [
|
|
5778
|
+
{
|
|
5779
|
+
date: "2026-10-22",
|
|
5780
|
+
title: "Policy is an org setting",
|
|
5781
|
+
lede: "There is one policy for the whole org, so it now lives under Settings beside Platform keys and AI Guidance, instead of on each workspace's rail where it read as a workspace's own. Nothing in it changed; old links redirect.",
|
|
5782
|
+
sections: [
|
|
5783
|
+
{
|
|
5784
|
+
title: "Where it went",
|
|
5785
|
+
where: "Settings › Policy",
|
|
5786
|
+
body: "Open your profile menu › Settings, then Policy. The page is the same: every rule as one sentence, saved as a new version. The rail's Plan group keeps Seats."
|
|
5787
|
+
}
|
|
5788
|
+
],
|
|
5789
|
+
also: [
|
|
5790
|
+
{
|
|
5791
|
+
label: "Next",
|
|
5792
|
+
text: "Each workspace's Jira projects get their own pages for fields and statuses, and the org's Jira page keeps only the connection, the webhook and what SLEDS may write."
|
|
5793
|
+
}
|
|
5794
|
+
]
|
|
5795
|
+
},
|
|
5630
5796
|
{
|
|
5631
5797
|
date: "2026-10-21",
|
|
5632
5798
|
title: "The Slack thread is the item's discussion",
|
|
@@ -5650,18 +5816,28 @@ var init_whatsNew = __esm(() => {
|
|
|
5650
5816
|
{
|
|
5651
5817
|
title: "Forms are read as fields",
|
|
5652
5818
|
where: "Slack, filing from a workflow post",
|
|
5653
|
-
body: "A support ticket form posted by a Slack workflow is read field by field: the issue description is the
|
|
5819
|
+
body: "A support ticket form posted by a Slack workflow is read field by field: the issue description is the title, the users and IDs go on one details line, and the count of clients and the revenue answer to demand. SLEDS then asks only what the form did not say."
|
|
5820
|
+
},
|
|
5821
|
+
{
|
|
5822
|
+
title: "Replies you can read",
|
|
5823
|
+
where: "Slack, filing from a thread",
|
|
5824
|
+
body: `One summary message per thread, kept up to date as you answer, with the title in plain words and the form's other fields on one line. Answer several questions at once, one per line, or say which one you're answering: "done means the sync works", "high priority". Say "send it to Core" to change the team before filing. A yes with something missing asks for just that one thing.`
|
|
5825
|
+
},
|
|
5826
|
+
{
|
|
5827
|
+
title: "A model can write the replies",
|
|
5828
|
+
where: "Policy › Intake",
|
|
5829
|
+
body: "Turn on Slack intake replies written by a model, Claude Haiku 5.5 by default, and it answers in the thread the way the channel's AI Guidance says: what it understood, a workaround when there is one, and the questions that matter for this kind of issue. It spends your own Anthropic key from Platform keys, up to a daily cap you set. Filing still waits for a yes, and it never sets the priority."
|
|
5654
5830
|
},
|
|
5655
5831
|
{
|
|
5656
5832
|
title: "Which workspace, picked",
|
|
5657
5833
|
where: "Slack, filing",
|
|
5658
|
-
body: "A channel tied to a workspace files there. With several workspaces and none tied to the channel, SLEDS asks which
|
|
5834
|
+
body: "A channel tied to a workspace files there. With several workspaces and none tied to the channel, SLEDS asks which team: reply with the number or the name. The Jira key is never typed — the Filed as reply shows it."
|
|
5659
5835
|
}
|
|
5660
5836
|
],
|
|
5661
5837
|
also: [
|
|
5662
5838
|
{
|
|
5663
5839
|
label: "Fixed",
|
|
5664
|
-
text: "An ask filed from Slack now creates its Jira ticket in the workspace it was filed on, and
|
|
5840
|
+
text: "An ask filed from Slack now creates its Jira ticket in the workspace it was filed on, and a stated priority sets the ticket's priority. A form posted by a Slack workflow is now read field by field."
|
|
5665
5841
|
}
|
|
5666
5842
|
]
|
|
5667
5843
|
},
|
|
@@ -6297,6 +6473,56 @@ var init_pack2 = __esm(() => {
|
|
|
6297
6473
|
init_prHeader();
|
|
6298
6474
|
});
|
|
6299
6475
|
|
|
6476
|
+
// ../../packages/shared/src/deploys/deploys.ts
|
|
6477
|
+
function parseDeployBody(json) {
|
|
6478
|
+
if (!json || typeof json !== "object" || Array.isArray(json))
|
|
6479
|
+
return bad("body must be a JSON object");
|
|
6480
|
+
const j = json;
|
|
6481
|
+
const repo = typeof j.repo === "string" ? j.repo.trim().toLowerCase() : "";
|
|
6482
|
+
if (!REPO_RE.test(repo))
|
|
6483
|
+
return bad("repo must be owner/name");
|
|
6484
|
+
const sha = typeof j.sha === "string" ? j.sha.trim().toLowerCase() : "";
|
|
6485
|
+
if (!SHA_RE.test(sha))
|
|
6486
|
+
return bad("sha must be 7–40 hex characters");
|
|
6487
|
+
const environment = typeof j.environment === "string" ? j.environment.trim() : "";
|
|
6488
|
+
if (!ENV_SLUG_RE.test(environment))
|
|
6489
|
+
return bad("environment must be a slug: lower-case letters, digits, - and _");
|
|
6490
|
+
const body = { repo, sha, environment };
|
|
6491
|
+
if (j.service !== undefined && j.service !== null) {
|
|
6492
|
+
const service = typeof j.service === "string" ? j.service.trim() : "";
|
|
6493
|
+
if (!SERVICE_RE.test(service))
|
|
6494
|
+
return bad("service must be a short name");
|
|
6495
|
+
body.service = service;
|
|
6496
|
+
}
|
|
6497
|
+
if (j.at !== undefined && j.at !== null) {
|
|
6498
|
+
const at = typeof j.at === "string" ? Date.parse(j.at) : typeof j.at === "number" ? j.at : Number.NaN;
|
|
6499
|
+
if (!Number.isFinite(at))
|
|
6500
|
+
return bad("at must be an ISO time");
|
|
6501
|
+
body.at = at;
|
|
6502
|
+
}
|
|
6503
|
+
if (j.url !== undefined && j.url !== null) {
|
|
6504
|
+
let ok = false;
|
|
6505
|
+
if (typeof j.url === "string") {
|
|
6506
|
+
try {
|
|
6507
|
+
ok = new URL(j.url).protocol === "https:";
|
|
6508
|
+
} catch {
|
|
6509
|
+
ok = false;
|
|
6510
|
+
}
|
|
6511
|
+
}
|
|
6512
|
+
if (!ok)
|
|
6513
|
+
return bad("url must be an https link");
|
|
6514
|
+
body.url = j.url;
|
|
6515
|
+
}
|
|
6516
|
+
return { ok: true, body };
|
|
6517
|
+
}
|
|
6518
|
+
var REPO_RE, SHA_RE, ENV_SLUG_RE, SERVICE_RE, bad = (error) => ({ ok: false, error });
|
|
6519
|
+
var init_deploys = __esm(() => {
|
|
6520
|
+
REPO_RE = /^[a-z0-9_.-]+\/[a-z0-9_.-]+$/;
|
|
6521
|
+
SHA_RE = /^[0-9a-f]{7,40}$/;
|
|
6522
|
+
ENV_SLUG_RE = /^[a-z0-9][a-z0-9_-]{0,39}$/;
|
|
6523
|
+
SERVICE_RE = /^[A-Za-z0-9][A-Za-z0-9_.-]{0,39}$/;
|
|
6524
|
+
});
|
|
6525
|
+
|
|
6300
6526
|
// ../../packages/shared/src/newrelic/newrelic.ts
|
|
6301
6527
|
var init_newrelic = () => {};
|
|
6302
6528
|
|
|
@@ -6696,7 +6922,7 @@ function nodePrompt(o) {
|
|
|
6696
6922
|
...claim.pack.includes(PR_HEADER_OPEN) ? [
|
|
6697
6923
|
`- The PR body starts with the pack's PR header block (\`${PR_HEADER_OPEN}\` … \`${PR_HEADER_CLOSE}\`, in its part 5), above everything else. The pack wrote it before your work existed: when you make a mock (a screenshot or an artifact) or write a doc, put its link on that part's line in place of \`None\` — not only lower in the body (SL-1746). When you edit the body later, keep it on top.`
|
|
6698
6924
|
] : [],
|
|
6699
|
-
"- Run the repo's own gate before you push. Never merge: a person (or the musher, when the attachment says so) does.",
|
|
6925
|
+
o.gate ? `- Run the repo's gate before you push: exactly \`${o.gate}\`, as given. Never wrap it — no \`$?\`, \`$(...)\`, \`bash -c\` or \`; echo\` around it — or it stops at a permission dialog; read its result from the exit and the output. Never merge: a person (or the musher, when the attachment says so) does.` : "- Run the repo's own gate before you push. Never merge: a person (or the musher, when the attachment says so) does.",
|
|
6700
6926
|
"- Stay inside this node's scope. Work another node needs goes in the PR description, not in this branch.",
|
|
6701
6927
|
`- End every comment and reply you post on the PR with the line \`${ENGINE_MARK}\`, so the engine knows it is yours and not a reviewer's.`,
|
|
6702
6928
|
"- The approved brief and this route are the go-ahead: a person said yes to them before this session started. Build, commit, push and open the PR; a park is for a question the record does not settle, not for a go-ahead to build or push.",
|
|
@@ -7138,6 +7364,9 @@ var init_effort = __esm(() => {
|
|
|
7138
7364
|
EFFORTS = ["low", "medium", "high", "xhigh", "max"];
|
|
7139
7365
|
});
|
|
7140
7366
|
|
|
7367
|
+
// ../../packages/shared/src/workspaceLook.ts
|
|
7368
|
+
var init_workspaceLook = () => {};
|
|
7369
|
+
|
|
7141
7370
|
// src/settings.ts
|
|
7142
7371
|
import { chmodSync, existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
|
|
7143
7372
|
import { dirname, join, resolve as resolve2 } from "node:path";
|
|
@@ -7682,6 +7911,8 @@ var init_src = __esm(() => {
|
|
|
7682
7911
|
init_profile2();
|
|
7683
7912
|
init_outcome();
|
|
7684
7913
|
init_mention();
|
|
7914
|
+
init_agent();
|
|
7915
|
+
init_copy();
|
|
7685
7916
|
init_productHits();
|
|
7686
7917
|
init_model();
|
|
7687
7918
|
init_parse();
|
|
@@ -7696,6 +7927,7 @@ var init_src = __esm(() => {
|
|
|
7696
7927
|
init_cheatSheet();
|
|
7697
7928
|
init_pack2();
|
|
7698
7929
|
init_flags();
|
|
7930
|
+
init_deploys();
|
|
7699
7931
|
init_sentry();
|
|
7700
7932
|
init_newrelic();
|
|
7701
7933
|
init_musher();
|
|
@@ -7706,6 +7938,7 @@ var init_src = __esm(() => {
|
|
|
7706
7938
|
init_otlp();
|
|
7707
7939
|
init_session();
|
|
7708
7940
|
init_effort();
|
|
7941
|
+
init_workspaceLook();
|
|
7709
7942
|
});
|
|
7710
7943
|
|
|
7711
7944
|
// src/engine/json.ts
|
|
@@ -8383,6 +8616,8 @@ init_due();
|
|
|
8383
8616
|
init_profile2();
|
|
8384
8617
|
init_outcome();
|
|
8385
8618
|
init_mention();
|
|
8619
|
+
init_agent();
|
|
8620
|
+
init_copy();
|
|
8386
8621
|
init_productHits();
|
|
8387
8622
|
init_model();
|
|
8388
8623
|
init_parse();
|
|
@@ -8397,6 +8632,7 @@ init_whatsNew();
|
|
|
8397
8632
|
init_cheatSheet();
|
|
8398
8633
|
init_pack2();
|
|
8399
8634
|
init_flags();
|
|
8635
|
+
init_deploys();
|
|
8400
8636
|
init_sentry();
|
|
8401
8637
|
init_newrelic();
|
|
8402
8638
|
init_musher();
|
|
@@ -8407,6 +8643,7 @@ init_engine();
|
|
|
8407
8643
|
init_otlp();
|
|
8408
8644
|
init_session();
|
|
8409
8645
|
init_effort();
|
|
8646
|
+
init_workspaceLook();
|
|
8410
8647
|
|
|
8411
8648
|
// src/agentMcp.ts
|
|
8412
8649
|
import { execFileSync } from "node:child_process";
|
|
@@ -9561,24 +9798,142 @@ var defer = async (o) => {
|
|
|
9561
9798
|
return 0;
|
|
9562
9799
|
};
|
|
9563
9800
|
|
|
9801
|
+
// src/commands/deploy.ts
|
|
9802
|
+
init_src();
|
|
9803
|
+
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
9804
|
+
var DEPLOYS_URL = "https://hooks.sleds.ai/deploys";
|
|
9805
|
+
var USAGE = "usage: sleds deploy --env <slug> [--service <name>] [--sha <sha>] [--repo <owner/name>]";
|
|
9806
|
+
var FLAGS = ["env", "service", "sha", "repo"];
|
|
9807
|
+
var set = (x) => x?.trim() ? x.trim() : undefined;
|
|
9808
|
+
function repoFromRemote(remote) {
|
|
9809
|
+
const m = /^(?:[a-z+]+:\/\/)?(?:[^@/]+@)?[^/:]+[:/](.+?)(?:\.git)?\/?$/i.exec(remote.trim());
|
|
9810
|
+
const parts = m?.[1]?.split("/").filter(Boolean) ?? [];
|
|
9811
|
+
if (parts.length < 2)
|
|
9812
|
+
return null;
|
|
9813
|
+
return `${parts[parts.length - 2]}/${parts[parts.length - 1]}`.toLowerCase();
|
|
9814
|
+
}
|
|
9815
|
+
function resolveDeploy(argv, env, git) {
|
|
9816
|
+
const flags = {};
|
|
9817
|
+
for (let i = 0;i < argv.length; i++) {
|
|
9818
|
+
const a = argv[i] ?? "";
|
|
9819
|
+
const m = /^--([a-z]+)(?:=(.*))?$/.exec(a);
|
|
9820
|
+
const name = m?.[1];
|
|
9821
|
+
if (!m || !name || !FLAGS.includes(name))
|
|
9822
|
+
return { ok: false, error: `unknown argument ${a}` };
|
|
9823
|
+
const value = m[2] ?? argv[++i];
|
|
9824
|
+
if (!set(value))
|
|
9825
|
+
return { ok: false, error: `--${name} needs a value` };
|
|
9826
|
+
flags[name] = value?.trim();
|
|
9827
|
+
}
|
|
9828
|
+
if (!flags.env)
|
|
9829
|
+
return { ok: false, error: "--env <slug> is required" };
|
|
9830
|
+
const vercelRepo = set(env.VERCEL_GIT_REPO_OWNER) && set(env.VERCEL_GIT_REPO_SLUG) ? `${set(env.VERCEL_GIT_REPO_OWNER)}/${set(env.VERCEL_GIT_REPO_SLUG)}` : undefined;
|
|
9831
|
+
const remote = () => {
|
|
9832
|
+
const url = git(["remote", "get-url", "origin"]);
|
|
9833
|
+
return url && repoFromRemote(url) || undefined;
|
|
9834
|
+
};
|
|
9835
|
+
const repo = flags.repo ?? set(env.GITHUB_REPOSITORY) ?? vercelRepo ?? remote();
|
|
9836
|
+
if (!repo)
|
|
9837
|
+
return { ok: false, error: "no repo: pass --repo owner/name (no origin remote)" };
|
|
9838
|
+
const sha = flags.sha ?? set(env.SOURCE_VERSION) ?? set(env.HEROKU_SLUG_COMMIT) ?? set(env.VERCEL_GIT_COMMIT_SHA) ?? set(env.GITHUB_SHA) ?? set(git(["rev-parse", "HEAD"]) ?? undefined);
|
|
9839
|
+
if (!sha)
|
|
9840
|
+
return { ok: false, error: "no sha: pass --sha (not a git checkout)" };
|
|
9841
|
+
return parseDeployBody({
|
|
9842
|
+
repo,
|
|
9843
|
+
sha,
|
|
9844
|
+
environment: flags.env,
|
|
9845
|
+
...flags.service ? { service: flags.service } : {}
|
|
9846
|
+
});
|
|
9847
|
+
}
|
|
9848
|
+
var defaultGit = (args) => {
|
|
9849
|
+
try {
|
|
9850
|
+
return execFileSync2("git", args, { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).toString().trim();
|
|
9851
|
+
} catch {
|
|
9852
|
+
return null;
|
|
9853
|
+
}
|
|
9854
|
+
};
|
|
9855
|
+
var defaultPost = async (url, token, body) => {
|
|
9856
|
+
try {
|
|
9857
|
+
const res = await fetch(url, {
|
|
9858
|
+
method: "POST",
|
|
9859
|
+
headers: { authorization: `Bearer ${token}`, "content-type": "application/json" },
|
|
9860
|
+
body: JSON.stringify(body),
|
|
9861
|
+
signal: AbortSignal.timeout(15000)
|
|
9862
|
+
});
|
|
9863
|
+
let json = null;
|
|
9864
|
+
try {
|
|
9865
|
+
json = await res.json();
|
|
9866
|
+
} catch {}
|
|
9867
|
+
return { status: res.status, json };
|
|
9868
|
+
} catch (e) {
|
|
9869
|
+
return { error: e instanceof Error ? e.message : String(e) };
|
|
9870
|
+
}
|
|
9871
|
+
};
|
|
9872
|
+
function deployLine(body, json) {
|
|
9873
|
+
const where = `${body.repo}${body.service ? ` (${body.service})` : ""} → ${body.environment}`;
|
|
9874
|
+
if (typeof json.ignored === "string")
|
|
9875
|
+
return `ignored: ${json.ignored}`;
|
|
9876
|
+
if (json.duplicate)
|
|
9877
|
+
return `${where}: already at ${body.sha.slice(0, 7)}`;
|
|
9878
|
+
const prs = Array.isArray(json.newlyDeployed) ? json.newlyDeployed : [];
|
|
9879
|
+
const list = prs.map((p) => `#${p.number}`).join(", ");
|
|
9880
|
+
if (json.rollback)
|
|
9881
|
+
return `${where}: rollback to ${body.sha.slice(0, 7)}${list ? ` (carries ${list} again)` : ""}`;
|
|
9882
|
+
return prs.length ? `${where}: newly deployed ${list}` : `${where}: ${body.sha.slice(0, 7)} recorded, no newly deployed PRs`;
|
|
9883
|
+
}
|
|
9884
|
+
var deployCmd = async (o) => {
|
|
9885
|
+
const log = o.log ?? console.log;
|
|
9886
|
+
const error = o.error ?? console.error;
|
|
9887
|
+
const env = o.env ?? process.env;
|
|
9888
|
+
const token = set(env.SLEDS_DEPLOY_TOKEN);
|
|
9889
|
+
if (!token) {
|
|
9890
|
+
error("SLEDS_DEPLOY_TOKEN is not set — mint one in Settings › Deploys");
|
|
9891
|
+
return 2;
|
|
9892
|
+
}
|
|
9893
|
+
const resolved = resolveDeploy(o.argv, env, o.git ?? defaultGit);
|
|
9894
|
+
if (!resolved.ok) {
|
|
9895
|
+
error(resolved.error);
|
|
9896
|
+
error(USAGE);
|
|
9897
|
+
return 2;
|
|
9898
|
+
}
|
|
9899
|
+
const body = resolved.body;
|
|
9900
|
+
const url = set(env.SLEDS_DEPLOYS_URL) ?? DEPLOYS_URL;
|
|
9901
|
+
const r = await (o.post ?? defaultPost)(url, token, body);
|
|
9902
|
+
if ("error" in r) {
|
|
9903
|
+
error(`warning: SLEDS unreachable, deploy not recorded — ${r.error}`);
|
|
9904
|
+
return 0;
|
|
9905
|
+
}
|
|
9906
|
+
if (r.status === 200 && r.json) {
|
|
9907
|
+
log(deployLine(body, r.json));
|
|
9908
|
+
return 0;
|
|
9909
|
+
}
|
|
9910
|
+
const why = typeof r.json?.error === "string" ? r.json.error : `HTTP ${r.status}`;
|
|
9911
|
+
if (r.status >= 400 && r.status < 500) {
|
|
9912
|
+
error(r.status === 401 ? `SLEDS refused the deploy token (${why})` : why);
|
|
9913
|
+
return 1;
|
|
9914
|
+
}
|
|
9915
|
+
error(`warning: SLEDS answered ${why}, deploy not recorded`);
|
|
9916
|
+
return 0;
|
|
9917
|
+
};
|
|
9918
|
+
|
|
9564
9919
|
// src/commands/doctor.ts
|
|
9565
9920
|
init_src();
|
|
9566
|
-
import { execFileSync as
|
|
9921
|
+
import { execFileSync as execFileSync4, spawnSync as spawnSync5 } from "node:child_process";
|
|
9567
9922
|
import { randomBytes } from "node:crypto";
|
|
9568
9923
|
import {
|
|
9569
|
-
existsSync as
|
|
9570
|
-
lstatSync,
|
|
9924
|
+
existsSync as existsSync17,
|
|
9925
|
+
lstatSync as lstatSync2,
|
|
9571
9926
|
mkdtempSync,
|
|
9572
9927
|
readFileSync as readFileSync17,
|
|
9573
|
-
readlinkSync,
|
|
9928
|
+
readlinkSync as readlinkSync2,
|
|
9574
9929
|
rmSync as rmSync8,
|
|
9575
9930
|
writeFileSync as writeFileSync17
|
|
9576
9931
|
} from "node:fs";
|
|
9577
9932
|
import { tmpdir } from "node:os";
|
|
9578
|
-
import { join as
|
|
9933
|
+
import { join as join20, resolve as resolve9 } from "node:path";
|
|
9579
9934
|
|
|
9580
9935
|
// src/agentMcp.ts
|
|
9581
|
-
import { execFileSync as
|
|
9936
|
+
import { execFileSync as execFileSync3 } from "node:child_process";
|
|
9582
9937
|
import { existsSync as existsSync10, mkdirSync as mkdirSync9, readFileSync as readFileSync9, writeFileSync as writeFileSync9 } from "node:fs";
|
|
9583
9938
|
import { dirname as dirname8, join as join10, resolve as resolve6 } from "node:path";
|
|
9584
9939
|
init_settings();
|
|
@@ -9646,7 +10001,7 @@ function applyCodexMcp2(toml, home, env = process.env) {
|
|
|
9646
10001
|
`;
|
|
9647
10002
|
}
|
|
9648
10003
|
var defaultRunClaude2 = (args) => {
|
|
9649
|
-
|
|
10004
|
+
execFileSync3("claude", args, { stdio: ["ignore", "ignore", "pipe"], timeout: 20000 });
|
|
9650
10005
|
};
|
|
9651
10006
|
async function offerMcp2(o) {
|
|
9652
10007
|
const env = o.env ?? process.env;
|
|
@@ -10101,6 +10456,7 @@ var agentShellCommand = (o) => {
|
|
|
10101
10456
|
const effort = o.effort ? ` --effort ${shq2(o.effort)}` : "";
|
|
10102
10457
|
return `${sandboxedAgent("claude", o.sandbox, o.mainCheckout)}${hookSettingsFlag(o)}${mcpConfigFlag(o)} --permission-mode ${shq2(o.permissionMode)}${model}${effort} ${prompt}`;
|
|
10103
10458
|
};
|
|
10459
|
+
var execLine = (cmd) => /^[A-Za-z_][A-Za-z0-9_]*=/.test(cmd) ? `exec env ${cmd}` : `exec ${cmd}`;
|
|
10104
10460
|
var sandboxedAgent = (agent, sandbox, mainCheckout) => {
|
|
10105
10461
|
const kind = sandboxAgentKind(agent);
|
|
10106
10462
|
const line = kind === "claude" ? `${agent}${claudeAddDirFlag(mainCheckout)}` : agent;
|
|
@@ -10537,6 +10893,215 @@ function cmuxRunner(exec, deps = {}) {
|
|
|
10537
10893
|
};
|
|
10538
10894
|
}
|
|
10539
10895
|
|
|
10896
|
+
// src/runners/orca.ts
|
|
10897
|
+
import { existsSync as existsSync12, lstatSync, readlinkSync, writeFileSync as writeFileSync11 } from "node:fs";
|
|
10898
|
+
import { join as join14, resolve as resolve7 } from "node:path";
|
|
10899
|
+
var obj = (v) => v !== null && typeof v === "object" && !Array.isArray(v) ? v : null;
|
|
10900
|
+
var str8 = (v) => typeof v === "string" && v.length > 0 ? v : null;
|
|
10901
|
+
var parseOrcaJson = (stdout) => {
|
|
10902
|
+
try {
|
|
10903
|
+
return obj(JSON.parse(stdout));
|
|
10904
|
+
} catch {
|
|
10905
|
+
return null;
|
|
10906
|
+
}
|
|
10907
|
+
};
|
|
10908
|
+
var orcaError = (env) => {
|
|
10909
|
+
if (!env || env.ok !== false)
|
|
10910
|
+
return null;
|
|
10911
|
+
const e = obj(env.error);
|
|
10912
|
+
const message = str8(e?.message) ?? "Orca refused the call";
|
|
10913
|
+
const code = str8(e?.code);
|
|
10914
|
+
return code ? `${message} (${code})` : message;
|
|
10915
|
+
};
|
|
10916
|
+
var parseOrcaWorktree = (env) => {
|
|
10917
|
+
const result = obj(env.result) ?? {};
|
|
10918
|
+
return str8(obj(result.worktree)?.path) ?? str8(result.worktreePath) ?? str8(result.path);
|
|
10919
|
+
};
|
|
10920
|
+
var parseOrcaBranch = (env) => {
|
|
10921
|
+
const wt = obj(obj(env.result)?.worktree);
|
|
10922
|
+
const branch = str8(wt?.branch)?.replace(/^refs\/heads\//, "");
|
|
10923
|
+
const head = str8(wt?.head);
|
|
10924
|
+
return branch && head ? { branch, head } : null;
|
|
10925
|
+
};
|
|
10926
|
+
var parseOrcaTerminal = (env) => {
|
|
10927
|
+
const result = obj(env.result) ?? {};
|
|
10928
|
+
return str8(obj(result.terminal)?.handle) ?? str8(result.handle) ?? str8(result.terminalHandle) ?? null;
|
|
10929
|
+
};
|
|
10930
|
+
var orcaLauncher = (o) => `#!/bin/sh
|
|
10931
|
+
# sleds engine: ${o.name}
|
|
10932
|
+
${execLine(agentShellCommand(o))}
|
|
10933
|
+
`;
|
|
10934
|
+
var orcaCommand = (launcher) => `sh ${shq2(launcher)}; exit`;
|
|
10935
|
+
var GONE = /exit|clos|gone|dead|stopp|kill/i;
|
|
10936
|
+
var orcaTerminalLive = (status, stdout) => {
|
|
10937
|
+
const env = parseOrcaJson(stdout);
|
|
10938
|
+
if (str8(obj(env?.error)?.code) === "runtime_unavailable")
|
|
10939
|
+
return true;
|
|
10940
|
+
if (status !== 0 || !env || env.ok === false)
|
|
10941
|
+
return false;
|
|
10942
|
+
const t = obj(obj(env.result)?.terminal) ?? obj(env.result) ?? {};
|
|
10943
|
+
if (typeof t.status === "string" && GONE.test(t.status))
|
|
10944
|
+
return false;
|
|
10945
|
+
if (t.exited === true || t.alive === false)
|
|
10946
|
+
return false;
|
|
10947
|
+
if (typeof t.exitCode === "number")
|
|
10948
|
+
return false;
|
|
10949
|
+
if (obj(t.exitCause))
|
|
10950
|
+
return false;
|
|
10951
|
+
if (t.orphaned === true && t.writable === false)
|
|
10952
|
+
return false;
|
|
10953
|
+
return true;
|
|
10954
|
+
};
|
|
10955
|
+
var realFs = { lstat: lstatSync, readlink: readlinkSync, exists: existsSync12 };
|
|
10956
|
+
var ORCA_LINK_FIX = "reinstall Orca's shell command from Orca (it links `orca` onto your PATH)";
|
|
10957
|
+
function danglingOrca(path, fs) {
|
|
10958
|
+
for (const dir of path.split(":").filter(Boolean)) {
|
|
10959
|
+
const link = join14(dir, "orca");
|
|
10960
|
+
try {
|
|
10961
|
+
if (!fs.lstat(link).isSymbolicLink())
|
|
10962
|
+
return null;
|
|
10963
|
+
const target = resolve7(dir, fs.readlink(link));
|
|
10964
|
+
if (!fs.exists(target))
|
|
10965
|
+
return { link, target };
|
|
10966
|
+
return null;
|
|
10967
|
+
} catch {}
|
|
10968
|
+
}
|
|
10969
|
+
return null;
|
|
10970
|
+
}
|
|
10971
|
+
function orcaPreflight(exec, path, fs = realFs) {
|
|
10972
|
+
let r;
|
|
10973
|
+
try {
|
|
10974
|
+
r = exec("orca", ["--version"], { env: { PATH: path } });
|
|
10975
|
+
} catch (e) {
|
|
10976
|
+
r = { status: 127, stdout: "", stderr: e instanceof Error ? e.message : String(e) };
|
|
10977
|
+
}
|
|
10978
|
+
if (r.status === 0) {
|
|
10979
|
+
return { ok: true, version: r.stdout.trim().split(`
|
|
10980
|
+
`)[0]?.trim() || "orca" };
|
|
10981
|
+
}
|
|
10982
|
+
const dangling = danglingOrca(path, fs);
|
|
10983
|
+
if (dangling) {
|
|
10984
|
+
return {
|
|
10985
|
+
ok: false,
|
|
10986
|
+
why: `${dangling.link} links to ${dangling.target}, which is gone`,
|
|
10987
|
+
fix: ORCA_LINK_FIX
|
|
10988
|
+
};
|
|
10989
|
+
}
|
|
10990
|
+
if (!path.split(":").includes("/usr/bin")) {
|
|
10991
|
+
return { ok: false, why: "the engine's PATH has no /usr/bin", fix: "sleds engine install" };
|
|
10992
|
+
}
|
|
10993
|
+
if (r.status === 127) {
|
|
10994
|
+
return { ok: false, why: "not installed (no `orca` on the PATH)", fix: ORCA_LINK_FIX };
|
|
10995
|
+
}
|
|
10996
|
+
const said = `${r.stderr}
|
|
10997
|
+
${r.stdout}`.split(`
|
|
10998
|
+
`).map((l) => l.trim()).find(Boolean);
|
|
10999
|
+
return {
|
|
11000
|
+
ok: false,
|
|
11001
|
+
why: `\`orca --version\` failed: ${said ?? `exit ${r.status}`}`,
|
|
11002
|
+
fix: "reinstall Orca, or its shell command from Orca"
|
|
11003
|
+
};
|
|
11004
|
+
}
|
|
11005
|
+
var launcherPath2 = (promptFile) => `${promptFile.replace(/\.md$/, "")}.orca.sh`;
|
|
11006
|
+
function orcaRunner(exec, deps = {}) {
|
|
11007
|
+
const writeFile = deps.writeFile ?? ((p, t) => writeFileSync11(p, t, { mode: 384 }));
|
|
11008
|
+
const orca = (args, what) => {
|
|
11009
|
+
const r = exec("orca", args);
|
|
11010
|
+
const env = parseOrcaJson(r.stdout);
|
|
11011
|
+
const refused = orcaError(env);
|
|
11012
|
+
if (refused)
|
|
11013
|
+
throw new Error(`${what}: ${refused}`);
|
|
11014
|
+
if (r.status !== 0 || !env) {
|
|
11015
|
+
throw new Error(`${what} failed: ${r.stderr.trim() || r.stdout.trim() || `exit ${r.status}`}`);
|
|
11016
|
+
}
|
|
11017
|
+
return env;
|
|
11018
|
+
};
|
|
11019
|
+
return {
|
|
11020
|
+
name: "orca",
|
|
11021
|
+
makesWorktree: true,
|
|
11022
|
+
readsSession: true,
|
|
11023
|
+
start(o) {
|
|
11024
|
+
const created = orca([
|
|
11025
|
+
"worktree",
|
|
11026
|
+
"create",
|
|
11027
|
+
"--repo",
|
|
11028
|
+
`path:${o.repoPath}`,
|
|
11029
|
+
"--name",
|
|
11030
|
+
o.name,
|
|
11031
|
+
"--base-branch",
|
|
11032
|
+
`origin/${o.base ?? "main"}`,
|
|
11033
|
+
"--no-parent",
|
|
11034
|
+
"--json"
|
|
11035
|
+
], `orca worktree create ${o.name}`);
|
|
11036
|
+
const cwd = parseOrcaWorktree(created);
|
|
11037
|
+
if (!cwd) {
|
|
11038
|
+
throw new Error(`orca worktree create ${o.name} returned no worktree path; the agent was not started`);
|
|
11039
|
+
}
|
|
11040
|
+
const launcher = launcherPath2(o.promptFile);
|
|
11041
|
+
writeFile(launcher, orcaLauncher(o));
|
|
11042
|
+
const shown = orca([
|
|
11043
|
+
"terminal",
|
|
11044
|
+
"create",
|
|
11045
|
+
"--worktree",
|
|
11046
|
+
`path:${cwd}`,
|
|
11047
|
+
"--title",
|
|
11048
|
+
o.name,
|
|
11049
|
+
"--command",
|
|
11050
|
+
orcaCommand(launcher),
|
|
11051
|
+
"--json"
|
|
11052
|
+
], `orca terminal create ${o.name} (worktree ${cwd})`);
|
|
11053
|
+
const terminal = parseOrcaTerminal(shown);
|
|
11054
|
+
if (!terminal) {
|
|
11055
|
+
throw new Error(`orca terminal create ${o.name} started in ${cwd} but returned no terminal handle`);
|
|
11056
|
+
}
|
|
11057
|
+
const own = parseOrcaBranch(created);
|
|
11058
|
+
const h = { runner: "orca", id: terminal, cwd, ...own ? { own } : {} };
|
|
11059
|
+
return h;
|
|
11060
|
+
},
|
|
11061
|
+
status(h) {
|
|
11062
|
+
const r = exec("orca", ["terminal", "show", "--terminal", h.id, "--json"]);
|
|
11063
|
+
return orcaTerminalLive(r.status, r.stdout) ? "running" : "exited";
|
|
11064
|
+
},
|
|
11065
|
+
read(h) {
|
|
11066
|
+
const r = exec("orca", ["terminal", "read", "--terminal", h.id, "--limit", "200"]);
|
|
11067
|
+
return r.status === 0 ? r.stdout : "";
|
|
11068
|
+
},
|
|
11069
|
+
send(h, text) {
|
|
11070
|
+
const r = exec("orca", ["terminal", "send", "--terminal", h.id, "--text", text, "--enter"]);
|
|
11071
|
+
if (r.status !== 0) {
|
|
11072
|
+
throw new Error(`orca terminal send ${h.id} failed: ${r.stderr.trim() || `exit ${r.status}`}`);
|
|
11073
|
+
}
|
|
11074
|
+
},
|
|
11075
|
+
stop(h) {
|
|
11076
|
+
exec("orca", ["terminal", "close", "--terminal", h.id]);
|
|
11077
|
+
},
|
|
11078
|
+
watchHint(h) {
|
|
11079
|
+
return `orca terminal show --terminal ${h.id}`;
|
|
11080
|
+
},
|
|
11081
|
+
probe() {
|
|
11082
|
+
try {
|
|
11083
|
+
const p = orcaPreflight(exec, process.env.PATH ?? "", deps.fs);
|
|
11084
|
+
return p.ok ? null : `orca: ${p.why} — ${p.fix}`;
|
|
11085
|
+
} catch {
|
|
11086
|
+
return "orca: not installed";
|
|
11087
|
+
}
|
|
11088
|
+
},
|
|
11089
|
+
removeWorktree(h, repoPath) {
|
|
11090
|
+
orca(["worktree", "rm", "--worktree", `path:${h.cwd}`, "--json"], `orca worktree rm ${h.cwd}`);
|
|
11091
|
+
if (!h.own)
|
|
11092
|
+
return;
|
|
11093
|
+
const ref = `refs/heads/${h.own.branch}`;
|
|
11094
|
+
const r = exec("git", ["-C", repoPath, "update-ref", "-d", ref, h.own.head]);
|
|
11095
|
+
if (r.status === 0)
|
|
11096
|
+
return;
|
|
11097
|
+
const still = exec("git", ["-C", repoPath, "rev-parse", "--verify", "--quiet", ref]);
|
|
11098
|
+
if (still.status !== 0)
|
|
11099
|
+
return;
|
|
11100
|
+
return `kept Orca's branch ${h.own.branch}: ${r.stderr.trim() || `exit ${r.status}`}`;
|
|
11101
|
+
}
|
|
11102
|
+
};
|
|
11103
|
+
}
|
|
11104
|
+
|
|
10540
11105
|
// src/commands/doctor.ts
|
|
10541
11106
|
init_settings();
|
|
10542
11107
|
|
|
@@ -10554,7 +11119,7 @@ import {
|
|
|
10554
11119
|
writeFileSync as writeFileSync13
|
|
10555
11120
|
} from "node:fs";
|
|
10556
11121
|
import { hostname as hostname2 } from "node:os";
|
|
10557
|
-
import { join as
|
|
11122
|
+
import { join as join16, resolve as resolve8 } from "node:path";
|
|
10558
11123
|
|
|
10559
11124
|
// src/engine/beat.ts
|
|
10560
11125
|
var BEAT_EVERY_MS = 60000;
|
|
@@ -10566,7 +11131,7 @@ function beatBody(state, flags, host = {}, cap = {}) {
|
|
|
10566
11131
|
name: flags.name,
|
|
10567
11132
|
os: host.os ?? process.platform,
|
|
10568
11133
|
runner: flags.runner,
|
|
10569
|
-
version: host.version ?? "0.1.
|
|
11134
|
+
version: host.version ?? "0.1.45" ?? "dev",
|
|
10570
11135
|
running: live.length,
|
|
10571
11136
|
items: live.slice(0, BEAT_ITEMS).map((r) => `${r.item}/${r.node}`),
|
|
10572
11137
|
...cap.atOnce !== undefined ? { atOnce: cap.atOnce } : {},
|
|
@@ -11359,7 +11924,7 @@ function jiraFromEnv(epic, gate, env = process.env) {
|
|
|
11359
11924
|
init_src();
|
|
11360
11925
|
|
|
11361
11926
|
// src/commands/permissionEvent.ts
|
|
11362
|
-
import { chmodSync as chmodSync3, mkdirSync as mkdirSync10, readFileSync as readFileSync10, renameSync as renameSync6, rmSync as rmSync3, writeFileSync as
|
|
11927
|
+
import { chmodSync as chmodSync3, mkdirSync as mkdirSync10, readFileSync as readFileSync10, renameSync as renameSync6, rmSync as rmSync3, writeFileSync as writeFileSync12 } from "node:fs";
|
|
11363
11928
|
|
|
11364
11929
|
// src/commands/seatEvent.ts
|
|
11365
11930
|
init_config();
|
|
@@ -11559,7 +12124,7 @@ function keysIn(states, state) {
|
|
|
11559
12124
|
}
|
|
11560
12125
|
function statesLanded(states) {
|
|
11561
12126
|
const all = Object.values(states);
|
|
11562
|
-
return all.length > 0 && all.every((s) => s === "landed" || s === "skipped");
|
|
12127
|
+
return all.length > 0 && all.every((s) => s === "landed" || s === "skipped") && all.some((s) => s === "landed");
|
|
11563
12128
|
}
|
|
11564
12129
|
function armedTerms(armed) {
|
|
11565
12130
|
if (!isObject8(armed))
|
|
@@ -11838,11 +12403,12 @@ function sledsSource(client, o) {
|
|
|
11838
12403
|
async function draft(row, item, why) {
|
|
11839
12404
|
const project = str6(row.project) ?? item;
|
|
11840
12405
|
const pending = isObject8(row.route) && isObject8(row.route.redraft) ? row.route.redraft : null;
|
|
12406
|
+
const runner = typeof o.runner === "function" ? o.runner() : o.runner;
|
|
11841
12407
|
const r = await client.postFull("/proxy/route", {
|
|
11842
12408
|
project,
|
|
11843
12409
|
draft: "take",
|
|
11844
12410
|
engine: o.engine,
|
|
11845
|
-
...
|
|
12411
|
+
...runner ? { runner } : {},
|
|
11846
12412
|
...o.leaseMin !== undefined ? { leaseMin: o.leaseMin } : {}
|
|
11847
12413
|
});
|
|
11848
12414
|
if (!r || r.status >= 300 || !r.json || r.json.ok === false) {
|
|
@@ -12370,10 +12936,10 @@ function providerFailure(screen) {
|
|
|
12370
12936
|
|
|
12371
12937
|
// src/engine/worktree.ts
|
|
12372
12938
|
init_src();
|
|
12373
|
-
import { appendFileSync, existsSync as
|
|
12374
|
-
import { dirname as dirname11, isAbsolute, join as
|
|
12939
|
+
import { appendFileSync, existsSync as existsSync13, mkdirSync as mkdirSync11 } from "node:fs";
|
|
12940
|
+
import { dirname as dirname11, isAbsolute, join as join15 } from "node:path";
|
|
12375
12941
|
function worktreePath(repoPath, name) {
|
|
12376
|
-
return
|
|
12942
|
+
return join15(repoPath, ".worktrees", name);
|
|
12377
12943
|
}
|
|
12378
12944
|
var fail = (what, r) => new Error(`${what} failed: ${(r.stderr || r.stdout).trim() || `exit ${r.status}`}`);
|
|
12379
12945
|
function defaultBranch(exec, repoPath) {
|
|
@@ -12394,7 +12960,7 @@ function defaultBranch(exec, repoPath) {
|
|
|
12394
12960
|
return "main";
|
|
12395
12961
|
}
|
|
12396
12962
|
var realWorktreeFs = {
|
|
12397
|
-
exists:
|
|
12963
|
+
exists: existsSync13,
|
|
12398
12964
|
append: (p, text) => {
|
|
12399
12965
|
mkdirSync11(dirname11(p), { recursive: true });
|
|
12400
12966
|
appendFileSync(p, text);
|
|
@@ -12408,7 +12974,7 @@ function ensureIgnored(exec, repoPath, fs = realWorktreeFs) {
|
|
|
12408
12974
|
if (common.status !== 0)
|
|
12409
12975
|
return;
|
|
12410
12976
|
const dir = common.stdout.trim();
|
|
12411
|
-
const exclude =
|
|
12977
|
+
const exclude = join15(isAbsolute(dir) ? dir : join15(repoPath, dir), "info", "exclude");
|
|
12412
12978
|
fs.append(exclude, `
|
|
12413
12979
|
# sleds engine: one worktree per route node
|
|
12414
12980
|
.worktrees/
|
|
@@ -12420,7 +12986,7 @@ function ensureWorktree(exec, repoPath, branch, path, base = "main", fs = realWo
|
|
|
12420
12986
|
return path;
|
|
12421
12987
|
}
|
|
12422
12988
|
function makeWorktree(exec, repoPath, branch, path, base, fs, log) {
|
|
12423
|
-
if (fs.exists(
|
|
12989
|
+
if (fs.exists(join15(path, ".git"))) {
|
|
12424
12990
|
fastForwardUntouched(exec, path, base, log);
|
|
12425
12991
|
return path;
|
|
12426
12992
|
}
|
|
@@ -12493,13 +13059,13 @@ function installArgv(command) {
|
|
|
12493
13059
|
return argv;
|
|
12494
13060
|
}
|
|
12495
13061
|
function lockfileInstall(path, exists) {
|
|
12496
|
-
if (exists(
|
|
13062
|
+
if (exists(join15(path, "pnpm-lock.yaml")))
|
|
12497
13063
|
return ["pnpm", "install", "--frozen-lockfile"];
|
|
12498
|
-
if (exists(
|
|
13064
|
+
if (exists(join15(path, "bun.lock")) || exists(join15(path, "bun.lockb")))
|
|
12499
13065
|
return ["bun", "install", "--frozen-lockfile"];
|
|
12500
|
-
if (exists(
|
|
13066
|
+
if (exists(join15(path, "yarn.lock")))
|
|
12501
13067
|
return ["yarn", "install", "--frozen-lockfile"];
|
|
12502
|
-
if (exists(
|
|
13068
|
+
if (exists(join15(path, "package-lock.json")))
|
|
12503
13069
|
return ["npm", "ci"];
|
|
12504
13070
|
return null;
|
|
12505
13071
|
}
|
|
@@ -12523,8 +13089,39 @@ function packInstallOf(pack, repo) {
|
|
|
12523
13089
|
}
|
|
12524
13090
|
return first;
|
|
12525
13091
|
}
|
|
13092
|
+
function packGateOf(pack, repo) {
|
|
13093
|
+
let inDone = false;
|
|
13094
|
+
let first;
|
|
13095
|
+
const want = repo?.trim().toLowerCase();
|
|
13096
|
+
for (const line of pack.split(`
|
|
13097
|
+
`)) {
|
|
13098
|
+
if (/^##\s/.test(line)) {
|
|
13099
|
+
inDone = /^##\s+4\./.test(line);
|
|
13100
|
+
continue;
|
|
13101
|
+
}
|
|
13102
|
+
if (!inDone)
|
|
13103
|
+
continue;
|
|
13104
|
+
const m = line.match(/^\s*-\s*([^:`]+?):\s*`(.+)`\s*$/);
|
|
13105
|
+
if (!m?.[1] || !m[2])
|
|
13106
|
+
continue;
|
|
13107
|
+
const name = m[1].trim().toLowerCase();
|
|
13108
|
+
if (name === "done means" || name.startsWith("try it"))
|
|
13109
|
+
continue;
|
|
13110
|
+
if (want && name === want)
|
|
13111
|
+
return m[2];
|
|
13112
|
+
first ??= m[2];
|
|
13113
|
+
}
|
|
13114
|
+
return first;
|
|
13115
|
+
}
|
|
13116
|
+
function gateAllowRules(gate) {
|
|
13117
|
+
const whole = gate?.trim();
|
|
13118
|
+
if (!whole)
|
|
13119
|
+
return [];
|
|
13120
|
+
const parts = whole.split(/&&|\|\||;/).map((p) => p.trim()).filter(Boolean);
|
|
13121
|
+
return [...new Set([whole, ...parts])].map((c) => `Bash(${c})`);
|
|
13122
|
+
}
|
|
12526
13123
|
function ensureInstalled(exec, path, fs, o = {}) {
|
|
12527
|
-
if (!fs.exists(
|
|
13124
|
+
if (!fs.exists(join15(path, "package.json")) || fs.exists(join15(path, "node_modules")))
|
|
12528
13125
|
return null;
|
|
12529
13126
|
const argv = installArgv(o.command) ?? lockfileInstall(path, fs.exists);
|
|
12530
13127
|
if (!argv)
|
|
@@ -12539,7 +13136,7 @@ function ensureInstalled(exec, path, fs, o = {}) {
|
|
|
12539
13136
|
return { argv, ok };
|
|
12540
13137
|
}
|
|
12541
13138
|
function ensureDetachedWorktree(exec, repoPath, path, base = "main", fs = realWorktreeFs) {
|
|
12542
|
-
if (fs.exists(
|
|
13139
|
+
if (fs.exists(join15(path, ".git")))
|
|
12543
13140
|
return path;
|
|
12544
13141
|
ensureIgnored(exec, repoPath, fs);
|
|
12545
13142
|
const f = exec("git", ["-C", repoPath, "fetch", "origin", base]);
|
|
@@ -12615,17 +13212,17 @@ function fingerprint(text) {
|
|
|
12615
13212
|
}
|
|
12616
13213
|
return (h >>> 0).toString(16);
|
|
12617
13214
|
}
|
|
12618
|
-
function sandboxFor(d, name, cwd) {
|
|
13215
|
+
function sandboxFor(d, name, cwd, allow) {
|
|
12619
13216
|
if (!d.sandbox)
|
|
12620
13217
|
return {};
|
|
12621
13218
|
if (!d.writeSandboxSettings)
|
|
12622
13219
|
throw new Error("sessions are sandboxed, but nothing writes their settings — not started");
|
|
12623
|
-
return { sandbox: d.writeSandboxSettings(name, cwd) };
|
|
13220
|
+
return { sandbox: d.writeSandboxSettings(name, cwd, allow) };
|
|
12624
13221
|
}
|
|
12625
|
-
function hooksFor(d, name) {
|
|
13222
|
+
function hooksFor(d, name, allow) {
|
|
12626
13223
|
if (d.sandbox || !d.writeHookSettings)
|
|
12627
13224
|
return {};
|
|
12628
|
-
return { settingsFile: d.writeHookSettings(name) };
|
|
13225
|
+
return { settingsFile: d.writeHookSettings(name, allow) };
|
|
12629
13226
|
}
|
|
12630
13227
|
function mainCheckoutFor(d, cwd) {
|
|
12631
13228
|
const main = d.mainCheckoutOf?.(cwd);
|
|
@@ -12686,6 +13283,8 @@ async function tick(deps) {
|
|
|
12686
13283
|
deps.startBackoff = d.startBackoff;
|
|
12687
13284
|
deps.providerHold = d.providerHold;
|
|
12688
13285
|
deps.atOnce = d.atOnce;
|
|
13286
|
+
deps.runner = d.runner;
|
|
13287
|
+
deps.runnerRefused = d.runnerRefused;
|
|
12689
13288
|
}
|
|
12690
13289
|
removeEndedMcpConfigs(deps, made);
|
|
12691
13290
|
}
|
|
@@ -12728,6 +13327,7 @@ async function tickOnce(d) {
|
|
|
12728
13327
|
const changes = [];
|
|
12729
13328
|
const decided = [];
|
|
12730
13329
|
const save = () => d.writeState(state);
|
|
13330
|
+
await rereadRunner(d);
|
|
12731
13331
|
probeRunner(d);
|
|
12732
13332
|
if (d.startBackoff && --d.startBackoff.checks <= 0)
|
|
12733
13333
|
d.startBackoff = undefined;
|
|
@@ -13080,6 +13680,8 @@ async function tickOnce(d) {
|
|
|
13080
13680
|
const worktree = d.runner.makesWorktree ? repoPath : d.ensureWorktree(d.exec, repoPath, branch, worktreePath(repoPath, name), base, packInstallOf(claim.pack, claim.repos[0])) ?? worktreePath(repoPath, name);
|
|
13081
13681
|
const prTitle = d.prTitleOf?.(claim);
|
|
13082
13682
|
mcpConfig = engineMcpConfigFor(d, name, claim.runToken, at, worktree);
|
|
13683
|
+
const gate = packGateOf(claim.pack, claim.repos[0]);
|
|
13684
|
+
const allow = gateAllowRules(gate);
|
|
13083
13685
|
const prompt = nodePrompt({
|
|
13084
13686
|
claim,
|
|
13085
13687
|
branch,
|
|
@@ -13088,6 +13690,7 @@ async function tickOnce(d) {
|
|
|
13088
13690
|
sledsTools: d.source.name === "sleds",
|
|
13089
13691
|
engineMcp: Boolean(mcpConfig),
|
|
13090
13692
|
...prTitle ? { prTitle } : {},
|
|
13693
|
+
...gate ? { gate } : {},
|
|
13091
13694
|
...nodePlanContext(await briefsOf(claim.item), claim.node)
|
|
13092
13695
|
});
|
|
13093
13696
|
const promptFile = d.writePrompt(name, prompt);
|
|
@@ -13104,8 +13707,8 @@ async function tickOnce(d) {
|
|
|
13104
13707
|
...launch.effort && !launch.agent ? { effort: launch.effort } : {},
|
|
13105
13708
|
...launch.agent ? { agent: launch.agent } : {},
|
|
13106
13709
|
...mcpConfig ? { mcpConfig, ...d.strictMcp ? { strictMcp: true } : {} } : {},
|
|
13107
|
-
...sandboxFor(d, name, worktree),
|
|
13108
|
-
...hooksFor(d, name),
|
|
13710
|
+
...sandboxFor(d, name, worktree, allow),
|
|
13711
|
+
...hooksFor(d, name, allow),
|
|
13109
13712
|
...mainCheckoutFor(d, worktree),
|
|
13110
13713
|
...claim.group ? { group: claim.group } : {}
|
|
13111
13714
|
};
|
|
@@ -13363,6 +13966,24 @@ function pressAnswer(d, runner, run, options, answer) {
|
|
|
13363
13966
|
return false;
|
|
13364
13967
|
}
|
|
13365
13968
|
}
|
|
13969
|
+
function readFollowed(d, runner, run) {
|
|
13970
|
+
if (!runner.readsSession)
|
|
13971
|
+
return "";
|
|
13972
|
+
let shown = "";
|
|
13973
|
+
try {
|
|
13974
|
+
shown = runner.read(run.handle);
|
|
13975
|
+
} catch {
|
|
13976
|
+
shown = "";
|
|
13977
|
+
}
|
|
13978
|
+
const print = fingerprint(shown);
|
|
13979
|
+
if (run.seen !== print) {
|
|
13980
|
+
run.seen = print;
|
|
13981
|
+
run.seenAt = d.now();
|
|
13982
|
+
run.seenTicks = 0;
|
|
13983
|
+
} else
|
|
13984
|
+
run.seenTicks = (run.seenTicks ?? 0) + 1;
|
|
13985
|
+
return shown;
|
|
13986
|
+
}
|
|
13366
13987
|
async function dropPermission(d, run, why) {
|
|
13367
13988
|
const perms = permissionsOf(d.source);
|
|
13368
13989
|
if (!run.permission || !perms || !run.runId)
|
|
@@ -13513,6 +14134,29 @@ async function rereadAtOnce(d) {
|
|
|
13513
14134
|
d.atOnce = next;
|
|
13514
14135
|
d.log(`at once now ${next}, from your engine profile`);
|
|
13515
14136
|
}
|
|
14137
|
+
async function rereadRunner(d) {
|
|
14138
|
+
if (!d.rereadRunner)
|
|
14139
|
+
return;
|
|
14140
|
+
let next = null;
|
|
14141
|
+
try {
|
|
14142
|
+
next = await d.rereadRunner(profileOf2(d));
|
|
14143
|
+
} catch {}
|
|
14144
|
+
if (next === null)
|
|
14145
|
+
return;
|
|
14146
|
+
if (typeof next === "string") {
|
|
14147
|
+
if (d.runnerRefused !== next)
|
|
14148
|
+
d.log(next);
|
|
14149
|
+
d.runnerRefused = next;
|
|
14150
|
+
return;
|
|
14151
|
+
}
|
|
14152
|
+
d.runnerRefused = undefined;
|
|
14153
|
+
if (next === d.runner)
|
|
14154
|
+
return;
|
|
14155
|
+
const was = d.runner.name;
|
|
14156
|
+
d.runner = next;
|
|
14157
|
+
d.runnerHold = undefined;
|
|
14158
|
+
d.log(`runner now ${next.name}, from your engine profile — new sessions start on it; running ones stay on ${was}`);
|
|
14159
|
+
}
|
|
13516
14160
|
async function rereadMachine(w, d) {
|
|
13517
14161
|
if (!w.reread)
|
|
13518
14162
|
return;
|
|
@@ -14164,11 +14808,13 @@ async function follow(d, state, run, claim, runner, finished, attention, changes
|
|
|
14164
14808
|
return true;
|
|
14165
14809
|
await judgeLook(d, run, view);
|
|
14166
14810
|
if (view.state === "MERGED") {
|
|
14811
|
+
await dropPermission(d, run, "the PR merged");
|
|
14167
14812
|
const landed = await ripple(d, state, run, claim, runner, pr.url, finished, decided, view.head, view.mergeCommit);
|
|
14168
14813
|
return !landed;
|
|
14169
14814
|
}
|
|
14170
14815
|
if (view.state === "CLOSED") {
|
|
14171
14816
|
d.log(`${ref(run)}: ${pr.url} was closed without a merge — let go, its session stopped`);
|
|
14817
|
+
await dropPermission(d, run, "the PR closed without a merge");
|
|
14172
14818
|
stopOpen(d, run, runner);
|
|
14173
14819
|
return false;
|
|
14174
14820
|
}
|
|
@@ -14188,6 +14834,8 @@ async function follow(d, state, run, claim, runner, finished, attention, changes
|
|
|
14188
14834
|
kind: "review",
|
|
14189
14835
|
what: `${view.feedback - pr.feedback} new comment${view.feedback - pr.feedback === 1 ? "" : "s"}`
|
|
14190
14836
|
});
|
|
14837
|
+
if (await settlePermission(d, runner, run, readFollowed(d, runner, run)))
|
|
14838
|
+
return true;
|
|
14191
14839
|
const tell = (text, what) => {
|
|
14192
14840
|
let alive = false;
|
|
14193
14841
|
try {
|
|
@@ -15057,8 +15705,8 @@ function commandRunner(template, deps = {}) {
|
|
|
15057
15705
|
|
|
15058
15706
|
// src/runners/conductor.ts
|
|
15059
15707
|
import { readFileSync as readFileSync11 } from "node:fs";
|
|
15060
|
-
var
|
|
15061
|
-
var
|
|
15708
|
+
var obj2 = (v) => v !== null && typeof v === "object" && !Array.isArray(v) ? v : null;
|
|
15709
|
+
var str9 = (v) => typeof v === "string" && v.length > 0 ? v : null;
|
|
15062
15710
|
var CONDUCTOR_API = "https://api.conductor.build";
|
|
15063
15711
|
var CLAUDE_MODELS = new Set([
|
|
15064
15712
|
"fable-5-1",
|
|
@@ -15115,7 +15763,7 @@ var splitCurl = (stdout) => {
|
|
|
15115
15763
|
const code = Number(stdout.slice(cut + 1).trim()) || 0;
|
|
15116
15764
|
let body = null;
|
|
15117
15765
|
try {
|
|
15118
|
-
body =
|
|
15766
|
+
body = obj2(JSON.parse(text));
|
|
15119
15767
|
} catch {
|
|
15120
15768
|
body = null;
|
|
15121
15769
|
}
|
|
@@ -15130,7 +15778,7 @@ var messageText = (content, depth = 0) => {
|
|
|
15130
15778
|
return content.map((c) => messageText(c, depth + 1)).filter(Boolean).join(`
|
|
15131
15779
|
`);
|
|
15132
15780
|
}
|
|
15133
|
-
const o =
|
|
15781
|
+
const o = obj2(content);
|
|
15134
15782
|
if (!o)
|
|
15135
15783
|
return String(content);
|
|
15136
15784
|
for (const k of ["text", "content", "message", "result"]) {
|
|
@@ -15138,12 +15786,12 @@ var messageText = (content, depth = 0) => {
|
|
|
15138
15786
|
if (t)
|
|
15139
15787
|
return t;
|
|
15140
15788
|
}
|
|
15141
|
-
return
|
|
15789
|
+
return str9(o.type) ? `(${o.type}${str9(o.name) ? ` ${o.name}` : ""})` : "";
|
|
15142
15790
|
};
|
|
15143
15791
|
var renderTranscript = (messages, keep = 40, clip = 2000) => messages.slice(-keep).map((m) => {
|
|
15144
15792
|
const t = messageText(m.content).trim();
|
|
15145
15793
|
const body = t.length > clip ? `${t.slice(0, clip)}…` : t;
|
|
15146
|
-
return body ? `[${
|
|
15794
|
+
return body ? `[${str9(m.type) ?? "message"}] ${body}` : "";
|
|
15147
15795
|
}).filter(Boolean).join(`
|
|
15148
15796
|
|
|
15149
15797
|
`);
|
|
@@ -15173,7 +15821,7 @@ function conductorRunner(exec, deps = {}) {
|
|
|
15173
15821
|
}
|
|
15174
15822
|
const res = splitCurl(r.stdout);
|
|
15175
15823
|
if (res.code < 200 || res.code >= 300 || !res.body) {
|
|
15176
|
-
const why =
|
|
15824
|
+
const why = str9(res.body?.userMessage) ?? str9(res.body?.message) ?? (res.text.trim().slice(0, 300) || "no body");
|
|
15177
15825
|
throw new Error(`conductor ${method} ${path}: HTTP ${res.code}: ${why}`);
|
|
15178
15826
|
}
|
|
15179
15827
|
return res.body;
|
|
@@ -15203,15 +15851,15 @@ function conductorRunner(exec, deps = {}) {
|
|
|
15203
15851
|
...effort ? { effort } : {},
|
|
15204
15852
|
message: prompt
|
|
15205
15853
|
});
|
|
15206
|
-
const workspace =
|
|
15207
|
-
const session =
|
|
15854
|
+
const workspace = str9(created.workspaceId);
|
|
15855
|
+
const session = str9(created.sessionId);
|
|
15208
15856
|
if (!workspace || !session) {
|
|
15209
15857
|
throw new Error(`conductor: workspace create for ${o.name} returned no workspace or session id`);
|
|
15210
15858
|
}
|
|
15211
|
-
if (!
|
|
15859
|
+
if (!obj2(created.initialMessage)) {
|
|
15212
15860
|
api("POST", `/v0/sessions/${session}/messages`, { message: prompt });
|
|
15213
15861
|
}
|
|
15214
|
-
const link =
|
|
15862
|
+
const link = str9(created.deepLink);
|
|
15215
15863
|
return {
|
|
15216
15864
|
runner: "conductor",
|
|
15217
15865
|
id: `${workspace}/${session}`,
|
|
@@ -15233,8 +15881,8 @@ function conductorRunner(exec, deps = {}) {
|
|
|
15233
15881
|
let line = "";
|
|
15234
15882
|
try {
|
|
15235
15883
|
const s = api("GET", `/v0/sessions/${session}/status`);
|
|
15236
|
-
state =
|
|
15237
|
-
const err =
|
|
15884
|
+
state = str9(s.status) ?? "unknown";
|
|
15885
|
+
const err = str9(s.lastError) ?? str9(s.errorMessage);
|
|
15238
15886
|
line = state === "working" ? `conductor session: working (checked ${new Date(now()).toISOString()})` : `conductor session: ${state}${err ? ` — ${err}` : ""}`;
|
|
15239
15887
|
} catch (e) {
|
|
15240
15888
|
line = `conductor session: status unread (${e instanceof Error ? e.message : String(e)})`;
|
|
@@ -15282,7 +15930,7 @@ var headlessScript = (o, log) => {
|
|
|
15282
15930
|
...o.model ? [`--model ${shq2(o.model)}`] : [],
|
|
15283
15931
|
...o.effort ? [`--effort ${shq2(o.effort)}`] : []
|
|
15284
15932
|
].join(" ");
|
|
15285
|
-
return
|
|
15933
|
+
return `${execLine(cmd)} > ${shq2(log)} 2>&1`;
|
|
15286
15934
|
};
|
|
15287
15935
|
var transcriptTail = (log) => {
|
|
15288
15936
|
const lines = log.trimEnd().split(`
|
|
@@ -15346,8 +15994,8 @@ function headlessRunner(deps = {}) {
|
|
|
15346
15994
|
// src/runners/managed.ts
|
|
15347
15995
|
init_src();
|
|
15348
15996
|
import { readFileSync as readFileSync12 } from "node:fs";
|
|
15349
|
-
var
|
|
15350
|
-
var
|
|
15997
|
+
var obj3 = (v) => v !== null && typeof v === "object" && !Array.isArray(v) ? v : null;
|
|
15998
|
+
var str10 = (v) => typeof v === "string" && v.length > 0 ? v : null;
|
|
15351
15999
|
var ANTHROPIC_API = "https://api.anthropic.com";
|
|
15352
16000
|
var MANAGED_BETA = "managed-agents-2026-04-01";
|
|
15353
16001
|
var DEFAULT_BUDGET_USD = 25;
|
|
@@ -15422,7 +16070,7 @@ var lastIdle = (events) => {
|
|
|
15422
16070
|
return null;
|
|
15423
16071
|
};
|
|
15424
16072
|
var pendingAsks = (events, idle) => {
|
|
15425
|
-
const ids =
|
|
16073
|
+
const ids = obj3(idle?.stop_reason)?.event_ids;
|
|
15426
16074
|
if (!Array.isArray(ids))
|
|
15427
16075
|
return [];
|
|
15428
16076
|
const want = new Set(ids.filter((x) => typeof x === "string"));
|
|
@@ -15443,7 +16091,7 @@ var eventMessages = (events) => {
|
|
|
15443
16091
|
case "agent.custom_tool_use":
|
|
15444
16092
|
out.push({
|
|
15445
16093
|
type: "tool",
|
|
15446
|
-
content: `(${
|
|
16094
|
+
content: `(${str10(e.name) ?? "tool"}${e.evaluated_permission === "ask" ? " — waiting on a yes" : ""})`
|
|
15447
16095
|
});
|
|
15448
16096
|
break;
|
|
15449
16097
|
case "session.error":
|
|
@@ -15454,9 +16102,9 @@ var eventMessages = (events) => {
|
|
|
15454
16102
|
return out;
|
|
15455
16103
|
};
|
|
15456
16104
|
var statusLine2 = (session, events, now) => {
|
|
15457
|
-
const status =
|
|
15458
|
-
const spent = usd4(
|
|
15459
|
-
const cap = usd4(
|
|
16105
|
+
const status = str10(session.status) ?? "unknown";
|
|
16106
|
+
const spent = usd4(obj3(obj3(session.usage)?.list_cost)?.amount);
|
|
16107
|
+
const cap = usd4(obj3(obj3(session.budget)?.max_list_cost)?.amount);
|
|
15460
16108
|
const money = spent ? ` · ${spent}${cap ? ` of ${cap}` : ""}` : "";
|
|
15461
16109
|
if (status === "running" || status === "rescheduling") {
|
|
15462
16110
|
return `managed session: ${status} (checked ${new Date(now).toISOString()})${money}`;
|
|
@@ -15464,10 +16112,10 @@ var statusLine2 = (session, events, now) => {
|
|
|
15464
16112
|
if (status !== "idle")
|
|
15465
16113
|
return `managed session: ${status}${money}`;
|
|
15466
16114
|
const idle = lastIdle(events);
|
|
15467
|
-
const why =
|
|
16115
|
+
const why = str10(obj3(idle?.stop_reason)?.type);
|
|
15468
16116
|
switch (why) {
|
|
15469
16117
|
case "requires_action": {
|
|
15470
|
-
const tools = pendingAsks(events, idle).map((e) =>
|
|
16118
|
+
const tools = pendingAsks(events, idle).map((e) => str10(e.name) ?? "a tool").join(", ");
|
|
15471
16119
|
return `managed session: idle — waiting on a yes for ${tools || "a tool call"} (answer yes to allow, anything else denies)${money}`;
|
|
15472
16120
|
}
|
|
15473
16121
|
case "budget_reached":
|
|
@@ -15501,7 +16149,7 @@ function managedRunner(exec, deps = {}) {
|
|
|
15501
16149
|
}
|
|
15502
16150
|
const res = splitCurl(r.stdout);
|
|
15503
16151
|
if (res.code < 200 || res.code >= 300 || !res.body) {
|
|
15504
|
-
const why =
|
|
16152
|
+
const why = str10(obj3(res.body?.error)?.message) ?? str10(res.body?.message) ?? (res.text.trim().slice(0, 300) || "no body");
|
|
15505
16153
|
throw new Error(`managed ${method} ${path}: HTTP ${res.code}: ${why}`);
|
|
15506
16154
|
}
|
|
15507
16155
|
return res.body;
|
|
@@ -15513,7 +16161,7 @@ function managedRunner(exec, deps = {}) {
|
|
|
15513
16161
|
const got = api("GET", `/v1/sessions/${id}/events?limit=1000${page ? `&page=${encodeURIComponent(page)}` : ""}`);
|
|
15514
16162
|
const data = Array.isArray(got.data) ? got.data : [];
|
|
15515
16163
|
out.push(...data);
|
|
15516
|
-
page =
|
|
16164
|
+
page = str10(got.next_page);
|
|
15517
16165
|
if (!page || data.length === 0)
|
|
15518
16166
|
break;
|
|
15519
16167
|
}
|
|
@@ -15576,7 +16224,7 @@ function managedRunner(exec, deps = {}) {
|
|
|
15576
16224
|
}
|
|
15577
16225
|
]
|
|
15578
16226
|
});
|
|
15579
|
-
const id =
|
|
16227
|
+
const id = str10(created.id);
|
|
15580
16228
|
if (!id)
|
|
15581
16229
|
throw new Error(`managed: session create for ${o.name} returned no id`);
|
|
15582
16230
|
return { runner: "managed", id, cwd: o.repoPath, log: consoleUrl(id) };
|
|
@@ -15601,7 +16249,7 @@ ${renderTranscript(eventMessages(all))}`;
|
|
|
15601
16249
|
const session = api("GET", `/v1/sessions/${h.id}`);
|
|
15602
16250
|
const all = session.status === "idle" ? events(h.id) : [];
|
|
15603
16251
|
const idle = lastIdle(all);
|
|
15604
|
-
const asks =
|
|
16252
|
+
const asks = str10(obj3(idle?.stop_reason)?.type) === "requires_action" ? pendingAsks(all, idle) : [];
|
|
15605
16253
|
if (asks.length) {
|
|
15606
16254
|
const allow = YES2.test(text);
|
|
15607
16255
|
api("POST", `/v1/sessions/${h.id}/events`, {
|
|
@@ -15650,156 +16298,6 @@ ${renderTranscript(eventMessages(all))}`;
|
|
|
15650
16298
|
};
|
|
15651
16299
|
}
|
|
15652
16300
|
|
|
15653
|
-
// src/runners/orca.ts
|
|
15654
|
-
import { writeFileSync as writeFileSync12 } from "node:fs";
|
|
15655
|
-
var obj3 = (v) => v !== null && typeof v === "object" && !Array.isArray(v) ? v : null;
|
|
15656
|
-
var str10 = (v) => typeof v === "string" && v.length > 0 ? v : null;
|
|
15657
|
-
var parseOrcaJson = (stdout) => {
|
|
15658
|
-
try {
|
|
15659
|
-
return obj3(JSON.parse(stdout));
|
|
15660
|
-
} catch {
|
|
15661
|
-
return null;
|
|
15662
|
-
}
|
|
15663
|
-
};
|
|
15664
|
-
var orcaError = (env) => {
|
|
15665
|
-
if (!env || env.ok !== false)
|
|
15666
|
-
return null;
|
|
15667
|
-
const e = obj3(env.error);
|
|
15668
|
-
const message = str10(e?.message) ?? "Orca refused the call";
|
|
15669
|
-
const code = str10(e?.code);
|
|
15670
|
-
return code ? `${message} (${code})` : message;
|
|
15671
|
-
};
|
|
15672
|
-
var parseOrcaWorktree = (env) => {
|
|
15673
|
-
const result = obj3(env.result) ?? {};
|
|
15674
|
-
return str10(obj3(result.worktree)?.path) ?? str10(result.worktreePath) ?? str10(result.path);
|
|
15675
|
-
};
|
|
15676
|
-
var parseOrcaBranch = (env) => {
|
|
15677
|
-
const wt = obj3(obj3(env.result)?.worktree);
|
|
15678
|
-
const branch = str10(wt?.branch)?.replace(/^refs\/heads\//, "");
|
|
15679
|
-
const head = str10(wt?.head);
|
|
15680
|
-
return branch && head ? { branch, head } : null;
|
|
15681
|
-
};
|
|
15682
|
-
var parseOrcaTerminal = (env) => {
|
|
15683
|
-
const result = obj3(env.result) ?? {};
|
|
15684
|
-
return str10(obj3(result.terminal)?.handle) ?? str10(result.handle) ?? str10(result.terminalHandle) ?? null;
|
|
15685
|
-
};
|
|
15686
|
-
var orcaLauncher = (o) => `#!/bin/sh
|
|
15687
|
-
# sleds engine: ${o.name}
|
|
15688
|
-
exec ${agentShellCommand(o)}
|
|
15689
|
-
`;
|
|
15690
|
-
var orcaCommand = (launcher) => `sh ${shq2(launcher)}; exit`;
|
|
15691
|
-
var GONE = /exit|clos|gone|dead|stopp|kill/i;
|
|
15692
|
-
var orcaTerminalLive = (status, stdout) => {
|
|
15693
|
-
const env = parseOrcaJson(stdout);
|
|
15694
|
-
if (str10(obj3(env?.error)?.code) === "runtime_unavailable")
|
|
15695
|
-
return true;
|
|
15696
|
-
if (status !== 0 || !env || env.ok === false)
|
|
15697
|
-
return false;
|
|
15698
|
-
const t = obj3(obj3(env.result)?.terminal) ?? obj3(env.result) ?? {};
|
|
15699
|
-
if (typeof t.status === "string" && GONE.test(t.status))
|
|
15700
|
-
return false;
|
|
15701
|
-
if (t.exited === true || t.alive === false)
|
|
15702
|
-
return false;
|
|
15703
|
-
if (typeof t.exitCode === "number")
|
|
15704
|
-
return false;
|
|
15705
|
-
if (obj3(t.exitCause))
|
|
15706
|
-
return false;
|
|
15707
|
-
if (t.orphaned === true && t.writable === false)
|
|
15708
|
-
return false;
|
|
15709
|
-
return true;
|
|
15710
|
-
};
|
|
15711
|
-
var launcherPath2 = (promptFile) => `${promptFile.replace(/\.md$/, "")}.orca.sh`;
|
|
15712
|
-
function orcaRunner(exec, deps = {}) {
|
|
15713
|
-
const writeFile = deps.writeFile ?? ((p, t) => writeFileSync12(p, t, { mode: 384 }));
|
|
15714
|
-
const orca = (args, what) => {
|
|
15715
|
-
const r = exec("orca", args);
|
|
15716
|
-
const env = parseOrcaJson(r.stdout);
|
|
15717
|
-
const refused = orcaError(env);
|
|
15718
|
-
if (refused)
|
|
15719
|
-
throw new Error(`${what}: ${refused}`);
|
|
15720
|
-
if (r.status !== 0 || !env) {
|
|
15721
|
-
throw new Error(`${what} failed: ${r.stderr.trim() || r.stdout.trim() || `exit ${r.status}`}`);
|
|
15722
|
-
}
|
|
15723
|
-
return env;
|
|
15724
|
-
};
|
|
15725
|
-
return {
|
|
15726
|
-
name: "orca",
|
|
15727
|
-
makesWorktree: true,
|
|
15728
|
-
readsSession: true,
|
|
15729
|
-
start(o) {
|
|
15730
|
-
const created = orca([
|
|
15731
|
-
"worktree",
|
|
15732
|
-
"create",
|
|
15733
|
-
"--repo",
|
|
15734
|
-
`path:${o.repoPath}`,
|
|
15735
|
-
"--name",
|
|
15736
|
-
o.name,
|
|
15737
|
-
"--base-branch",
|
|
15738
|
-
`origin/${o.base ?? "main"}`,
|
|
15739
|
-
"--no-parent",
|
|
15740
|
-
"--json"
|
|
15741
|
-
], `orca worktree create ${o.name}`);
|
|
15742
|
-
const cwd = parseOrcaWorktree(created);
|
|
15743
|
-
if (!cwd) {
|
|
15744
|
-
throw new Error(`orca worktree create ${o.name} returned no worktree path; the agent was not started`);
|
|
15745
|
-
}
|
|
15746
|
-
const launcher = launcherPath2(o.promptFile);
|
|
15747
|
-
writeFile(launcher, orcaLauncher(o));
|
|
15748
|
-
const shown = orca([
|
|
15749
|
-
"terminal",
|
|
15750
|
-
"create",
|
|
15751
|
-
"--worktree",
|
|
15752
|
-
`path:${cwd}`,
|
|
15753
|
-
"--title",
|
|
15754
|
-
o.name,
|
|
15755
|
-
"--command",
|
|
15756
|
-
orcaCommand(launcher),
|
|
15757
|
-
"--json"
|
|
15758
|
-
], `orca terminal create ${o.name} (worktree ${cwd})`);
|
|
15759
|
-
const terminal = parseOrcaTerminal(shown);
|
|
15760
|
-
if (!terminal) {
|
|
15761
|
-
throw new Error(`orca terminal create ${o.name} started in ${cwd} but returned no terminal handle`);
|
|
15762
|
-
}
|
|
15763
|
-
const own = parseOrcaBranch(created);
|
|
15764
|
-
const h = { runner: "orca", id: terminal, cwd, ...own ? { own } : {} };
|
|
15765
|
-
return h;
|
|
15766
|
-
},
|
|
15767
|
-
status(h) {
|
|
15768
|
-
const r = exec("orca", ["terminal", "show", "--terminal", h.id, "--json"]);
|
|
15769
|
-
return orcaTerminalLive(r.status, r.stdout) ? "running" : "exited";
|
|
15770
|
-
},
|
|
15771
|
-
read(h) {
|
|
15772
|
-
const r = exec("orca", ["terminal", "read", "--terminal", h.id, "--limit", "200"]);
|
|
15773
|
-
return r.status === 0 ? r.stdout : "";
|
|
15774
|
-
},
|
|
15775
|
-
send(h, text) {
|
|
15776
|
-
const r = exec("orca", ["terminal", "send", "--terminal", h.id, "--text", text, "--enter"]);
|
|
15777
|
-
if (r.status !== 0) {
|
|
15778
|
-
throw new Error(`orca terminal send ${h.id} failed: ${r.stderr.trim() || `exit ${r.status}`}`);
|
|
15779
|
-
}
|
|
15780
|
-
},
|
|
15781
|
-
stop(h) {
|
|
15782
|
-
exec("orca", ["terminal", "close", "--terminal", h.id]);
|
|
15783
|
-
},
|
|
15784
|
-
watchHint(h) {
|
|
15785
|
-
return `orca terminal show --terminal ${h.id}`;
|
|
15786
|
-
},
|
|
15787
|
-
removeWorktree(h, repoPath) {
|
|
15788
|
-
orca(["worktree", "rm", "--worktree", `path:${h.cwd}`, "--json"], `orca worktree rm ${h.cwd}`);
|
|
15789
|
-
if (!h.own)
|
|
15790
|
-
return;
|
|
15791
|
-
const ref = `refs/heads/${h.own.branch}`;
|
|
15792
|
-
const r = exec("git", ["-C", repoPath, "update-ref", "-d", ref, h.own.head]);
|
|
15793
|
-
if (r.status === 0)
|
|
15794
|
-
return;
|
|
15795
|
-
const still = exec("git", ["-C", repoPath, "rev-parse", "--verify", "--quiet", ref]);
|
|
15796
|
-
if (still.status !== 0)
|
|
15797
|
-
return;
|
|
15798
|
-
return `kept Orca's branch ${h.own.branch}: ${r.stderr.trim() || `exit ${r.status}`}`;
|
|
15799
|
-
}
|
|
15800
|
-
};
|
|
15801
|
-
}
|
|
15802
|
-
|
|
15803
16301
|
// src/runners/tmux.ts
|
|
15804
16302
|
var tmuxSession = (name) => `=${name}`;
|
|
15805
16303
|
var tmuxPane = (name) => `=${name}:`;
|
|
@@ -16160,7 +16658,7 @@ function flagsFromProfile(flags, given, profile, home) {
|
|
|
16160
16658
|
}
|
|
16161
16659
|
}
|
|
16162
16660
|
if (profile.reposDir && !has("repos-dir") && !has("repo")) {
|
|
16163
|
-
out.reposDir = splitReposDirs(profile.reposDir).map((d) => home && /^~(\/|$)/.test(d) ?
|
|
16661
|
+
out.reposDir = splitReposDirs(profile.reposDir).map((d) => home && /^~(\/|$)/.test(d) ? join16(home, d.slice(1)) : d).join(":");
|
|
16164
16662
|
used.push(`repos ${profile.reposDir}`);
|
|
16165
16663
|
}
|
|
16166
16664
|
if (profile.mcpStrict !== undefined && !has("strict-mcp") && !has("no-strict-mcp")) {
|
|
@@ -16290,12 +16788,12 @@ function memberMcpConfigs(o, cwd) {
|
|
|
16290
16788
|
});
|
|
16291
16789
|
const claudeDir = (o.env ?? process.env).CLAUDE_CONFIG_DIR;
|
|
16292
16790
|
return [
|
|
16293
|
-
read(claudeDir ?
|
|
16294
|
-
...cwd ? [read(
|
|
16791
|
+
read(claudeDir ? join16(claudeDir, ".claude.json") : join16(o.home, ".claude.json")),
|
|
16792
|
+
...cwd ? [read(join16(cwd, ".mcp.json"))] : []
|
|
16295
16793
|
];
|
|
16296
16794
|
}
|
|
16297
16795
|
function engineStateDir(o, org, seat) {
|
|
16298
|
-
return seatStateDir(orgStateDir(o.stateDir ??
|
|
16796
|
+
return seatStateDir(orgStateDir(o.stateDir ?? join16(configDir(o.home, o.env ?? process.env), "engine"), org), seat);
|
|
16299
16797
|
}
|
|
16300
16798
|
function engineSeat(o, org) {
|
|
16301
16799
|
try {
|
|
@@ -16322,11 +16820,11 @@ function engineDeps(o, flags, source, runner, stateName) {
|
|
|
16322
16820
|
const stateDir = engineStateDir(o, flags.org, flags.seat);
|
|
16323
16821
|
const file = statePath(stateDir, stateName);
|
|
16324
16822
|
const mcpOrg = flags.org && engineOrg(o, flags.org).seated ? flags.org : undefined;
|
|
16325
|
-
const repo = flags.repo ?
|
|
16326
|
-
const reposDirs = splitReposDirs(flags.reposDir).map((d) =>
|
|
16823
|
+
const repo = flags.repo ? resolve8(cwd, flags.repo) : undefined;
|
|
16824
|
+
const reposDirs = splitReposDirs(flags.reposDir).map((d) => resolve8(cwd, d));
|
|
16327
16825
|
const { branch, prTitle } = flags;
|
|
16328
|
-
const hooksDir =
|
|
16329
|
-
const permissionMark = (name) =>
|
|
16826
|
+
const hooksDir = join16(stateDir, "hooks");
|
|
16827
|
+
const permissionMark = (name) => join16(hooksDir, `${name}.permission.json`);
|
|
16330
16828
|
const withHook = (name, settings) => applyPermissionHook(settings, [...o.sledsArgv ?? ["sleds"], "permission-event", "--mark", permissionMark(name)].map(shq2).join(" "));
|
|
16331
16829
|
return {
|
|
16332
16830
|
source,
|
|
@@ -16349,15 +16847,18 @@ function engineDeps(o, flags, source, runner, stateName) {
|
|
|
16349
16847
|
...flags.strictMcp ? { strictMcp: true } : {},
|
|
16350
16848
|
...flags.sandbox ? {
|
|
16351
16849
|
sandbox: true,
|
|
16352
|
-
writeSandboxSettings: o.writeSandboxSettings ?? ((name, cwd) => {
|
|
16850
|
+
writeSandboxSettings: o.writeSandboxSettings ?? ((name, cwd, allow) => {
|
|
16353
16851
|
const gitCommonDir = gitCommonDirOf(exec, cwd);
|
|
16354
|
-
const dir =
|
|
16852
|
+
const dir = join16(stateDir, "sandbox");
|
|
16355
16853
|
mkdirSync12(dir, { recursive: true, mode: 448 });
|
|
16356
|
-
const p =
|
|
16854
|
+
const p = join16(dir, `${name}.json`);
|
|
16357
16855
|
const walls = engineSandboxSettings(o.home, o.env ?? process.env, gitCommonDir, [
|
|
16358
16856
|
...SANDBOX_DEFAULT_ALLOW,
|
|
16359
16857
|
...flags.sandboxAllow ?? []
|
|
16360
16858
|
]);
|
|
16859
|
+
walls.permissions.allow = [
|
|
16860
|
+
...new Set([...walls.permissions.allow, ...allow ?? []])
|
|
16861
|
+
];
|
|
16361
16862
|
const settings = withHook(name, { ...walls });
|
|
16362
16863
|
writeFileSync13(p, `${JSON.stringify(settings, null, 2)}
|
|
16363
16864
|
`, { mode: 384 });
|
|
@@ -16365,10 +16866,11 @@ function engineDeps(o, flags, source, runner, stateName) {
|
|
|
16365
16866
|
return { settingsFile: p, gitCommonDir };
|
|
16366
16867
|
})
|
|
16367
16868
|
} : {},
|
|
16368
|
-
writeHookSettings: (name) => {
|
|
16869
|
+
writeHookSettings: (name, allow) => {
|
|
16369
16870
|
mkdirSync12(hooksDir, { recursive: true, mode: 448 });
|
|
16370
|
-
const p =
|
|
16371
|
-
|
|
16871
|
+
const p = join16(hooksDir, `${name}.json`);
|
|
16872
|
+
const base = allow?.length ? { permissions: { allow: [...allow] } } : {};
|
|
16873
|
+
writeFileSync13(p, `${JSON.stringify(withHook(name, base), null, 2)}
|
|
16372
16874
|
`, { mode: 384 });
|
|
16373
16875
|
chmodSync4(p, 384);
|
|
16374
16876
|
return p;
|
|
@@ -16394,9 +16896,9 @@ function engineDeps(o, flags, source, runner, stateName) {
|
|
|
16394
16896
|
exists
|
|
16395
16897
|
}),
|
|
16396
16898
|
writeMcpConfig: o.writeMcpConfig ?? ((name, runToken, worktree) => {
|
|
16397
|
-
const dir =
|
|
16899
|
+
const dir = join16(stateDir, "mcp");
|
|
16398
16900
|
mkdirSync12(dir, { recursive: true, mode: 448 });
|
|
16399
|
-
const p =
|
|
16901
|
+
const p = join16(dir, `${name}.json`);
|
|
16400
16902
|
let others = {};
|
|
16401
16903
|
if (flags.strictMcp && flags.mcpAllow?.length) {
|
|
16402
16904
|
const r = mcpPassThrough(flags.mcpAllow, memberMcpConfigs(o, worktree));
|
|
@@ -16412,9 +16914,9 @@ function engineDeps(o, flags, source, runner, stateName) {
|
|
|
16412
16914
|
}),
|
|
16413
16915
|
removeMcpConfig: o.removeMcpConfig ?? ((p) => rmSync4(p, { force: true })),
|
|
16414
16916
|
writePrompt: o.writePrompt ?? ((name, text) => {
|
|
16415
|
-
const dir =
|
|
16917
|
+
const dir = join16(stateDir, "prompts");
|
|
16416
16918
|
mkdirSync12(dir, { recursive: true, mode: 448 });
|
|
16417
|
-
const p =
|
|
16919
|
+
const p = join16(dir, `${name}.md`);
|
|
16418
16920
|
writeFileSync13(p, text, { mode: 384 });
|
|
16419
16921
|
return p;
|
|
16420
16922
|
}),
|
|
@@ -16441,8 +16943,8 @@ function engineDeps(o, flags, source, runner, stateName) {
|
|
|
16441
16943
|
}
|
|
16442
16944
|
function sandboxDenyRead(home, env) {
|
|
16443
16945
|
return {
|
|
16444
|
-
dirs: [...new Set([configDir(home, env),
|
|
16445
|
-
files: [...new Set([tokenPath(home, env),
|
|
16946
|
+
dirs: [...new Set([configDir(home, env), join16(home, ".config", "sleds")])],
|
|
16947
|
+
files: [...new Set([tokenPath(home, env), join16(home, ".sleds-token")])]
|
|
16446
16948
|
};
|
|
16447
16949
|
}
|
|
16448
16950
|
function engineSandboxSettings(home, env, gitCommonDir, allow) {
|
|
@@ -16457,7 +16959,7 @@ function gitCommonDirOf(exec, cwd) {
|
|
|
16457
16959
|
const dir = r.status === 0 ? r.stdout.trim() : "";
|
|
16458
16960
|
if (!dir)
|
|
16459
16961
|
throw new Error(`git has no common dir for ${cwd}, so the sandbox has nowhere to commit`);
|
|
16460
|
-
return
|
|
16962
|
+
return resolve8(cwd, dir);
|
|
16461
16963
|
}
|
|
16462
16964
|
function pinOrg2(configJson, org) {
|
|
16463
16965
|
if (!org)
|
|
@@ -16499,13 +17001,14 @@ async function driveEngine(o, flags, deps, until = {}) {
|
|
|
16499
17001
|
runner: deps.runner.name,
|
|
16500
17002
|
atOnce: flags.atOnce,
|
|
16501
17003
|
interval: flags.interval,
|
|
16502
|
-
cliVersion: "0.1.
|
|
17004
|
+
cliVersion: "0.1.45"
|
|
16503
17005
|
};
|
|
16504
17006
|
const beat = (checkedAt) => {
|
|
16505
17007
|
const m = deps.machine;
|
|
16506
17008
|
try {
|
|
16507
17009
|
writeLock(lockFile, checkedAt === undefined ? lock : {
|
|
16508
17010
|
...lock,
|
|
17011
|
+
runner: deps.runner.name,
|
|
16509
17012
|
atOnce: deps.atOnce,
|
|
16510
17013
|
checkedAt,
|
|
16511
17014
|
nextCheckAt: checkedAt + flags.interval * 1000,
|
|
@@ -16811,7 +17314,7 @@ async function enginePauseCmd(o) {
|
|
|
16811
17314
|
// src/commands/engineInstall.ts
|
|
16812
17315
|
init_config();
|
|
16813
17316
|
import {
|
|
16814
|
-
existsSync as
|
|
17317
|
+
existsSync as existsSync15,
|
|
16815
17318
|
mkdirSync as mkdirSync14,
|
|
16816
17319
|
readFileSync as readFileSync14,
|
|
16817
17320
|
readdirSync as readdirSync2,
|
|
@@ -16820,13 +17323,13 @@ import {
|
|
|
16820
17323
|
writeFileSync as writeFileSync15
|
|
16821
17324
|
} from "node:fs";
|
|
16822
17325
|
import { hostname as hostname3 } from "node:os";
|
|
16823
|
-
import { basename as basename4, dirname as dirname12, isAbsolute as isAbsolute2, join as
|
|
17326
|
+
import { basename as basename4, dirname as dirname12, isAbsolute as isAbsolute2, join as join19 } from "node:path";
|
|
16824
17327
|
init_state();
|
|
16825
17328
|
|
|
16826
17329
|
// src/schedule.ts
|
|
16827
17330
|
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
16828
|
-
import { existsSync as
|
|
16829
|
-
import { join as
|
|
17331
|
+
import { existsSync as existsSync14, mkdirSync as mkdirSync13, rmSync as rmSync5, writeFileSync as writeFileSync14 } from "node:fs";
|
|
17332
|
+
import { join as join17 } from "node:path";
|
|
16830
17333
|
var REVERT_LABEL = "ai.sleds.revert";
|
|
16831
17334
|
var defaultExec2 = (cmd, args) => {
|
|
16832
17335
|
const r = spawnSync4(cmd, [...args], { stdio: ["ignore", "ignore", "pipe"], encoding: "utf8" });
|
|
@@ -16834,7 +17337,7 @@ var defaultExec2 = (cmd, args) => {
|
|
|
16834
17337
|
return { ok: false, stderr: r.error.message };
|
|
16835
17338
|
return { ok: r.status === 0, stderr: r.stderr ?? "" };
|
|
16836
17339
|
};
|
|
16837
|
-
var plistPath = (home) =>
|
|
17340
|
+
var plistPath = (home) => join17(home, "Library", "LaunchAgents", `${REVERT_LABEL}.plist`);
|
|
16838
17341
|
var xmlEscape2 = (s) => s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
16839
17342
|
var buildLaunchdPlist = (sledsArgv, deadline) => {
|
|
16840
17343
|
const args = [...sledsArgv, "off"].map((a) => ` <string>${xmlEscape2(a)}</string>`).join(`
|
|
@@ -16873,7 +17376,7 @@ var scheduleRevert = (d, deadlineMs) => {
|
|
|
16873
17376
|
if (d.platform === "darwin") {
|
|
16874
17377
|
const p = plistPath(d.home);
|
|
16875
17378
|
try {
|
|
16876
|
-
mkdirSync13(
|
|
17379
|
+
mkdirSync13(join17(d.home, "Library", "LaunchAgents"), { recursive: true });
|
|
16877
17380
|
d.exec("launchctl", ["unload", p]);
|
|
16878
17381
|
writeFileSync14(p, buildLaunchdPlist(d.sledsArgv, deadline));
|
|
16879
17382
|
const r = d.exec("launchctl", ["load", p]);
|
|
@@ -16902,7 +17405,7 @@ var scheduleRevert = (d, deadlineMs) => {
|
|
|
16902
17405
|
var cancelRevert = (d, kind) => {
|
|
16903
17406
|
if (kind === "launchd" || d.platform === "darwin") {
|
|
16904
17407
|
const p = plistPath(d.home);
|
|
16905
|
-
if (
|
|
17408
|
+
if (existsSync14(p)) {
|
|
16906
17409
|
d.exec("launchctl", ["unload", p]);
|
|
16907
17410
|
rmSync5(p, { force: true });
|
|
16908
17411
|
}
|
|
@@ -16913,16 +17416,16 @@ var cancelRevert = (d, kind) => {
|
|
|
16913
17416
|
};
|
|
16914
17417
|
|
|
16915
17418
|
// src/commands/engineSystemd.ts
|
|
16916
|
-
import { join as
|
|
17419
|
+
import { join as join18 } from "node:path";
|
|
16917
17420
|
var BOX_ENV_UNIT_PATH = "%h/.config/sleds/box.env";
|
|
16918
17421
|
function boxEnvPath(home) {
|
|
16919
|
-
return
|
|
17422
|
+
return join18(home, ".config", "sleds", "box.env");
|
|
16920
17423
|
}
|
|
16921
17424
|
function engineUnitDir(home) {
|
|
16922
|
-
return
|
|
17425
|
+
return join18(home, ".config", "systemd", "user");
|
|
16923
17426
|
}
|
|
16924
17427
|
function engineUnitPath(home, label) {
|
|
16925
|
-
return
|
|
17428
|
+
return join18(engineUnitDir(home), `${label}.service`);
|
|
16926
17429
|
}
|
|
16927
17430
|
function systemdQuote(s) {
|
|
16928
17431
|
const body = s.replace(/\\/g, "\\\\").replace(/"/g, "\\\"").replace(/\n/g, "\\n").replace(/%/g, "%%").replace(/\$/g, "$$$$");
|
|
@@ -16980,13 +17483,13 @@ function engineLabel(name, org, seat) {
|
|
|
16980
17483
|
return [ENGINE_LABEL, org, seat?.trim(), name].filter((p) => Boolean(p)).map((p, i) => i === 0 ? p : engineSlug(p)).join(".");
|
|
16981
17484
|
}
|
|
16982
17485
|
function enginePlistPath(home, name, org, seat) {
|
|
16983
|
-
return
|
|
17486
|
+
return join19(home, "Library", "LaunchAgents", `${engineLabel(name, org, seat)}.plist`);
|
|
16984
17487
|
}
|
|
16985
17488
|
function engineLogPath(stateDir, name) {
|
|
16986
|
-
return
|
|
17489
|
+
return join19(stateDir, `${engineSlug(name)}.log`);
|
|
16987
17490
|
}
|
|
16988
17491
|
function engineEventsPath(stateDir, name) {
|
|
16989
|
-
return
|
|
17492
|
+
return join19(stateDir, `${engineSlug(name)}.events.jsonl`);
|
|
16990
17493
|
}
|
|
16991
17494
|
var xmlEscape3 = (s) => s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
16992
17495
|
function buildEnginePlist(o) {
|
|
@@ -17062,7 +17565,13 @@ function parseEnginePlist(text) {
|
|
|
17062
17565
|
if (!label || array === undefined)
|
|
17063
17566
|
return null;
|
|
17064
17567
|
const programArgs = [...array.matchAll(/<string>([^<]*)<\/string>/g)].map((m) => xmlUnescape(m[1] ?? ""));
|
|
17065
|
-
|
|
17568
|
+
const envDict = /<key>EnvironmentVariables<\/key>\s*<dict>([\s\S]*?)<\/dict>/.exec(text)?.[1];
|
|
17569
|
+
const path = envDict && /<key>PATH<\/key>\s*<string>([^<]*)<\/string>/.exec(envDict)?.[1];
|
|
17570
|
+
return {
|
|
17571
|
+
label: xmlUnescape(label),
|
|
17572
|
+
programArgs,
|
|
17573
|
+
...typeof path === "string" ? { path: xmlUnescape(path) } : {}
|
|
17574
|
+
};
|
|
17066
17575
|
}
|
|
17067
17576
|
function engineArg(programArgs, flag) {
|
|
17068
17577
|
const at = programArgs.indexOf(`--${flag}`);
|
|
@@ -17077,7 +17586,7 @@ function readEngines(dir, suffix, manager, parse) {
|
|
|
17077
17586
|
}
|
|
17078
17587
|
const out = [];
|
|
17079
17588
|
for (const name of names.filter((n) => n.startsWith(`${ENGINE_LABEL}.`) && n.endsWith(suffix))) {
|
|
17080
|
-
const path =
|
|
17589
|
+
const path = join19(dir, name);
|
|
17081
17590
|
let parsed;
|
|
17082
17591
|
try {
|
|
17083
17592
|
parsed = parse(readFileSync14(path, "utf8"), name);
|
|
@@ -17097,7 +17606,7 @@ function readEngines(dir, suffix, manager, parse) {
|
|
|
17097
17606
|
}
|
|
17098
17607
|
function installedEngines(home) {
|
|
17099
17608
|
return [
|
|
17100
|
-
...readEngines(
|
|
17609
|
+
...readEngines(join19(home, "Library", "LaunchAgents"), ".plist", "launchd", parseEnginePlist),
|
|
17101
17610
|
...readEngines(engineUnitDir(home), ".service", "systemd", (text, file) => {
|
|
17102
17611
|
const parsed = parseEngineUnit(text);
|
|
17103
17612
|
return parsed && { label: basename4(file, ".service"), ...parsed };
|
|
@@ -17148,7 +17657,7 @@ function engineInstall(o) {
|
|
|
17148
17657
|
return installUnit({ o, parsed, label, sledsArgv, env: agentEnv, logPath, exec });
|
|
17149
17658
|
const plist = enginePlistPath(o.home, parsed.name, parsed.org, seat);
|
|
17150
17659
|
mkdirSync14(dirname12(plist), { recursive: true });
|
|
17151
|
-
if (
|
|
17660
|
+
if (existsSync15(plist))
|
|
17152
17661
|
exec("launchctl", ["unload", plist]);
|
|
17153
17662
|
for (const other of installedEngines(o.home)) {
|
|
17154
17663
|
if (other.manager !== "launchd" || other.plist === plist || other.org !== parsed.org)
|
|
@@ -17160,7 +17669,7 @@ function engineInstall(o) {
|
|
|
17160
17669
|
log(`replaced ${other.label}, this engine as installed before`);
|
|
17161
17670
|
}
|
|
17162
17671
|
const legacy = enginePlistPath(o.home, parsed.name);
|
|
17163
|
-
if (parsed.org &&
|
|
17672
|
+
if (parsed.org && existsSync15(legacy)) {
|
|
17164
17673
|
const cfg = readConfig(o.home);
|
|
17165
17674
|
if (org.sole || parsed.org === (cfg.activeOrg ?? cfg.org?.slug)) {
|
|
17166
17675
|
exec("launchctl", ["unload", legacy]);
|
|
@@ -17190,7 +17699,7 @@ function installUnit(a) {
|
|
|
17190
17699
|
const unitName = basename4(unit);
|
|
17191
17700
|
const systemctl = (...args) => exec("systemctl", ["--user", ...args]);
|
|
17192
17701
|
mkdirSync14(dirname12(unit), { recursive: true });
|
|
17193
|
-
if (
|
|
17702
|
+
if (existsSync15(unit))
|
|
17194
17703
|
systemctl("stop", unitName);
|
|
17195
17704
|
for (const other of installedEngines(o.home)) {
|
|
17196
17705
|
if (other.manager !== "systemd" || other.plist === unit || other.org !== parsed.org)
|
|
@@ -17248,7 +17757,7 @@ function engineUninstall(o) {
|
|
|
17248
17757
|
...installed.filter((e) => e.manager === manager).map((e) => e.plist),
|
|
17249
17758
|
...args.org ? [] : [pathOf(manager)]
|
|
17250
17759
|
]);
|
|
17251
|
-
const file = candidates.find((p) =>
|
|
17760
|
+
const file = candidates.find((p) => existsSync15(p));
|
|
17252
17761
|
if (!file) {
|
|
17253
17762
|
log(`engine:${name} was not installed (no ${candidates[0]}).`);
|
|
17254
17763
|
return 0;
|
|
@@ -17267,20 +17776,20 @@ function engineUninstall(o) {
|
|
|
17267
17776
|
}
|
|
17268
17777
|
|
|
17269
17778
|
// src/commands/musher.ts
|
|
17270
|
-
import { existsSync as
|
|
17779
|
+
import { existsSync as existsSync16, mkdirSync as mkdirSync15, readFileSync as readFileSync15, rmSync as rmSync7, writeFileSync as writeFileSync16 } from "node:fs";
|
|
17271
17780
|
init_config();
|
|
17272
17781
|
init_meter();
|
|
17273
17782
|
var defaultWhich = (cmd) => {
|
|
17274
17783
|
const path = process.env.PATH ?? "";
|
|
17275
17784
|
for (const dir of path.split(":").filter(Boolean)) {
|
|
17276
17785
|
const p = `${dir}/${cmd}`;
|
|
17277
|
-
if (
|
|
17786
|
+
if (existsSync16(p))
|
|
17278
17787
|
return p;
|
|
17279
17788
|
}
|
|
17280
17789
|
return null;
|
|
17281
17790
|
};
|
|
17282
17791
|
var findClaude = (home, configured, which) => {
|
|
17283
|
-
if (configured &&
|
|
17792
|
+
if (configured && existsSync16(configured))
|
|
17284
17793
|
return configured;
|
|
17285
17794
|
const found = which("claude");
|
|
17286
17795
|
if (found)
|
|
@@ -17291,13 +17800,13 @@ var findClaude = (home, configured, which) => {
|
|
|
17291
17800
|
"/usr/local/bin/claude",
|
|
17292
17801
|
`${home}/.npm-global/bin/claude`
|
|
17293
17802
|
])
|
|
17294
|
-
if (
|
|
17803
|
+
if (existsSync16(p))
|
|
17295
17804
|
return p;
|
|
17296
17805
|
return null;
|
|
17297
17806
|
};
|
|
17298
17807
|
var musherAgentState = (home, env = process.env) => {
|
|
17299
17808
|
const plist = musherPlistPath(home, env);
|
|
17300
|
-
if (!
|
|
17809
|
+
if (!existsSync16(plist))
|
|
17301
17810
|
return { installed: false, thisSeat: false };
|
|
17302
17811
|
try {
|
|
17303
17812
|
const text = readFileSync15(plist, "utf8");
|
|
@@ -17394,7 +17903,7 @@ function formatDoctor(checks) {
|
|
|
17394
17903
|
}
|
|
17395
17904
|
function defaultWhich2(cmd) {
|
|
17396
17905
|
try {
|
|
17397
|
-
return
|
|
17906
|
+
return execFileSync4("which", ["-a", cmd], {
|
|
17398
17907
|
encoding: "utf8",
|
|
17399
17908
|
stdio: ["ignore", "pipe", "ignore"]
|
|
17400
17909
|
}).split(`
|
|
@@ -17413,7 +17922,7 @@ async function runDoctor(o) {
|
|
|
17413
17922
|
].filter(Boolean);
|
|
17414
17923
|
const cfgDir = configDir(home, env);
|
|
17415
17924
|
const cfg = readConfig(home);
|
|
17416
|
-
const cfgExists =
|
|
17925
|
+
const cfgExists = existsSync17(configPath(home));
|
|
17417
17926
|
if (!cfgExists) {
|
|
17418
17927
|
checks.push({
|
|
17419
17928
|
level: "fail",
|
|
@@ -17429,7 +17938,7 @@ async function runDoctor(o) {
|
|
|
17429
17938
|
});
|
|
17430
17939
|
}
|
|
17431
17940
|
const sPath = settingsPath(home);
|
|
17432
|
-
const settings =
|
|
17941
|
+
const settings = existsSync17(sPath) ? readSettings(home) : null;
|
|
17433
17942
|
const pointed = settings ? readProxy(settings) : null;
|
|
17434
17943
|
if (!settings) {
|
|
17435
17944
|
checks.push({
|
|
@@ -17476,7 +17985,7 @@ async function runDoctor(o) {
|
|
|
17476
17985
|
detail: `seat-event ${seat ? "on" : "missing"} · spec-event ${spec ? "on" : "missing"} · session-event ${session ? "on" : "missing"} · stop-event ${stop ? "on" : "missing"}`,
|
|
17477
17986
|
fix: all ? undefined : "sleds hooks"
|
|
17478
17987
|
});
|
|
17479
|
-
const want = env.SLEDS_HOME?.trim() ?
|
|
17988
|
+
const want = env.SLEDS_HOME?.trim() ? resolve9(home, env.SLEDS_HOME.trim()) : undefined;
|
|
17480
17989
|
const cmds = sledsCommandsIn(settings);
|
|
17481
17990
|
const wrong = cmds.filter((c) => {
|
|
17482
17991
|
const got = bakedHome(c);
|
|
@@ -17514,7 +18023,7 @@ async function runDoctor(o) {
|
|
|
17514
18023
|
fix: other.length + unpinned.length === 0 ? undefined : `${[...overrides, `SLEDS_ORG=${wantOrg}`].join(" ")} sleds hooks (pins the status line + hooks to ${wantOrg}; use the org this Claude config is for)`
|
|
17515
18024
|
});
|
|
17516
18025
|
}
|
|
17517
|
-
const skill =
|
|
18026
|
+
const skill = existsSync17(skillPath(home));
|
|
17518
18027
|
checks.push({
|
|
17519
18028
|
level: skill ? "ok" : "warn",
|
|
17520
18029
|
name: "skill",
|
|
@@ -17548,7 +18057,7 @@ async function runDoctor(o) {
|
|
|
17548
18057
|
const which = (o.which ?? defaultWhich2)("sleds");
|
|
17549
18058
|
const stale = which.filter((p) => {
|
|
17550
18059
|
try {
|
|
17551
|
-
const target =
|
|
18060
|
+
const target = lstatSync2(p).isSymbolicLink() ? readlinkSync2(p) : p;
|
|
17552
18061
|
return /\/src\/index\.ts$/.test(target) || /\.bun\/bin\/sleds$/.test(p);
|
|
17553
18062
|
} catch {
|
|
17554
18063
|
return false;
|
|
@@ -17683,7 +18192,7 @@ function sandboxChecks(o, env) {
|
|
|
17683
18192
|
fix: "npm install -g @anthropic-ai/claude-code"
|
|
17684
18193
|
}
|
|
17685
18194
|
];
|
|
17686
|
-
if (!
|
|
18195
|
+
if (!existsSync17(dir))
|
|
17687
18196
|
return [
|
|
17688
18197
|
{
|
|
17689
18198
|
level: "warn",
|
|
@@ -17693,12 +18202,12 @@ function sandboxChecks(o, env) {
|
|
|
17693
18202
|
}
|
|
17694
18203
|
];
|
|
17695
18204
|
const nonce = o.nonce ?? `sleds-probe-${randomBytes(16).toString("hex")}`;
|
|
17696
|
-
const probe =
|
|
17697
|
-
const work = mkdtempSync(
|
|
18205
|
+
const probe = join20(dir, SANDBOX_PROBE);
|
|
18206
|
+
const work = mkdtempSync(join20(tmpdir(), "sleds-sandbox-"));
|
|
17698
18207
|
try {
|
|
17699
18208
|
writeFileSync17(probe, `${nonce}
|
|
17700
18209
|
`, { mode: 384 });
|
|
17701
|
-
const settingsFile =
|
|
18210
|
+
const settingsFile = join20(work, "settings.json");
|
|
17702
18211
|
const settings = sandboxSettings({
|
|
17703
18212
|
denyRead: [dir],
|
|
17704
18213
|
gitCommonDir: work,
|
|
@@ -17734,7 +18243,7 @@ function engineChecks(o, env) {
|
|
|
17734
18243
|
const checks = [];
|
|
17735
18244
|
const which = (c) => (o.which ?? defaultWhich2)(c)[0] ?? null;
|
|
17736
18245
|
const exec = o.exec ?? defaultExec3;
|
|
17737
|
-
const exists = o.exists ??
|
|
18246
|
+
const exists = o.exists ?? existsSync17;
|
|
17738
18247
|
const platform = o.platform ?? process.platform;
|
|
17739
18248
|
const mac = platform === "darwin";
|
|
17740
18249
|
const linux = platform === "linux";
|
|
@@ -17776,6 +18285,11 @@ function engineChecks(o, env) {
|
|
|
17776
18285
|
fix: mac ? "brew install tmux" : "install tmux with your package manager"
|
|
17777
18286
|
});
|
|
17778
18287
|
}
|
|
18288
|
+
if (runner === "orca") {
|
|
18289
|
+
const path = engines.find((e) => engineArg(e.programArgs, "runner") === "orca")?.path ?? env.PATH ?? "";
|
|
18290
|
+
const p = orcaPreflight(exec, path, o.orcaFs);
|
|
18291
|
+
checks.push(p.ok ? { level: "ok", name: "orca", detail: `${p.version} · the engine's runner` } : { level: need, name: "orca", detail: p.why, fix: p.fix });
|
|
18292
|
+
}
|
|
17779
18293
|
if (linux)
|
|
17780
18294
|
checks.push(...boxChecks(o, env, which, need));
|
|
17781
18295
|
if (!manager)
|
|
@@ -17865,7 +18379,7 @@ function boxChecks(o, env, which, need) {
|
|
|
17865
18379
|
}
|
|
17866
18380
|
function cmuxCheck(o, env) {
|
|
17867
18381
|
const exec = o.exec ?? defaultExec3;
|
|
17868
|
-
const bin = resolveCmuxBin(o.exists ??
|
|
18382
|
+
const bin = resolveCmuxBin(o.exists ?? existsSync17, env.PATH ?? "");
|
|
17869
18383
|
const pw = readCmuxPassword(exec);
|
|
17870
18384
|
const ping = pw ? exec(bin, ["ping"], { env: { CMUX_SOCKET_PASSWORD: pw } }) : exec(bin, ["ping"]);
|
|
17871
18385
|
if (ping.status === 127)
|
|
@@ -17926,7 +18440,7 @@ function parsePmset(text) {
|
|
|
17926
18440
|
}
|
|
17927
18441
|
var defaultPmset = () => {
|
|
17928
18442
|
try {
|
|
17929
|
-
return parsePmset(
|
|
18443
|
+
return parsePmset(execFileSync4("pmset", ["-g", "custom"], { encoding: "utf8", timeout: 2000 }));
|
|
17930
18444
|
} catch {
|
|
17931
18445
|
return null;
|
|
17932
18446
|
}
|
|
@@ -17956,7 +18470,7 @@ import {
|
|
|
17956
18470
|
writeFileSync as writeFileSync18
|
|
17957
18471
|
} from "node:fs";
|
|
17958
18472
|
import { hostname as hostname4 } from "node:os";
|
|
17959
|
-
import { join as
|
|
18473
|
+
import { join as join21, resolve as resolve10 } from "node:path";
|
|
17960
18474
|
init_machine();
|
|
17961
18475
|
init_state();
|
|
17962
18476
|
init_meter();
|
|
@@ -18211,7 +18725,7 @@ function flagsFromProfile2(flags, given, profile, home) {
|
|
|
18211
18725
|
}
|
|
18212
18726
|
}
|
|
18213
18727
|
if (profile.reposDir && !has("repos-dir") && !has("repo")) {
|
|
18214
|
-
out.reposDir = splitReposDirs(profile.reposDir).map((d) => home && /^~(\/|$)/.test(d) ?
|
|
18728
|
+
out.reposDir = splitReposDirs(profile.reposDir).map((d) => home && /^~(\/|$)/.test(d) ? join21(home, d.slice(1)) : d).join(":");
|
|
18215
18729
|
used.push(`repos ${profile.reposDir}`);
|
|
18216
18730
|
}
|
|
18217
18731
|
if (profile.mcpStrict !== undefined && !has("strict-mcp") && !has("no-strict-mcp")) {
|
|
@@ -18301,6 +18815,29 @@ async function rereadAtOnce2(read, flags, given) {
|
|
|
18301
18815
|
const profile = await read();
|
|
18302
18816
|
return profile ? profile.atOnce ?? 1 : null;
|
|
18303
18817
|
}
|
|
18818
|
+
async function rereadRunner2(read, flags, given, home) {
|
|
18819
|
+
if (given.includes("runner"))
|
|
18820
|
+
return null;
|
|
18821
|
+
const profile = await read();
|
|
18822
|
+
if (!profile)
|
|
18823
|
+
return null;
|
|
18824
|
+
const base = {
|
|
18825
|
+
...flags,
|
|
18826
|
+
runner: "tmux",
|
|
18827
|
+
runnerCmd: given.includes("runner-cmd") ? flags.runnerCmd : undefined
|
|
18828
|
+
};
|
|
18829
|
+
const r = flagsFromProfile2(base, given, profile, home);
|
|
18830
|
+
const skipped = r.skipped.find((s) => s.startsWith("runner"));
|
|
18831
|
+
if (skipped)
|
|
18832
|
+
return { refused: `skipped from your engine profile: ${skipped} — fix it in Settings` };
|
|
18833
|
+
const refusal = sandboxRefusal({ sandbox: flags.sandbox, runner: r.flags.runner });
|
|
18834
|
+
if (refusal)
|
|
18835
|
+
return { refused: `runner ${r.flags.runner} not taken — ${refusal}` };
|
|
18836
|
+
return {
|
|
18837
|
+
runner: r.flags.runner,
|
|
18838
|
+
...r.flags.runner === "command" && r.flags.runnerCmd ? { runnerCmd: r.flags.runnerCmd } : {}
|
|
18839
|
+
};
|
|
18840
|
+
}
|
|
18304
18841
|
var PROFILE_RETRY_MS2 = [2000, 5000];
|
|
18305
18842
|
async function withProfile2(client, flags, given, home, retry) {
|
|
18306
18843
|
let profile = await readProfile(client);
|
|
@@ -18379,12 +18916,12 @@ function memberMcpConfigs2(o, cwd) {
|
|
|
18379
18916
|
});
|
|
18380
18917
|
const claudeDir = (o.env ?? process.env).CLAUDE_CONFIG_DIR;
|
|
18381
18918
|
return [
|
|
18382
|
-
read(claudeDir ?
|
|
18383
|
-
...cwd ? [read(
|
|
18919
|
+
read(claudeDir ? join21(claudeDir, ".claude.json") : join21(o.home, ".claude.json")),
|
|
18920
|
+
...cwd ? [read(join21(cwd, ".mcp.json"))] : []
|
|
18384
18921
|
];
|
|
18385
18922
|
}
|
|
18386
18923
|
function engineStateDir2(o, org, seat) {
|
|
18387
|
-
return seatStateDir(orgStateDir(o.stateDir ??
|
|
18924
|
+
return seatStateDir(orgStateDir(o.stateDir ?? join21(configDir(o.home, o.env ?? process.env), "engine"), org), seat);
|
|
18388
18925
|
}
|
|
18389
18926
|
function engineSeat2(o, org) {
|
|
18390
18927
|
try {
|
|
@@ -18411,11 +18948,11 @@ function engineDeps2(o, flags, source, runner, stateName) {
|
|
|
18411
18948
|
const stateDir = engineStateDir2(o, flags.org, flags.seat);
|
|
18412
18949
|
const file = statePath(stateDir, stateName);
|
|
18413
18950
|
const mcpOrg = flags.org && engineOrg2(o, flags.org).seated ? flags.org : undefined;
|
|
18414
|
-
const repo = flags.repo ?
|
|
18415
|
-
const reposDirs = splitReposDirs(flags.reposDir).map((d) =>
|
|
18951
|
+
const repo = flags.repo ? resolve10(cwd, flags.repo) : undefined;
|
|
18952
|
+
const reposDirs = splitReposDirs(flags.reposDir).map((d) => resolve10(cwd, d));
|
|
18416
18953
|
const { branch, prTitle } = flags;
|
|
18417
|
-
const hooksDir =
|
|
18418
|
-
const permissionMark = (name) =>
|
|
18954
|
+
const hooksDir = join21(stateDir, "hooks");
|
|
18955
|
+
const permissionMark = (name) => join21(hooksDir, `${name}.permission.json`);
|
|
18419
18956
|
const withHook = (name, settings) => applyPermissionHook(settings, [...o.sledsArgv ?? ["sleds"], "permission-event", "--mark", permissionMark(name)].map(shq2).join(" "));
|
|
18420
18957
|
return {
|
|
18421
18958
|
source,
|
|
@@ -18438,15 +18975,18 @@ function engineDeps2(o, flags, source, runner, stateName) {
|
|
|
18438
18975
|
...flags.strictMcp ? { strictMcp: true } : {},
|
|
18439
18976
|
...flags.sandbox ? {
|
|
18440
18977
|
sandbox: true,
|
|
18441
|
-
writeSandboxSettings: o.writeSandboxSettings ?? ((name, cwd) => {
|
|
18978
|
+
writeSandboxSettings: o.writeSandboxSettings ?? ((name, cwd, allow) => {
|
|
18442
18979
|
const gitCommonDir = gitCommonDirOf2(exec, cwd);
|
|
18443
|
-
const dir =
|
|
18980
|
+
const dir = join21(stateDir, "sandbox");
|
|
18444
18981
|
mkdirSync16(dir, { recursive: true, mode: 448 });
|
|
18445
|
-
const p =
|
|
18982
|
+
const p = join21(dir, `${name}.json`);
|
|
18446
18983
|
const walls = engineSandboxSettings2(o.home, o.env ?? process.env, gitCommonDir, [
|
|
18447
18984
|
...SANDBOX_DEFAULT_ALLOW,
|
|
18448
18985
|
...flags.sandboxAllow ?? []
|
|
18449
18986
|
]);
|
|
18987
|
+
walls.permissions.allow = [
|
|
18988
|
+
...new Set([...walls.permissions.allow, ...allow ?? []])
|
|
18989
|
+
];
|
|
18450
18990
|
const settings = withHook(name, { ...walls });
|
|
18451
18991
|
writeFileSync18(p, `${JSON.stringify(settings, null, 2)}
|
|
18452
18992
|
`, { mode: 384 });
|
|
@@ -18454,10 +18994,11 @@ function engineDeps2(o, flags, source, runner, stateName) {
|
|
|
18454
18994
|
return { settingsFile: p, gitCommonDir };
|
|
18455
18995
|
})
|
|
18456
18996
|
} : {},
|
|
18457
|
-
writeHookSettings: (name) => {
|
|
18997
|
+
writeHookSettings: (name, allow) => {
|
|
18458
18998
|
mkdirSync16(hooksDir, { recursive: true, mode: 448 });
|
|
18459
|
-
const p =
|
|
18460
|
-
|
|
18999
|
+
const p = join21(hooksDir, `${name}.json`);
|
|
19000
|
+
const base = allow?.length ? { permissions: { allow: [...allow] } } : {};
|
|
19001
|
+
writeFileSync18(p, `${JSON.stringify(withHook(name, base), null, 2)}
|
|
18461
19002
|
`, { mode: 384 });
|
|
18462
19003
|
chmodSync5(p, 384);
|
|
18463
19004
|
return p;
|
|
@@ -18483,9 +19024,9 @@ function engineDeps2(o, flags, source, runner, stateName) {
|
|
|
18483
19024
|
exists
|
|
18484
19025
|
}),
|
|
18485
19026
|
writeMcpConfig: o.writeMcpConfig ?? ((name, runToken, worktree) => {
|
|
18486
|
-
const dir =
|
|
19027
|
+
const dir = join21(stateDir, "mcp");
|
|
18487
19028
|
mkdirSync16(dir, { recursive: true, mode: 448 });
|
|
18488
|
-
const p =
|
|
19029
|
+
const p = join21(dir, `${name}.json`);
|
|
18489
19030
|
let others = {};
|
|
18490
19031
|
if (flags.strictMcp && flags.mcpAllow?.length) {
|
|
18491
19032
|
const r = mcpPassThrough(flags.mcpAllow, memberMcpConfigs2(o, worktree));
|
|
@@ -18501,9 +19042,9 @@ function engineDeps2(o, flags, source, runner, stateName) {
|
|
|
18501
19042
|
}),
|
|
18502
19043
|
removeMcpConfig: o.removeMcpConfig ?? ((p) => rmSync9(p, { force: true })),
|
|
18503
19044
|
writePrompt: o.writePrompt ?? ((name, text) => {
|
|
18504
|
-
const dir =
|
|
19045
|
+
const dir = join21(stateDir, "prompts");
|
|
18505
19046
|
mkdirSync16(dir, { recursive: true, mode: 448 });
|
|
18506
|
-
const p =
|
|
19047
|
+
const p = join21(dir, `${name}.md`);
|
|
18507
19048
|
writeFileSync18(p, text, { mode: 384 });
|
|
18508
19049
|
return p;
|
|
18509
19050
|
}),
|
|
@@ -18530,8 +19071,8 @@ function engineDeps2(o, flags, source, runner, stateName) {
|
|
|
18530
19071
|
}
|
|
18531
19072
|
function sandboxDenyRead2(home, env) {
|
|
18532
19073
|
return {
|
|
18533
|
-
dirs: [...new Set([configDir(home, env),
|
|
18534
|
-
files: [...new Set([tokenPath(home, env),
|
|
19074
|
+
dirs: [...new Set([configDir(home, env), join21(home, ".config", "sleds")])],
|
|
19075
|
+
files: [...new Set([tokenPath(home, env), join21(home, ".sleds-token")])]
|
|
18535
19076
|
};
|
|
18536
19077
|
}
|
|
18537
19078
|
function engineSandboxSettings2(home, env, gitCommonDir, allow) {
|
|
@@ -18546,7 +19087,7 @@ function gitCommonDirOf2(exec, cwd) {
|
|
|
18546
19087
|
const dir = r.status === 0 ? r.stdout.trim() : "";
|
|
18547
19088
|
if (!dir)
|
|
18548
19089
|
throw new Error(`git has no common dir for ${cwd}, so the sandbox has nowhere to commit`);
|
|
18549
|
-
return
|
|
19090
|
+
return resolve10(cwd, dir);
|
|
18550
19091
|
}
|
|
18551
19092
|
function sandboxRefusal(flags) {
|
|
18552
19093
|
if (!flags.sandbox)
|
|
@@ -18597,13 +19138,14 @@ async function driveEngine2(o, flags, deps, until = {}) {
|
|
|
18597
19138
|
runner: deps.runner.name,
|
|
18598
19139
|
atOnce: flags.atOnce,
|
|
18599
19140
|
interval: flags.interval,
|
|
18600
|
-
cliVersion: "0.1.
|
|
19141
|
+
cliVersion: "0.1.45"
|
|
18601
19142
|
};
|
|
18602
19143
|
const beat = (checkedAt) => {
|
|
18603
19144
|
const m = deps.machine;
|
|
18604
19145
|
try {
|
|
18605
19146
|
writeLock(lockFile, checkedAt === undefined ? lock : {
|
|
18606
19147
|
...lock,
|
|
19148
|
+
runner: deps.runner.name,
|
|
18607
19149
|
atOnce: deps.atOnce,
|
|
18608
19150
|
checkedAt,
|
|
18609
19151
|
nextCheckAt: checkedAt + flags.interval * 1000,
|
|
@@ -18876,7 +19418,7 @@ var engineCmd = async (o) => {
|
|
|
18876
19418
|
armedOnly: true,
|
|
18877
19419
|
engine: `engine:${flags.name}`,
|
|
18878
19420
|
...parsed.given.includes("at-once") ? { atOnce: flags.atOnce } : {},
|
|
18879
|
-
runner: runner.name,
|
|
19421
|
+
runner: () => deps.runner.name,
|
|
18880
19422
|
leaseMin: flags.lease,
|
|
18881
19423
|
log
|
|
18882
19424
|
});
|
|
@@ -18894,6 +19436,21 @@ var engineCmd = async (o) => {
|
|
|
18894
19436
|
const started = flags;
|
|
18895
19437
|
deps.rereadRoles = (read) => rereadRoles(read, started, parsed.given);
|
|
18896
19438
|
deps.rereadAtOnce = (read) => rereadAtOnce2(read, started, parsed.given);
|
|
19439
|
+
let current = { runner, cmd: flags.runner === "command" ? flags.runnerCmd : undefined };
|
|
19440
|
+
deps.rereadRunner = async (read) => {
|
|
19441
|
+
const next = await rereadRunner2(read, started, parsed.given, o.home);
|
|
19442
|
+
if (!next)
|
|
19443
|
+
return null;
|
|
19444
|
+
if ("refused" in next)
|
|
19445
|
+
return next.refused;
|
|
19446
|
+
if (next.runner === current.runner.name && next.runnerCmd === current.cmd)
|
|
19447
|
+
return current.runner;
|
|
19448
|
+
const made = engineRunner2(o, { ...started, ...next });
|
|
19449
|
+
if (!made)
|
|
19450
|
+
return `runner ${next.runner} not taken — this CLI cannot start it`;
|
|
19451
|
+
current = { runner: made, cmd: next.runnerCmd };
|
|
19452
|
+
return made;
|
|
19453
|
+
};
|
|
18897
19454
|
}
|
|
18898
19455
|
log(engineHeader2(terminalOf2(o).c, deps.engine, (source.name === "jira" ? `Jira epic ${flags.jira}` : pickup === "auto" ? "your sled, Pickup: Auto (it arms the next ready item)" : "the routes armed on your sled") + (flags.org ? ` · org ${flags.org}` : "") + (flags.seat ? ` · as ${flags.seat}` : ""), runner.name, flags));
|
|
18899
19456
|
if (flags.strictMcp && flags.agent)
|
|
@@ -18903,7 +19460,6 @@ var engineCmd = async (o) => {
|
|
|
18903
19460
|
let afterTick;
|
|
18904
19461
|
if (client) {
|
|
18905
19462
|
const send = beater(client, o.now ?? Date.now);
|
|
18906
|
-
const who = { name: flags.name, runner: runner.name };
|
|
18907
19463
|
let refused;
|
|
18908
19464
|
const sayRefused = (r) => {
|
|
18909
19465
|
if (!r || r.ok || !r.refused || r.refused === refused)
|
|
@@ -18914,7 +19470,7 @@ var engineCmd = async (o) => {
|
|
|
18914
19470
|
afterTick = () => {
|
|
18915
19471
|
try {
|
|
18916
19472
|
const m = deps.machine;
|
|
18917
|
-
send(beatBody(deps.readState(),
|
|
19473
|
+
send(beatBody(deps.readState(), { name: flags.name, runner: deps.runner.name }, {}, {
|
|
18918
19474
|
atOnce: deps.atOnce,
|
|
18919
19475
|
...m?.last ? { machine: m.last } : {},
|
|
18920
19476
|
...m?.hold && !m.off ? { hold: m.hold } : {}
|
|
@@ -19047,7 +19603,7 @@ async function enginePauseCmd2(o) {
|
|
|
19047
19603
|
// src/commands/engineInstall.ts
|
|
19048
19604
|
init_config();
|
|
19049
19605
|
import {
|
|
19050
|
-
existsSync as
|
|
19606
|
+
existsSync as existsSync18,
|
|
19051
19607
|
mkdirSync as mkdirSync17,
|
|
19052
19608
|
readFileSync as readFileSync19,
|
|
19053
19609
|
readdirSync as readdirSync4,
|
|
@@ -19056,7 +19612,7 @@ import {
|
|
|
19056
19612
|
writeFileSync as writeFileSync19
|
|
19057
19613
|
} from "node:fs";
|
|
19058
19614
|
import { hostname as hostname5 } from "node:os";
|
|
19059
|
-
import { basename as basename5, dirname as dirname13, isAbsolute as isAbsolute3, join as
|
|
19615
|
+
import { basename as basename5, dirname as dirname13, isAbsolute as isAbsolute3, join as join22 } from "node:path";
|
|
19060
19616
|
init_state();
|
|
19061
19617
|
var ENGINE_LABEL2 = "ai.sleds.engine";
|
|
19062
19618
|
var ENGINE_INSTALL_USAGE2 = `usage: sleds engine install [engine flags] | sleds engine uninstall [--name <n>] [--org <slug>]
|
|
@@ -19076,13 +19632,13 @@ function engineLabel2(name, org, seat) {
|
|
|
19076
19632
|
return [ENGINE_LABEL2, org, seat?.trim(), name].filter((p) => Boolean(p)).map((p, i) => i === 0 ? p : engineSlug(p)).join(".");
|
|
19077
19633
|
}
|
|
19078
19634
|
function enginePlistPath2(home, name, org, seat) {
|
|
19079
|
-
return
|
|
19635
|
+
return join22(home, "Library", "LaunchAgents", `${engineLabel2(name, org, seat)}.plist`);
|
|
19080
19636
|
}
|
|
19081
19637
|
function engineLogPath2(stateDir, name) {
|
|
19082
|
-
return
|
|
19638
|
+
return join22(stateDir, `${engineSlug(name)}.log`);
|
|
19083
19639
|
}
|
|
19084
19640
|
function engineEventsPath2(stateDir, name) {
|
|
19085
|
-
return
|
|
19641
|
+
return join22(stateDir, `${engineSlug(name)}.events.jsonl`);
|
|
19086
19642
|
}
|
|
19087
19643
|
var xmlEscape4 = (s) => s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
19088
19644
|
function buildEnginePlist2(o) {
|
|
@@ -19158,7 +19714,13 @@ function parseEnginePlist2(text) {
|
|
|
19158
19714
|
if (!label || array === undefined)
|
|
19159
19715
|
return null;
|
|
19160
19716
|
const programArgs = [...array.matchAll(/<string>([^<]*)<\/string>/g)].map((m) => xmlUnescape2(m[1] ?? ""));
|
|
19161
|
-
|
|
19717
|
+
const envDict = /<key>EnvironmentVariables<\/key>\s*<dict>([\s\S]*?)<\/dict>/.exec(text)?.[1];
|
|
19718
|
+
const path = envDict && /<key>PATH<\/key>\s*<string>([^<]*)<\/string>/.exec(envDict)?.[1];
|
|
19719
|
+
return {
|
|
19720
|
+
label: xmlUnescape2(label),
|
|
19721
|
+
programArgs,
|
|
19722
|
+
...typeof path === "string" ? { path: xmlUnescape2(path) } : {}
|
|
19723
|
+
};
|
|
19162
19724
|
}
|
|
19163
19725
|
function engineArg2(programArgs, flag) {
|
|
19164
19726
|
const at = programArgs.indexOf(`--${flag}`);
|
|
@@ -19173,7 +19735,7 @@ function readEngines2(dir, suffix, manager, parse) {
|
|
|
19173
19735
|
}
|
|
19174
19736
|
const out = [];
|
|
19175
19737
|
for (const name of names.filter((n) => n.startsWith(`${ENGINE_LABEL2}.`) && n.endsWith(suffix))) {
|
|
19176
|
-
const path =
|
|
19738
|
+
const path = join22(dir, name);
|
|
19177
19739
|
let parsed;
|
|
19178
19740
|
try {
|
|
19179
19741
|
parsed = parse(readFileSync19(path, "utf8"), name);
|
|
@@ -19193,7 +19755,7 @@ function readEngines2(dir, suffix, manager, parse) {
|
|
|
19193
19755
|
}
|
|
19194
19756
|
function installedEngines2(home) {
|
|
19195
19757
|
return [
|
|
19196
|
-
...readEngines2(
|
|
19758
|
+
...readEngines2(join22(home, "Library", "LaunchAgents"), ".plist", "launchd", parseEnginePlist2),
|
|
19197
19759
|
...readEngines2(engineUnitDir(home), ".service", "systemd", (text, file) => {
|
|
19198
19760
|
const parsed = parseEngineUnit(text);
|
|
19199
19761
|
return parsed && { label: basename5(file, ".service"), ...parsed };
|
|
@@ -19230,8 +19792,8 @@ async function ensureReposDir(o) {
|
|
|
19230
19792
|
error(REPOS_DIR_NEEDED);
|
|
19231
19793
|
return false;
|
|
19232
19794
|
}
|
|
19233
|
-
const exists = o.exists ??
|
|
19234
|
-
const missing = dirs.filter((d) => !exists(/^~(\/|$)/.test(d) ?
|
|
19795
|
+
const exists = o.exists ?? existsSync18;
|
|
19796
|
+
const missing = dirs.filter((d) => !exists(/^~(\/|$)/.test(d) ? join22(o.home, d.slice(1)) : d));
|
|
19235
19797
|
if (missing.length) {
|
|
19236
19798
|
error(`Not a folder on this machine: ${missing.join(", ")}. ${REPOS_DIR_NEEDED}`);
|
|
19237
19799
|
return false;
|
|
@@ -19288,7 +19850,7 @@ function engineInstall2(o) {
|
|
|
19288
19850
|
return installUnit2({ o, parsed, label, sledsArgv, env: agentEnv, logPath, exec });
|
|
19289
19851
|
const plist = enginePlistPath2(o.home, parsed.name, parsed.org, seat);
|
|
19290
19852
|
mkdirSync17(dirname13(plist), { recursive: true });
|
|
19291
|
-
if (
|
|
19853
|
+
if (existsSync18(plist))
|
|
19292
19854
|
exec("launchctl", ["unload", plist]);
|
|
19293
19855
|
for (const other of installedEngines2(o.home)) {
|
|
19294
19856
|
if (other.manager !== "launchd" || other.plist === plist || other.org !== parsed.org)
|
|
@@ -19300,7 +19862,7 @@ function engineInstall2(o) {
|
|
|
19300
19862
|
log(`replaced ${other.label}, this engine as installed before`);
|
|
19301
19863
|
}
|
|
19302
19864
|
const legacy = enginePlistPath2(o.home, parsed.name);
|
|
19303
|
-
if (parsed.org &&
|
|
19865
|
+
if (parsed.org && existsSync18(legacy)) {
|
|
19304
19866
|
const cfg = readConfig(o.home);
|
|
19305
19867
|
if (org.sole || parsed.org === (cfg.activeOrg ?? cfg.org?.slug)) {
|
|
19306
19868
|
exec("launchctl", ["unload", legacy]);
|
|
@@ -19330,7 +19892,7 @@ function installUnit2(a) {
|
|
|
19330
19892
|
const unitName = basename5(unit);
|
|
19331
19893
|
const systemctl = (...args) => exec("systemctl", ["--user", ...args]);
|
|
19332
19894
|
mkdirSync17(dirname13(unit), { recursive: true });
|
|
19333
|
-
if (
|
|
19895
|
+
if (existsSync18(unit))
|
|
19334
19896
|
systemctl("stop", unitName);
|
|
19335
19897
|
for (const other of installedEngines2(o.home)) {
|
|
19336
19898
|
if (other.manager !== "systemd" || other.plist === unit || other.org !== parsed.org)
|
|
@@ -19388,7 +19950,7 @@ function engineUninstall2(o) {
|
|
|
19388
19950
|
...installed.filter((e) => e.manager === manager).map((e) => e.plist),
|
|
19389
19951
|
...args.org ? [] : [pathOf(manager)]
|
|
19390
19952
|
]);
|
|
19391
|
-
const file = candidates.find((p) =>
|
|
19953
|
+
const file = candidates.find((p) => existsSync18(p));
|
|
19392
19954
|
if (!file) {
|
|
19393
19955
|
log(`engine:${name} was not installed (no ${candidates[0]}).`);
|
|
19394
19956
|
return 0;
|
|
@@ -19530,7 +20092,7 @@ import { closeSync as closeSync3, fstatSync as fstatSync2, openSync as openSync3
|
|
|
19530
20092
|
import { hostname as hostname6 } from "node:os";
|
|
19531
20093
|
|
|
19532
20094
|
// src/commands/engineStatus.ts
|
|
19533
|
-
import { existsSync as
|
|
20095
|
+
import { existsSync as existsSync19, readFileSync as readFileSync20 } from "node:fs";
|
|
19534
20096
|
init_state();
|
|
19535
20097
|
|
|
19536
20098
|
// src/engine/status.ts
|
|
@@ -19713,7 +20275,7 @@ function statusDir(o, engine, org) {
|
|
|
19713
20275
|
const env = o.env ?? process.env;
|
|
19714
20276
|
const which = engineOrg({ home: o.home, env }, org);
|
|
19715
20277
|
const slug = which.seated || org ? which.slug : null;
|
|
19716
|
-
const has = (d) =>
|
|
20278
|
+
const has = (d) => existsSync19(statePath(d, engine)) || existsSync19(lockPath(d, engine));
|
|
19717
20279
|
const seat = engineSeat({ home: o.home, env }, slug ?? undefined);
|
|
19718
20280
|
const dir = engineStateDir({ ...o, env }, slug);
|
|
19719
20281
|
const seatDir = engineStateDir({ ...o, env }, slug, seat);
|
|
@@ -19820,7 +20382,7 @@ var engineLogCmd = (o) => {
|
|
|
19820
20382
|
|
|
19821
20383
|
// src/commands/engineProfile.ts
|
|
19822
20384
|
init_src();
|
|
19823
|
-
import { existsSync as
|
|
20385
|
+
import { existsSync as existsSync20, readFileSync as readFileSync21 } from "node:fs";
|
|
19824
20386
|
import { hostname as hostname7 } from "node:os";
|
|
19825
20387
|
var PROFILE_USAGE = "usage: sleds engine profile [--json] | sleds engine profile set <field>=<value>… [--workspace] [--json] | sleds engine profile set --stdin [--workspace] [--json] | sleds engine profile --strict-mcp [--mcp-allow <a,b>] | --no-strict-mcp [--workspace] [--json] | sleds engine profile --sandbox [--sandbox-allow <a,b>] | --no-sandbox [--workspace] [--json] | sleds engine profile set --hold on|off [--workspace] [--json] | sleds engine profile set cmuxPassword=<pw> | --cmux-password <pw> [--json]";
|
|
19826
20388
|
var PROFILE_FIELDS = [
|
|
@@ -20048,7 +20610,7 @@ function localOf(o) {
|
|
|
20048
20610
|
return {
|
|
20049
20611
|
engine: `engine:${name}`,
|
|
20050
20612
|
canInstall,
|
|
20051
|
-
installed: canInstall &&
|
|
20613
|
+
installed: canInstall && existsSync20(enginePlistPath(o.home, name))
|
|
20052
20614
|
};
|
|
20053
20615
|
}
|
|
20054
20616
|
async function engineProfileCmd(o) {
|
|
@@ -20170,7 +20732,7 @@ var keychainLine = (k) => k === "set" ? "cmuxPassword: set (Keychain)" : k === "
|
|
|
20170
20732
|
|
|
20171
20733
|
// src/commands/engineStatus.ts
|
|
20172
20734
|
init_state();
|
|
20173
|
-
import { existsSync as
|
|
20735
|
+
import { existsSync as existsSync21, readFileSync as readFileSync22 } from "node:fs";
|
|
20174
20736
|
import { hostname as hostname8 } from "node:os";
|
|
20175
20737
|
var ENGINE_STATUS_USAGE = "usage: sleds engine status [--name <n>] [--org <slug>] [--json]";
|
|
20176
20738
|
function parseStatusArgs(argv, host) {
|
|
@@ -20199,7 +20761,7 @@ function statusDir2(o, engine, org) {
|
|
|
20199
20761
|
const env = o.env ?? process.env;
|
|
20200
20762
|
const which = engineOrg({ home: o.home, env }, org);
|
|
20201
20763
|
const slug = which.seated || org ? which.slug : null;
|
|
20202
|
-
const has = (d) =>
|
|
20764
|
+
const has = (d) => existsSync21(statePath(d, engine)) || existsSync21(lockPath(d, engine));
|
|
20203
20765
|
const seat = engineSeat({ home: o.home, env }, slug ?? undefined);
|
|
20204
20766
|
const dir = engineStateDir({ ...o, env }, slug);
|
|
20205
20767
|
const seatDir = engineStateDir({ ...o, env }, slug, seat);
|
|
@@ -20212,7 +20774,7 @@ function statusDir2(o, engine, org) {
|
|
|
20212
20774
|
}
|
|
20213
20775
|
function readEngineStatus(stateDir, engine, host, pidAlive, seat) {
|
|
20214
20776
|
const file = statePath(stateDir, engine);
|
|
20215
|
-
const text =
|
|
20777
|
+
const text = existsSync21(file) ? safeRead(file) : "";
|
|
20216
20778
|
return engineStatus({
|
|
20217
20779
|
engine,
|
|
20218
20780
|
state: text ? parseState(text) : { runs: [] },
|
|
@@ -20347,10 +20909,10 @@ init_meter();
|
|
|
20347
20909
|
|
|
20348
20910
|
// src/owed.ts
|
|
20349
20911
|
init_config();
|
|
20350
|
-
import { existsSync as
|
|
20351
|
-
import { dirname as dirname14, join as
|
|
20352
|
-
var owedPath = (home) =>
|
|
20353
|
-
var sessionPath = (home) =>
|
|
20912
|
+
import { existsSync as existsSync22, mkdirSync as mkdirSync18, readFileSync as readFileSync23, renameSync as renameSync7, writeFileSync as writeFileSync20 } from "node:fs";
|
|
20913
|
+
import { dirname as dirname14, join as join23 } from "node:path";
|
|
20914
|
+
var owedPath = (home) => join23(configDir(home), "owed.json");
|
|
20915
|
+
var sessionPath = (home) => join23(configDir(home), "session.json");
|
|
20354
20916
|
var OWED_TTL_MS = 3 * 24 * 60 * 60000;
|
|
20355
20917
|
var str13 = (v) => typeof v === "string" ? v : undefined;
|
|
20356
20918
|
function sourceKey(s) {
|
|
@@ -20365,7 +20927,7 @@ function sourceText2(s) {
|
|
|
20365
20927
|
return [`${str13(s.kind) ?? "?"} ${str13(s.project) ?? "?"}`, str13(s.doc), str13(s.item)].filter(Boolean).join(" › ");
|
|
20366
20928
|
}
|
|
20367
20929
|
var readJson = (p) => {
|
|
20368
|
-
if (!
|
|
20930
|
+
if (!existsSync22(p))
|
|
20369
20931
|
return null;
|
|
20370
20932
|
try {
|
|
20371
20933
|
return JSON.parse(readFileSync23(p, "utf8"));
|
|
@@ -21395,7 +21957,7 @@ var login = async (o) => {
|
|
|
21395
21957
|
// src/commands/logout.ts
|
|
21396
21958
|
init_config();
|
|
21397
21959
|
init_state();
|
|
21398
|
-
import { existsSync as
|
|
21960
|
+
import { existsSync as existsSync23, readFileSync as readFileSync24, rmSync as rmSync11 } from "node:fs";
|
|
21399
21961
|
import { hostname as hostname10 } from "node:os";
|
|
21400
21962
|
init_settings();
|
|
21401
21963
|
var LOGOUT_USAGE = "usage: sleds logout [--org <slug>] [--json]";
|
|
@@ -21552,7 +22114,7 @@ async function logout(o) {
|
|
|
21552
22114
|
};
|
|
21553
22115
|
writeConfig(o.home, next);
|
|
21554
22116
|
const tp = tokenPath(o.home, env);
|
|
21555
|
-
if (
|
|
22117
|
+
if (existsSync23(tp) && readFileSync24(tp, "utf8").trim() === seat.token)
|
|
21556
22118
|
rmSync11(tp, { force: true });
|
|
21557
22119
|
const active = next.activeOrg ?? next.org?.slug ?? null;
|
|
21558
22120
|
const result = { ok: true, email, org: slug, paused, active };
|
|
@@ -21630,20 +22192,20 @@ var meter = async (o) => {
|
|
|
21630
22192
|
init_config();
|
|
21631
22193
|
init_meter();
|
|
21632
22194
|
import { spawnSync as spawnSync7 } from "node:child_process";
|
|
21633
|
-
import { existsSync as
|
|
22195
|
+
import { existsSync as existsSync24, mkdirSync as mkdirSync20, readFileSync as readFileSync25, rmSync as rmSync12, writeFileSync as writeFileSync22 } from "node:fs";
|
|
21634
22196
|
import { hostname as hostname11 } from "node:os";
|
|
21635
22197
|
import { dirname as dirname15 } from "node:path";
|
|
21636
22198
|
var defaultWhich3 = (cmd) => {
|
|
21637
22199
|
const path = process.env.PATH ?? "";
|
|
21638
22200
|
for (const dir of path.split(":").filter(Boolean)) {
|
|
21639
22201
|
const p = `${dir}/${cmd}`;
|
|
21640
|
-
if (
|
|
22202
|
+
if (existsSync24(p))
|
|
21641
22203
|
return p;
|
|
21642
22204
|
}
|
|
21643
22205
|
return null;
|
|
21644
22206
|
};
|
|
21645
22207
|
var findClaude2 = (home, configured, which) => {
|
|
21646
|
-
if (configured &&
|
|
22208
|
+
if (configured && existsSync24(configured))
|
|
21647
22209
|
return configured;
|
|
21648
22210
|
const found = which("claude");
|
|
21649
22211
|
if (found)
|
|
@@ -21654,7 +22216,7 @@ var findClaude2 = (home, configured, which) => {
|
|
|
21654
22216
|
"/usr/local/bin/claude",
|
|
21655
22217
|
`${home}/.npm-global/bin/claude`
|
|
21656
22218
|
])
|
|
21657
|
-
if (
|
|
22219
|
+
if (existsSync24(p))
|
|
21658
22220
|
return p;
|
|
21659
22221
|
return null;
|
|
21660
22222
|
};
|
|
@@ -21680,7 +22242,7 @@ var musherInstall = (o) => {
|
|
|
21680
22242
|
...env.CLAUDE_CONFIG_DIR ? { CLAUDE_CONFIG_DIR: env.CLAUDE_CONFIG_DIR } : {}
|
|
21681
22243
|
};
|
|
21682
22244
|
const exec = o.exec ?? defaultExecSync;
|
|
21683
|
-
if (
|
|
22245
|
+
if (existsSync24(plist))
|
|
21684
22246
|
exec("launchctl", ["unload", plist]);
|
|
21685
22247
|
writeFileSync22(plist, buildMusherPlist({ sledsArgv: o.sledsArgv, env: agentEnv, logPath: musherLogPath(dir), label }));
|
|
21686
22248
|
const r = exec("launchctl", ["load", plist]);
|
|
@@ -21701,7 +22263,7 @@ var musherUninstall = (o) => {
|
|
|
21701
22263
|
const env = o.env ?? process.env;
|
|
21702
22264
|
const plist = musherPlistPath(o.home, env);
|
|
21703
22265
|
const exec = o.exec ?? defaultExecSync;
|
|
21704
|
-
if (
|
|
22266
|
+
if (existsSync24(plist)) {
|
|
21705
22267
|
exec("launchctl", ["unload", plist]);
|
|
21706
22268
|
rmSync12(plist, { force: true });
|
|
21707
22269
|
log(`musher removed: ${musherLabel(env)}`);
|
|
@@ -21736,7 +22298,7 @@ var musherStatus = async (o) => {
|
|
|
21736
22298
|
const env = o.env ?? process.env;
|
|
21737
22299
|
const plist = musherPlistPath(o.home, env);
|
|
21738
22300
|
const cfg = readConfig(o.home);
|
|
21739
|
-
log(
|
|
22301
|
+
log(existsSync24(plist) ? `agent ${musherLabel(env)} installed (${plist})` : "agent not installed — sleds musher --install");
|
|
21740
22302
|
const claude = findClaude2(o.home, cfg.musher?.claudePath, o.which ?? defaultWhich3);
|
|
21741
22303
|
log(`claude ${claude ?? "not found — sleds musher --claude /path/to/claude"}`);
|
|
21742
22304
|
log(`mode ${cfg.musher?.permissionMode ?? DEFAULT_PERMISSION_MODE} · timeout ${cfg.musher?.timeoutMinutes ?? DEFAULT_TIMEOUT_MINUTES} min`);
|
|
@@ -21966,7 +22528,7 @@ var on = (o) => {
|
|
|
21966
22528
|
init_config();
|
|
21967
22529
|
init_meter();
|
|
21968
22530
|
init_settings();
|
|
21969
|
-
import { existsSync as
|
|
22531
|
+
import { existsSync as existsSync25, rmSync as rmSync13 } from "node:fs";
|
|
21970
22532
|
var ORG_USAGE = "usage: sleds org list [--json] | sleds org use <slug>";
|
|
21971
22533
|
var orgRows = (cfg, activeSlug) => {
|
|
21972
22534
|
const seats = { ...cfg.orgs ?? {} };
|
|
@@ -22033,7 +22595,7 @@ var orgUse = (o) => {
|
|
|
22033
22595
|
}
|
|
22034
22596
|
writeConfig(o.home, withActiveOrg(cfg, slug, seat));
|
|
22035
22597
|
writeTokenFile(o.home, seat.token);
|
|
22036
|
-
if (
|
|
22598
|
+
if (existsSync25(settingsPath(o.home)) && cfg.mode !== "on") {
|
|
22037
22599
|
const settings = readSettings(o.home);
|
|
22038
22600
|
const pointed = readProxy(settings);
|
|
22039
22601
|
const url = pointed?.url ?? cfg.proxyUrl;
|
|
@@ -22070,7 +22632,7 @@ var orgCmd = (o) => {
|
|
|
22070
22632
|
init_src();
|
|
22071
22633
|
import { spawnSync as spawnSync8 } from "node:child_process";
|
|
22072
22634
|
import { writeFileSync as writeFileSync23 } from "node:fs";
|
|
22073
|
-
import { join as
|
|
22635
|
+
import { join as join24 } from "node:path";
|
|
22074
22636
|
function packFileName(key) {
|
|
22075
22637
|
return `pack-${slugify(key, 80) || "item"}.md`;
|
|
22076
22638
|
}
|
|
@@ -22098,7 +22660,7 @@ var packCmd = async (o) => {
|
|
|
22098
22660
|
log(r.text);
|
|
22099
22661
|
return 0;
|
|
22100
22662
|
}
|
|
22101
|
-
const path =
|
|
22663
|
+
const path = join24(o.cwd ?? process.cwd(), packFileName(key));
|
|
22102
22664
|
(o.write ?? ((p, t) => writeFileSync23(p, t.endsWith(`
|
|
22103
22665
|
`) ? t : `${t}
|
|
22104
22666
|
`)))(path, r.text);
|
|
@@ -22224,12 +22786,81 @@ var promptEvent = async (o) => {
|
|
|
22224
22786
|
return code;
|
|
22225
22787
|
};
|
|
22226
22788
|
|
|
22789
|
+
// src/commands/reopen.ts
|
|
22790
|
+
var REOPEN_USAGE = 'usage: sleds reopen <key> --reason "<why it is reopened>" [--json]';
|
|
22791
|
+
function parseReopenArgs(argv) {
|
|
22792
|
+
let key;
|
|
22793
|
+
let reason = "";
|
|
22794
|
+
let json = false;
|
|
22795
|
+
for (let i = 0;i < argv.length; i++) {
|
|
22796
|
+
const a = argv[i];
|
|
22797
|
+
if (a === "--json")
|
|
22798
|
+
json = true;
|
|
22799
|
+
else if (a === "--reason") {
|
|
22800
|
+
const value = argv[i + 1];
|
|
22801
|
+
if (value === undefined || value.startsWith("--"))
|
|
22802
|
+
return { error: "--reason needs a value" };
|
|
22803
|
+
reason = value;
|
|
22804
|
+
i++;
|
|
22805
|
+
} else if (a.startsWith("--"))
|
|
22806
|
+
return { error: `unknown flag ${a}` };
|
|
22807
|
+
else if (key === undefined)
|
|
22808
|
+
key = a;
|
|
22809
|
+
else
|
|
22810
|
+
return { error: `one item at a time — "${a}" is extra` };
|
|
22811
|
+
}
|
|
22812
|
+
if (!key)
|
|
22813
|
+
return { error: "which item?" };
|
|
22814
|
+
return { key, reason: reason.trim(), json };
|
|
22815
|
+
}
|
|
22816
|
+
function reopenedLine(key, j) {
|
|
22817
|
+
const shown = typeof j.key === "string" ? j.key : key;
|
|
22818
|
+
return typeof j.sled === "string" && j.sled ? `${shown} reopened — back on ${j.sled}'s sled` : `${shown} reopened — back to ready, on no sled`;
|
|
22819
|
+
}
|
|
22820
|
+
async function reopenCmd(o) {
|
|
22821
|
+
const log = o.log ?? console.log;
|
|
22822
|
+
const error = o.error ?? console.error;
|
|
22823
|
+
const a = parseReopenArgs(o.argv);
|
|
22824
|
+
if ("error" in a) {
|
|
22825
|
+
error(a.error);
|
|
22826
|
+
error(REOPEN_USAGE);
|
|
22827
|
+
return 2;
|
|
22828
|
+
}
|
|
22829
|
+
if (!a.reason) {
|
|
22830
|
+
error(`${a.key}: say why it is reopened — it goes on the item's thread`);
|
|
22831
|
+
error(REOPEN_USAGE);
|
|
22832
|
+
return 1;
|
|
22833
|
+
}
|
|
22834
|
+
const client = engineClient({ home: o.home, ...o.client ? { client: o.client } : {} });
|
|
22835
|
+
if ("error" in client) {
|
|
22836
|
+
error(client.error);
|
|
22837
|
+
return 1;
|
|
22838
|
+
}
|
|
22839
|
+
const r = await client.postFull("/proxy/reopen", { project: a.key, reason: a.reason });
|
|
22840
|
+
if (!r) {
|
|
22841
|
+
error(`${a.key}: SLEDS unreachable — nothing reopened`);
|
|
22842
|
+
return 1;
|
|
22843
|
+
}
|
|
22844
|
+
const ok = r.status < 300 && r.json?.ok !== false;
|
|
22845
|
+
if (a.json)
|
|
22846
|
+
log(JSON.stringify(r.json ?? { ok: false, error: `HTTP ${r.status}` }));
|
|
22847
|
+
if (!ok) {
|
|
22848
|
+
const why = typeof r.json?.error === "string" ? r.json.error : `HTTP ${r.status}`;
|
|
22849
|
+
if (!a.json)
|
|
22850
|
+
error(r.status === 404 && !r.json ? `${a.key}: SLEDS does not know this seat — run \`sleds init\`` : `${a.key}: ${why}`);
|
|
22851
|
+
return 1;
|
|
22852
|
+
}
|
|
22853
|
+
if (!a.json)
|
|
22854
|
+
log(reopenedLine(a.key, r.json ?? {}));
|
|
22855
|
+
return 0;
|
|
22856
|
+
}
|
|
22857
|
+
|
|
22227
22858
|
// src/commands/route.ts
|
|
22228
22859
|
init_src();
|
|
22229
22860
|
import { readFileSync as readFileSync27 } from "node:fs";
|
|
22230
22861
|
import { hostname as hostname12 } from "node:os";
|
|
22231
22862
|
var ARM_USAGE = "usage: sleds route arm <key> [--off] [--json] [--name <engine>] [--at-once <n>] [--runner tmux|headless|command|orca|cmux] [--runner-cmd '<launcher>'] [--agent '<command>'] [--mode <permission-mode>] [--model <id>] [--effort <e>] [--branch '<template>'] [--pr-title '<template>'] [--repos-dir <dir>]";
|
|
22232
|
-
var
|
|
22863
|
+
var USAGE2 = "usage: sleds route <key> [--long | --dot | --json] | sleds route arm <key> [--off] [--json] | sleds route answer <key>/<node> --stdin | sleds route propose <key> --file <nodes.json> | sleds route approve <key> | sleds route redraft <key> | sleds route single <key> | sleds route decisions [<key>] [--flagged] | sleds route decision <key> <id> --seen | --answer --stdin [--route accept|keep|redraft] | --keep-route | --apply [--answer --stdin] | --overrule --stdin | --ready | sleds route run <key> [--at-once <n>] [--runner tmux|headless|command|orca|cmux] [--runner-cmd '<launcher>'] [--agent '<command>'] [--interval <s>] [--lease <min>] [--idle <min>] [--repo <path>] [--repos-dir <dir>] [--mode <permission-mode>] [--model <id>] [--branch '<template>'] [--pr-title '<template>'] [--once]";
|
|
22233
22864
|
var ANSWER_USAGE = "usage: sleds route answer <key>/<node> <answer…> | sleds route answer <key>/<node> --stdin [--json]";
|
|
22234
22865
|
var PROPOSE_USAGE = "usage: sleds route propose <key> (--file <nodes.json> | --stdin) [--name <engine>] [--json]";
|
|
22235
22866
|
var APPROVE_USAGE = "usage: sleds route approve <key> [--file <nodes.json>] [--json]";
|
|
@@ -22784,14 +23415,14 @@ var routeCmd = async (o) => {
|
|
|
22784
23415
|
const parsed = parseEngineArgs(o.argv.filter((a) => a !== "--dot" && a !== "--long" && a !== "--json"), o.host ?? hostname12());
|
|
22785
23416
|
if ("error" in parsed) {
|
|
22786
23417
|
error(parsed.error);
|
|
22787
|
-
error(
|
|
23418
|
+
error(USAGE2);
|
|
22788
23419
|
return 2;
|
|
22789
23420
|
}
|
|
22790
23421
|
const { positional } = parsed;
|
|
22791
23422
|
const isRun = positional[0] === "run";
|
|
22792
23423
|
const key = isRun ? positional[1] : positional[0];
|
|
22793
23424
|
if (!key) {
|
|
22794
|
-
error(
|
|
23425
|
+
error(USAGE2);
|
|
22795
23426
|
return 2;
|
|
22796
23427
|
}
|
|
22797
23428
|
if (asJson) {
|
|
@@ -23047,15 +23678,15 @@ var seatEvent = async (o) => {
|
|
|
23047
23678
|
};
|
|
23048
23679
|
|
|
23049
23680
|
// src/commands/seatSync.ts
|
|
23050
|
-
import { execFileSync as
|
|
23051
|
-
import { existsSync as
|
|
23681
|
+
import { execFileSync as execFileSync5 } from "node:child_process";
|
|
23682
|
+
import { existsSync as existsSync26, mkdirSync as mkdirSync22, readFileSync as readFileSync28, readdirSync as readdirSync5, rmSync as rmSync16, writeFileSync as writeFileSync25 } from "node:fs";
|
|
23052
23683
|
import { platform as platform2 } from "node:os";
|
|
23053
|
-
import { dirname as dirname17, join as
|
|
23684
|
+
import { dirname as dirname17, join as join26 } from "node:path";
|
|
23054
23685
|
|
|
23055
23686
|
// src/cookieDb.ts
|
|
23056
23687
|
import { copyFileSync, rmSync as rmSync15 } from "node:fs";
|
|
23057
23688
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
23058
|
-
import { join as
|
|
23689
|
+
import { join as join25 } from "node:path";
|
|
23059
23690
|
var QUERY = "SELECT name, hex(encrypted_value) AS ev FROM cookies WHERE host_key LIKE '%claude.ai%'";
|
|
23060
23691
|
var isBun = () => typeof globalThis.Bun !== "undefined";
|
|
23061
23692
|
async function queryRows(dbPath) {
|
|
@@ -23084,7 +23715,7 @@ function nodeSqlite() {
|
|
|
23084
23715
|
return mod;
|
|
23085
23716
|
}
|
|
23086
23717
|
async function readCookieRows(dbPath) {
|
|
23087
|
-
const copy =
|
|
23718
|
+
const copy = join25(tmpdir2(), `sleds-cookies-${process.pid}-${Date.now()}.sqlite`);
|
|
23088
23719
|
copyFileSync(dbPath, copy);
|
|
23089
23720
|
try {
|
|
23090
23721
|
const rows = await queryRows(copy);
|
|
@@ -23368,7 +23999,7 @@ function findFile(root, name) {
|
|
|
23368
23999
|
const stack = [root];
|
|
23369
24000
|
while (stack.length > 0) {
|
|
23370
24001
|
const dir = stack.pop();
|
|
23371
|
-
if (!dir || !
|
|
24002
|
+
if (!dir || !existsSync26(dir))
|
|
23372
24003
|
continue;
|
|
23373
24004
|
let entries;
|
|
23374
24005
|
try {
|
|
@@ -23377,7 +24008,7 @@ function findFile(root, name) {
|
|
|
23377
24008
|
continue;
|
|
23378
24009
|
}
|
|
23379
24010
|
for (const e of entries) {
|
|
23380
|
-
const p =
|
|
24011
|
+
const p = join26(dir, e.name);
|
|
23381
24012
|
if (e.isDirectory())
|
|
23382
24013
|
stack.push(p);
|
|
23383
24014
|
else if (e.name === name)
|
|
@@ -23388,8 +24019,8 @@ function findFile(root, name) {
|
|
|
23388
24019
|
}
|
|
23389
24020
|
function findCookieDb(home, source) {
|
|
23390
24021
|
const b = BROWSERS[source.browser];
|
|
23391
|
-
const root =
|
|
23392
|
-
if (!
|
|
24022
|
+
const root = join26(home, APP_SUPPORT, b.appDir);
|
|
24023
|
+
if (!existsSync26(root))
|
|
23393
24024
|
throw new Error(`no ${source.browser} data at ${root} (is it installed?)`);
|
|
23394
24025
|
if (!b.profiled) {
|
|
23395
24026
|
const db = findFile(root, "Cookies");
|
|
@@ -23397,15 +24028,15 @@ function findCookieDb(home, source) {
|
|
|
23397
24028
|
throw new Error(`no cookie store under ${root}`);
|
|
23398
24029
|
return db;
|
|
23399
24030
|
}
|
|
23400
|
-
const localStatePath =
|
|
23401
|
-
const localState =
|
|
24031
|
+
const localStatePath = join26(root, "Local State");
|
|
24032
|
+
const localState = existsSync26(localStatePath) ? readFileSync28(localStatePath, "utf8") : null;
|
|
23402
24033
|
const wanted = source.profile ?? "Default";
|
|
23403
24034
|
const { dir, ambiguous } = resolveProfileDir(localState, wanted);
|
|
23404
24035
|
if (ambiguous)
|
|
23405
24036
|
throw new Error(`"${wanted}" names ${ambiguous.length} ${source.browser} profiles (${ambiguous.join(", ")}) — pass the directory or the account email instead`);
|
|
23406
24037
|
if (!dir)
|
|
23407
24038
|
throw new Error(`no ${source.browser} profile named "${wanted}" (check chrome://version)`);
|
|
23408
|
-
const db = cookieDbCandidates(
|
|
24039
|
+
const db = cookieDbCandidates(join26(root, dir)).find((p) => existsSync26(p));
|
|
23409
24040
|
if (!db)
|
|
23410
24041
|
throw new Error(`profile "${dir}" has no cookie store yet (open claude.ai in it once)`);
|
|
23411
24042
|
return db;
|
|
@@ -23415,7 +24046,7 @@ async function readCookieRowsBun(home, source) {
|
|
|
23415
24046
|
}
|
|
23416
24047
|
function keychainKey(service) {
|
|
23417
24048
|
try {
|
|
23418
|
-
return
|
|
24049
|
+
return execFileSync5("security", ["find-generic-password", "-w", "-s", service], {
|
|
23419
24050
|
encoding: "utf8"
|
|
23420
24051
|
}).trim();
|
|
23421
24052
|
} catch {
|
|
@@ -23427,7 +24058,7 @@ function decryptJar(rows, safeStoragePw) {
|
|
|
23427
24058
|
}
|
|
23428
24059
|
var curlUsage = (org, cookie) => {
|
|
23429
24060
|
try {
|
|
23430
|
-
const out =
|
|
24061
|
+
const out = execFileSync5("curl", usageCurlArgs(), {
|
|
23431
24062
|
input: usageCurlConfig(org, cookie),
|
|
23432
24063
|
encoding: "utf8",
|
|
23433
24064
|
maxBuffer: 8000000
|
|
@@ -23449,7 +24080,7 @@ function fetchSeatUsage(jar, org, now, run) {
|
|
|
23449
24080
|
}
|
|
23450
24081
|
function launchAgentPlist(bun, script, home, source) {
|
|
23451
24082
|
const label = launchLabel(source);
|
|
23452
|
-
const log =
|
|
24083
|
+
const log = join26(home, ".config", "sleds", `${label.slice("ai.sleds.".length)}.log`);
|
|
23453
24084
|
const esc = (s) => s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
23454
24085
|
const args = [bun, script, "seat-sync", ...sourceArgs(source)].map((a) => ` <string>${esc(a)}</string>`).join(`
|
|
23455
24086
|
`);
|
|
@@ -23471,7 +24102,7 @@ ${args}
|
|
|
23471
24102
|
`;
|
|
23472
24103
|
}
|
|
23473
24104
|
function plistPath2(home, source) {
|
|
23474
|
-
return
|
|
24105
|
+
return join26(home, "Library", "LaunchAgents", `${launchLabel(source)}.plist`);
|
|
23475
24106
|
}
|
|
23476
24107
|
function install(home, argv, source, log) {
|
|
23477
24108
|
const bun = argv[0] ?? process.execPath;
|
|
@@ -23480,7 +24111,7 @@ function install(home, argv, source, log) {
|
|
|
23480
24111
|
log("cannot resolve the sleds script path to install the timer");
|
|
23481
24112
|
return 1;
|
|
23482
24113
|
}
|
|
23483
|
-
const dir =
|
|
24114
|
+
const dir = join26(home, "Library", "LaunchAgents");
|
|
23484
24115
|
mkdirSync22(dir, { recursive: true });
|
|
23485
24116
|
const path = plistPath2(home, source);
|
|
23486
24117
|
const tokenPath = seatTokenPath(home, source);
|
|
@@ -23492,19 +24123,19 @@ function install(home, argv, source, log) {
|
|
|
23492
24123
|
}
|
|
23493
24124
|
writeFileSync25(path, launchAgentPlist(bun, script, home, source), { mode: 384 });
|
|
23494
24125
|
try {
|
|
23495
|
-
|
|
24126
|
+
execFileSync5("launchctl", ["unload", path], { stdio: "ignore" });
|
|
23496
24127
|
} catch {}
|
|
23497
|
-
|
|
24128
|
+
execFileSync5("launchctl", ["load", "-w", path]);
|
|
23498
24129
|
log(`seat-sync timer installed (every ${SYNC_INTERVAL_S / 60} min) → ${path}`);
|
|
23499
24130
|
return 0;
|
|
23500
24131
|
}
|
|
23501
24132
|
function uninstall(home, source, log) {
|
|
23502
24133
|
const path = plistPath2(home, source);
|
|
23503
24134
|
try {
|
|
23504
|
-
|
|
24135
|
+
execFileSync5("launchctl", ["unload", "-w", path], { stdio: "ignore" });
|
|
23505
24136
|
} catch {}
|
|
23506
24137
|
rmSync16(seatTokenPath(home, source), { force: true });
|
|
23507
|
-
log(
|
|
24138
|
+
log(existsSync26(path) ? `seat-sync timer removed (${path} can be deleted)` : "no seat-sync timer");
|
|
23508
24139
|
return 0;
|
|
23509
24140
|
}
|
|
23510
24141
|
var seatSync = async (o) => {
|
|
@@ -23776,7 +24407,7 @@ var specEvent = async (o) => {
|
|
|
23776
24407
|
};
|
|
23777
24408
|
|
|
23778
24409
|
// src/commands/status.ts
|
|
23779
|
-
import { existsSync as
|
|
24410
|
+
import { existsSync as existsSync27 } from "node:fs";
|
|
23780
24411
|
init_config();
|
|
23781
24412
|
init_settings();
|
|
23782
24413
|
var HEALTH_TIMEOUT_MS2 = 4000;
|
|
@@ -23864,7 +24495,7 @@ var status3 = async (o) => {
|
|
|
23864
24495
|
const log = o.log ?? console.log;
|
|
23865
24496
|
const nowMs = (o.now ?? Date.now)();
|
|
23866
24497
|
const cfg = readConfig(o.home);
|
|
23867
|
-
const settings =
|
|
24498
|
+
const settings = existsSync27(settingsPath(o.home)) ? readSettings(o.home) : {};
|
|
23868
24499
|
const inSettings = readProxy(settings);
|
|
23869
24500
|
const picked = selectOrg(cfg, undefined, o.env ?? process.env);
|
|
23870
24501
|
const viaEnv = picked.from === "env";
|
|
@@ -24057,7 +24688,7 @@ var announce = (o, events) => {
|
|
|
24057
24688
|
|
|
24058
24689
|
// src/commands/stopEvent.ts
|
|
24059
24690
|
init_meter();
|
|
24060
|
-
import { existsSync as
|
|
24691
|
+
import { existsSync as existsSync28, readFileSync as readFileSync30 } from "node:fs";
|
|
24061
24692
|
|
|
24062
24693
|
// src/transcript.ts
|
|
24063
24694
|
var isObject16 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
@@ -24153,7 +24784,7 @@ async function postTranscript(o, payload, now) {
|
|
|
24153
24784
|
const src = meterSource(o.home);
|
|
24154
24785
|
if (!src)
|
|
24155
24786
|
return false;
|
|
24156
|
-
const read = o.readFile ?? ((p) =>
|
|
24787
|
+
const read = o.readFile ?? ((p) => existsSync28(p) ? readFileSync30(p, "utf8") : null);
|
|
24157
24788
|
let text;
|
|
24158
24789
|
try {
|
|
24159
24790
|
text = read(path);
|
|
@@ -24231,7 +24862,7 @@ var stopEvent = async (o) => {
|
|
|
24231
24862
|
|
|
24232
24863
|
// src/commands/uninstall.ts
|
|
24233
24864
|
import { spawnSync as spawnSync9 } from "node:child_process";
|
|
24234
|
-
import { existsSync as
|
|
24865
|
+
import { existsSync as existsSync29, rmSync as rmSync17 } from "node:fs";
|
|
24235
24866
|
init_config();
|
|
24236
24867
|
init_meter();
|
|
24237
24868
|
init_settings();
|
|
@@ -24244,7 +24875,7 @@ var uninstall2 = (o) => {
|
|
|
24244
24875
|
(o.log ?? console.log)(` ${codexLine}`);
|
|
24245
24876
|
const log = o.log ?? console.log;
|
|
24246
24877
|
const removed = [];
|
|
24247
|
-
if (
|
|
24878
|
+
if (existsSync29(settingsPath(o.home))) {
|
|
24248
24879
|
let settings = readSettings(o.home);
|
|
24249
24880
|
let changed = false;
|
|
24250
24881
|
if (hasEitherKey(settings)) {
|
|
@@ -24291,18 +24922,18 @@ var uninstall2 = (o) => {
|
|
|
24291
24922
|
writeSettings(o.home, settings);
|
|
24292
24923
|
}
|
|
24293
24924
|
for (const file of [meterCachePath(o.home), ...allSeatStatePaths(o.home)]) {
|
|
24294
|
-
if (
|
|
24925
|
+
if (existsSync29(file)) {
|
|
24295
24926
|
rmSync17(file, { force: true });
|
|
24296
24927
|
removed.push(file);
|
|
24297
24928
|
}
|
|
24298
24929
|
}
|
|
24299
24930
|
const skill = skillDir(o.home);
|
|
24300
|
-
if (
|
|
24931
|
+
if (existsSync29(skill)) {
|
|
24301
24932
|
rmSync17(skill, { recursive: true, force: true });
|
|
24302
24933
|
removed.push(skill);
|
|
24303
24934
|
}
|
|
24304
24935
|
const plist = musherPlistPath(o.home);
|
|
24305
|
-
if (
|
|
24936
|
+
if (existsSync29(plist)) {
|
|
24306
24937
|
(o.unloadAgent ?? defaultUnload)(plist);
|
|
24307
24938
|
rmSync17(plist, { force: true });
|
|
24308
24939
|
removed.push(`launchd agent ${musherLabel()}`);
|
|
@@ -24373,7 +25004,7 @@ var verify = async (o) => {
|
|
|
24373
25004
|
};
|
|
24374
25005
|
|
|
24375
25006
|
// src/commands/webhooks.ts
|
|
24376
|
-
var
|
|
25007
|
+
var USAGE3 = "usage: sleds webhooks [--json] | sleds webhooks deliveries <id or name> [--json]";
|
|
24377
25008
|
var str14 = (x) => typeof x === "string" ? x : "";
|
|
24378
25009
|
function ago3(at, now) {
|
|
24379
25010
|
const m = Math.max(0, Math.floor((now - at) / 60000));
|
|
@@ -24424,7 +25055,7 @@ var webhooksCmd = async (o) => {
|
|
|
24424
25055
|
const now = (o.now ?? Date.now)();
|
|
24425
25056
|
const sub = words[0];
|
|
24426
25057
|
if (sub && (sub !== "deliveries" || words.length !== 2)) {
|
|
24427
|
-
error(
|
|
25058
|
+
error(USAGE3);
|
|
24428
25059
|
return 2;
|
|
24429
25060
|
}
|
|
24430
25061
|
const path = sub === "deliveries" ? `/proxy/webhooks/deliveries?hook=${encodeURIComponent(words[1] ?? "")}` : "/proxy/webhooks";
|
|
@@ -24457,13 +25088,13 @@ var webhooksCmd = async (o) => {
|
|
|
24457
25088
|
|
|
24458
25089
|
// src/config.ts
|
|
24459
25090
|
init_settings();
|
|
24460
|
-
import { chmodSync as chmodSync7, existsSync as
|
|
24461
|
-
import { join as
|
|
25091
|
+
import { chmodSync as chmodSync7, existsSync as existsSync30, mkdirSync as mkdirSync23, readFileSync as readFileSync31, renameSync as renameSync9, writeFileSync as writeFileSync26 } from "node:fs";
|
|
25092
|
+
import { join as join27, resolve as resolve11 } from "node:path";
|
|
24462
25093
|
var configDir2 = (home, env = process.env) => {
|
|
24463
25094
|
const o = env.SLEDS_HOME?.trim();
|
|
24464
|
-
return o ?
|
|
25095
|
+
return o ? resolve11(home, o) : join27(home, ".config", "sleds");
|
|
24465
25096
|
};
|
|
24466
|
-
var configPath2 = (home) =>
|
|
25097
|
+
var configPath2 = (home) => join27(configDir2(home), "config.json");
|
|
24467
25098
|
var isObject17 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
24468
25099
|
var optString2 = (v) => typeof v === "string" ? v : undefined;
|
|
24469
25100
|
var parsePriorEnv2 = (v) => {
|
|
@@ -24595,7 +25226,7 @@ var parseOrgInfo2 = (raw) => {
|
|
|
24595
25226
|
};
|
|
24596
25227
|
var readConfig2 = (home) => {
|
|
24597
25228
|
const p = configPath2(home);
|
|
24598
|
-
if (!
|
|
25229
|
+
if (!existsSync30(p))
|
|
24599
25230
|
return {};
|
|
24600
25231
|
return parseConfig2(JSON.parse(readFileSync31(p, "utf8")));
|
|
24601
25232
|
};
|
|
@@ -24649,15 +25280,15 @@ var defaultExec5 = (cmd, args) => {
|
|
|
24649
25280
|
};
|
|
24650
25281
|
|
|
24651
25282
|
// src/settings.ts
|
|
24652
|
-
import { dirname as dirname18, join as
|
|
25283
|
+
import { dirname as dirname18, join as join28, resolve as resolve12 } from "node:path";
|
|
24653
25284
|
var withSeatEnv2 = (command, home, env = process.env, org) => {
|
|
24654
25285
|
const parts = [];
|
|
24655
25286
|
const sh = env.SLEDS_HOME?.trim();
|
|
24656
25287
|
const cc = env.CLAUDE_CONFIG_DIR?.trim();
|
|
24657
25288
|
if (sh)
|
|
24658
|
-
parts.push(`SLEDS_HOME=${shellWord2(
|
|
25289
|
+
parts.push(`SLEDS_HOME=${shellWord2(resolve12(home, sh))}`);
|
|
24659
25290
|
if (cc)
|
|
24660
|
-
parts.push(`CLAUDE_CONFIG_DIR=${shellWord2(
|
|
25291
|
+
parts.push(`CLAUDE_CONFIG_DIR=${shellWord2(resolve12(home, cc))}`);
|
|
24661
25292
|
parts.push(command);
|
|
24662
25293
|
return pinOrg4(parts.join(" "), org);
|
|
24663
25294
|
};
|
|
@@ -24684,7 +25315,7 @@ var isSledsStopHook2 = isSledsHookFor2("stop-event");
|
|
|
24684
25315
|
var isSledsPermissionHook2 = isSledsHookFor2("permission-event");
|
|
24685
25316
|
|
|
24686
25317
|
// src/index.ts
|
|
24687
|
-
var
|
|
25318
|
+
var USAGE4 = `usage: sleds <command> [options]
|
|
24688
25319
|
|
|
24689
25320
|
commands:
|
|
24690
25321
|
init Sign in through the browser, make this machine a seat, install the /sled skill
|
|
@@ -24739,6 +25370,10 @@ commands:
|
|
|
24739
25370
|
--run start Claude Code with the pack as the first prompt
|
|
24740
25371
|
webhooks The org's outbound webhooks (admins); deliveries <id or name> lists one hook's
|
|
24741
25372
|
last hundred deliveries. Registering and Redeliver stay in the app.
|
|
25373
|
+
deploy A release step's one line: tell SLEDS this commit is now running in an environment.
|
|
25374
|
+
--env <slug> the environment (required); --service <name> for a monorepo
|
|
25375
|
+
--sha / --repo else the CI's own variables, then git
|
|
25376
|
+
Token from SLEDS_DEPLOY_TOKEN (Settings \u203A Deploys). SLEDS down never fails a release.
|
|
24742
25377
|
sled --json What is on your sled (mine with each route, proposed, sent), for a front end.
|
|
24743
25378
|
sleds sled top KEY put KEY first on your sled; the engine picks it next
|
|
24744
25379
|
route <key> Print an item's route: its nodes, what each waits on, what is ready, where each
|
|
@@ -24766,6 +25401,9 @@ commands:
|
|
|
24766
25401
|
--mode <permission-mode> Claude Code's (default auto; acceptEdits stops an unattended
|
|
24767
25402
|
session at git push; an account without auto passes acceptEdits)
|
|
24768
25403
|
--model <id> Claude Code's model
|
|
25404
|
+
reopen <key> Put back an item that landed by mistake (a skipped node closed its route): its
|
|
25405
|
+
landing is cleared and it goes back on the sled it came off, or to ready.
|
|
25406
|
+
--reason "<text>" why \u2014 required; it goes on the item's thread (--json)
|
|
24769
25407
|
engine The long-running loop over the routes Run armed on your sled: takes each ready
|
|
24770
25408
|
node, starts a session on its own branch, follows its PR to the merge (CI failures
|
|
24771
25409
|
and review feedback go back to the session; a merge lands the node, closes the
|
|
@@ -24887,11 +25525,11 @@ var wantsHelp = (argv) => {
|
|
|
24887
25525
|
return false;
|
|
24888
25526
|
};
|
|
24889
25527
|
var commandUsage = (cmd) => {
|
|
24890
|
-
const lines =
|
|
25528
|
+
const lines = USAGE4.split(`
|
|
24891
25529
|
`);
|
|
24892
25530
|
const start = lines.findIndex((l) => l === ` ${cmd}` || l.startsWith(` ${cmd} `));
|
|
24893
25531
|
if (start === -1)
|
|
24894
|
-
return
|
|
25532
|
+
return USAGE4;
|
|
24895
25533
|
const section = [lines[start]];
|
|
24896
25534
|
for (const l of lines.slice(start + 1)) {
|
|
24897
25535
|
if (!/^ {3,}\S/.test(l))
|
|
@@ -24947,8 +25585,12 @@ var main = async (argv) => {
|
|
|
24947
25585
|
return packCmd({ home, argv: argv.slice(1) });
|
|
24948
25586
|
case "webhooks":
|
|
24949
25587
|
return webhooksCmd({ home, argv: argv.slice(1) });
|
|
25588
|
+
case "deploy":
|
|
25589
|
+
return deployCmd({ argv: argv.slice(1) });
|
|
24950
25590
|
case "sled":
|
|
24951
25591
|
return sledCmd({ home, argv: argv.slice(1) });
|
|
25592
|
+
case "reopen":
|
|
25593
|
+
return reopenCmd({ home, argv: argv.slice(1) });
|
|
24952
25594
|
case "engine":
|
|
24953
25595
|
case "route": {
|
|
24954
25596
|
if (cmd === "engine" && argv[1] === "status")
|
|
@@ -25141,19 +25783,19 @@ var main = async (argv) => {
|
|
|
25141
25783
|
case "help":
|
|
25142
25784
|
case "--help":
|
|
25143
25785
|
case "-h":
|
|
25144
|
-
console.log(
|
|
25786
|
+
console.log(USAGE4);
|
|
25145
25787
|
console.log(`
|
|
25146
25788
|
${renderHelpText(argv[1])}`);
|
|
25147
25789
|
return 0;
|
|
25148
25790
|
case "version":
|
|
25149
25791
|
case "--version":
|
|
25150
25792
|
case "-v":
|
|
25151
|
-
console.log("sleds 0.1.
|
|
25793
|
+
console.log("sleds 0.1.45");
|
|
25152
25794
|
return 0;
|
|
25153
25795
|
default:
|
|
25154
25796
|
console.error(`unknown command: ${cmd}
|
|
25155
25797
|
`);
|
|
25156
|
-
console.error(
|
|
25798
|
+
console.error(USAGE4);
|
|
25157
25799
|
return 1;
|
|
25158
25800
|
}
|
|
25159
25801
|
};
|