@sleds/cli 0.1.42 → 0.1.44
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 +977 -430
- 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,10 @@ 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."
|
|
801
804
|
},
|
|
802
805
|
{
|
|
803
806
|
id: "archive",
|
|
@@ -1083,6 +1086,112 @@ var init_verbs = __esm(() => {
|
|
|
1083
1086
|
];
|
|
1084
1087
|
});
|
|
1085
1088
|
|
|
1089
|
+
// ../../packages/shared/src/intake/forms.ts
|
|
1090
|
+
function norm(label) {
|
|
1091
|
+
return label.replace(/[*_`~]/g, "").replace(/\(s\)/gi, "s").replace(/[:?]+\s*$/, "").replace(/,\s*including\b.*$/i, "").replace(/\s+/g, " ").trim().toLowerCase();
|
|
1092
|
+
}
|
|
1093
|
+
function plain(s) {
|
|
1094
|
+
return s.replace(/[*_`~]+/g, "").replace(/\s+/g, " ").trim();
|
|
1095
|
+
}
|
|
1096
|
+
function titleOf(s, max = 80) {
|
|
1097
|
+
const t = plain(s);
|
|
1098
|
+
const first = /^(.+?[.!?])(\s|$)/.exec(t)?.[1] ?? t;
|
|
1099
|
+
if (first.length <= max)
|
|
1100
|
+
return first;
|
|
1101
|
+
const cut = first.slice(0, max);
|
|
1102
|
+
const sp = cut.lastIndexOf(" ");
|
|
1103
|
+
return `${(sp > max / 2 ? cut.slice(0, sp) : cut).replace(/[,;:\s]+$/, "")}…`;
|
|
1104
|
+
}
|
|
1105
|
+
function readForm(text) {
|
|
1106
|
+
const lines = text.split(/\r?\n/);
|
|
1107
|
+
for (const def of FORMS) {
|
|
1108
|
+
const found = [];
|
|
1109
|
+
let cur = null;
|
|
1110
|
+
let known = 0;
|
|
1111
|
+
for (const raw of lines) {
|
|
1112
|
+
const line = raw.trim();
|
|
1113
|
+
if (!line)
|
|
1114
|
+
continue;
|
|
1115
|
+
const bold = /^\*[^*]{2,80}\*:?$/.test(line);
|
|
1116
|
+
const whole = line.length <= 80 ? slotOf(def, line) : null;
|
|
1117
|
+
const inline = /^\**([^:*]{2,60}?)\**\s*:\s*(.+)$/.exec(line);
|
|
1118
|
+
const slotInline = inline ? slotOf(def, inline[1] ?? "") : null;
|
|
1119
|
+
if (whole && (bold || !inline)) {
|
|
1120
|
+
cur = { label: plain(norm(line)), slot: whole, value: [] };
|
|
1121
|
+
found.push(cur);
|
|
1122
|
+
known++;
|
|
1123
|
+
} else if (slotInline && inline) {
|
|
1124
|
+
cur = { label: plain(norm(inline[1] ?? "")), slot: slotInline, value: [inline[2] ?? ""] };
|
|
1125
|
+
found.push(cur);
|
|
1126
|
+
known++;
|
|
1127
|
+
} else if (bold && cur) {
|
|
1128
|
+
cur = { label: plain(line.replace(/:$/, "")), slot: null, value: [] };
|
|
1129
|
+
found.push(cur);
|
|
1130
|
+
} else if (cur)
|
|
1131
|
+
cur.value.push(line);
|
|
1132
|
+
}
|
|
1133
|
+
if (known < def.min)
|
|
1134
|
+
continue;
|
|
1135
|
+
const val = (f) => plain(f.value.join(" "));
|
|
1136
|
+
const one = (slot) => found.find((f) => f.slot === slot && val(f));
|
|
1137
|
+
const ask = one("ask");
|
|
1138
|
+
const users = one("users");
|
|
1139
|
+
const sid = one("sid");
|
|
1140
|
+
const loc = one("location");
|
|
1141
|
+
const clients = one("clients");
|
|
1142
|
+
const rev = one("revenue");
|
|
1143
|
+
const n = clients ? val(clients) : "";
|
|
1144
|
+
const clientsText = n ? /^\d+$/.test(n) ? `${n} client${n === "1" ? "" : "s"} affected` : `clients affected: ${n}` : "";
|
|
1145
|
+
const r = rev ? val(rev) : "";
|
|
1146
|
+
const revenue = YES.test(r) ? true : NO.test(r) ? false : undefined;
|
|
1147
|
+
const revText = revenue === true ? "Impacting revenue" : revenue === false ? "No revenue impact" : "";
|
|
1148
|
+
const unknown = found.filter((f) => f.slot === null && val(f)).map((f) => `${f.label}: ${val(f)}`);
|
|
1149
|
+
const who = [
|
|
1150
|
+
users ? `Users: ${val(users)}` : "",
|
|
1151
|
+
sid ? `SID: ${val(sid)}` : "",
|
|
1152
|
+
loc ? `Location ${val(loc)}` : "",
|
|
1153
|
+
...unknown
|
|
1154
|
+
].filter(Boolean);
|
|
1155
|
+
const touches = who.join(" · ");
|
|
1156
|
+
const demand = [clientsText, revText].filter(Boolean).join(" · ");
|
|
1157
|
+
const details = [...who, clientsText, revText].filter(Boolean).join(" · ");
|
|
1158
|
+
return {
|
|
1159
|
+
form: def.name,
|
|
1160
|
+
...ask ? { ask: val(ask) } : {},
|
|
1161
|
+
...details ? { details } : {},
|
|
1162
|
+
...touches ? { touches } : {},
|
|
1163
|
+
...demand ? { demand } : {},
|
|
1164
|
+
...revenue !== undefined ? { revenue } : {}
|
|
1165
|
+
};
|
|
1166
|
+
}
|
|
1167
|
+
return null;
|
|
1168
|
+
}
|
|
1169
|
+
var FORMS, YES, NO, slotOf = (def, label) => {
|
|
1170
|
+
const n = norm(label);
|
|
1171
|
+
for (const [re, slot] of def.labels)
|
|
1172
|
+
if (re.test(n))
|
|
1173
|
+
return slot;
|
|
1174
|
+
return null;
|
|
1175
|
+
};
|
|
1176
|
+
var init_forms = __esm(() => {
|
|
1177
|
+
FORMS = [
|
|
1178
|
+
{
|
|
1179
|
+
name: "Create Tech Support Ticket",
|
|
1180
|
+
labels: [
|
|
1181
|
+
[/^users? experiencing\b/, "users"],
|
|
1182
|
+
[/^sids?\b/, "sid"],
|
|
1183
|
+
[/^location ids?\b/, "location"],
|
|
1184
|
+
[/^issue description\b/, "ask"],
|
|
1185
|
+
[/^(?:number of )?clients? affected\b|^number of clients\b/, "clients"],
|
|
1186
|
+
[/\bimpacting revenue\b|\brevenue impact\b/, "revenue"]
|
|
1187
|
+
],
|
|
1188
|
+
min: 3
|
|
1189
|
+
}
|
|
1190
|
+
];
|
|
1191
|
+
YES = /^(yes|y|yep|true|definitely|it is)\b/i;
|
|
1192
|
+
NO = /^(no|n|nope|false|not)\b/i;
|
|
1193
|
+
});
|
|
1194
|
+
|
|
1086
1195
|
// ../../packages/shared/src/intake/brief.ts
|
|
1087
1196
|
function labelOf(line) {
|
|
1088
1197
|
const h = /^#{1,6}\s+(.+?)\s*#*\s*$/.exec(line);
|
|
@@ -1155,8 +1264,12 @@ function worthFromAnswer(answer) {
|
|
|
1155
1264
|
function draftBrief(input) {
|
|
1156
1265
|
const text = (input.text ?? "").trim();
|
|
1157
1266
|
const a = input.answers ?? {};
|
|
1158
|
-
const
|
|
1159
|
-
const
|
|
1267
|
+
const form = readForm(text);
|
|
1268
|
+
const parsed = sections(text);
|
|
1269
|
+
const preamble = parsed.preamble;
|
|
1270
|
+
const byKey = form ? { touches: form.touches, demand: form.demand } : parsed.byKey;
|
|
1271
|
+
const fromText = form?.ask ? form.ask : askFrom(text, preamble);
|
|
1272
|
+
const askText = a.ask?.trim() || (fromText ? titleOf(fromText) : undefined);
|
|
1160
1273
|
if (!askText)
|
|
1161
1274
|
return null;
|
|
1162
1275
|
const ask = a.ask?.trim() ? { text: clip(a.ask, MAX_ASK), from: "filer" } : { text: clip(askText, MAX_ASK), from: "artifact" };
|
|
@@ -1288,12 +1401,13 @@ var MAX_FIELD = 280, MAX_ASK = 120, MAX_SOURCES = 12, QUESTIONS, REQUIRED_TO_FIL
|
|
|
1288
1401
|
}, fromWord = (f) => f.from === "filer" ? "from you" : f.from === "product" ? "from the product map" : "from what you sent";
|
|
1289
1402
|
var init_brief = __esm(() => {
|
|
1290
1403
|
init_worth();
|
|
1404
|
+
init_forms();
|
|
1291
1405
|
QUESTIONS = {
|
|
1292
|
-
why: "Who is
|
|
1293
|
-
done: "
|
|
1294
|
-
smallest: "What
|
|
1295
|
-
worth: "
|
|
1296
|
-
touches: "
|
|
1406
|
+
why: "Who is this for, and why does it matter now?",
|
|
1407
|
+
done: "How will we know it's done?",
|
|
1408
|
+
smallest: "What's the smallest version that would still help?",
|
|
1409
|
+
worth: "How important is this: high, medium, or low? If a specific customer is waiting on it, tell me who.",
|
|
1410
|
+
touches: "Which part of the product is this about?",
|
|
1297
1411
|
demand: "How many people have asked for this, or how often does it come up?"
|
|
1298
1412
|
};
|
|
1299
1413
|
REQUIRED_TO_FILE = ["why", "done"];
|
|
@@ -1554,7 +1668,8 @@ function intakePlan(g) {
|
|
|
1554
1668
|
card: g.card ? [...g.card] : base.card,
|
|
1555
1669
|
limits: [...g.limits ?? []],
|
|
1556
1670
|
followed: [...g.followed ?? []],
|
|
1557
|
-
text: g.text ?? ""
|
|
1671
|
+
text: g.text ?? "",
|
|
1672
|
+
...g.references ? { references: g.references } : {}
|
|
1558
1673
|
};
|
|
1559
1674
|
}
|
|
1560
1675
|
function planOpen(b, plan, extra = {}) {
|
|
@@ -1586,11 +1701,14 @@ var init_intake = __esm(() => {
|
|
|
1586
1701
|
ORDER = ["why", "done", "smallest", "worth", "touches", "demand"];
|
|
1587
1702
|
DEFAULT_CARD = CARD_FIELDS.filter((f) => f !== "product map details");
|
|
1588
1703
|
FIELD_WORDS = [
|
|
1589
|
-
["why", /\bwho is it for\b|\bwhy now\b|\bwho (?:hits|needs|asked)\b|\bwhy\b/i],
|
|
1590
|
-
["done", /\bdone\b|\blook like\b|\bsuccess\b|\bacceptance\b/i],
|
|
1704
|
+
["why", /\bwho is (?:it|this) for\b|\bwhy now\b|\bwho (?:hits|needs|asked)\b|\bwhy\b/i],
|
|
1705
|
+
["done", /\bdone\b|\blook like\b|\bsuccess\b|\bacceptance\b|\bis fixed\b|\bit'?s fixed\b/i],
|
|
1591
1706
|
["smallest", /\bsmallest\b|\bminimum\b|\bmvp\b|\bsmaller version\b/i],
|
|
1592
|
-
["worth", /\bworth\b|\bvalue to\b|\bthe business\b|\brevenue\b/i],
|
|
1593
|
-
[
|
|
1707
|
+
["worth", /\bworth\b|\bvalue to\b|\bthe business\b|\brevenue\b|\bhow important\b/i],
|
|
1708
|
+
[
|
|
1709
|
+
"touches",
|
|
1710
|
+
/\btouch(?:es)?\b|\bwhich (?:product|system|area)s?\b|\bwhere in the product\b|\bwhich part of the product\b/i
|
|
1711
|
+
],
|
|
1594
1712
|
["demand", /\bhow many\b|\bhow often\b|\bcome up\b|\basked for this\b/i]
|
|
1595
1713
|
];
|
|
1596
1714
|
});
|
|
@@ -1769,7 +1887,7 @@ function buildRoute(drafts, opts = {}) {
|
|
|
1769
1887
|
problems.push({ level: "error", text: "a route needs at least one node" });
|
|
1770
1888
|
if (drafts.length > MAX_NODES)
|
|
1771
1889
|
problems.push({ level: "error", text: `a route holds at most ${MAX_NODES} nodes` });
|
|
1772
|
-
const given = drafts.map((d) => d.key ?
|
|
1890
|
+
const given = drafts.map((d) => d.key ? norm2(d.key) : "");
|
|
1773
1891
|
const fresh = mintKeys(given.filter((k) => !k).length, given.filter((k) => k));
|
|
1774
1892
|
let f = 0;
|
|
1775
1893
|
const keys = given.map((k) => k || fresh[f++]);
|
|
@@ -1784,7 +1902,7 @@ function buildRoute(drafts, opts = {}) {
|
|
|
1784
1902
|
const title = stripControl(d.title.replace(/\s+/g, " ")).trim().slice(0, 160);
|
|
1785
1903
|
if (!title)
|
|
1786
1904
|
problems.push({ level: "error", node: key, text: `node ${key} has no title` });
|
|
1787
|
-
const blockedBy = [...new Set((d.blockedBy ?? []).map(
|
|
1905
|
+
const blockedBy = [...new Set((d.blockedBy ?? []).map(norm2).filter(Boolean))];
|
|
1788
1906
|
for (const b of blockedBy) {
|
|
1789
1907
|
if (b === key)
|
|
1790
1908
|
problems.push({ level: "error", node: key, text: `node ${key} blocks itself` });
|
|
@@ -1961,7 +2079,7 @@ ${state}`)}, fillcolor="${fill}", color="${border}"${style}];`);
|
|
|
1961
2079
|
`)}
|
|
1962
2080
|
`;
|
|
1963
2081
|
}
|
|
1964
|
-
var ROUTE_APPROACH_MAX = 2000, PLAN_APPROACH_MAX = 1200, PLAN_LIST_MAX = 8, PLAN_LINE_MAX = 240, CONTROL, MAX_NODES = 24, DEPTH_WARN = 4,
|
|
2082
|
+
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}`.";
|
|
1965
2083
|
var init_route = __esm(() => {
|
|
1966
2084
|
CONTROL = /[\u0000-\u0008\u000b-\u001f\u007f-\u009f]/g;
|
|
1967
2085
|
AREA_ROOTS = new Set(["apps", "packages", "services", "libs", "crates", "modules"]);
|
|
@@ -3073,6 +3191,13 @@ async function updateTool(ctx, args) {
|
|
|
3073
3191
|
patch.repos = args.repos.split(/[,\s]+/).filter(Boolean);
|
|
3074
3192
|
if (Array.isArray(args.relations))
|
|
3075
3193
|
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));
|
|
3194
|
+
if (args.worth === null)
|
|
3195
|
+
patch.worth = null;
|
|
3196
|
+
else if (typeof args.worth === "string" && args.worth.trim()) {
|
|
3197
|
+
patch.worth = args.worth.trim();
|
|
3198
|
+
if (args.carry === true)
|
|
3199
|
+
patch.carry = true;
|
|
3200
|
+
}
|
|
3076
3201
|
if (args.sensitivity === null)
|
|
3077
3202
|
patch.sensitivity = null;
|
|
3078
3203
|
else if (typeof args.sensitivity === "string" && /^(public|internal|restricted)$/.test(args.sensitivity)) {
|
|
@@ -3082,7 +3207,7 @@ async function updateTool(ctx, args) {
|
|
|
3082
3207
|
}
|
|
3083
3208
|
if (Object.keys(patch).length === 0)
|
|
3084
3209
|
return {
|
|
3085
|
-
text: "Nothing to change. Pass the fields that change: ask, why, done, smallest, touches, demand, description, due, parent, repos, relations.",
|
|
3210
|
+
text: "Nothing to change. Pass the fields that change: ask, why, done, smallest, touches, demand, worth, description, due, parent, repos, relations.",
|
|
3086
3211
|
isError: true
|
|
3087
3212
|
};
|
|
3088
3213
|
const res = await postSaying(ctx, "/proxy/item/update", { project, ...patch }, "Could not update the item.");
|
|
@@ -3090,13 +3215,27 @@ async function updateTool(ctx, args) {
|
|
|
3090
3215
|
return res.fail;
|
|
3091
3216
|
const changed = Array.isArray(res.json.changed) ? res.json.changed.filter((x) => typeof x === "string") : [];
|
|
3092
3217
|
const key = str5(res.json.key) ?? project;
|
|
3218
|
+
const carried = carriedLine(res.json.carried);
|
|
3093
3219
|
if (changed.length === 0)
|
|
3094
|
-
return { text: `${key}: nothing differed from what is on the item
|
|
3220
|
+
return { text: `${key}: nothing differed from what is on the item.${carried}` };
|
|
3095
3221
|
const stale = res.json.sizeStale === true;
|
|
3096
3222
|
return {
|
|
3097
|
-
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)." : ""}`
|
|
3223
|
+
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)." : ""}`
|
|
3098
3224
|
};
|
|
3099
3225
|
}
|
|
3226
|
+
function carriedLine(c) {
|
|
3227
|
+
if (!isObject4(c))
|
|
3228
|
+
return "";
|
|
3229
|
+
const set = Array.isArray(c.set) ? c.set.filter((x) => typeof x === "string") : [];
|
|
3230
|
+
const valued = num3(c.valued) ?? 0;
|
|
3231
|
+
const skipped = num3(c.skipped) ?? 0;
|
|
3232
|
+
const parts = [
|
|
3233
|
+
set.length ? `worth set on ${set.length} ${set.length === 1 ? "child" : "children"} (${set.join(", ")})` : "no child took it",
|
|
3234
|
+
...valued ? [`${valued} already had a worth and kept it`] : [],
|
|
3235
|
+
...skipped ? [`${skipped} you may not change (a mirror, or not yours) — left as they were`] : []
|
|
3236
|
+
];
|
|
3237
|
+
return ` ${parts.join("; ").replace(/^./, (ch) => ch.toUpperCase())}.`;
|
|
3238
|
+
}
|
|
3100
3239
|
async function archiveTool(ctx, args) {
|
|
3101
3240
|
if (!ctx.client)
|
|
3102
3241
|
return NOT_SET_UP;
|
|
@@ -4773,7 +4912,7 @@ var init_server = __esm(() => {
|
|
|
4773
4912
|
},
|
|
4774
4913
|
{
|
|
4775
4914
|
name: "update",
|
|
4776
|
-
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.",
|
|
4915
|
+
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.",
|
|
4777
4916
|
inputSchema: {
|
|
4778
4917
|
type: "object",
|
|
4779
4918
|
properties: {
|
|
@@ -4784,6 +4923,14 @@ var init_server = __esm(() => {
|
|
|
4784
4923
|
smallest: { type: "string" },
|
|
4785
4924
|
touches: { type: "string" },
|
|
4786
4925
|
demand: { type: "string" },
|
|
4926
|
+
worth: {
|
|
4927
|
+
type: ["string", "null"],
|
|
4928
|
+
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."
|
|
4929
|
+
},
|
|
4930
|
+
carry: {
|
|
4931
|
+
type: "boolean",
|
|
4932
|
+
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."
|
|
4933
|
+
},
|
|
4787
4934
|
description: {
|
|
4788
4935
|
type: "string",
|
|
4789
4936
|
description: "Markdown. Links and images inline. Empty clears it."
|
|
@@ -5242,9 +5389,33 @@ var init_outcome = __esm(() => {
|
|
|
5242
5389
|
});
|
|
5243
5390
|
|
|
5244
5391
|
// ../../packages/shared/src/slack/mention.ts
|
|
5392
|
+
var MENTION_HELP;
|
|
5245
5393
|
var init_mention = __esm(() => {
|
|
5246
5394
|
init_intake();
|
|
5247
5395
|
init_brief();
|
|
5396
|
+
MENTION_HELP = [
|
|
5397
|
+
"Here's what I can do:",
|
|
5398
|
+
"• *@SLEDS file this*: turn the message above into a request for engineering",
|
|
5399
|
+
"• *@SLEDS status*: see where something stands",
|
|
5400
|
+
"You can also ask me to *watch* or *mute* a thread, or *run QW-123*."
|
|
5401
|
+
].join(`
|
|
5402
|
+
`);
|
|
5403
|
+
});
|
|
5404
|
+
|
|
5405
|
+
// ../../packages/shared/src/slack/copy.ts
|
|
5406
|
+
var init_copy = __esm(() => {
|
|
5407
|
+
init_intake();
|
|
5408
|
+
init_forms();
|
|
5409
|
+
init_mention();
|
|
5410
|
+
init_mention();
|
|
5411
|
+
});
|
|
5412
|
+
|
|
5413
|
+
// ../../packages/shared/src/slack/agent.ts
|
|
5414
|
+
var init_agent = __esm(() => {
|
|
5415
|
+
init_intake();
|
|
5416
|
+
init_brief();
|
|
5417
|
+
init_copy();
|
|
5418
|
+
init_mention();
|
|
5248
5419
|
});
|
|
5249
5420
|
|
|
5250
5421
|
// ../../packages/shared/src/item/productHits.ts
|
|
@@ -5546,6 +5717,72 @@ var init_fieldMap = __esm(() => {
|
|
|
5546
5717
|
var SEPT_30 = "/whats-new/2026-09-30", WHATS_NEW;
|
|
5547
5718
|
var init_whatsNew = __esm(() => {
|
|
5548
5719
|
WHATS_NEW = [
|
|
5720
|
+
{
|
|
5721
|
+
date: "2026-10-22",
|
|
5722
|
+
title: "Policy is an org setting",
|
|
5723
|
+
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.",
|
|
5724
|
+
sections: [
|
|
5725
|
+
{
|
|
5726
|
+
title: "Where it went",
|
|
5727
|
+
where: "Settings › Policy",
|
|
5728
|
+
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."
|
|
5729
|
+
}
|
|
5730
|
+
],
|
|
5731
|
+
also: [
|
|
5732
|
+
{
|
|
5733
|
+
label: "Next",
|
|
5734
|
+
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."
|
|
5735
|
+
}
|
|
5736
|
+
]
|
|
5737
|
+
},
|
|
5738
|
+
{
|
|
5739
|
+
date: "2026-10-21",
|
|
5740
|
+
title: "The Slack thread is the item's discussion",
|
|
5741
|
+
lede: "An item filed from a Slack thread keeps that thread. What people say there is on the item, what is said on the item goes back there, every move is one line in the thread, and everyone who has spoken hears what happens next.",
|
|
5742
|
+
sections: [
|
|
5743
|
+
{
|
|
5744
|
+
title: "Replies are comments; moves are lines",
|
|
5745
|
+
where: "Slack, after Filed as",
|
|
5746
|
+
body: "After Filed as, a reply in the thread is a comment on the item, as that person, marked from Slack. Approve, defer, take, land and archive each post one line in the thread with a link to the item. The item's Details has Slack, which opens the thread."
|
|
5747
|
+
},
|
|
5748
|
+
{
|
|
5749
|
+
title: "Comments go out",
|
|
5750
|
+
where: "The item › Discussion",
|
|
5751
|
+
body: "A comment written on the item is posted in its thread as the person who wrote it, and to Jira when the comments switch is on. The line under the composer says where it will go. SLEDS's own strips stay on SLEDS."
|
|
5752
|
+
},
|
|
5753
|
+
{
|
|
5754
|
+
title: "Watchers",
|
|
5755
|
+
where: "The item › Discussion, and @SLEDS in the thread",
|
|
5756
|
+
body: "Whoever filed it, holds it, or has spoken on it hears each move — once, never their own. Watch on the Discussion adds you; Unwatch stops it. In a thread, @SLEDS watch QW-123 ties an item to that thread, @SLEDS status answers with the item's status, and @SLEDS ask: … puts a question to the filer."
|
|
5757
|
+
},
|
|
5758
|
+
{
|
|
5759
|
+
title: "Forms are read as fields",
|
|
5760
|
+
where: "Slack, filing from a workflow post",
|
|
5761
|
+
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."
|
|
5762
|
+
},
|
|
5763
|
+
{
|
|
5764
|
+
title: "Replies you can read",
|
|
5765
|
+
where: "Slack, filing from a thread",
|
|
5766
|
+
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.`
|
|
5767
|
+
},
|
|
5768
|
+
{
|
|
5769
|
+
title: "A model can write the replies",
|
|
5770
|
+
where: "Policy › Intake",
|
|
5771
|
+
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."
|
|
5772
|
+
},
|
|
5773
|
+
{
|
|
5774
|
+
title: "Which workspace, picked",
|
|
5775
|
+
where: "Slack, filing",
|
|
5776
|
+
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."
|
|
5777
|
+
}
|
|
5778
|
+
],
|
|
5779
|
+
also: [
|
|
5780
|
+
{
|
|
5781
|
+
label: "Fixed",
|
|
5782
|
+
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."
|
|
5783
|
+
}
|
|
5784
|
+
]
|
|
5785
|
+
},
|
|
5549
5786
|
{
|
|
5550
5787
|
date: "2026-10-20",
|
|
5551
5788
|
title: "Intake: the gate gets its own page",
|
|
@@ -6178,6 +6415,9 @@ var init_pack2 = __esm(() => {
|
|
|
6178
6415
|
init_prHeader();
|
|
6179
6416
|
});
|
|
6180
6417
|
|
|
6418
|
+
// ../../packages/shared/src/deploys/deploys.ts
|
|
6419
|
+
var init_deploys = () => {};
|
|
6420
|
+
|
|
6181
6421
|
// ../../packages/shared/src/newrelic/newrelic.ts
|
|
6182
6422
|
var init_newrelic = () => {};
|
|
6183
6423
|
|
|
@@ -6575,7 +6815,7 @@ function nodePrompt(o) {
|
|
|
6575
6815
|
`- Work on branch \`${o.branch}\` from the latest \`origin/${base}\` (create it if this checkout is not on it). One branch, one PR.`,
|
|
6576
6816
|
`- Open the PR against \`${base}\` with \`gh pr create${visual ? " --draft" : ""}\`${o.prTitle ? ` and title it exactly \`${o.prTitle}\` (the repo's CI checks it)` : ""}. Its body carries \`SLEDS-Node: ${claim.node}\` on the line under \`SLEDS-Source:\` when the pack names a source.`,
|
|
6577
6817
|
...claim.pack.includes(PR_HEADER_OPEN) ? [
|
|
6578
|
-
`- The PR body starts with the pack's PR header block (\`${PR_HEADER_OPEN}\` … \`${PR_HEADER_CLOSE}\`, in its part 5), above everything else
|
|
6818
|
+
`- 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.`
|
|
6579
6819
|
] : [],
|
|
6580
6820
|
"- Run the repo's own gate before you push. Never merge: a person (or the musher, when the attachment says so) does.",
|
|
6581
6821
|
"- Stay inside this node's scope. Work another node needs goes in the PR description, not in this branch.",
|
|
@@ -7019,6 +7259,9 @@ var init_effort = __esm(() => {
|
|
|
7019
7259
|
EFFORTS = ["low", "medium", "high", "xhigh", "max"];
|
|
7020
7260
|
});
|
|
7021
7261
|
|
|
7262
|
+
// ../../packages/shared/src/workspaceLook.ts
|
|
7263
|
+
var init_workspaceLook = () => {};
|
|
7264
|
+
|
|
7022
7265
|
// src/settings.ts
|
|
7023
7266
|
import { chmodSync, existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
|
|
7024
7267
|
import { dirname, join, resolve as resolve2 } from "node:path";
|
|
@@ -7553,6 +7796,7 @@ var init_src = __esm(() => {
|
|
|
7553
7796
|
init_guides();
|
|
7554
7797
|
init_verbs();
|
|
7555
7798
|
init_brief();
|
|
7799
|
+
init_forms();
|
|
7556
7800
|
init_related();
|
|
7557
7801
|
init_rank();
|
|
7558
7802
|
init_markdown2();
|
|
@@ -7562,6 +7806,8 @@ var init_src = __esm(() => {
|
|
|
7562
7806
|
init_profile2();
|
|
7563
7807
|
init_outcome();
|
|
7564
7808
|
init_mention();
|
|
7809
|
+
init_agent();
|
|
7810
|
+
init_copy();
|
|
7565
7811
|
init_productHits();
|
|
7566
7812
|
init_model();
|
|
7567
7813
|
init_parse();
|
|
@@ -7576,6 +7822,7 @@ var init_src = __esm(() => {
|
|
|
7576
7822
|
init_cheatSheet();
|
|
7577
7823
|
init_pack2();
|
|
7578
7824
|
init_flags();
|
|
7825
|
+
init_deploys();
|
|
7579
7826
|
init_sentry();
|
|
7580
7827
|
init_newrelic();
|
|
7581
7828
|
init_musher();
|
|
@@ -7586,6 +7833,7 @@ var init_src = __esm(() => {
|
|
|
7586
7833
|
init_otlp();
|
|
7587
7834
|
init_session();
|
|
7588
7835
|
init_effort();
|
|
7836
|
+
init_workspaceLook();
|
|
7589
7837
|
});
|
|
7590
7838
|
|
|
7591
7839
|
// src/engine/json.ts
|
|
@@ -8253,6 +8501,7 @@ init_cowork();
|
|
|
8253
8501
|
init_guides();
|
|
8254
8502
|
init_verbs();
|
|
8255
8503
|
init_brief();
|
|
8504
|
+
init_forms();
|
|
8256
8505
|
init_related();
|
|
8257
8506
|
init_rank();
|
|
8258
8507
|
init_markdown2();
|
|
@@ -8262,6 +8511,8 @@ init_due();
|
|
|
8262
8511
|
init_profile2();
|
|
8263
8512
|
init_outcome();
|
|
8264
8513
|
init_mention();
|
|
8514
|
+
init_agent();
|
|
8515
|
+
init_copy();
|
|
8265
8516
|
init_productHits();
|
|
8266
8517
|
init_model();
|
|
8267
8518
|
init_parse();
|
|
@@ -8276,6 +8527,7 @@ init_whatsNew();
|
|
|
8276
8527
|
init_cheatSheet();
|
|
8277
8528
|
init_pack2();
|
|
8278
8529
|
init_flags();
|
|
8530
|
+
init_deploys();
|
|
8279
8531
|
init_sentry();
|
|
8280
8532
|
init_newrelic();
|
|
8281
8533
|
init_musher();
|
|
@@ -8286,6 +8538,7 @@ init_engine();
|
|
|
8286
8538
|
init_otlp();
|
|
8287
8539
|
init_session();
|
|
8288
8540
|
init_effort();
|
|
8541
|
+
init_workspaceLook();
|
|
8289
8542
|
|
|
8290
8543
|
// src/agentMcp.ts
|
|
8291
8544
|
import { execFileSync } from "node:child_process";
|
|
@@ -9232,12 +9485,13 @@ var scanTranscriptForCredits = (path, prior) => {
|
|
|
9232
9485
|
return { hit: false, next: prior && prior.path === path ? prior : { path, offset: 0 } };
|
|
9233
9486
|
}
|
|
9234
9487
|
};
|
|
9235
|
-
var seatWindowBody = (w) => ({
|
|
9488
|
+
var seatWindowBody = (w, account) => ({
|
|
9236
9489
|
capturedAt: w.capturedAt,
|
|
9237
9490
|
source: "claude",
|
|
9238
9491
|
...w.fiveHour ? { fiveHour: w.fiveHour } : {},
|
|
9239
9492
|
...w.sevenDay ? { sevenDay: w.sevenDay } : {},
|
|
9240
|
-
...w.spendLimit ? { spendLimit: w.spendLimit } : {}
|
|
9493
|
+
...w.spendLimit ? { spendLimit: w.spendLimit } : {},
|
|
9494
|
+
...account ? { account } : {}
|
|
9241
9495
|
});
|
|
9242
9496
|
var postJson = async (o, path, body) => {
|
|
9243
9497
|
const fetchFn = o.fetchFn ?? fetch;
|
|
@@ -9253,7 +9507,7 @@ var postJson = async (o, path, body) => {
|
|
|
9253
9507
|
return false;
|
|
9254
9508
|
}
|
|
9255
9509
|
};
|
|
9256
|
-
var postSeatWindow = (o, w) => postJson(o, "/proxy/seat", seatWindowBody(w));
|
|
9510
|
+
var postSeatWindow = (o, w, account) => postJson(o, "/proxy/seat", seatWindowBody(w, account));
|
|
9257
9511
|
var postSeatWall = (o, input) => postJson(o, "/proxy/seat-wall", { source: "claude", ...input });
|
|
9258
9512
|
var SEAT_WALL_LINE = "Seat limit hit. sleds on --until-reset to continue on the platform key, or sleds defer to resume here at the reset.";
|
|
9259
9513
|
var SEAT_WALL_LINE_NO_KEY = "Seat limit hit. sleds defer resumes this session on this Mac at the reset; /sled 5 to hand off — this org has no platform key to switch to.";
|
|
@@ -9444,16 +9698,16 @@ init_src();
|
|
|
9444
9698
|
import { execFileSync as execFileSync3, spawnSync as spawnSync5 } from "node:child_process";
|
|
9445
9699
|
import { randomBytes } from "node:crypto";
|
|
9446
9700
|
import {
|
|
9447
|
-
existsSync as
|
|
9448
|
-
lstatSync,
|
|
9701
|
+
existsSync as existsSync17,
|
|
9702
|
+
lstatSync as lstatSync2,
|
|
9449
9703
|
mkdtempSync,
|
|
9450
|
-
readFileSync as
|
|
9451
|
-
readlinkSync,
|
|
9704
|
+
readFileSync as readFileSync17,
|
|
9705
|
+
readlinkSync as readlinkSync2,
|
|
9452
9706
|
rmSync as rmSync8,
|
|
9453
9707
|
writeFileSync as writeFileSync17
|
|
9454
9708
|
} from "node:fs";
|
|
9455
9709
|
import { tmpdir } from "node:os";
|
|
9456
|
-
import { join as
|
|
9710
|
+
import { join as join20, resolve as resolve9 } from "node:path";
|
|
9457
9711
|
|
|
9458
9712
|
// src/agentMcp.ts
|
|
9459
9713
|
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
@@ -9850,7 +10104,7 @@ function resolveRepoPath(o) {
|
|
|
9850
10104
|
const want = o.repos.length ? o.repos.join(", ") : "this node";
|
|
9851
10105
|
const dir = names[0] ? ` (the folder that holds ${names[0]})` : "";
|
|
9852
10106
|
return {
|
|
9853
|
-
error: `No repo for ${want}: this engine runs in ${o.cwd}, which is not a git checkout. Set Repos
|
|
10107
|
+
error: `No repo for ${want}: this engine runs in ${o.cwd}, which is not a git checkout. Set Repos folders in Settings › Running work${dir}, or start the engine with --repo.`
|
|
9854
10108
|
};
|
|
9855
10109
|
}
|
|
9856
10110
|
for (const name of names) {
|
|
@@ -10375,7 +10629,7 @@ function cmuxRunner(exec, deps = {}) {
|
|
|
10375
10629
|
throw new Error(`cmux send-key ${h.id} failed: ${failed(r)}`);
|
|
10376
10630
|
},
|
|
10377
10631
|
stop(h) {
|
|
10378
|
-
|
|
10632
|
+
closeWorkspace(h.id);
|
|
10379
10633
|
if (!h.group)
|
|
10380
10634
|
return;
|
|
10381
10635
|
const l = cmux(LIST_GROUPS);
|
|
@@ -10415,6 +10669,215 @@ function cmuxRunner(exec, deps = {}) {
|
|
|
10415
10669
|
};
|
|
10416
10670
|
}
|
|
10417
10671
|
|
|
10672
|
+
// src/runners/orca.ts
|
|
10673
|
+
import { existsSync as existsSync12, lstatSync, readlinkSync, writeFileSync as writeFileSync11 } from "node:fs";
|
|
10674
|
+
import { join as join14, resolve as resolve7 } from "node:path";
|
|
10675
|
+
var obj = (v) => v !== null && typeof v === "object" && !Array.isArray(v) ? v : null;
|
|
10676
|
+
var str8 = (v) => typeof v === "string" && v.length > 0 ? v : null;
|
|
10677
|
+
var parseOrcaJson = (stdout) => {
|
|
10678
|
+
try {
|
|
10679
|
+
return obj(JSON.parse(stdout));
|
|
10680
|
+
} catch {
|
|
10681
|
+
return null;
|
|
10682
|
+
}
|
|
10683
|
+
};
|
|
10684
|
+
var orcaError = (env) => {
|
|
10685
|
+
if (!env || env.ok !== false)
|
|
10686
|
+
return null;
|
|
10687
|
+
const e = obj(env.error);
|
|
10688
|
+
const message = str8(e?.message) ?? "Orca refused the call";
|
|
10689
|
+
const code = str8(e?.code);
|
|
10690
|
+
return code ? `${message} (${code})` : message;
|
|
10691
|
+
};
|
|
10692
|
+
var parseOrcaWorktree = (env) => {
|
|
10693
|
+
const result = obj(env.result) ?? {};
|
|
10694
|
+
return str8(obj(result.worktree)?.path) ?? str8(result.worktreePath) ?? str8(result.path);
|
|
10695
|
+
};
|
|
10696
|
+
var parseOrcaBranch = (env) => {
|
|
10697
|
+
const wt = obj(obj(env.result)?.worktree);
|
|
10698
|
+
const branch = str8(wt?.branch)?.replace(/^refs\/heads\//, "");
|
|
10699
|
+
const head = str8(wt?.head);
|
|
10700
|
+
return branch && head ? { branch, head } : null;
|
|
10701
|
+
};
|
|
10702
|
+
var parseOrcaTerminal = (env) => {
|
|
10703
|
+
const result = obj(env.result) ?? {};
|
|
10704
|
+
return str8(obj(result.terminal)?.handle) ?? str8(result.handle) ?? str8(result.terminalHandle) ?? null;
|
|
10705
|
+
};
|
|
10706
|
+
var orcaLauncher = (o) => `#!/bin/sh
|
|
10707
|
+
# sleds engine: ${o.name}
|
|
10708
|
+
exec ${agentShellCommand(o)}
|
|
10709
|
+
`;
|
|
10710
|
+
var orcaCommand = (launcher) => `sh ${shq2(launcher)}; exit`;
|
|
10711
|
+
var GONE = /exit|clos|gone|dead|stopp|kill/i;
|
|
10712
|
+
var orcaTerminalLive = (status, stdout) => {
|
|
10713
|
+
const env = parseOrcaJson(stdout);
|
|
10714
|
+
if (str8(obj(env?.error)?.code) === "runtime_unavailable")
|
|
10715
|
+
return true;
|
|
10716
|
+
if (status !== 0 || !env || env.ok === false)
|
|
10717
|
+
return false;
|
|
10718
|
+
const t = obj(obj(env.result)?.terminal) ?? obj(env.result) ?? {};
|
|
10719
|
+
if (typeof t.status === "string" && GONE.test(t.status))
|
|
10720
|
+
return false;
|
|
10721
|
+
if (t.exited === true || t.alive === false)
|
|
10722
|
+
return false;
|
|
10723
|
+
if (typeof t.exitCode === "number")
|
|
10724
|
+
return false;
|
|
10725
|
+
if (obj(t.exitCause))
|
|
10726
|
+
return false;
|
|
10727
|
+
if (t.orphaned === true && t.writable === false)
|
|
10728
|
+
return false;
|
|
10729
|
+
return true;
|
|
10730
|
+
};
|
|
10731
|
+
var realFs = { lstat: lstatSync, readlink: readlinkSync, exists: existsSync12 };
|
|
10732
|
+
var ORCA_LINK_FIX = "reinstall Orca's shell command from Orca (it links `orca` onto your PATH)";
|
|
10733
|
+
function danglingOrca(path, fs) {
|
|
10734
|
+
for (const dir of path.split(":").filter(Boolean)) {
|
|
10735
|
+
const link = join14(dir, "orca");
|
|
10736
|
+
try {
|
|
10737
|
+
if (!fs.lstat(link).isSymbolicLink())
|
|
10738
|
+
return null;
|
|
10739
|
+
const target = resolve7(dir, fs.readlink(link));
|
|
10740
|
+
if (!fs.exists(target))
|
|
10741
|
+
return { link, target };
|
|
10742
|
+
return null;
|
|
10743
|
+
} catch {}
|
|
10744
|
+
}
|
|
10745
|
+
return null;
|
|
10746
|
+
}
|
|
10747
|
+
function orcaPreflight(exec, path, fs = realFs) {
|
|
10748
|
+
let r;
|
|
10749
|
+
try {
|
|
10750
|
+
r = exec("orca", ["--version"], { env: { PATH: path } });
|
|
10751
|
+
} catch (e) {
|
|
10752
|
+
r = { status: 127, stdout: "", stderr: e instanceof Error ? e.message : String(e) };
|
|
10753
|
+
}
|
|
10754
|
+
if (r.status === 0) {
|
|
10755
|
+
return { ok: true, version: r.stdout.trim().split(`
|
|
10756
|
+
`)[0]?.trim() || "orca" };
|
|
10757
|
+
}
|
|
10758
|
+
const dangling = danglingOrca(path, fs);
|
|
10759
|
+
if (dangling) {
|
|
10760
|
+
return {
|
|
10761
|
+
ok: false,
|
|
10762
|
+
why: `${dangling.link} links to ${dangling.target}, which is gone`,
|
|
10763
|
+
fix: ORCA_LINK_FIX
|
|
10764
|
+
};
|
|
10765
|
+
}
|
|
10766
|
+
if (!path.split(":").includes("/usr/bin")) {
|
|
10767
|
+
return { ok: false, why: "the engine's PATH has no /usr/bin", fix: "sleds engine install" };
|
|
10768
|
+
}
|
|
10769
|
+
if (r.status === 127) {
|
|
10770
|
+
return { ok: false, why: "not installed (no `orca` on the PATH)", fix: ORCA_LINK_FIX };
|
|
10771
|
+
}
|
|
10772
|
+
const said = `${r.stderr}
|
|
10773
|
+
${r.stdout}`.split(`
|
|
10774
|
+
`).map((l) => l.trim()).find(Boolean);
|
|
10775
|
+
return {
|
|
10776
|
+
ok: false,
|
|
10777
|
+
why: `\`orca --version\` failed: ${said ?? `exit ${r.status}`}`,
|
|
10778
|
+
fix: "reinstall Orca, or its shell command from Orca"
|
|
10779
|
+
};
|
|
10780
|
+
}
|
|
10781
|
+
var launcherPath2 = (promptFile) => `${promptFile.replace(/\.md$/, "")}.orca.sh`;
|
|
10782
|
+
function orcaRunner(exec, deps = {}) {
|
|
10783
|
+
const writeFile = deps.writeFile ?? ((p, t) => writeFileSync11(p, t, { mode: 384 }));
|
|
10784
|
+
const orca = (args, what) => {
|
|
10785
|
+
const r = exec("orca", args);
|
|
10786
|
+
const env = parseOrcaJson(r.stdout);
|
|
10787
|
+
const refused = orcaError(env);
|
|
10788
|
+
if (refused)
|
|
10789
|
+
throw new Error(`${what}: ${refused}`);
|
|
10790
|
+
if (r.status !== 0 || !env) {
|
|
10791
|
+
throw new Error(`${what} failed: ${r.stderr.trim() || r.stdout.trim() || `exit ${r.status}`}`);
|
|
10792
|
+
}
|
|
10793
|
+
return env;
|
|
10794
|
+
};
|
|
10795
|
+
return {
|
|
10796
|
+
name: "orca",
|
|
10797
|
+
makesWorktree: true,
|
|
10798
|
+
readsSession: true,
|
|
10799
|
+
start(o) {
|
|
10800
|
+
const created = orca([
|
|
10801
|
+
"worktree",
|
|
10802
|
+
"create",
|
|
10803
|
+
"--repo",
|
|
10804
|
+
`path:${o.repoPath}`,
|
|
10805
|
+
"--name",
|
|
10806
|
+
o.name,
|
|
10807
|
+
"--base-branch",
|
|
10808
|
+
`origin/${o.base ?? "main"}`,
|
|
10809
|
+
"--no-parent",
|
|
10810
|
+
"--json"
|
|
10811
|
+
], `orca worktree create ${o.name}`);
|
|
10812
|
+
const cwd = parseOrcaWorktree(created);
|
|
10813
|
+
if (!cwd) {
|
|
10814
|
+
throw new Error(`orca worktree create ${o.name} returned no worktree path; the agent was not started`);
|
|
10815
|
+
}
|
|
10816
|
+
const launcher = launcherPath2(o.promptFile);
|
|
10817
|
+
writeFile(launcher, orcaLauncher(o));
|
|
10818
|
+
const shown = orca([
|
|
10819
|
+
"terminal",
|
|
10820
|
+
"create",
|
|
10821
|
+
"--worktree",
|
|
10822
|
+
`path:${cwd}`,
|
|
10823
|
+
"--title",
|
|
10824
|
+
o.name,
|
|
10825
|
+
"--command",
|
|
10826
|
+
orcaCommand(launcher),
|
|
10827
|
+
"--json"
|
|
10828
|
+
], `orca terminal create ${o.name} (worktree ${cwd})`);
|
|
10829
|
+
const terminal = parseOrcaTerminal(shown);
|
|
10830
|
+
if (!terminal) {
|
|
10831
|
+
throw new Error(`orca terminal create ${o.name} started in ${cwd} but returned no terminal handle`);
|
|
10832
|
+
}
|
|
10833
|
+
const own = parseOrcaBranch(created);
|
|
10834
|
+
const h = { runner: "orca", id: terminal, cwd, ...own ? { own } : {} };
|
|
10835
|
+
return h;
|
|
10836
|
+
},
|
|
10837
|
+
status(h) {
|
|
10838
|
+
const r = exec("orca", ["terminal", "show", "--terminal", h.id, "--json"]);
|
|
10839
|
+
return orcaTerminalLive(r.status, r.stdout) ? "running" : "exited";
|
|
10840
|
+
},
|
|
10841
|
+
read(h) {
|
|
10842
|
+
const r = exec("orca", ["terminal", "read", "--terminal", h.id, "--limit", "200"]);
|
|
10843
|
+
return r.status === 0 ? r.stdout : "";
|
|
10844
|
+
},
|
|
10845
|
+
send(h, text) {
|
|
10846
|
+
const r = exec("orca", ["terminal", "send", "--terminal", h.id, "--text", text, "--enter"]);
|
|
10847
|
+
if (r.status !== 0) {
|
|
10848
|
+
throw new Error(`orca terminal send ${h.id} failed: ${r.stderr.trim() || `exit ${r.status}`}`);
|
|
10849
|
+
}
|
|
10850
|
+
},
|
|
10851
|
+
stop(h) {
|
|
10852
|
+
exec("orca", ["terminal", "close", "--terminal", h.id]);
|
|
10853
|
+
},
|
|
10854
|
+
watchHint(h) {
|
|
10855
|
+
return `orca terminal show --terminal ${h.id}`;
|
|
10856
|
+
},
|
|
10857
|
+
probe() {
|
|
10858
|
+
try {
|
|
10859
|
+
const p = orcaPreflight(exec, process.env.PATH ?? "", deps.fs);
|
|
10860
|
+
return p.ok ? null : `orca: ${p.why} — ${p.fix}`;
|
|
10861
|
+
} catch {
|
|
10862
|
+
return "orca: not installed";
|
|
10863
|
+
}
|
|
10864
|
+
},
|
|
10865
|
+
removeWorktree(h, repoPath) {
|
|
10866
|
+
orca(["worktree", "rm", "--worktree", `path:${h.cwd}`, "--json"], `orca worktree rm ${h.cwd}`);
|
|
10867
|
+
if (!h.own)
|
|
10868
|
+
return;
|
|
10869
|
+
const ref = `refs/heads/${h.own.branch}`;
|
|
10870
|
+
const r = exec("git", ["-C", repoPath, "update-ref", "-d", ref, h.own.head]);
|
|
10871
|
+
if (r.status === 0)
|
|
10872
|
+
return;
|
|
10873
|
+
const still = exec("git", ["-C", repoPath, "rev-parse", "--verify", "--quiet", ref]);
|
|
10874
|
+
if (still.status !== 0)
|
|
10875
|
+
return;
|
|
10876
|
+
return `kept Orca's branch ${h.own.branch}: ${r.stderr.trim() || `exit ${r.status}`}`;
|
|
10877
|
+
}
|
|
10878
|
+
};
|
|
10879
|
+
}
|
|
10880
|
+
|
|
10418
10881
|
// src/commands/doctor.ts
|
|
10419
10882
|
init_settings();
|
|
10420
10883
|
|
|
@@ -10432,7 +10895,7 @@ import {
|
|
|
10432
10895
|
writeFileSync as writeFileSync13
|
|
10433
10896
|
} from "node:fs";
|
|
10434
10897
|
import { hostname as hostname2 } from "node:os";
|
|
10435
|
-
import { join as
|
|
10898
|
+
import { join as join16, resolve as resolve8 } from "node:path";
|
|
10436
10899
|
|
|
10437
10900
|
// src/engine/beat.ts
|
|
10438
10901
|
var BEAT_EVERY_MS = 60000;
|
|
@@ -10444,7 +10907,7 @@ function beatBody(state, flags, host = {}, cap = {}) {
|
|
|
10444
10907
|
name: flags.name,
|
|
10445
10908
|
os: host.os ?? process.platform,
|
|
10446
10909
|
runner: flags.runner,
|
|
10447
|
-
version: host.version ?? "0.1.
|
|
10910
|
+
version: host.version ?? "0.1.44" ?? "dev",
|
|
10448
10911
|
running: live.length,
|
|
10449
10912
|
items: live.slice(0, BEAT_ITEMS).map((r) => `${r.item}/${r.node}`),
|
|
10450
10913
|
...cap.atOnce !== undefined ? { atOnce: cap.atOnce } : {},
|
|
@@ -10463,10 +10926,13 @@ function beatBody(state, flags, host = {}, cap = {}) {
|
|
|
10463
10926
|
}
|
|
10464
10927
|
async function postBeat(client, body) {
|
|
10465
10928
|
try {
|
|
10466
|
-
const r = await client.
|
|
10467
|
-
|
|
10929
|
+
const r = await client.postFull("/proxy/engine/beat", { ...body });
|
|
10930
|
+
if (r?.status === 200 && r.json?.ok === true)
|
|
10931
|
+
return { ok: true };
|
|
10932
|
+
const error = r?.json?.error;
|
|
10933
|
+
return r?.status === 409 && typeof error === "string" ? { ok: false, refused: error } : { ok: false };
|
|
10468
10934
|
} catch {
|
|
10469
|
-
return false;
|
|
10935
|
+
return { ok: false };
|
|
10470
10936
|
}
|
|
10471
10937
|
}
|
|
10472
10938
|
function beater(client, now, everyMs = BEAT_EVERY_MS) {
|
|
@@ -11234,7 +11700,7 @@ function jiraFromEnv(epic, gate, env = process.env) {
|
|
|
11234
11700
|
init_src();
|
|
11235
11701
|
|
|
11236
11702
|
// src/commands/permissionEvent.ts
|
|
11237
|
-
import { chmodSync as chmodSync3, mkdirSync as mkdirSync10, readFileSync as readFileSync10, renameSync as renameSync6, rmSync as rmSync3, writeFileSync as
|
|
11703
|
+
import { chmodSync as chmodSync3, mkdirSync as mkdirSync10, readFileSync as readFileSync10, renameSync as renameSync6, rmSync as rmSync3, writeFileSync as writeFileSync12 } from "node:fs";
|
|
11238
11704
|
|
|
11239
11705
|
// src/commands/seatEvent.ts
|
|
11240
11706
|
init_config();
|
|
@@ -11254,6 +11720,11 @@ var notificationType = (text) => {
|
|
|
11254
11720
|
|
|
11255
11721
|
// src/commands/permissionEvent.ts
|
|
11256
11722
|
var isObject12 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
11723
|
+
var NOT_A_TOOL = new Set(["askuserquestion", "exitplanmode", "enterplanmode"]);
|
|
11724
|
+
var isToolPermission = (message) => {
|
|
11725
|
+
const tool = /permission to use\s+(.+?)\.?\s*$/i.exec(message)?.[1];
|
|
11726
|
+
return !tool || !NOT_A_TOOL.has(tool.replace(/[\s_-]/g, "").toLowerCase());
|
|
11727
|
+
};
|
|
11257
11728
|
var readPermissionMark = (file) => {
|
|
11258
11729
|
try {
|
|
11259
11730
|
const raw = JSON.parse(readFileSync10(file, "utf8"));
|
|
@@ -11708,11 +12179,12 @@ function sledsSource(client, o) {
|
|
|
11708
12179
|
async function draft(row, item, why) {
|
|
11709
12180
|
const project = str6(row.project) ?? item;
|
|
11710
12181
|
const pending = isObject8(row.route) && isObject8(row.route.redraft) ? row.route.redraft : null;
|
|
12182
|
+
const runner = typeof o.runner === "function" ? o.runner() : o.runner;
|
|
11711
12183
|
const r = await client.postFull("/proxy/route", {
|
|
11712
12184
|
project,
|
|
11713
12185
|
draft: "take",
|
|
11714
12186
|
engine: o.engine,
|
|
11715
|
-
...
|
|
12187
|
+
...runner ? { runner } : {},
|
|
11716
12188
|
...o.leaseMin !== undefined ? { leaseMin: o.leaseMin } : {}
|
|
11717
12189
|
});
|
|
11718
12190
|
if (!r || r.status >= 300 || !r.json || r.json.ok === false) {
|
|
@@ -12136,7 +12608,36 @@ var DIALOG_ASKS = /^Do you want to .+\?$/;
|
|
|
12136
12608
|
var DIALOG_OPTION = /^(❯\s*)?(\d)\.\s+(.+)$/;
|
|
12137
12609
|
var DIALOG_FOOTER = /^(?:esc to |tab to |enter to |ctrl\+)/i;
|
|
12138
12610
|
var unbox = (line) => line.trimEnd().replace(/^\s*[│┃║]\s?/, "").replace(/\s*[│┃║]$/, "");
|
|
12611
|
+
var PICKER_CHIP = /^[☐☒✔✓□■]\s+\S/;
|
|
12612
|
+
var PICKER_OPTION = /^(?:Type something\.?|Chat about this|Submit answers?)$/i;
|
|
12613
|
+
var PICKER_FOOTER = /\benter to select\b|\bto navigate\b/i;
|
|
12614
|
+
var PICKER_ASKS = /^(?:Would you like to proceed\?|Ready to code\?)$/;
|
|
12615
|
+
var PICKER_REACH = 40;
|
|
12616
|
+
function choicePicker(screen) {
|
|
12617
|
+
const lines = screen.split(`
|
|
12618
|
+
`).map((l) => unbox(l).trim());
|
|
12619
|
+
let c = -1;
|
|
12620
|
+
for (let i = lines.length - 1;i >= 0; i--)
|
|
12621
|
+
if (/^❯\s*\d\.\s/.test(lines[i])) {
|
|
12622
|
+
c = i;
|
|
12623
|
+
break;
|
|
12624
|
+
}
|
|
12625
|
+
if (c < 0)
|
|
12626
|
+
return false;
|
|
12627
|
+
let from = c;
|
|
12628
|
+
while (from > 0 && c - from < PICKER_REACH && !/^⏺/.test(lines[from - 1]))
|
|
12629
|
+
from--;
|
|
12630
|
+
for (let i = from;i < lines.length; i++) {
|
|
12631
|
+
const t = lines[i];
|
|
12632
|
+
const choice = DIALOG_OPTION.exec(t)?.[3]?.trim() ?? "";
|
|
12633
|
+
if (PICKER_CHIP.test(t) || PICKER_ASKS.test(t) || PICKER_OPTION.test(choice) || i > c && PICKER_FOOTER.test(t))
|
|
12634
|
+
return true;
|
|
12635
|
+
}
|
|
12636
|
+
return false;
|
|
12637
|
+
}
|
|
12139
12638
|
function permissionDialog(screen) {
|
|
12639
|
+
if (choicePicker(screen))
|
|
12640
|
+
return null;
|
|
12140
12641
|
const raw = screen.split(`
|
|
12141
12642
|
`);
|
|
12142
12643
|
const lines = raw.map(unbox);
|
|
@@ -12211,10 +12712,10 @@ function providerFailure(screen) {
|
|
|
12211
12712
|
|
|
12212
12713
|
// src/engine/worktree.ts
|
|
12213
12714
|
init_src();
|
|
12214
|
-
import { appendFileSync, existsSync as
|
|
12215
|
-
import { dirname as dirname11, isAbsolute, join as
|
|
12715
|
+
import { appendFileSync, existsSync as existsSync13, mkdirSync as mkdirSync11 } from "node:fs";
|
|
12716
|
+
import { dirname as dirname11, isAbsolute, join as join15 } from "node:path";
|
|
12216
12717
|
function worktreePath(repoPath, name) {
|
|
12217
|
-
return
|
|
12718
|
+
return join15(repoPath, ".worktrees", name);
|
|
12218
12719
|
}
|
|
12219
12720
|
var fail = (what, r) => new Error(`${what} failed: ${(r.stderr || r.stdout).trim() || `exit ${r.status}`}`);
|
|
12220
12721
|
function defaultBranch(exec, repoPath) {
|
|
@@ -12235,7 +12736,7 @@ function defaultBranch(exec, repoPath) {
|
|
|
12235
12736
|
return "main";
|
|
12236
12737
|
}
|
|
12237
12738
|
var realWorktreeFs = {
|
|
12238
|
-
exists:
|
|
12739
|
+
exists: existsSync13,
|
|
12239
12740
|
append: (p, text) => {
|
|
12240
12741
|
mkdirSync11(dirname11(p), { recursive: true });
|
|
12241
12742
|
appendFileSync(p, text);
|
|
@@ -12249,7 +12750,7 @@ function ensureIgnored(exec, repoPath, fs = realWorktreeFs) {
|
|
|
12249
12750
|
if (common.status !== 0)
|
|
12250
12751
|
return;
|
|
12251
12752
|
const dir = common.stdout.trim();
|
|
12252
|
-
const exclude =
|
|
12753
|
+
const exclude = join15(isAbsolute(dir) ? dir : join15(repoPath, dir), "info", "exclude");
|
|
12253
12754
|
fs.append(exclude, `
|
|
12254
12755
|
# sleds engine: one worktree per route node
|
|
12255
12756
|
.worktrees/
|
|
@@ -12261,7 +12762,7 @@ function ensureWorktree(exec, repoPath, branch, path, base = "main", fs = realWo
|
|
|
12261
12762
|
return path;
|
|
12262
12763
|
}
|
|
12263
12764
|
function makeWorktree(exec, repoPath, branch, path, base, fs, log) {
|
|
12264
|
-
if (fs.exists(
|
|
12765
|
+
if (fs.exists(join15(path, ".git"))) {
|
|
12265
12766
|
fastForwardUntouched(exec, path, base, log);
|
|
12266
12767
|
return path;
|
|
12267
12768
|
}
|
|
@@ -12334,13 +12835,13 @@ function installArgv(command) {
|
|
|
12334
12835
|
return argv;
|
|
12335
12836
|
}
|
|
12336
12837
|
function lockfileInstall(path, exists) {
|
|
12337
|
-
if (exists(
|
|
12838
|
+
if (exists(join15(path, "pnpm-lock.yaml")))
|
|
12338
12839
|
return ["pnpm", "install", "--frozen-lockfile"];
|
|
12339
|
-
if (exists(
|
|
12840
|
+
if (exists(join15(path, "bun.lock")) || exists(join15(path, "bun.lockb")))
|
|
12340
12841
|
return ["bun", "install", "--frozen-lockfile"];
|
|
12341
|
-
if (exists(
|
|
12842
|
+
if (exists(join15(path, "yarn.lock")))
|
|
12342
12843
|
return ["yarn", "install", "--frozen-lockfile"];
|
|
12343
|
-
if (exists(
|
|
12844
|
+
if (exists(join15(path, "package-lock.json")))
|
|
12344
12845
|
return ["npm", "ci"];
|
|
12345
12846
|
return null;
|
|
12346
12847
|
}
|
|
@@ -12365,7 +12866,7 @@ function packInstallOf(pack, repo) {
|
|
|
12365
12866
|
return first;
|
|
12366
12867
|
}
|
|
12367
12868
|
function ensureInstalled(exec, path, fs, o = {}) {
|
|
12368
|
-
if (!fs.exists(
|
|
12869
|
+
if (!fs.exists(join15(path, "package.json")) || fs.exists(join15(path, "node_modules")))
|
|
12369
12870
|
return null;
|
|
12370
12871
|
const argv = installArgv(o.command) ?? lockfileInstall(path, fs.exists);
|
|
12371
12872
|
if (!argv)
|
|
@@ -12380,7 +12881,7 @@ function ensureInstalled(exec, path, fs, o = {}) {
|
|
|
12380
12881
|
return { argv, ok };
|
|
12381
12882
|
}
|
|
12382
12883
|
function ensureDetachedWorktree(exec, repoPath, path, base = "main", fs = realWorktreeFs) {
|
|
12383
|
-
if (fs.exists(
|
|
12884
|
+
if (fs.exists(join15(path, ".git")))
|
|
12384
12885
|
return path;
|
|
12385
12886
|
ensureIgnored(exec, repoPath, fs);
|
|
12386
12887
|
const f = exec("git", ["-C", repoPath, "fetch", "origin", base]);
|
|
@@ -12527,6 +13028,8 @@ async function tick(deps) {
|
|
|
12527
13028
|
deps.startBackoff = d.startBackoff;
|
|
12528
13029
|
deps.providerHold = d.providerHold;
|
|
12529
13030
|
deps.atOnce = d.atOnce;
|
|
13031
|
+
deps.runner = d.runner;
|
|
13032
|
+
deps.runnerRefused = d.runnerRefused;
|
|
12530
13033
|
}
|
|
12531
13034
|
removeEndedMcpConfigs(deps, made);
|
|
12532
13035
|
}
|
|
@@ -12569,6 +13072,7 @@ async function tickOnce(d) {
|
|
|
12569
13072
|
const changes = [];
|
|
12570
13073
|
const decided = [];
|
|
12571
13074
|
const save = () => d.writeState(state);
|
|
13075
|
+
await rereadRunner(d);
|
|
12572
13076
|
probeRunner(d);
|
|
12573
13077
|
if (d.startBackoff && --d.startBackoff.checks <= 0)
|
|
12574
13078
|
d.startBackoff = undefined;
|
|
@@ -13151,7 +13655,11 @@ async function settlePermission(d, runner, run, shown) {
|
|
|
13151
13655
|
run.permission = undefined;
|
|
13152
13656
|
return false;
|
|
13153
13657
|
}
|
|
13154
|
-
|
|
13658
|
+
let mark = markFile ? readPermissionMark(markFile) : null;
|
|
13659
|
+
if (mark && markFile && (!isToolPermission(mark.message) || runner.readsSession && choicePicker(shown))) {
|
|
13660
|
+
clearPermissionMark(markFile);
|
|
13661
|
+
mark = null;
|
|
13662
|
+
}
|
|
13155
13663
|
if (!dialog && !mark)
|
|
13156
13664
|
return false;
|
|
13157
13665
|
if (!dialog && runner.readsSession && !run.seenTicks)
|
|
@@ -13350,6 +13858,29 @@ async function rereadAtOnce(d) {
|
|
|
13350
13858
|
d.atOnce = next;
|
|
13351
13859
|
d.log(`at once now ${next}, from your engine profile`);
|
|
13352
13860
|
}
|
|
13861
|
+
async function rereadRunner(d) {
|
|
13862
|
+
if (!d.rereadRunner)
|
|
13863
|
+
return;
|
|
13864
|
+
let next = null;
|
|
13865
|
+
try {
|
|
13866
|
+
next = await d.rereadRunner(profileOf2(d));
|
|
13867
|
+
} catch {}
|
|
13868
|
+
if (next === null)
|
|
13869
|
+
return;
|
|
13870
|
+
if (typeof next === "string") {
|
|
13871
|
+
if (d.runnerRefused !== next)
|
|
13872
|
+
d.log(next);
|
|
13873
|
+
d.runnerRefused = next;
|
|
13874
|
+
return;
|
|
13875
|
+
}
|
|
13876
|
+
d.runnerRefused = undefined;
|
|
13877
|
+
if (next === d.runner)
|
|
13878
|
+
return;
|
|
13879
|
+
const was = d.runner.name;
|
|
13880
|
+
d.runner = next;
|
|
13881
|
+
d.runnerHold = undefined;
|
|
13882
|
+
d.log(`runner now ${next.name}, from your engine profile — new sessions start on it; running ones stay on ${was}`);
|
|
13883
|
+
}
|
|
13353
13884
|
async function rereadMachine(w, d) {
|
|
13354
13885
|
if (!w.reread)
|
|
13355
13886
|
return;
|
|
@@ -14894,8 +15425,8 @@ function commandRunner(template, deps = {}) {
|
|
|
14894
15425
|
|
|
14895
15426
|
// src/runners/conductor.ts
|
|
14896
15427
|
import { readFileSync as readFileSync11 } from "node:fs";
|
|
14897
|
-
var
|
|
14898
|
-
var
|
|
15428
|
+
var obj2 = (v) => v !== null && typeof v === "object" && !Array.isArray(v) ? v : null;
|
|
15429
|
+
var str9 = (v) => typeof v === "string" && v.length > 0 ? v : null;
|
|
14899
15430
|
var CONDUCTOR_API = "https://api.conductor.build";
|
|
14900
15431
|
var CLAUDE_MODELS = new Set([
|
|
14901
15432
|
"fable-5-1",
|
|
@@ -14952,7 +15483,7 @@ var splitCurl = (stdout) => {
|
|
|
14952
15483
|
const code = Number(stdout.slice(cut + 1).trim()) || 0;
|
|
14953
15484
|
let body = null;
|
|
14954
15485
|
try {
|
|
14955
|
-
body =
|
|
15486
|
+
body = obj2(JSON.parse(text));
|
|
14956
15487
|
} catch {
|
|
14957
15488
|
body = null;
|
|
14958
15489
|
}
|
|
@@ -14967,7 +15498,7 @@ var messageText = (content, depth = 0) => {
|
|
|
14967
15498
|
return content.map((c) => messageText(c, depth + 1)).filter(Boolean).join(`
|
|
14968
15499
|
`);
|
|
14969
15500
|
}
|
|
14970
|
-
const o =
|
|
15501
|
+
const o = obj2(content);
|
|
14971
15502
|
if (!o)
|
|
14972
15503
|
return String(content);
|
|
14973
15504
|
for (const k of ["text", "content", "message", "result"]) {
|
|
@@ -14975,12 +15506,12 @@ var messageText = (content, depth = 0) => {
|
|
|
14975
15506
|
if (t)
|
|
14976
15507
|
return t;
|
|
14977
15508
|
}
|
|
14978
|
-
return
|
|
15509
|
+
return str9(o.type) ? `(${o.type}${str9(o.name) ? ` ${o.name}` : ""})` : "";
|
|
14979
15510
|
};
|
|
14980
15511
|
var renderTranscript = (messages, keep = 40, clip = 2000) => messages.slice(-keep).map((m) => {
|
|
14981
15512
|
const t = messageText(m.content).trim();
|
|
14982
15513
|
const body = t.length > clip ? `${t.slice(0, clip)}…` : t;
|
|
14983
|
-
return body ? `[${
|
|
15514
|
+
return body ? `[${str9(m.type) ?? "message"}] ${body}` : "";
|
|
14984
15515
|
}).filter(Boolean).join(`
|
|
14985
15516
|
|
|
14986
15517
|
`);
|
|
@@ -15010,7 +15541,7 @@ function conductorRunner(exec, deps = {}) {
|
|
|
15010
15541
|
}
|
|
15011
15542
|
const res = splitCurl(r.stdout);
|
|
15012
15543
|
if (res.code < 200 || res.code >= 300 || !res.body) {
|
|
15013
|
-
const why =
|
|
15544
|
+
const why = str9(res.body?.userMessage) ?? str9(res.body?.message) ?? (res.text.trim().slice(0, 300) || "no body");
|
|
15014
15545
|
throw new Error(`conductor ${method} ${path}: HTTP ${res.code}: ${why}`);
|
|
15015
15546
|
}
|
|
15016
15547
|
return res.body;
|
|
@@ -15040,15 +15571,15 @@ function conductorRunner(exec, deps = {}) {
|
|
|
15040
15571
|
...effort ? { effort } : {},
|
|
15041
15572
|
message: prompt
|
|
15042
15573
|
});
|
|
15043
|
-
const workspace =
|
|
15044
|
-
const session =
|
|
15574
|
+
const workspace = str9(created.workspaceId);
|
|
15575
|
+
const session = str9(created.sessionId);
|
|
15045
15576
|
if (!workspace || !session) {
|
|
15046
15577
|
throw new Error(`conductor: workspace create for ${o.name} returned no workspace or session id`);
|
|
15047
15578
|
}
|
|
15048
|
-
if (!
|
|
15579
|
+
if (!obj2(created.initialMessage)) {
|
|
15049
15580
|
api("POST", `/v0/sessions/${session}/messages`, { message: prompt });
|
|
15050
15581
|
}
|
|
15051
|
-
const link =
|
|
15582
|
+
const link = str9(created.deepLink);
|
|
15052
15583
|
return {
|
|
15053
15584
|
runner: "conductor",
|
|
15054
15585
|
id: `${workspace}/${session}`,
|
|
@@ -15070,8 +15601,8 @@ function conductorRunner(exec, deps = {}) {
|
|
|
15070
15601
|
let line = "";
|
|
15071
15602
|
try {
|
|
15072
15603
|
const s = api("GET", `/v0/sessions/${session}/status`);
|
|
15073
|
-
state =
|
|
15074
|
-
const err =
|
|
15604
|
+
state = str9(s.status) ?? "unknown";
|
|
15605
|
+
const err = str9(s.lastError) ?? str9(s.errorMessage);
|
|
15075
15606
|
line = state === "working" ? `conductor session: working (checked ${new Date(now()).toISOString()})` : `conductor session: ${state}${err ? ` — ${err}` : ""}`;
|
|
15076
15607
|
} catch (e) {
|
|
15077
15608
|
line = `conductor session: status unread (${e instanceof Error ? e.message : String(e)})`;
|
|
@@ -15183,8 +15714,8 @@ function headlessRunner(deps = {}) {
|
|
|
15183
15714
|
// src/runners/managed.ts
|
|
15184
15715
|
init_src();
|
|
15185
15716
|
import { readFileSync as readFileSync12 } from "node:fs";
|
|
15186
|
-
var
|
|
15187
|
-
var
|
|
15717
|
+
var obj3 = (v) => v !== null && typeof v === "object" && !Array.isArray(v) ? v : null;
|
|
15718
|
+
var str10 = (v) => typeof v === "string" && v.length > 0 ? v : null;
|
|
15188
15719
|
var ANTHROPIC_API = "https://api.anthropic.com";
|
|
15189
15720
|
var MANAGED_BETA = "managed-agents-2026-04-01";
|
|
15190
15721
|
var DEFAULT_BUDGET_USD = 25;
|
|
@@ -15259,7 +15790,7 @@ var lastIdle = (events) => {
|
|
|
15259
15790
|
return null;
|
|
15260
15791
|
};
|
|
15261
15792
|
var pendingAsks = (events, idle) => {
|
|
15262
|
-
const ids =
|
|
15793
|
+
const ids = obj3(idle?.stop_reason)?.event_ids;
|
|
15263
15794
|
if (!Array.isArray(ids))
|
|
15264
15795
|
return [];
|
|
15265
15796
|
const want = new Set(ids.filter((x) => typeof x === "string"));
|
|
@@ -15280,7 +15811,7 @@ var eventMessages = (events) => {
|
|
|
15280
15811
|
case "agent.custom_tool_use":
|
|
15281
15812
|
out.push({
|
|
15282
15813
|
type: "tool",
|
|
15283
|
-
content: `(${
|
|
15814
|
+
content: `(${str10(e.name) ?? "tool"}${e.evaluated_permission === "ask" ? " — waiting on a yes" : ""})`
|
|
15284
15815
|
});
|
|
15285
15816
|
break;
|
|
15286
15817
|
case "session.error":
|
|
@@ -15291,9 +15822,9 @@ var eventMessages = (events) => {
|
|
|
15291
15822
|
return out;
|
|
15292
15823
|
};
|
|
15293
15824
|
var statusLine2 = (session, events, now) => {
|
|
15294
|
-
const status =
|
|
15295
|
-
const spent = usd4(
|
|
15296
|
-
const cap = usd4(
|
|
15825
|
+
const status = str10(session.status) ?? "unknown";
|
|
15826
|
+
const spent = usd4(obj3(obj3(session.usage)?.list_cost)?.amount);
|
|
15827
|
+
const cap = usd4(obj3(obj3(session.budget)?.max_list_cost)?.amount);
|
|
15297
15828
|
const money = spent ? ` · ${spent}${cap ? ` of ${cap}` : ""}` : "";
|
|
15298
15829
|
if (status === "running" || status === "rescheduling") {
|
|
15299
15830
|
return `managed session: ${status} (checked ${new Date(now).toISOString()})${money}`;
|
|
@@ -15301,10 +15832,10 @@ var statusLine2 = (session, events, now) => {
|
|
|
15301
15832
|
if (status !== "idle")
|
|
15302
15833
|
return `managed session: ${status}${money}`;
|
|
15303
15834
|
const idle = lastIdle(events);
|
|
15304
|
-
const why =
|
|
15835
|
+
const why = str10(obj3(idle?.stop_reason)?.type);
|
|
15305
15836
|
switch (why) {
|
|
15306
15837
|
case "requires_action": {
|
|
15307
|
-
const tools = pendingAsks(events, idle).map((e) =>
|
|
15838
|
+
const tools = pendingAsks(events, idle).map((e) => str10(e.name) ?? "a tool").join(", ");
|
|
15308
15839
|
return `managed session: idle — waiting on a yes for ${tools || "a tool call"} (answer yes to allow, anything else denies)${money}`;
|
|
15309
15840
|
}
|
|
15310
15841
|
case "budget_reached":
|
|
@@ -15315,7 +15846,7 @@ var statusLine2 = (session, events, now) => {
|
|
|
15315
15846
|
return `managed session: idle — its turn ended${money}`;
|
|
15316
15847
|
}
|
|
15317
15848
|
};
|
|
15318
|
-
var
|
|
15849
|
+
var YES2 = /^\s*(?:y|yes|allow|ok|okay|go|go ahead|approve[d]?)\b/i;
|
|
15319
15850
|
function managedRunner(exec, deps = {}) {
|
|
15320
15851
|
const env = deps.env ?? process.env;
|
|
15321
15852
|
const readFile = deps.readFile ?? ((p) => readFileSync12(p, "utf8"));
|
|
@@ -15338,7 +15869,7 @@ function managedRunner(exec, deps = {}) {
|
|
|
15338
15869
|
}
|
|
15339
15870
|
const res = splitCurl(r.stdout);
|
|
15340
15871
|
if (res.code < 200 || res.code >= 300 || !res.body) {
|
|
15341
|
-
const why =
|
|
15872
|
+
const why = str10(obj3(res.body?.error)?.message) ?? str10(res.body?.message) ?? (res.text.trim().slice(0, 300) || "no body");
|
|
15342
15873
|
throw new Error(`managed ${method} ${path}: HTTP ${res.code}: ${why}`);
|
|
15343
15874
|
}
|
|
15344
15875
|
return res.body;
|
|
@@ -15350,7 +15881,7 @@ function managedRunner(exec, deps = {}) {
|
|
|
15350
15881
|
const got = api("GET", `/v1/sessions/${id}/events?limit=1000${page ? `&page=${encodeURIComponent(page)}` : ""}`);
|
|
15351
15882
|
const data = Array.isArray(got.data) ? got.data : [];
|
|
15352
15883
|
out.push(...data);
|
|
15353
|
-
page =
|
|
15884
|
+
page = str10(got.next_page);
|
|
15354
15885
|
if (!page || data.length === 0)
|
|
15355
15886
|
break;
|
|
15356
15887
|
}
|
|
@@ -15413,7 +15944,7 @@ function managedRunner(exec, deps = {}) {
|
|
|
15413
15944
|
}
|
|
15414
15945
|
]
|
|
15415
15946
|
});
|
|
15416
|
-
const id =
|
|
15947
|
+
const id = str10(created.id);
|
|
15417
15948
|
if (!id)
|
|
15418
15949
|
throw new Error(`managed: session create for ${o.name} returned no id`);
|
|
15419
15950
|
return { runner: "managed", id, cwd: o.repoPath, log: consoleUrl(id) };
|
|
@@ -15438,9 +15969,9 @@ ${renderTranscript(eventMessages(all))}`;
|
|
|
15438
15969
|
const session = api("GET", `/v1/sessions/${h.id}`);
|
|
15439
15970
|
const all = session.status === "idle" ? events(h.id) : [];
|
|
15440
15971
|
const idle = lastIdle(all);
|
|
15441
|
-
const asks =
|
|
15972
|
+
const asks = str10(obj3(idle?.stop_reason)?.type) === "requires_action" ? pendingAsks(all, idle) : [];
|
|
15442
15973
|
if (asks.length) {
|
|
15443
|
-
const allow =
|
|
15974
|
+
const allow = YES2.test(text);
|
|
15444
15975
|
api("POST", `/v1/sessions/${h.id}/events`, {
|
|
15445
15976
|
events: asks.map((e) => ({
|
|
15446
15977
|
type: "user.tool_confirmation",
|
|
@@ -15487,156 +16018,6 @@ ${renderTranscript(eventMessages(all))}`;
|
|
|
15487
16018
|
};
|
|
15488
16019
|
}
|
|
15489
16020
|
|
|
15490
|
-
// src/runners/orca.ts
|
|
15491
|
-
import { writeFileSync as writeFileSync12 } from "node:fs";
|
|
15492
|
-
var obj3 = (v) => v !== null && typeof v === "object" && !Array.isArray(v) ? v : null;
|
|
15493
|
-
var str10 = (v) => typeof v === "string" && v.length > 0 ? v : null;
|
|
15494
|
-
var parseOrcaJson = (stdout) => {
|
|
15495
|
-
try {
|
|
15496
|
-
return obj3(JSON.parse(stdout));
|
|
15497
|
-
} catch {
|
|
15498
|
-
return null;
|
|
15499
|
-
}
|
|
15500
|
-
};
|
|
15501
|
-
var orcaError = (env) => {
|
|
15502
|
-
if (!env || env.ok !== false)
|
|
15503
|
-
return null;
|
|
15504
|
-
const e = obj3(env.error);
|
|
15505
|
-
const message = str10(e?.message) ?? "Orca refused the call";
|
|
15506
|
-
const code = str10(e?.code);
|
|
15507
|
-
return code ? `${message} (${code})` : message;
|
|
15508
|
-
};
|
|
15509
|
-
var parseOrcaWorktree = (env) => {
|
|
15510
|
-
const result = obj3(env.result) ?? {};
|
|
15511
|
-
return str10(obj3(result.worktree)?.path) ?? str10(result.worktreePath) ?? str10(result.path);
|
|
15512
|
-
};
|
|
15513
|
-
var parseOrcaBranch = (env) => {
|
|
15514
|
-
const wt = obj3(obj3(env.result)?.worktree);
|
|
15515
|
-
const branch = str10(wt?.branch)?.replace(/^refs\/heads\//, "");
|
|
15516
|
-
const head = str10(wt?.head);
|
|
15517
|
-
return branch && head ? { branch, head } : null;
|
|
15518
|
-
};
|
|
15519
|
-
var parseOrcaTerminal = (env) => {
|
|
15520
|
-
const result = obj3(env.result) ?? {};
|
|
15521
|
-
return str10(obj3(result.terminal)?.handle) ?? str10(result.handle) ?? str10(result.terminalHandle) ?? null;
|
|
15522
|
-
};
|
|
15523
|
-
var orcaLauncher = (o) => `#!/bin/sh
|
|
15524
|
-
# sleds engine: ${o.name}
|
|
15525
|
-
exec ${agentShellCommand(o)}
|
|
15526
|
-
`;
|
|
15527
|
-
var orcaCommand = (launcher) => `sh ${shq2(launcher)}; exit`;
|
|
15528
|
-
var GONE = /exit|clos|gone|dead|stopp|kill/i;
|
|
15529
|
-
var orcaTerminalLive = (status, stdout) => {
|
|
15530
|
-
const env = parseOrcaJson(stdout);
|
|
15531
|
-
if (str10(obj3(env?.error)?.code) === "runtime_unavailable")
|
|
15532
|
-
return true;
|
|
15533
|
-
if (status !== 0 || !env || env.ok === false)
|
|
15534
|
-
return false;
|
|
15535
|
-
const t = obj3(obj3(env.result)?.terminal) ?? obj3(env.result) ?? {};
|
|
15536
|
-
if (typeof t.status === "string" && GONE.test(t.status))
|
|
15537
|
-
return false;
|
|
15538
|
-
if (t.exited === true || t.alive === false)
|
|
15539
|
-
return false;
|
|
15540
|
-
if (typeof t.exitCode === "number")
|
|
15541
|
-
return false;
|
|
15542
|
-
if (obj3(t.exitCause))
|
|
15543
|
-
return false;
|
|
15544
|
-
if (t.orphaned === true && t.writable === false)
|
|
15545
|
-
return false;
|
|
15546
|
-
return true;
|
|
15547
|
-
};
|
|
15548
|
-
var launcherPath2 = (promptFile) => `${promptFile.replace(/\.md$/, "")}.orca.sh`;
|
|
15549
|
-
function orcaRunner(exec, deps = {}) {
|
|
15550
|
-
const writeFile = deps.writeFile ?? ((p, t) => writeFileSync12(p, t, { mode: 384 }));
|
|
15551
|
-
const orca = (args, what) => {
|
|
15552
|
-
const r = exec("orca", args);
|
|
15553
|
-
const env = parseOrcaJson(r.stdout);
|
|
15554
|
-
const refused = orcaError(env);
|
|
15555
|
-
if (refused)
|
|
15556
|
-
throw new Error(`${what}: ${refused}`);
|
|
15557
|
-
if (r.status !== 0 || !env) {
|
|
15558
|
-
throw new Error(`${what} failed: ${r.stderr.trim() || r.stdout.trim() || `exit ${r.status}`}`);
|
|
15559
|
-
}
|
|
15560
|
-
return env;
|
|
15561
|
-
};
|
|
15562
|
-
return {
|
|
15563
|
-
name: "orca",
|
|
15564
|
-
makesWorktree: true,
|
|
15565
|
-
readsSession: true,
|
|
15566
|
-
start(o) {
|
|
15567
|
-
const created = orca([
|
|
15568
|
-
"worktree",
|
|
15569
|
-
"create",
|
|
15570
|
-
"--repo",
|
|
15571
|
-
`path:${o.repoPath}`,
|
|
15572
|
-
"--name",
|
|
15573
|
-
o.name,
|
|
15574
|
-
"--base-branch",
|
|
15575
|
-
`origin/${o.base ?? "main"}`,
|
|
15576
|
-
"--no-parent",
|
|
15577
|
-
"--json"
|
|
15578
|
-
], `orca worktree create ${o.name}`);
|
|
15579
|
-
const cwd = parseOrcaWorktree(created);
|
|
15580
|
-
if (!cwd) {
|
|
15581
|
-
throw new Error(`orca worktree create ${o.name} returned no worktree path; the agent was not started`);
|
|
15582
|
-
}
|
|
15583
|
-
const launcher = launcherPath2(o.promptFile);
|
|
15584
|
-
writeFile(launcher, orcaLauncher(o));
|
|
15585
|
-
const shown = orca([
|
|
15586
|
-
"terminal",
|
|
15587
|
-
"create",
|
|
15588
|
-
"--worktree",
|
|
15589
|
-
`path:${cwd}`,
|
|
15590
|
-
"--title",
|
|
15591
|
-
o.name,
|
|
15592
|
-
"--command",
|
|
15593
|
-
orcaCommand(launcher),
|
|
15594
|
-
"--json"
|
|
15595
|
-
], `orca terminal create ${o.name} (worktree ${cwd})`);
|
|
15596
|
-
const terminal = parseOrcaTerminal(shown);
|
|
15597
|
-
if (!terminal) {
|
|
15598
|
-
throw new Error(`orca terminal create ${o.name} started in ${cwd} but returned no terminal handle`);
|
|
15599
|
-
}
|
|
15600
|
-
const own = parseOrcaBranch(created);
|
|
15601
|
-
const h = { runner: "orca", id: terminal, cwd, ...own ? { own } : {} };
|
|
15602
|
-
return h;
|
|
15603
|
-
},
|
|
15604
|
-
status(h) {
|
|
15605
|
-
const r = exec("orca", ["terminal", "show", "--terminal", h.id, "--json"]);
|
|
15606
|
-
return orcaTerminalLive(r.status, r.stdout) ? "running" : "exited";
|
|
15607
|
-
},
|
|
15608
|
-
read(h) {
|
|
15609
|
-
const r = exec("orca", ["terminal", "read", "--terminal", h.id, "--limit", "200"]);
|
|
15610
|
-
return r.status === 0 ? r.stdout : "";
|
|
15611
|
-
},
|
|
15612
|
-
send(h, text) {
|
|
15613
|
-
const r = exec("orca", ["terminal", "send", "--terminal", h.id, "--text", text, "--enter"]);
|
|
15614
|
-
if (r.status !== 0) {
|
|
15615
|
-
throw new Error(`orca terminal send ${h.id} failed: ${r.stderr.trim() || `exit ${r.status}`}`);
|
|
15616
|
-
}
|
|
15617
|
-
},
|
|
15618
|
-
stop(h) {
|
|
15619
|
-
exec("orca", ["terminal", "close", "--terminal", h.id]);
|
|
15620
|
-
},
|
|
15621
|
-
watchHint(h) {
|
|
15622
|
-
return `orca terminal show --terminal ${h.id}`;
|
|
15623
|
-
},
|
|
15624
|
-
removeWorktree(h, repoPath) {
|
|
15625
|
-
orca(["worktree", "rm", "--worktree", `path:${h.cwd}`, "--json"], `orca worktree rm ${h.cwd}`);
|
|
15626
|
-
if (!h.own)
|
|
15627
|
-
return;
|
|
15628
|
-
const ref = `refs/heads/${h.own.branch}`;
|
|
15629
|
-
const r = exec("git", ["-C", repoPath, "update-ref", "-d", ref, h.own.head]);
|
|
15630
|
-
if (r.status === 0)
|
|
15631
|
-
return;
|
|
15632
|
-
const still = exec("git", ["-C", repoPath, "rev-parse", "--verify", "--quiet", ref]);
|
|
15633
|
-
if (still.status !== 0)
|
|
15634
|
-
return;
|
|
15635
|
-
return `kept Orca's branch ${h.own.branch}: ${r.stderr.trim() || `exit ${r.status}`}`;
|
|
15636
|
-
}
|
|
15637
|
-
};
|
|
15638
|
-
}
|
|
15639
|
-
|
|
15640
16021
|
// src/runners/tmux.ts
|
|
15641
16022
|
var tmuxSession = (name) => `=${name}`;
|
|
15642
16023
|
var tmuxPane = (name) => `=${name}:`;
|
|
@@ -15997,7 +16378,7 @@ function flagsFromProfile(flags, given, profile, home) {
|
|
|
15997
16378
|
}
|
|
15998
16379
|
}
|
|
15999
16380
|
if (profile.reposDir && !has("repos-dir") && !has("repo")) {
|
|
16000
|
-
out.reposDir = splitReposDirs(profile.reposDir).map((d) => home && /^~(\/|$)/.test(d) ?
|
|
16381
|
+
out.reposDir = splitReposDirs(profile.reposDir).map((d) => home && /^~(\/|$)/.test(d) ? join16(home, d.slice(1)) : d).join(":");
|
|
16001
16382
|
used.push(`repos ${profile.reposDir}`);
|
|
16002
16383
|
}
|
|
16003
16384
|
if (profile.mcpStrict !== undefined && !has("strict-mcp") && !has("no-strict-mcp")) {
|
|
@@ -16127,12 +16508,12 @@ function memberMcpConfigs(o, cwd) {
|
|
|
16127
16508
|
});
|
|
16128
16509
|
const claudeDir = (o.env ?? process.env).CLAUDE_CONFIG_DIR;
|
|
16129
16510
|
return [
|
|
16130
|
-
read(claudeDir ?
|
|
16131
|
-
...cwd ? [read(
|
|
16511
|
+
read(claudeDir ? join16(claudeDir, ".claude.json") : join16(o.home, ".claude.json")),
|
|
16512
|
+
...cwd ? [read(join16(cwd, ".mcp.json"))] : []
|
|
16132
16513
|
];
|
|
16133
16514
|
}
|
|
16134
16515
|
function engineStateDir(o, org, seat) {
|
|
16135
|
-
return seatStateDir(orgStateDir(o.stateDir ??
|
|
16516
|
+
return seatStateDir(orgStateDir(o.stateDir ?? join16(configDir(o.home, o.env ?? process.env), "engine"), org), seat);
|
|
16136
16517
|
}
|
|
16137
16518
|
function engineSeat(o, org) {
|
|
16138
16519
|
try {
|
|
@@ -16159,11 +16540,11 @@ function engineDeps(o, flags, source, runner, stateName) {
|
|
|
16159
16540
|
const stateDir = engineStateDir(o, flags.org, flags.seat);
|
|
16160
16541
|
const file = statePath(stateDir, stateName);
|
|
16161
16542
|
const mcpOrg = flags.org && engineOrg(o, flags.org).seated ? flags.org : undefined;
|
|
16162
|
-
const repo = flags.repo ?
|
|
16163
|
-
const reposDirs = splitReposDirs(flags.reposDir).map((d) =>
|
|
16543
|
+
const repo = flags.repo ? resolve8(cwd, flags.repo) : undefined;
|
|
16544
|
+
const reposDirs = splitReposDirs(flags.reposDir).map((d) => resolve8(cwd, d));
|
|
16164
16545
|
const { branch, prTitle } = flags;
|
|
16165
|
-
const hooksDir =
|
|
16166
|
-
const permissionMark = (name) =>
|
|
16546
|
+
const hooksDir = join16(stateDir, "hooks");
|
|
16547
|
+
const permissionMark = (name) => join16(hooksDir, `${name}.permission.json`);
|
|
16167
16548
|
const withHook = (name, settings) => applyPermissionHook(settings, [...o.sledsArgv ?? ["sleds"], "permission-event", "--mark", permissionMark(name)].map(shq2).join(" "));
|
|
16168
16549
|
return {
|
|
16169
16550
|
source,
|
|
@@ -16188,9 +16569,9 @@ function engineDeps(o, flags, source, runner, stateName) {
|
|
|
16188
16569
|
sandbox: true,
|
|
16189
16570
|
writeSandboxSettings: o.writeSandboxSettings ?? ((name, cwd) => {
|
|
16190
16571
|
const gitCommonDir = gitCommonDirOf(exec, cwd);
|
|
16191
|
-
const dir =
|
|
16572
|
+
const dir = join16(stateDir, "sandbox");
|
|
16192
16573
|
mkdirSync12(dir, { recursive: true, mode: 448 });
|
|
16193
|
-
const p =
|
|
16574
|
+
const p = join16(dir, `${name}.json`);
|
|
16194
16575
|
const walls = engineSandboxSettings(o.home, o.env ?? process.env, gitCommonDir, [
|
|
16195
16576
|
...SANDBOX_DEFAULT_ALLOW,
|
|
16196
16577
|
...flags.sandboxAllow ?? []
|
|
@@ -16204,7 +16585,7 @@ function engineDeps(o, flags, source, runner, stateName) {
|
|
|
16204
16585
|
} : {},
|
|
16205
16586
|
writeHookSettings: (name) => {
|
|
16206
16587
|
mkdirSync12(hooksDir, { recursive: true, mode: 448 });
|
|
16207
|
-
const p =
|
|
16588
|
+
const p = join16(hooksDir, `${name}.json`);
|
|
16208
16589
|
writeFileSync13(p, `${JSON.stringify(withHook(name, {}), null, 2)}
|
|
16209
16590
|
`, { mode: 384 });
|
|
16210
16591
|
chmodSync4(p, 384);
|
|
@@ -16231,9 +16612,9 @@ function engineDeps(o, flags, source, runner, stateName) {
|
|
|
16231
16612
|
exists
|
|
16232
16613
|
}),
|
|
16233
16614
|
writeMcpConfig: o.writeMcpConfig ?? ((name, runToken, worktree) => {
|
|
16234
|
-
const dir =
|
|
16615
|
+
const dir = join16(stateDir, "mcp");
|
|
16235
16616
|
mkdirSync12(dir, { recursive: true, mode: 448 });
|
|
16236
|
-
const p =
|
|
16617
|
+
const p = join16(dir, `${name}.json`);
|
|
16237
16618
|
let others = {};
|
|
16238
16619
|
if (flags.strictMcp && flags.mcpAllow?.length) {
|
|
16239
16620
|
const r = mcpPassThrough(flags.mcpAllow, memberMcpConfigs(o, worktree));
|
|
@@ -16249,9 +16630,9 @@ function engineDeps(o, flags, source, runner, stateName) {
|
|
|
16249
16630
|
}),
|
|
16250
16631
|
removeMcpConfig: o.removeMcpConfig ?? ((p) => rmSync4(p, { force: true })),
|
|
16251
16632
|
writePrompt: o.writePrompt ?? ((name, text) => {
|
|
16252
|
-
const dir =
|
|
16633
|
+
const dir = join16(stateDir, "prompts");
|
|
16253
16634
|
mkdirSync12(dir, { recursive: true, mode: 448 });
|
|
16254
|
-
const p =
|
|
16635
|
+
const p = join16(dir, `${name}.md`);
|
|
16255
16636
|
writeFileSync13(p, text, { mode: 384 });
|
|
16256
16637
|
return p;
|
|
16257
16638
|
}),
|
|
@@ -16278,8 +16659,8 @@ function engineDeps(o, flags, source, runner, stateName) {
|
|
|
16278
16659
|
}
|
|
16279
16660
|
function sandboxDenyRead(home, env) {
|
|
16280
16661
|
return {
|
|
16281
|
-
dirs: [...new Set([configDir(home, env),
|
|
16282
|
-
files: [...new Set([tokenPath(home, env),
|
|
16662
|
+
dirs: [...new Set([configDir(home, env), join16(home, ".config", "sleds")])],
|
|
16663
|
+
files: [...new Set([tokenPath(home, env), join16(home, ".sleds-token")])]
|
|
16283
16664
|
};
|
|
16284
16665
|
}
|
|
16285
16666
|
function engineSandboxSettings(home, env, gitCommonDir, allow) {
|
|
@@ -16294,7 +16675,7 @@ function gitCommonDirOf(exec, cwd) {
|
|
|
16294
16675
|
const dir = r.status === 0 ? r.stdout.trim() : "";
|
|
16295
16676
|
if (!dir)
|
|
16296
16677
|
throw new Error(`git has no common dir for ${cwd}, so the sandbox has nowhere to commit`);
|
|
16297
|
-
return
|
|
16678
|
+
return resolve8(cwd, dir);
|
|
16298
16679
|
}
|
|
16299
16680
|
function pinOrg2(configJson, org) {
|
|
16300
16681
|
if (!org)
|
|
@@ -16336,13 +16717,14 @@ async function driveEngine(o, flags, deps, until = {}) {
|
|
|
16336
16717
|
runner: deps.runner.name,
|
|
16337
16718
|
atOnce: flags.atOnce,
|
|
16338
16719
|
interval: flags.interval,
|
|
16339
|
-
cliVersion: "0.1.
|
|
16720
|
+
cliVersion: "0.1.44"
|
|
16340
16721
|
};
|
|
16341
16722
|
const beat = (checkedAt) => {
|
|
16342
16723
|
const m = deps.machine;
|
|
16343
16724
|
try {
|
|
16344
16725
|
writeLock(lockFile, checkedAt === undefined ? lock : {
|
|
16345
16726
|
...lock,
|
|
16727
|
+
runner: deps.runner.name,
|
|
16346
16728
|
atOnce: deps.atOnce,
|
|
16347
16729
|
checkedAt,
|
|
16348
16730
|
nextCheckAt: checkedAt + flags.interval * 1000,
|
|
@@ -16647,9 +17029,8 @@ async function enginePauseCmd(o) {
|
|
|
16647
17029
|
|
|
16648
17030
|
// src/commands/engineInstall.ts
|
|
16649
17031
|
init_config();
|
|
16650
|
-
init_state();
|
|
16651
17032
|
import {
|
|
16652
|
-
existsSync as
|
|
17033
|
+
existsSync as existsSync15,
|
|
16653
17034
|
mkdirSync as mkdirSync14,
|
|
16654
17035
|
readFileSync as readFileSync14,
|
|
16655
17036
|
readdirSync as readdirSync2,
|
|
@@ -16658,12 +17039,13 @@ import {
|
|
|
16658
17039
|
writeFileSync as writeFileSync15
|
|
16659
17040
|
} from "node:fs";
|
|
16660
17041
|
import { hostname as hostname3 } from "node:os";
|
|
16661
|
-
import { basename as basename4, dirname as dirname12, isAbsolute as isAbsolute2, join as
|
|
17042
|
+
import { basename as basename4, dirname as dirname12, isAbsolute as isAbsolute2, join as join19 } from "node:path";
|
|
17043
|
+
init_state();
|
|
16662
17044
|
|
|
16663
17045
|
// src/schedule.ts
|
|
16664
17046
|
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
16665
|
-
import { existsSync as
|
|
16666
|
-
import { join as
|
|
17047
|
+
import { existsSync as existsSync14, mkdirSync as mkdirSync13, rmSync as rmSync5, writeFileSync as writeFileSync14 } from "node:fs";
|
|
17048
|
+
import { join as join17 } from "node:path";
|
|
16667
17049
|
var REVERT_LABEL = "ai.sleds.revert";
|
|
16668
17050
|
var defaultExec2 = (cmd, args) => {
|
|
16669
17051
|
const r = spawnSync4(cmd, [...args], { stdio: ["ignore", "ignore", "pipe"], encoding: "utf8" });
|
|
@@ -16671,7 +17053,7 @@ var defaultExec2 = (cmd, args) => {
|
|
|
16671
17053
|
return { ok: false, stderr: r.error.message };
|
|
16672
17054
|
return { ok: r.status === 0, stderr: r.stderr ?? "" };
|
|
16673
17055
|
};
|
|
16674
|
-
var plistPath = (home) =>
|
|
17056
|
+
var plistPath = (home) => join17(home, "Library", "LaunchAgents", `${REVERT_LABEL}.plist`);
|
|
16675
17057
|
var xmlEscape2 = (s) => s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
16676
17058
|
var buildLaunchdPlist = (sledsArgv, deadline) => {
|
|
16677
17059
|
const args = [...sledsArgv, "off"].map((a) => ` <string>${xmlEscape2(a)}</string>`).join(`
|
|
@@ -16710,7 +17092,7 @@ var scheduleRevert = (d, deadlineMs) => {
|
|
|
16710
17092
|
if (d.platform === "darwin") {
|
|
16711
17093
|
const p = plistPath(d.home);
|
|
16712
17094
|
try {
|
|
16713
|
-
mkdirSync13(
|
|
17095
|
+
mkdirSync13(join17(d.home, "Library", "LaunchAgents"), { recursive: true });
|
|
16714
17096
|
d.exec("launchctl", ["unload", p]);
|
|
16715
17097
|
writeFileSync14(p, buildLaunchdPlist(d.sledsArgv, deadline));
|
|
16716
17098
|
const r = d.exec("launchctl", ["load", p]);
|
|
@@ -16739,7 +17121,7 @@ var scheduleRevert = (d, deadlineMs) => {
|
|
|
16739
17121
|
var cancelRevert = (d, kind) => {
|
|
16740
17122
|
if (kind === "launchd" || d.platform === "darwin") {
|
|
16741
17123
|
const p = plistPath(d.home);
|
|
16742
|
-
if (
|
|
17124
|
+
if (existsSync14(p)) {
|
|
16743
17125
|
d.exec("launchctl", ["unload", p]);
|
|
16744
17126
|
rmSync5(p, { force: true });
|
|
16745
17127
|
}
|
|
@@ -16750,16 +17132,16 @@ var cancelRevert = (d, kind) => {
|
|
|
16750
17132
|
};
|
|
16751
17133
|
|
|
16752
17134
|
// src/commands/engineSystemd.ts
|
|
16753
|
-
import { join as
|
|
17135
|
+
import { join as join18 } from "node:path";
|
|
16754
17136
|
var BOX_ENV_UNIT_PATH = "%h/.config/sleds/box.env";
|
|
16755
17137
|
function boxEnvPath(home) {
|
|
16756
|
-
return
|
|
17138
|
+
return join18(home, ".config", "sleds", "box.env");
|
|
16757
17139
|
}
|
|
16758
17140
|
function engineUnitDir(home) {
|
|
16759
|
-
return
|
|
17141
|
+
return join18(home, ".config", "systemd", "user");
|
|
16760
17142
|
}
|
|
16761
17143
|
function engineUnitPath(home, label) {
|
|
16762
|
-
return
|
|
17144
|
+
return join18(engineUnitDir(home), `${label}.service`);
|
|
16763
17145
|
}
|
|
16764
17146
|
function systemdQuote(s) {
|
|
16765
17147
|
const body = s.replace(/\\/g, "\\\\").replace(/"/g, "\\\"").replace(/\n/g, "\\n").replace(/%/g, "%%").replace(/\$/g, "$$$$");
|
|
@@ -16817,13 +17199,13 @@ function engineLabel(name, org, seat) {
|
|
|
16817
17199
|
return [ENGINE_LABEL, org, seat?.trim(), name].filter((p) => Boolean(p)).map((p, i) => i === 0 ? p : engineSlug(p)).join(".");
|
|
16818
17200
|
}
|
|
16819
17201
|
function enginePlistPath(home, name, org, seat) {
|
|
16820
|
-
return
|
|
17202
|
+
return join19(home, "Library", "LaunchAgents", `${engineLabel(name, org, seat)}.plist`);
|
|
16821
17203
|
}
|
|
16822
17204
|
function engineLogPath(stateDir, name) {
|
|
16823
|
-
return
|
|
17205
|
+
return join19(stateDir, `${engineSlug(name)}.log`);
|
|
16824
17206
|
}
|
|
16825
17207
|
function engineEventsPath(stateDir, name) {
|
|
16826
|
-
return
|
|
17208
|
+
return join19(stateDir, `${engineSlug(name)}.events.jsonl`);
|
|
16827
17209
|
}
|
|
16828
17210
|
var xmlEscape3 = (s) => s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
16829
17211
|
function buildEnginePlist(o) {
|
|
@@ -16899,7 +17281,13 @@ function parseEnginePlist(text) {
|
|
|
16899
17281
|
if (!label || array === undefined)
|
|
16900
17282
|
return null;
|
|
16901
17283
|
const programArgs = [...array.matchAll(/<string>([^<]*)<\/string>/g)].map((m) => xmlUnescape(m[1] ?? ""));
|
|
16902
|
-
|
|
17284
|
+
const envDict = /<key>EnvironmentVariables<\/key>\s*<dict>([\s\S]*?)<\/dict>/.exec(text)?.[1];
|
|
17285
|
+
const path = envDict && /<key>PATH<\/key>\s*<string>([^<]*)<\/string>/.exec(envDict)?.[1];
|
|
17286
|
+
return {
|
|
17287
|
+
label: xmlUnescape(label),
|
|
17288
|
+
programArgs,
|
|
17289
|
+
...typeof path === "string" ? { path: xmlUnescape(path) } : {}
|
|
17290
|
+
};
|
|
16903
17291
|
}
|
|
16904
17292
|
function engineArg(programArgs, flag) {
|
|
16905
17293
|
const at = programArgs.indexOf(`--${flag}`);
|
|
@@ -16914,7 +17302,7 @@ function readEngines(dir, suffix, manager, parse) {
|
|
|
16914
17302
|
}
|
|
16915
17303
|
const out = [];
|
|
16916
17304
|
for (const name of names.filter((n) => n.startsWith(`${ENGINE_LABEL}.`) && n.endsWith(suffix))) {
|
|
16917
|
-
const path =
|
|
17305
|
+
const path = join19(dir, name);
|
|
16918
17306
|
let parsed;
|
|
16919
17307
|
try {
|
|
16920
17308
|
parsed = parse(readFileSync14(path, "utf8"), name);
|
|
@@ -16934,7 +17322,7 @@ function readEngines(dir, suffix, manager, parse) {
|
|
|
16934
17322
|
}
|
|
16935
17323
|
function installedEngines(home) {
|
|
16936
17324
|
return [
|
|
16937
|
-
...readEngines(
|
|
17325
|
+
...readEngines(join19(home, "Library", "LaunchAgents"), ".plist", "launchd", parseEnginePlist),
|
|
16938
17326
|
...readEngines(engineUnitDir(home), ".service", "systemd", (text, file) => {
|
|
16939
17327
|
const parsed = parseEngineUnit(text);
|
|
16940
17328
|
return parsed && { label: basename4(file, ".service"), ...parsed };
|
|
@@ -16985,7 +17373,7 @@ function engineInstall(o) {
|
|
|
16985
17373
|
return installUnit({ o, parsed, label, sledsArgv, env: agentEnv, logPath, exec });
|
|
16986
17374
|
const plist = enginePlistPath(o.home, parsed.name, parsed.org, seat);
|
|
16987
17375
|
mkdirSync14(dirname12(plist), { recursive: true });
|
|
16988
|
-
if (
|
|
17376
|
+
if (existsSync15(plist))
|
|
16989
17377
|
exec("launchctl", ["unload", plist]);
|
|
16990
17378
|
for (const other of installedEngines(o.home)) {
|
|
16991
17379
|
if (other.manager !== "launchd" || other.plist === plist || other.org !== parsed.org)
|
|
@@ -16997,7 +17385,7 @@ function engineInstall(o) {
|
|
|
16997
17385
|
log(`replaced ${other.label}, this engine as installed before`);
|
|
16998
17386
|
}
|
|
16999
17387
|
const legacy = enginePlistPath(o.home, parsed.name);
|
|
17000
|
-
if (parsed.org &&
|
|
17388
|
+
if (parsed.org && existsSync15(legacy)) {
|
|
17001
17389
|
const cfg = readConfig(o.home);
|
|
17002
17390
|
if (org.sole || parsed.org === (cfg.activeOrg ?? cfg.org?.slug)) {
|
|
17003
17391
|
exec("launchctl", ["unload", legacy]);
|
|
@@ -17027,7 +17415,7 @@ function installUnit(a) {
|
|
|
17027
17415
|
const unitName = basename4(unit);
|
|
17028
17416
|
const systemctl = (...args) => exec("systemctl", ["--user", ...args]);
|
|
17029
17417
|
mkdirSync14(dirname12(unit), { recursive: true });
|
|
17030
|
-
if (
|
|
17418
|
+
if (existsSync15(unit))
|
|
17031
17419
|
systemctl("stop", unitName);
|
|
17032
17420
|
for (const other of installedEngines(o.home)) {
|
|
17033
17421
|
if (other.manager !== "systemd" || other.plist === unit || other.org !== parsed.org)
|
|
@@ -17085,7 +17473,7 @@ function engineUninstall(o) {
|
|
|
17085
17473
|
...installed.filter((e) => e.manager === manager).map((e) => e.plist),
|
|
17086
17474
|
...args.org ? [] : [pathOf(manager)]
|
|
17087
17475
|
]);
|
|
17088
|
-
const file = candidates.find((p) =>
|
|
17476
|
+
const file = candidates.find((p) => existsSync15(p));
|
|
17089
17477
|
if (!file) {
|
|
17090
17478
|
log(`engine:${name} was not installed (no ${candidates[0]}).`);
|
|
17091
17479
|
return 0;
|
|
@@ -17104,20 +17492,20 @@ function engineUninstall(o) {
|
|
|
17104
17492
|
}
|
|
17105
17493
|
|
|
17106
17494
|
// src/commands/musher.ts
|
|
17107
|
-
import { existsSync as
|
|
17495
|
+
import { existsSync as existsSync16, mkdirSync as mkdirSync15, readFileSync as readFileSync15, rmSync as rmSync7, writeFileSync as writeFileSync16 } from "node:fs";
|
|
17108
17496
|
init_config();
|
|
17109
17497
|
init_meter();
|
|
17110
17498
|
var defaultWhich = (cmd) => {
|
|
17111
17499
|
const path = process.env.PATH ?? "";
|
|
17112
17500
|
for (const dir of path.split(":").filter(Boolean)) {
|
|
17113
17501
|
const p = `${dir}/${cmd}`;
|
|
17114
|
-
if (
|
|
17502
|
+
if (existsSync16(p))
|
|
17115
17503
|
return p;
|
|
17116
17504
|
}
|
|
17117
17505
|
return null;
|
|
17118
17506
|
};
|
|
17119
17507
|
var findClaude = (home, configured, which) => {
|
|
17120
|
-
if (configured &&
|
|
17508
|
+
if (configured && existsSync16(configured))
|
|
17121
17509
|
return configured;
|
|
17122
17510
|
const found = which("claude");
|
|
17123
17511
|
if (found)
|
|
@@ -17128,13 +17516,13 @@ var findClaude = (home, configured, which) => {
|
|
|
17128
17516
|
"/usr/local/bin/claude",
|
|
17129
17517
|
`${home}/.npm-global/bin/claude`
|
|
17130
17518
|
])
|
|
17131
|
-
if (
|
|
17519
|
+
if (existsSync16(p))
|
|
17132
17520
|
return p;
|
|
17133
17521
|
return null;
|
|
17134
17522
|
};
|
|
17135
17523
|
var musherAgentState = (home, env = process.env) => {
|
|
17136
17524
|
const plist = musherPlistPath(home, env);
|
|
17137
|
-
if (!
|
|
17525
|
+
if (!existsSync16(plist))
|
|
17138
17526
|
return { installed: false, thisSeat: false };
|
|
17139
17527
|
try {
|
|
17140
17528
|
const text = readFileSync15(plist, "utf8");
|
|
@@ -17146,6 +17534,42 @@ var musherAgentState = (home, env = process.env) => {
|
|
|
17146
17534
|
}
|
|
17147
17535
|
};
|
|
17148
17536
|
|
|
17537
|
+
// src/claudeAccount.ts
|
|
17538
|
+
import { readFileSync as readFileSync16 } from "node:fs";
|
|
17539
|
+
var PLANS = {
|
|
17540
|
+
claude_max: "Max",
|
|
17541
|
+
claude_pro: "Pro",
|
|
17542
|
+
claude_team: "Team",
|
|
17543
|
+
claude_enterprise: "Enterprise"
|
|
17544
|
+
};
|
|
17545
|
+
var str11 = (v) => typeof v === "string" && v.trim() !== "" ? v.trim() : undefined;
|
|
17546
|
+
var parseClaudeAccount = (raw) => {
|
|
17547
|
+
if (typeof raw !== "object" || raw === null)
|
|
17548
|
+
return null;
|
|
17549
|
+
const acct = raw.oauthAccount;
|
|
17550
|
+
if (typeof acct !== "object" || acct === null)
|
|
17551
|
+
return null;
|
|
17552
|
+
const a = acct;
|
|
17553
|
+
const email = str11(a.emailAddress);
|
|
17554
|
+
if (!email)
|
|
17555
|
+
return null;
|
|
17556
|
+
const out = { email };
|
|
17557
|
+
const type = str11(a.organizationType);
|
|
17558
|
+
if (type && PLANS[type])
|
|
17559
|
+
out.plan = PLANS[type];
|
|
17560
|
+
const orgName = str11(a.organizationName);
|
|
17561
|
+
if (orgName)
|
|
17562
|
+
out.orgName = orgName;
|
|
17563
|
+
return out;
|
|
17564
|
+
};
|
|
17565
|
+
var readClaudeAccount = (home, env = process.env) => {
|
|
17566
|
+
try {
|
|
17567
|
+
return parseClaudeAccount(JSON.parse(readFileSync16(claudeUserConfigPath2(home, env), "utf8")));
|
|
17568
|
+
} catch {
|
|
17569
|
+
return null;
|
|
17570
|
+
}
|
|
17571
|
+
};
|
|
17572
|
+
|
|
17149
17573
|
// src/commands/status.ts
|
|
17150
17574
|
init_config();
|
|
17151
17575
|
init_settings();
|
|
@@ -17214,7 +17638,7 @@ async function runDoctor(o) {
|
|
|
17214
17638
|
].filter(Boolean);
|
|
17215
17639
|
const cfgDir = configDir(home, env);
|
|
17216
17640
|
const cfg = readConfig(home);
|
|
17217
|
-
const cfgExists =
|
|
17641
|
+
const cfgExists = existsSync17(configPath(home));
|
|
17218
17642
|
if (!cfgExists) {
|
|
17219
17643
|
checks.push({
|
|
17220
17644
|
level: "fail",
|
|
@@ -17230,7 +17654,7 @@ async function runDoctor(o) {
|
|
|
17230
17654
|
});
|
|
17231
17655
|
}
|
|
17232
17656
|
const sPath = settingsPath(home);
|
|
17233
|
-
const settings =
|
|
17657
|
+
const settings = existsSync17(sPath) ? readSettings(home) : null;
|
|
17234
17658
|
const pointed = settings ? readProxy(settings) : null;
|
|
17235
17659
|
if (!settings) {
|
|
17236
17660
|
checks.push({
|
|
@@ -17277,7 +17701,7 @@ async function runDoctor(o) {
|
|
|
17277
17701
|
detail: `seat-event ${seat ? "on" : "missing"} · spec-event ${spec ? "on" : "missing"} · session-event ${session ? "on" : "missing"} · stop-event ${stop ? "on" : "missing"}`,
|
|
17278
17702
|
fix: all ? undefined : "sleds hooks"
|
|
17279
17703
|
});
|
|
17280
|
-
const want = env.SLEDS_HOME?.trim() ?
|
|
17704
|
+
const want = env.SLEDS_HOME?.trim() ? resolve9(home, env.SLEDS_HOME.trim()) : undefined;
|
|
17281
17705
|
const cmds = sledsCommandsIn(settings);
|
|
17282
17706
|
const wrong = cmds.filter((c) => {
|
|
17283
17707
|
const got = bakedHome(c);
|
|
@@ -17315,7 +17739,7 @@ async function runDoctor(o) {
|
|
|
17315
17739
|
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)`
|
|
17316
17740
|
});
|
|
17317
17741
|
}
|
|
17318
|
-
const skill =
|
|
17742
|
+
const skill = existsSync17(skillPath(home));
|
|
17319
17743
|
checks.push({
|
|
17320
17744
|
level: skill ? "ok" : "warn",
|
|
17321
17745
|
name: "skill",
|
|
@@ -17349,7 +17773,7 @@ async function runDoctor(o) {
|
|
|
17349
17773
|
const which = (o.which ?? defaultWhich2)("sleds");
|
|
17350
17774
|
const stale = which.filter((p) => {
|
|
17351
17775
|
try {
|
|
17352
|
-
const target =
|
|
17776
|
+
const target = lstatSync2(p).isSymbolicLink() ? readlinkSync2(p) : p;
|
|
17353
17777
|
return /\/src\/index\.ts$/.test(target) || /\.bun\/bin\/sleds$/.test(p);
|
|
17354
17778
|
} catch {
|
|
17355
17779
|
return false;
|
|
@@ -17484,7 +17908,7 @@ function sandboxChecks(o, env) {
|
|
|
17484
17908
|
fix: "npm install -g @anthropic-ai/claude-code"
|
|
17485
17909
|
}
|
|
17486
17910
|
];
|
|
17487
|
-
if (!
|
|
17911
|
+
if (!existsSync17(dir))
|
|
17488
17912
|
return [
|
|
17489
17913
|
{
|
|
17490
17914
|
level: "warn",
|
|
@@ -17494,12 +17918,12 @@ function sandboxChecks(o, env) {
|
|
|
17494
17918
|
}
|
|
17495
17919
|
];
|
|
17496
17920
|
const nonce = o.nonce ?? `sleds-probe-${randomBytes(16).toString("hex")}`;
|
|
17497
|
-
const probe =
|
|
17498
|
-
const work = mkdtempSync(
|
|
17921
|
+
const probe = join20(dir, SANDBOX_PROBE);
|
|
17922
|
+
const work = mkdtempSync(join20(tmpdir(), "sleds-sandbox-"));
|
|
17499
17923
|
try {
|
|
17500
17924
|
writeFileSync17(probe, `${nonce}
|
|
17501
17925
|
`, { mode: 384 });
|
|
17502
|
-
const settingsFile =
|
|
17926
|
+
const settingsFile = join20(work, "settings.json");
|
|
17503
17927
|
const settings = sandboxSettings({
|
|
17504
17928
|
denyRead: [dir],
|
|
17505
17929
|
gitCommonDir: work,
|
|
@@ -17535,7 +17959,7 @@ function engineChecks(o, env) {
|
|
|
17535
17959
|
const checks = [];
|
|
17536
17960
|
const which = (c) => (o.which ?? defaultWhich2)(c)[0] ?? null;
|
|
17537
17961
|
const exec = o.exec ?? defaultExec3;
|
|
17538
|
-
const exists = o.exists ??
|
|
17962
|
+
const exists = o.exists ?? existsSync17;
|
|
17539
17963
|
const platform = o.platform ?? process.platform;
|
|
17540
17964
|
const mac = platform === "darwin";
|
|
17541
17965
|
const linux = platform === "linux";
|
|
@@ -17577,6 +18001,11 @@ function engineChecks(o, env) {
|
|
|
17577
18001
|
fix: mac ? "brew install tmux" : "install tmux with your package manager"
|
|
17578
18002
|
});
|
|
17579
18003
|
}
|
|
18004
|
+
if (runner === "orca") {
|
|
18005
|
+
const path = engines.find((e) => engineArg(e.programArgs, "runner") === "orca")?.path ?? env.PATH ?? "";
|
|
18006
|
+
const p = orcaPreflight(exec, path, o.orcaFs);
|
|
18007
|
+
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 });
|
|
18008
|
+
}
|
|
17580
18009
|
if (linux)
|
|
17581
18010
|
checks.push(...boxChecks(o, env, which, need));
|
|
17582
18011
|
if (!manager)
|
|
@@ -17630,12 +18059,12 @@ function engineChecks(o, env) {
|
|
|
17630
18059
|
level: "warn",
|
|
17631
18060
|
name: "repos dir",
|
|
17632
18061
|
detail: `${dirs.join(" → ")} — ${missing.join(" and ")} ${missing.length === 1 ? "does" : "do"} not exist`,
|
|
17633
|
-
fix: "create the folder or drop it from Repos
|
|
18062
|
+
fix: "create the folder or drop it from Repos folders in Settings › Running work"
|
|
17634
18063
|
} : dirs.length ? { level: "ok", name: "repos dir", detail: dirs.join(" → ") } : repo ? { level: "ok", name: "repos dir", detail: repo } : {
|
|
17635
18064
|
level: "warn",
|
|
17636
18065
|
name: "repos dir",
|
|
17637
|
-
detail: `${e.label} names no --repos-dir — it works only if
|
|
17638
|
-
fix: "set Repos
|
|
18066
|
+
detail: `${e.label} names no --repos-dir — it works only if your engine profile sets Repos folders`,
|
|
18067
|
+
fix: "set Repos folders in Settings › Running work, or sleds engine install --repos-dir ~/code"
|
|
17639
18068
|
});
|
|
17640
18069
|
}
|
|
17641
18070
|
return checks;
|
|
@@ -17645,7 +18074,7 @@ function boxChecks(o, env, which, need) {
|
|
|
17645
18074
|
const file = boxEnvPath(o.home);
|
|
17646
18075
|
let named = false;
|
|
17647
18076
|
try {
|
|
17648
|
-
named = /^\s*(?:export\s+)?ANTHROPIC_API_KEY\s*=\s*\S/m.test(
|
|
18077
|
+
named = /^\s*(?:export\s+)?ANTHROPIC_API_KEY\s*=\s*\S/m.test(readFileSync17(file, "utf8"));
|
|
17649
18078
|
} catch {
|
|
17650
18079
|
named = false;
|
|
17651
18080
|
}
|
|
@@ -17666,7 +18095,7 @@ function boxChecks(o, env, which, need) {
|
|
|
17666
18095
|
}
|
|
17667
18096
|
function cmuxCheck(o, env) {
|
|
17668
18097
|
const exec = o.exec ?? defaultExec3;
|
|
17669
|
-
const bin = resolveCmuxBin(o.exists ??
|
|
18098
|
+
const bin = resolveCmuxBin(o.exists ?? existsSync17, env.PATH ?? "");
|
|
17670
18099
|
const pw = readCmuxPassword(exec);
|
|
17671
18100
|
const ping = pw ? exec(bin, ["ping"], { env: { CMUX_SOCKET_PASSWORD: pw } }) : exec(bin, ["ping"]);
|
|
17672
18101
|
if (ping.status === 127)
|
|
@@ -17751,13 +18180,13 @@ import {
|
|
|
17751
18180
|
appendFileSync as appendFileSync3,
|
|
17752
18181
|
chmodSync as chmodSync5,
|
|
17753
18182
|
mkdirSync as mkdirSync16,
|
|
17754
|
-
readFileSync as
|
|
18183
|
+
readFileSync as readFileSync18,
|
|
17755
18184
|
existsSync as realExists2,
|
|
17756
18185
|
rmSync as rmSync9,
|
|
17757
18186
|
writeFileSync as writeFileSync18
|
|
17758
18187
|
} from "node:fs";
|
|
17759
18188
|
import { hostname as hostname4 } from "node:os";
|
|
17760
|
-
import { join as
|
|
18189
|
+
import { join as join21, resolve as resolve10 } from "node:path";
|
|
17761
18190
|
init_machine();
|
|
17762
18191
|
init_state();
|
|
17763
18192
|
init_meter();
|
|
@@ -18012,7 +18441,7 @@ function flagsFromProfile2(flags, given, profile, home) {
|
|
|
18012
18441
|
}
|
|
18013
18442
|
}
|
|
18014
18443
|
if (profile.reposDir && !has("repos-dir") && !has("repo")) {
|
|
18015
|
-
out.reposDir = splitReposDirs(profile.reposDir).map((d) => home && /^~(\/|$)/.test(d) ?
|
|
18444
|
+
out.reposDir = splitReposDirs(profile.reposDir).map((d) => home && /^~(\/|$)/.test(d) ? join21(home, d.slice(1)) : d).join(":");
|
|
18016
18445
|
used.push(`repos ${profile.reposDir}`);
|
|
18017
18446
|
}
|
|
18018
18447
|
if (profile.mcpStrict !== undefined && !has("strict-mcp") && !has("no-strict-mcp")) {
|
|
@@ -18102,6 +18531,29 @@ async function rereadAtOnce2(read, flags, given) {
|
|
|
18102
18531
|
const profile = await read();
|
|
18103
18532
|
return profile ? profile.atOnce ?? 1 : null;
|
|
18104
18533
|
}
|
|
18534
|
+
async function rereadRunner2(read, flags, given, home) {
|
|
18535
|
+
if (given.includes("runner"))
|
|
18536
|
+
return null;
|
|
18537
|
+
const profile = await read();
|
|
18538
|
+
if (!profile)
|
|
18539
|
+
return null;
|
|
18540
|
+
const base = {
|
|
18541
|
+
...flags,
|
|
18542
|
+
runner: "tmux",
|
|
18543
|
+
runnerCmd: given.includes("runner-cmd") ? flags.runnerCmd : undefined
|
|
18544
|
+
};
|
|
18545
|
+
const r = flagsFromProfile2(base, given, profile, home);
|
|
18546
|
+
const skipped = r.skipped.find((s) => s.startsWith("runner"));
|
|
18547
|
+
if (skipped)
|
|
18548
|
+
return { refused: `skipped from your engine profile: ${skipped} — fix it in Settings` };
|
|
18549
|
+
const refusal = sandboxRefusal({ sandbox: flags.sandbox, runner: r.flags.runner });
|
|
18550
|
+
if (refusal)
|
|
18551
|
+
return { refused: `runner ${r.flags.runner} not taken — ${refusal}` };
|
|
18552
|
+
return {
|
|
18553
|
+
runner: r.flags.runner,
|
|
18554
|
+
...r.flags.runner === "command" && r.flags.runnerCmd ? { runnerCmd: r.flags.runnerCmd } : {}
|
|
18555
|
+
};
|
|
18556
|
+
}
|
|
18105
18557
|
var PROFILE_RETRY_MS2 = [2000, 5000];
|
|
18106
18558
|
async function withProfile2(client, flags, given, home, retry) {
|
|
18107
18559
|
let profile = await readProfile(client);
|
|
@@ -18173,19 +18625,19 @@ function realPidAlive2(pid) {
|
|
|
18173
18625
|
function memberMcpConfigs2(o, cwd) {
|
|
18174
18626
|
const read = o.readJson ?? ((p) => {
|
|
18175
18627
|
try {
|
|
18176
|
-
return JSON.parse(
|
|
18628
|
+
return JSON.parse(readFileSync18(p, "utf8"));
|
|
18177
18629
|
} catch {
|
|
18178
18630
|
return null;
|
|
18179
18631
|
}
|
|
18180
18632
|
});
|
|
18181
18633
|
const claudeDir = (o.env ?? process.env).CLAUDE_CONFIG_DIR;
|
|
18182
18634
|
return [
|
|
18183
|
-
read(claudeDir ?
|
|
18184
|
-
...cwd ? [read(
|
|
18635
|
+
read(claudeDir ? join21(claudeDir, ".claude.json") : join21(o.home, ".claude.json")),
|
|
18636
|
+
...cwd ? [read(join21(cwd, ".mcp.json"))] : []
|
|
18185
18637
|
];
|
|
18186
18638
|
}
|
|
18187
18639
|
function engineStateDir2(o, org, seat) {
|
|
18188
|
-
return seatStateDir(orgStateDir(o.stateDir ??
|
|
18640
|
+
return seatStateDir(orgStateDir(o.stateDir ?? join21(configDir(o.home, o.env ?? process.env), "engine"), org), seat);
|
|
18189
18641
|
}
|
|
18190
18642
|
function engineSeat2(o, org) {
|
|
18191
18643
|
try {
|
|
@@ -18212,11 +18664,11 @@ function engineDeps2(o, flags, source, runner, stateName) {
|
|
|
18212
18664
|
const stateDir = engineStateDir2(o, flags.org, flags.seat);
|
|
18213
18665
|
const file = statePath(stateDir, stateName);
|
|
18214
18666
|
const mcpOrg = flags.org && engineOrg2(o, flags.org).seated ? flags.org : undefined;
|
|
18215
|
-
const repo = flags.repo ?
|
|
18216
|
-
const reposDirs = splitReposDirs(flags.reposDir).map((d) =>
|
|
18667
|
+
const repo = flags.repo ? resolve10(cwd, flags.repo) : undefined;
|
|
18668
|
+
const reposDirs = splitReposDirs(flags.reposDir).map((d) => resolve10(cwd, d));
|
|
18217
18669
|
const { branch, prTitle } = flags;
|
|
18218
|
-
const hooksDir =
|
|
18219
|
-
const permissionMark = (name) =>
|
|
18670
|
+
const hooksDir = join21(stateDir, "hooks");
|
|
18671
|
+
const permissionMark = (name) => join21(hooksDir, `${name}.permission.json`);
|
|
18220
18672
|
const withHook = (name, settings) => applyPermissionHook(settings, [...o.sledsArgv ?? ["sleds"], "permission-event", "--mark", permissionMark(name)].map(shq2).join(" "));
|
|
18221
18673
|
return {
|
|
18222
18674
|
source,
|
|
@@ -18241,9 +18693,9 @@ function engineDeps2(o, flags, source, runner, stateName) {
|
|
|
18241
18693
|
sandbox: true,
|
|
18242
18694
|
writeSandboxSettings: o.writeSandboxSettings ?? ((name, cwd) => {
|
|
18243
18695
|
const gitCommonDir = gitCommonDirOf2(exec, cwd);
|
|
18244
|
-
const dir =
|
|
18696
|
+
const dir = join21(stateDir, "sandbox");
|
|
18245
18697
|
mkdirSync16(dir, { recursive: true, mode: 448 });
|
|
18246
|
-
const p =
|
|
18698
|
+
const p = join21(dir, `${name}.json`);
|
|
18247
18699
|
const walls = engineSandboxSettings2(o.home, o.env ?? process.env, gitCommonDir, [
|
|
18248
18700
|
...SANDBOX_DEFAULT_ALLOW,
|
|
18249
18701
|
...flags.sandboxAllow ?? []
|
|
@@ -18257,7 +18709,7 @@ function engineDeps2(o, flags, source, runner, stateName) {
|
|
|
18257
18709
|
} : {},
|
|
18258
18710
|
writeHookSettings: (name) => {
|
|
18259
18711
|
mkdirSync16(hooksDir, { recursive: true, mode: 448 });
|
|
18260
|
-
const p =
|
|
18712
|
+
const p = join21(hooksDir, `${name}.json`);
|
|
18261
18713
|
writeFileSync18(p, `${JSON.stringify(withHook(name, {}), null, 2)}
|
|
18262
18714
|
`, { mode: 384 });
|
|
18263
18715
|
chmodSync5(p, 384);
|
|
@@ -18284,9 +18736,9 @@ function engineDeps2(o, flags, source, runner, stateName) {
|
|
|
18284
18736
|
exists
|
|
18285
18737
|
}),
|
|
18286
18738
|
writeMcpConfig: o.writeMcpConfig ?? ((name, runToken, worktree) => {
|
|
18287
|
-
const dir =
|
|
18739
|
+
const dir = join21(stateDir, "mcp");
|
|
18288
18740
|
mkdirSync16(dir, { recursive: true, mode: 448 });
|
|
18289
|
-
const p =
|
|
18741
|
+
const p = join21(dir, `${name}.json`);
|
|
18290
18742
|
let others = {};
|
|
18291
18743
|
if (flags.strictMcp && flags.mcpAllow?.length) {
|
|
18292
18744
|
const r = mcpPassThrough(flags.mcpAllow, memberMcpConfigs2(o, worktree));
|
|
@@ -18302,9 +18754,9 @@ function engineDeps2(o, flags, source, runner, stateName) {
|
|
|
18302
18754
|
}),
|
|
18303
18755
|
removeMcpConfig: o.removeMcpConfig ?? ((p) => rmSync9(p, { force: true })),
|
|
18304
18756
|
writePrompt: o.writePrompt ?? ((name, text) => {
|
|
18305
|
-
const dir =
|
|
18757
|
+
const dir = join21(stateDir, "prompts");
|
|
18306
18758
|
mkdirSync16(dir, { recursive: true, mode: 448 });
|
|
18307
|
-
const p =
|
|
18759
|
+
const p = join21(dir, `${name}.md`);
|
|
18308
18760
|
writeFileSync18(p, text, { mode: 384 });
|
|
18309
18761
|
return p;
|
|
18310
18762
|
}),
|
|
@@ -18331,8 +18783,8 @@ function engineDeps2(o, flags, source, runner, stateName) {
|
|
|
18331
18783
|
}
|
|
18332
18784
|
function sandboxDenyRead2(home, env) {
|
|
18333
18785
|
return {
|
|
18334
|
-
dirs: [...new Set([configDir(home, env),
|
|
18335
|
-
files: [...new Set([tokenPath(home, env),
|
|
18786
|
+
dirs: [...new Set([configDir(home, env), join21(home, ".config", "sleds")])],
|
|
18787
|
+
files: [...new Set([tokenPath(home, env), join21(home, ".sleds-token")])]
|
|
18336
18788
|
};
|
|
18337
18789
|
}
|
|
18338
18790
|
function engineSandboxSettings2(home, env, gitCommonDir, allow) {
|
|
@@ -18347,7 +18799,7 @@ function gitCommonDirOf2(exec, cwd) {
|
|
|
18347
18799
|
const dir = r.status === 0 ? r.stdout.trim() : "";
|
|
18348
18800
|
if (!dir)
|
|
18349
18801
|
throw new Error(`git has no common dir for ${cwd}, so the sandbox has nowhere to commit`);
|
|
18350
|
-
return
|
|
18802
|
+
return resolve10(cwd, dir);
|
|
18351
18803
|
}
|
|
18352
18804
|
function sandboxRefusal(flags) {
|
|
18353
18805
|
if (!flags.sandbox)
|
|
@@ -18398,13 +18850,14 @@ async function driveEngine2(o, flags, deps, until = {}) {
|
|
|
18398
18850
|
runner: deps.runner.name,
|
|
18399
18851
|
atOnce: flags.atOnce,
|
|
18400
18852
|
interval: flags.interval,
|
|
18401
|
-
cliVersion: "0.1.
|
|
18853
|
+
cliVersion: "0.1.44"
|
|
18402
18854
|
};
|
|
18403
18855
|
const beat = (checkedAt) => {
|
|
18404
18856
|
const m = deps.machine;
|
|
18405
18857
|
try {
|
|
18406
18858
|
writeLock(lockFile, checkedAt === undefined ? lock : {
|
|
18407
18859
|
...lock,
|
|
18860
|
+
runner: deps.runner.name,
|
|
18408
18861
|
atOnce: deps.atOnce,
|
|
18409
18862
|
checkedAt,
|
|
18410
18863
|
nextCheckAt: checkedAt + flags.interval * 1000,
|
|
@@ -18677,7 +19130,7 @@ var engineCmd = async (o) => {
|
|
|
18677
19130
|
armedOnly: true,
|
|
18678
19131
|
engine: `engine:${flags.name}`,
|
|
18679
19132
|
...parsed.given.includes("at-once") ? { atOnce: flags.atOnce } : {},
|
|
18680
|
-
runner: runner.name,
|
|
19133
|
+
runner: () => deps.runner.name,
|
|
18681
19134
|
leaseMin: flags.lease,
|
|
18682
19135
|
log
|
|
18683
19136
|
});
|
|
@@ -18695,6 +19148,21 @@ var engineCmd = async (o) => {
|
|
|
18695
19148
|
const started = flags;
|
|
18696
19149
|
deps.rereadRoles = (read) => rereadRoles(read, started, parsed.given);
|
|
18697
19150
|
deps.rereadAtOnce = (read) => rereadAtOnce2(read, started, parsed.given);
|
|
19151
|
+
let current = { runner, cmd: flags.runner === "command" ? flags.runnerCmd : undefined };
|
|
19152
|
+
deps.rereadRunner = async (read) => {
|
|
19153
|
+
const next = await rereadRunner2(read, started, parsed.given, o.home);
|
|
19154
|
+
if (!next)
|
|
19155
|
+
return null;
|
|
19156
|
+
if ("refused" in next)
|
|
19157
|
+
return next.refused;
|
|
19158
|
+
if (next.runner === current.runner.name && next.runnerCmd === current.cmd)
|
|
19159
|
+
return current.runner;
|
|
19160
|
+
const made = engineRunner2(o, { ...started, ...next });
|
|
19161
|
+
if (!made)
|
|
19162
|
+
return `runner ${next.runner} not taken — this CLI cannot start it`;
|
|
19163
|
+
current = { runner: made, cmd: next.runnerCmd };
|
|
19164
|
+
return made;
|
|
19165
|
+
};
|
|
18698
19166
|
}
|
|
18699
19167
|
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));
|
|
18700
19168
|
if (flags.strictMcp && flags.agent)
|
|
@@ -18704,15 +19172,21 @@ var engineCmd = async (o) => {
|
|
|
18704
19172
|
let afterTick;
|
|
18705
19173
|
if (client) {
|
|
18706
19174
|
const send = beater(client, o.now ?? Date.now);
|
|
18707
|
-
|
|
19175
|
+
let refused;
|
|
19176
|
+
const sayRefused = (r) => {
|
|
19177
|
+
if (!r || r.ok || !r.refused || r.refused === refused)
|
|
19178
|
+
return;
|
|
19179
|
+
refused = r.refused;
|
|
19180
|
+
error(`${r.refused}. This engine is not shown as up until it beats under a name of its own.`);
|
|
19181
|
+
};
|
|
18708
19182
|
afterTick = () => {
|
|
18709
19183
|
try {
|
|
18710
19184
|
const m = deps.machine;
|
|
18711
|
-
send(beatBody(deps.readState(),
|
|
19185
|
+
send(beatBody(deps.readState(), { name: flags.name, runner: deps.runner.name }, {}, {
|
|
18712
19186
|
atOnce: deps.atOnce,
|
|
18713
19187
|
...m?.last ? { machine: m.last } : {},
|
|
18714
19188
|
...m?.hold && !m.off ? { hold: m.hold } : {}
|
|
18715
|
-
}));
|
|
19189
|
+
}))?.then(sayRefused, () => {});
|
|
18716
19190
|
} catch {}
|
|
18717
19191
|
};
|
|
18718
19192
|
}
|
|
@@ -18840,18 +19314,18 @@ async function enginePauseCmd2(o) {
|
|
|
18840
19314
|
|
|
18841
19315
|
// src/commands/engineInstall.ts
|
|
18842
19316
|
init_config();
|
|
18843
|
-
init_state();
|
|
18844
19317
|
import {
|
|
18845
|
-
existsSync as
|
|
19318
|
+
existsSync as existsSync18,
|
|
18846
19319
|
mkdirSync as mkdirSync17,
|
|
18847
|
-
readFileSync as
|
|
19320
|
+
readFileSync as readFileSync19,
|
|
18848
19321
|
readdirSync as readdirSync4,
|
|
18849
19322
|
realpathSync as realpathSync2,
|
|
18850
19323
|
rmSync as rmSync10,
|
|
18851
19324
|
writeFileSync as writeFileSync19
|
|
18852
19325
|
} from "node:fs";
|
|
18853
19326
|
import { hostname as hostname5 } from "node:os";
|
|
18854
|
-
import { basename as basename5, dirname as dirname13, isAbsolute as isAbsolute3, join as
|
|
19327
|
+
import { basename as basename5, dirname as dirname13, isAbsolute as isAbsolute3, join as join22 } from "node:path";
|
|
19328
|
+
init_state();
|
|
18855
19329
|
var ENGINE_LABEL2 = "ai.sleds.engine";
|
|
18856
19330
|
var ENGINE_INSTALL_USAGE2 = `usage: sleds engine install [engine flags] | sleds engine uninstall [--name <n>] [--org <slug>]
|
|
18857
19331
|
${ENGINE_USAGE}`;
|
|
@@ -18870,13 +19344,13 @@ function engineLabel2(name, org, seat) {
|
|
|
18870
19344
|
return [ENGINE_LABEL2, org, seat?.trim(), name].filter((p) => Boolean(p)).map((p, i) => i === 0 ? p : engineSlug(p)).join(".");
|
|
18871
19345
|
}
|
|
18872
19346
|
function enginePlistPath2(home, name, org, seat) {
|
|
18873
|
-
return
|
|
19347
|
+
return join22(home, "Library", "LaunchAgents", `${engineLabel2(name, org, seat)}.plist`);
|
|
18874
19348
|
}
|
|
18875
19349
|
function engineLogPath2(stateDir, name) {
|
|
18876
|
-
return
|
|
19350
|
+
return join22(stateDir, `${engineSlug(name)}.log`);
|
|
18877
19351
|
}
|
|
18878
19352
|
function engineEventsPath2(stateDir, name) {
|
|
18879
|
-
return
|
|
19353
|
+
return join22(stateDir, `${engineSlug(name)}.events.jsonl`);
|
|
18880
19354
|
}
|
|
18881
19355
|
var xmlEscape4 = (s) => s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
18882
19356
|
function buildEnginePlist2(o) {
|
|
@@ -18952,7 +19426,13 @@ function parseEnginePlist2(text) {
|
|
|
18952
19426
|
if (!label || array === undefined)
|
|
18953
19427
|
return null;
|
|
18954
19428
|
const programArgs = [...array.matchAll(/<string>([^<]*)<\/string>/g)].map((m) => xmlUnescape2(m[1] ?? ""));
|
|
18955
|
-
|
|
19429
|
+
const envDict = /<key>EnvironmentVariables<\/key>\s*<dict>([\s\S]*?)<\/dict>/.exec(text)?.[1];
|
|
19430
|
+
const path = envDict && /<key>PATH<\/key>\s*<string>([^<]*)<\/string>/.exec(envDict)?.[1];
|
|
19431
|
+
return {
|
|
19432
|
+
label: xmlUnescape2(label),
|
|
19433
|
+
programArgs,
|
|
19434
|
+
...typeof path === "string" ? { path: xmlUnescape2(path) } : {}
|
|
19435
|
+
};
|
|
18956
19436
|
}
|
|
18957
19437
|
function engineArg2(programArgs, flag) {
|
|
18958
19438
|
const at = programArgs.indexOf(`--${flag}`);
|
|
@@ -18967,10 +19447,10 @@ function readEngines2(dir, suffix, manager, parse) {
|
|
|
18967
19447
|
}
|
|
18968
19448
|
const out = [];
|
|
18969
19449
|
for (const name of names.filter((n) => n.startsWith(`${ENGINE_LABEL2}.`) && n.endsWith(suffix))) {
|
|
18970
|
-
const path =
|
|
19450
|
+
const path = join22(dir, name);
|
|
18971
19451
|
let parsed;
|
|
18972
19452
|
try {
|
|
18973
|
-
parsed = parse(
|
|
19453
|
+
parsed = parse(readFileSync19(path, "utf8"), name);
|
|
18974
19454
|
} catch {
|
|
18975
19455
|
parsed = null;
|
|
18976
19456
|
}
|
|
@@ -18987,7 +19467,7 @@ function readEngines2(dir, suffix, manager, parse) {
|
|
|
18987
19467
|
}
|
|
18988
19468
|
function installedEngines2(home) {
|
|
18989
19469
|
return [
|
|
18990
|
-
...readEngines2(
|
|
19470
|
+
...readEngines2(join22(home, "Library", "LaunchAgents"), ".plist", "launchd", parseEnginePlist2),
|
|
18991
19471
|
...readEngines2(engineUnitDir(home), ".service", "systemd", (text, file) => {
|
|
18992
19472
|
const parsed = parseEngineUnit(text);
|
|
18993
19473
|
return parsed && { label: basename5(file, ".service"), ...parsed };
|
|
@@ -18995,6 +19475,50 @@ function installedEngines2(home) {
|
|
|
18995
19475
|
];
|
|
18996
19476
|
}
|
|
18997
19477
|
var unsupported2 = (platform) => `sleds engine install needs launchd (macOS) or systemd (Linux). On ${platform}, keep \`sleds engine\` running under your own service manager (a scheduled task, tmux).`;
|
|
19478
|
+
var REPOS_DIR_NEEDED = "Set the folder your repos are cloned in first: `sleds engine profile set reposDir=~/code` (Settings › Running work › Repos folders), or `sleds engine install --repos-dir ~/code`. An installed engine starts outside any checkout, so without one every node it takes parks on No repo.";
|
|
19479
|
+
async function ensureReposDir(o) {
|
|
19480
|
+
const log = o.log ?? console.log;
|
|
19481
|
+
const error = o.error ?? console.error;
|
|
19482
|
+
const parsed = parseEngineArgs(o.argv, o.host ?? hostname5());
|
|
19483
|
+
if ("error" in parsed)
|
|
19484
|
+
return true;
|
|
19485
|
+
if (parsed.given.includes("repos-dir") || parsed.given.includes("repo"))
|
|
19486
|
+
return true;
|
|
19487
|
+
const client = engineClient({ home: o.home, ...o.env ? { env: o.env } : {}, ...o.client ? { client: o.client } : {} }, parsed.flags.org);
|
|
19488
|
+
if ("error" in client)
|
|
19489
|
+
return true;
|
|
19490
|
+
const profile = await readProfile(client);
|
|
19491
|
+
if (!profile) {
|
|
19492
|
+
log("note: your engine profile could not be read — if it names no repos folder, the engine says so when it starts");
|
|
19493
|
+
return true;
|
|
19494
|
+
}
|
|
19495
|
+
if (profile.reposDir)
|
|
19496
|
+
return true;
|
|
19497
|
+
if (!o.ask) {
|
|
19498
|
+
error(REPOS_DIR_NEEDED);
|
|
19499
|
+
return false;
|
|
19500
|
+
}
|
|
19501
|
+
const answer = (await o.ask("Where are your repos cloned? Folders searched in order, split on : (~/code): ")).trim();
|
|
19502
|
+
const dirs = splitReposDirs(answer);
|
|
19503
|
+
if (dirs.length === 0) {
|
|
19504
|
+
error(REPOS_DIR_NEEDED);
|
|
19505
|
+
return false;
|
|
19506
|
+
}
|
|
19507
|
+
const exists = o.exists ?? existsSync18;
|
|
19508
|
+
const missing = dirs.filter((d) => !exists(/^~(\/|$)/.test(d) ? join22(o.home, d.slice(1)) : d));
|
|
19509
|
+
if (missing.length) {
|
|
19510
|
+
error(`Not a folder on this machine: ${missing.join(", ")}. ${REPOS_DIR_NEEDED}`);
|
|
19511
|
+
return false;
|
|
19512
|
+
}
|
|
19513
|
+
const r = await client.postFull("/proxy/engine/profile", { profile: { reposDir: dirs.join(":") } }).catch(() => null);
|
|
19514
|
+
if (!r || r.status >= 300 || r.json?.ok === false) {
|
|
19515
|
+
const why = typeof r?.json?.error === "string" ? r.json.error : "SLEDS unreachable";
|
|
19516
|
+
error(`The repos folder was not saved (${why}). ${REPOS_DIR_NEEDED}`);
|
|
19517
|
+
return false;
|
|
19518
|
+
}
|
|
19519
|
+
log(`repos folders saved to your engine profile: ${dirs.join(" → ")}`);
|
|
19520
|
+
return true;
|
|
19521
|
+
}
|
|
18998
19522
|
function engineInstall2(o) {
|
|
18999
19523
|
const log = o.log ?? console.log;
|
|
19000
19524
|
const error = o.error ?? console.error;
|
|
@@ -19038,7 +19562,7 @@ function engineInstall2(o) {
|
|
|
19038
19562
|
return installUnit2({ o, parsed, label, sledsArgv, env: agentEnv, logPath, exec });
|
|
19039
19563
|
const plist = enginePlistPath2(o.home, parsed.name, parsed.org, seat);
|
|
19040
19564
|
mkdirSync17(dirname13(plist), { recursive: true });
|
|
19041
|
-
if (
|
|
19565
|
+
if (existsSync18(plist))
|
|
19042
19566
|
exec("launchctl", ["unload", plist]);
|
|
19043
19567
|
for (const other of installedEngines2(o.home)) {
|
|
19044
19568
|
if (other.manager !== "launchd" || other.plist === plist || other.org !== parsed.org)
|
|
@@ -19050,7 +19574,7 @@ function engineInstall2(o) {
|
|
|
19050
19574
|
log(`replaced ${other.label}, this engine as installed before`);
|
|
19051
19575
|
}
|
|
19052
19576
|
const legacy = enginePlistPath2(o.home, parsed.name);
|
|
19053
|
-
if (parsed.org &&
|
|
19577
|
+
if (parsed.org && existsSync18(legacy)) {
|
|
19054
19578
|
const cfg = readConfig(o.home);
|
|
19055
19579
|
if (org.sole || parsed.org === (cfg.activeOrg ?? cfg.org?.slug)) {
|
|
19056
19580
|
exec("launchctl", ["unload", legacy]);
|
|
@@ -19080,7 +19604,7 @@ function installUnit2(a) {
|
|
|
19080
19604
|
const unitName = basename5(unit);
|
|
19081
19605
|
const systemctl = (...args) => exec("systemctl", ["--user", ...args]);
|
|
19082
19606
|
mkdirSync17(dirname13(unit), { recursive: true });
|
|
19083
|
-
if (
|
|
19607
|
+
if (existsSync18(unit))
|
|
19084
19608
|
systemctl("stop", unitName);
|
|
19085
19609
|
for (const other of installedEngines2(o.home)) {
|
|
19086
19610
|
if (other.manager !== "systemd" || other.plist === unit || other.org !== parsed.org)
|
|
@@ -19138,7 +19662,7 @@ function engineUninstall2(o) {
|
|
|
19138
19662
|
...installed.filter((e) => e.manager === manager).map((e) => e.plist),
|
|
19139
19663
|
...args.org ? [] : [pathOf(manager)]
|
|
19140
19664
|
]);
|
|
19141
|
-
const file = candidates.find((p) =>
|
|
19665
|
+
const file = candidates.find((p) => existsSync18(p));
|
|
19142
19666
|
if (!file) {
|
|
19143
19667
|
log(`engine:${name} was not installed (no ${candidates[0]}).`);
|
|
19144
19668
|
return 0;
|
|
@@ -19169,13 +19693,13 @@ function ago(at, now) {
|
|
|
19169
19693
|
|
|
19170
19694
|
// src/commands/engineList.ts
|
|
19171
19695
|
var ENGINE_LIST_USAGE = "usage: sleds engine list [--json]";
|
|
19172
|
-
var
|
|
19696
|
+
var str12 = (x) => typeof x === "string" ? x : "";
|
|
19173
19697
|
var numOrNull = (x) => typeof x === "number" && Number.isFinite(x) ? x : null;
|
|
19174
19698
|
function machineOf(x) {
|
|
19175
19699
|
if (!x || typeof x !== "object")
|
|
19176
19700
|
return null;
|
|
19177
19701
|
const m = x;
|
|
19178
|
-
const out = { pressure:
|
|
19702
|
+
const out = { pressure: str12(m.pressure) || "unknown" };
|
|
19179
19703
|
for (const k of ["cpuPct", "load1", "cores", "loadPerCore"]) {
|
|
19180
19704
|
const n = numOrNull(m[k]);
|
|
19181
19705
|
if (n !== null)
|
|
@@ -19191,8 +19715,8 @@ function workOf(x) {
|
|
|
19191
19715
|
if (!w || typeof w !== "object")
|
|
19192
19716
|
return [];
|
|
19193
19717
|
const o = w;
|
|
19194
|
-
const item =
|
|
19195
|
-
return item ? [{ item, node:
|
|
19718
|
+
const item = str12(o.item);
|
|
19719
|
+
return item ? [{ item, node: str12(o.node), title: typeof o.title === "string" ? o.title : null }] : [];
|
|
19196
19720
|
});
|
|
19197
19721
|
}
|
|
19198
19722
|
function engineRows(raw) {
|
|
@@ -19201,16 +19725,16 @@ function engineRows(raw) {
|
|
|
19201
19725
|
if (!r || typeof r !== "object")
|
|
19202
19726
|
return [];
|
|
19203
19727
|
const e = r;
|
|
19204
|
-
const name =
|
|
19728
|
+
const name = str12(e.name);
|
|
19205
19729
|
if (!name)
|
|
19206
19730
|
return [];
|
|
19207
19731
|
return [
|
|
19208
19732
|
{
|
|
19209
19733
|
name,
|
|
19210
|
-
email:
|
|
19211
|
-
os:
|
|
19212
|
-
runner:
|
|
19213
|
-
version:
|
|
19734
|
+
email: str12(e.email),
|
|
19735
|
+
os: str12(e.os),
|
|
19736
|
+
runner: str12(e.runner),
|
|
19737
|
+
version: str12(e.version),
|
|
19214
19738
|
running: typeof e.running === "number" ? e.running : 0,
|
|
19215
19739
|
items: Array.isArray(e.items) ? e.items.filter((i) => typeof i === "string") : [],
|
|
19216
19740
|
seatKind: typeof e.seatKind === "string" ? e.seatKind : null,
|
|
@@ -19263,7 +19787,7 @@ var engineListCmd = async (o) => {
|
|
|
19263
19787
|
return 1;
|
|
19264
19788
|
}
|
|
19265
19789
|
if (r.status !== 200 || !r.json || r.json.ok === false) {
|
|
19266
|
-
error(
|
|
19790
|
+
error(str12(r.json?.error) || `HTTP ${r.status}`);
|
|
19267
19791
|
return 1;
|
|
19268
19792
|
}
|
|
19269
19793
|
const rows = engineRows(r.json);
|
|
@@ -19280,7 +19804,7 @@ import { closeSync as closeSync3, fstatSync as fstatSync2, openSync as openSync3
|
|
|
19280
19804
|
import { hostname as hostname6 } from "node:os";
|
|
19281
19805
|
|
|
19282
19806
|
// src/commands/engineStatus.ts
|
|
19283
|
-
import { existsSync as
|
|
19807
|
+
import { existsSync as existsSync19, readFileSync as readFileSync20 } from "node:fs";
|
|
19284
19808
|
init_state();
|
|
19285
19809
|
|
|
19286
19810
|
// src/engine/status.ts
|
|
@@ -19463,7 +19987,7 @@ function statusDir(o, engine, org) {
|
|
|
19463
19987
|
const env = o.env ?? process.env;
|
|
19464
19988
|
const which = engineOrg({ home: o.home, env }, org);
|
|
19465
19989
|
const slug = which.seated || org ? which.slug : null;
|
|
19466
|
-
const has = (d) =>
|
|
19990
|
+
const has = (d) => existsSync19(statePath(d, engine)) || existsSync19(lockPath(d, engine));
|
|
19467
19991
|
const seat = engineSeat({ home: o.home, env }, slug ?? undefined);
|
|
19468
19992
|
const dir = engineStateDir({ ...o, env }, slug);
|
|
19469
19993
|
const seatDir = engineStateDir({ ...o, env }, slug, seat);
|
|
@@ -19570,7 +20094,7 @@ var engineLogCmd = (o) => {
|
|
|
19570
20094
|
|
|
19571
20095
|
// src/commands/engineProfile.ts
|
|
19572
20096
|
init_src();
|
|
19573
|
-
import { existsSync as
|
|
20097
|
+
import { existsSync as existsSync20, readFileSync as readFileSync21 } from "node:fs";
|
|
19574
20098
|
import { hostname as hostname7 } from "node:os";
|
|
19575
20099
|
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]";
|
|
19576
20100
|
var PROFILE_FIELDS = [
|
|
@@ -19798,7 +20322,7 @@ function localOf(o) {
|
|
|
19798
20322
|
return {
|
|
19799
20323
|
engine: `engine:${name}`,
|
|
19800
20324
|
canInstall,
|
|
19801
|
-
installed: canInstall &&
|
|
20325
|
+
installed: canInstall && existsSync20(enginePlistPath(o.home, name))
|
|
19802
20326
|
};
|
|
19803
20327
|
}
|
|
19804
20328
|
async function engineProfileCmd(o) {
|
|
@@ -19835,7 +20359,7 @@ async function engineProfileCmd(o) {
|
|
|
19835
20359
|
const rest = words.slice(1);
|
|
19836
20360
|
let stdin = null;
|
|
19837
20361
|
if (fromStdin) {
|
|
19838
|
-
const taken = cmuxPasswordFromJson((o.stdin ?? (() =>
|
|
20362
|
+
const taken = cmuxPasswordFromJson((o.stdin ?? (() => readFileSync21(0, "utf8")))());
|
|
19839
20363
|
if ("error" in taken || taken.password !== undefined && password !== undefined) {
|
|
19840
20364
|
error("error" in taken ? taken.error : "the cmux password is given once");
|
|
19841
20365
|
error(PROFILE_USAGE);
|
|
@@ -19920,7 +20444,7 @@ var keychainLine = (k) => k === "set" ? "cmuxPassword: set (Keychain)" : k === "
|
|
|
19920
20444
|
|
|
19921
20445
|
// src/commands/engineStatus.ts
|
|
19922
20446
|
init_state();
|
|
19923
|
-
import { existsSync as
|
|
20447
|
+
import { existsSync as existsSync21, readFileSync as readFileSync22 } from "node:fs";
|
|
19924
20448
|
import { hostname as hostname8 } from "node:os";
|
|
19925
20449
|
var ENGINE_STATUS_USAGE = "usage: sleds engine status [--name <n>] [--org <slug>] [--json]";
|
|
19926
20450
|
function parseStatusArgs(argv, host) {
|
|
@@ -19949,7 +20473,7 @@ function statusDir2(o, engine, org) {
|
|
|
19949
20473
|
const env = o.env ?? process.env;
|
|
19950
20474
|
const which = engineOrg({ home: o.home, env }, org);
|
|
19951
20475
|
const slug = which.seated || org ? which.slug : null;
|
|
19952
|
-
const has = (d) =>
|
|
20476
|
+
const has = (d) => existsSync21(statePath(d, engine)) || existsSync21(lockPath(d, engine));
|
|
19953
20477
|
const seat = engineSeat({ home: o.home, env }, slug ?? undefined);
|
|
19954
20478
|
const dir = engineStateDir({ ...o, env }, slug);
|
|
19955
20479
|
const seatDir = engineStateDir({ ...o, env }, slug, seat);
|
|
@@ -19962,7 +20486,7 @@ function statusDir2(o, engine, org) {
|
|
|
19962
20486
|
}
|
|
19963
20487
|
function readEngineStatus(stateDir, engine, host, pidAlive, seat) {
|
|
19964
20488
|
const file = statePath(stateDir, engine);
|
|
19965
|
-
const text =
|
|
20489
|
+
const text = existsSync21(file) ? safeRead(file) : "";
|
|
19966
20490
|
return engineStatus({
|
|
19967
20491
|
engine,
|
|
19968
20492
|
state: text ? parseState(text) : { runs: [] },
|
|
@@ -19976,7 +20500,7 @@ function readEngineStatus(stateDir, engine, host, pidAlive, seat) {
|
|
|
19976
20500
|
}
|
|
19977
20501
|
function safeRead(path) {
|
|
19978
20502
|
try {
|
|
19979
|
-
return
|
|
20503
|
+
return readFileSync22(path, "utf8");
|
|
19980
20504
|
} catch {
|
|
19981
20505
|
return "";
|
|
19982
20506
|
}
|
|
@@ -20097,28 +20621,28 @@ init_meter();
|
|
|
20097
20621
|
|
|
20098
20622
|
// src/owed.ts
|
|
20099
20623
|
init_config();
|
|
20100
|
-
import { existsSync as
|
|
20101
|
-
import { dirname as dirname14, join as
|
|
20102
|
-
var owedPath = (home) =>
|
|
20103
|
-
var sessionPath = (home) =>
|
|
20624
|
+
import { existsSync as existsSync22, mkdirSync as mkdirSync18, readFileSync as readFileSync23, renameSync as renameSync7, writeFileSync as writeFileSync20 } from "node:fs";
|
|
20625
|
+
import { dirname as dirname14, join as join23 } from "node:path";
|
|
20626
|
+
var owedPath = (home) => join23(configDir(home), "owed.json");
|
|
20627
|
+
var sessionPath = (home) => join23(configDir(home), "session.json");
|
|
20104
20628
|
var OWED_TTL_MS = 3 * 24 * 60 * 60000;
|
|
20105
|
-
var
|
|
20629
|
+
var str13 = (v) => typeof v === "string" ? v : undefined;
|
|
20106
20630
|
function sourceKey(s) {
|
|
20107
20631
|
const trim = (x) => x.trim().slice(0, 120);
|
|
20108
|
-
const kind =
|
|
20109
|
-
const project = trim(
|
|
20110
|
-
const doc =
|
|
20111
|
-
const item =
|
|
20632
|
+
const kind = str13(s.kind) ?? "manual";
|
|
20633
|
+
const project = trim(str13(s.project) ?? "");
|
|
20634
|
+
const doc = str13(s.doc);
|
|
20635
|
+
const item = str13(s.item);
|
|
20112
20636
|
return `${kind}:${project}${doc ? `/${trim(doc)}` : ""}${item ? `#${trim(item)}` : ""}`;
|
|
20113
20637
|
}
|
|
20114
20638
|
function sourceText2(s) {
|
|
20115
|
-
return [`${
|
|
20639
|
+
return [`${str13(s.kind) ?? "?"} ${str13(s.project) ?? "?"}`, str13(s.doc), str13(s.item)].filter(Boolean).join(" › ");
|
|
20116
20640
|
}
|
|
20117
20641
|
var readJson = (p) => {
|
|
20118
|
-
if (!
|
|
20642
|
+
if (!existsSync22(p))
|
|
20119
20643
|
return null;
|
|
20120
20644
|
try {
|
|
20121
|
-
return JSON.parse(
|
|
20645
|
+
return JSON.parse(readFileSync23(p, "utf8"));
|
|
20122
20646
|
} catch {
|
|
20123
20647
|
return null;
|
|
20124
20648
|
}
|
|
@@ -21145,7 +21669,7 @@ var login = async (o) => {
|
|
|
21145
21669
|
// src/commands/logout.ts
|
|
21146
21670
|
init_config();
|
|
21147
21671
|
init_state();
|
|
21148
|
-
import { existsSync as
|
|
21672
|
+
import { existsSync as existsSync23, readFileSync as readFileSync24, rmSync as rmSync11 } from "node:fs";
|
|
21149
21673
|
import { hostname as hostname10 } from "node:os";
|
|
21150
21674
|
init_settings();
|
|
21151
21675
|
var LOGOUT_USAGE = "usage: sleds logout [--org <slug>] [--json]";
|
|
@@ -21302,7 +21826,7 @@ async function logout(o) {
|
|
|
21302
21826
|
};
|
|
21303
21827
|
writeConfig(o.home, next);
|
|
21304
21828
|
const tp = tokenPath(o.home, env);
|
|
21305
|
-
if (
|
|
21829
|
+
if (existsSync23(tp) && readFileSync24(tp, "utf8").trim() === seat.token)
|
|
21306
21830
|
rmSync11(tp, { force: true });
|
|
21307
21831
|
const active = next.activeOrg ?? next.org?.slug ?? null;
|
|
21308
21832
|
const result = { ok: true, email, org: slug, paused, active };
|
|
@@ -21380,20 +21904,20 @@ var meter = async (o) => {
|
|
|
21380
21904
|
init_config();
|
|
21381
21905
|
init_meter();
|
|
21382
21906
|
import { spawnSync as spawnSync7 } from "node:child_process";
|
|
21383
|
-
import { existsSync as
|
|
21907
|
+
import { existsSync as existsSync24, mkdirSync as mkdirSync20, readFileSync as readFileSync25, rmSync as rmSync12, writeFileSync as writeFileSync22 } from "node:fs";
|
|
21384
21908
|
import { hostname as hostname11 } from "node:os";
|
|
21385
21909
|
import { dirname as dirname15 } from "node:path";
|
|
21386
21910
|
var defaultWhich3 = (cmd) => {
|
|
21387
21911
|
const path = process.env.PATH ?? "";
|
|
21388
21912
|
for (const dir of path.split(":").filter(Boolean)) {
|
|
21389
21913
|
const p = `${dir}/${cmd}`;
|
|
21390
|
-
if (
|
|
21914
|
+
if (existsSync24(p))
|
|
21391
21915
|
return p;
|
|
21392
21916
|
}
|
|
21393
21917
|
return null;
|
|
21394
21918
|
};
|
|
21395
21919
|
var findClaude2 = (home, configured, which) => {
|
|
21396
|
-
if (configured &&
|
|
21920
|
+
if (configured && existsSync24(configured))
|
|
21397
21921
|
return configured;
|
|
21398
21922
|
const found = which("claude");
|
|
21399
21923
|
if (found)
|
|
@@ -21404,7 +21928,7 @@ var findClaude2 = (home, configured, which) => {
|
|
|
21404
21928
|
"/usr/local/bin/claude",
|
|
21405
21929
|
`${home}/.npm-global/bin/claude`
|
|
21406
21930
|
])
|
|
21407
|
-
if (
|
|
21931
|
+
if (existsSync24(p))
|
|
21408
21932
|
return p;
|
|
21409
21933
|
return null;
|
|
21410
21934
|
};
|
|
@@ -21430,7 +21954,7 @@ var musherInstall = (o) => {
|
|
|
21430
21954
|
...env.CLAUDE_CONFIG_DIR ? { CLAUDE_CONFIG_DIR: env.CLAUDE_CONFIG_DIR } : {}
|
|
21431
21955
|
};
|
|
21432
21956
|
const exec = o.exec ?? defaultExecSync;
|
|
21433
|
-
if (
|
|
21957
|
+
if (existsSync24(plist))
|
|
21434
21958
|
exec("launchctl", ["unload", plist]);
|
|
21435
21959
|
writeFileSync22(plist, buildMusherPlist({ sledsArgv: o.sledsArgv, env: agentEnv, logPath: musherLogPath(dir), label }));
|
|
21436
21960
|
const r = exec("launchctl", ["load", plist]);
|
|
@@ -21451,7 +21975,7 @@ var musherUninstall = (o) => {
|
|
|
21451
21975
|
const env = o.env ?? process.env;
|
|
21452
21976
|
const plist = musherPlistPath(o.home, env);
|
|
21453
21977
|
const exec = o.exec ?? defaultExecSync;
|
|
21454
|
-
if (
|
|
21978
|
+
if (existsSync24(plist)) {
|
|
21455
21979
|
exec("launchctl", ["unload", plist]);
|
|
21456
21980
|
rmSync12(plist, { force: true });
|
|
21457
21981
|
log(`musher removed: ${musherLabel(env)}`);
|
|
@@ -21486,7 +22010,7 @@ var musherStatus = async (o) => {
|
|
|
21486
22010
|
const env = o.env ?? process.env;
|
|
21487
22011
|
const plist = musherPlistPath(o.home, env);
|
|
21488
22012
|
const cfg = readConfig(o.home);
|
|
21489
|
-
log(
|
|
22013
|
+
log(existsSync24(plist) ? `agent ${musherLabel(env)} installed (${plist})` : "agent not installed — sleds musher --install");
|
|
21490
22014
|
const claude = findClaude2(o.home, cfg.musher?.claudePath, o.which ?? defaultWhich3);
|
|
21491
22015
|
log(`claude ${claude ?? "not found — sleds musher --claude /path/to/claude"}`);
|
|
21492
22016
|
log(`mode ${cfg.musher?.permissionMode ?? DEFAULT_PERMISSION_MODE} · timeout ${cfg.musher?.timeoutMinutes ?? DEFAULT_TIMEOUT_MINUTES} min`);
|
|
@@ -21716,7 +22240,7 @@ var on = (o) => {
|
|
|
21716
22240
|
init_config();
|
|
21717
22241
|
init_meter();
|
|
21718
22242
|
init_settings();
|
|
21719
|
-
import { existsSync as
|
|
22243
|
+
import { existsSync as existsSync25, rmSync as rmSync13 } from "node:fs";
|
|
21720
22244
|
var ORG_USAGE = "usage: sleds org list [--json] | sleds org use <slug>";
|
|
21721
22245
|
var orgRows = (cfg, activeSlug) => {
|
|
21722
22246
|
const seats = { ...cfg.orgs ?? {} };
|
|
@@ -21783,7 +22307,7 @@ var orgUse = (o) => {
|
|
|
21783
22307
|
}
|
|
21784
22308
|
writeConfig(o.home, withActiveOrg(cfg, slug, seat));
|
|
21785
22309
|
writeTokenFile(o.home, seat.token);
|
|
21786
|
-
if (
|
|
22310
|
+
if (existsSync25(settingsPath(o.home)) && cfg.mode !== "on") {
|
|
21787
22311
|
const settings = readSettings(o.home);
|
|
21788
22312
|
const pointed = readProxy(settings);
|
|
21789
22313
|
const url = pointed?.url ?? cfg.proxyUrl;
|
|
@@ -21820,7 +22344,7 @@ var orgCmd = (o) => {
|
|
|
21820
22344
|
init_src();
|
|
21821
22345
|
import { spawnSync as spawnSync8 } from "node:child_process";
|
|
21822
22346
|
import { writeFileSync as writeFileSync23 } from "node:fs";
|
|
21823
|
-
import { join as
|
|
22347
|
+
import { join as join24 } from "node:path";
|
|
21824
22348
|
function packFileName(key) {
|
|
21825
22349
|
return `pack-${slugify(key, 80) || "item"}.md`;
|
|
21826
22350
|
}
|
|
@@ -21848,7 +22372,7 @@ var packCmd = async (o) => {
|
|
|
21848
22372
|
log(r.text);
|
|
21849
22373
|
return 0;
|
|
21850
22374
|
}
|
|
21851
|
-
const path =
|
|
22375
|
+
const path = join24(o.cwd ?? process.cwd(), packFileName(key));
|
|
21852
22376
|
(o.write ?? ((p, t) => writeFileSync23(p, t.endsWith(`
|
|
21853
22377
|
`) ? t : `${t}
|
|
21854
22378
|
`)))(path, r.text);
|
|
@@ -21857,7 +22381,7 @@ var packCmd = async (o) => {
|
|
|
21857
22381
|
};
|
|
21858
22382
|
|
|
21859
22383
|
// src/commands/permissionEvent.ts
|
|
21860
|
-
import { chmodSync as chmodSync6, mkdirSync as mkdirSync21, readFileSync as
|
|
22384
|
+
import { chmodSync as chmodSync6, mkdirSync as mkdirSync21, readFileSync as readFileSync26, renameSync as renameSync8, rmSync as rmSync14, writeFileSync as writeFileSync24 } from "node:fs";
|
|
21861
22385
|
import { dirname as dirname16 } from "node:path";
|
|
21862
22386
|
var isObject13 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
21863
22387
|
var messageOf = (text) => {
|
|
@@ -21868,12 +22392,20 @@ var messageOf = (text) => {
|
|
|
21868
22392
|
return "";
|
|
21869
22393
|
}
|
|
21870
22394
|
};
|
|
22395
|
+
var NOT_A_TOOL2 = new Set(["askuserquestion", "exitplanmode", "enterplanmode"]);
|
|
22396
|
+
var isToolPermission2 = (message) => {
|
|
22397
|
+
const tool = /permission to use\s+(.+?)\.?\s*$/i.exec(message)?.[1];
|
|
22398
|
+
return !tool || !NOT_A_TOOL2.has(tool.replace(/[\s_-]/g, "").toLowerCase());
|
|
22399
|
+
};
|
|
21871
22400
|
var permissionEvent = async (o) => {
|
|
21872
22401
|
try {
|
|
21873
22402
|
const input = o.stdin ?? await readStdin(HOOK_STDIN_TIMEOUT_MS);
|
|
21874
22403
|
if (!o.mark || notificationType(input) !== "permission_prompt")
|
|
21875
22404
|
return 0;
|
|
21876
|
-
const
|
|
22405
|
+
const message = messageOf(input);
|
|
22406
|
+
if (!isToolPermission2(message))
|
|
22407
|
+
return 0;
|
|
22408
|
+
const mark = { message, at: (o.now ?? Date.now)() };
|
|
21877
22409
|
mkdirSync21(dirname16(o.mark), { recursive: true, mode: 448 });
|
|
21878
22410
|
const tmp = `${o.mark}.${process.pid}.tmp`;
|
|
21879
22411
|
writeFileSync24(tmp, `${JSON.stringify(mark)}
|
|
@@ -21968,7 +22500,7 @@ var promptEvent = async (o) => {
|
|
|
21968
22500
|
|
|
21969
22501
|
// src/commands/route.ts
|
|
21970
22502
|
init_src();
|
|
21971
|
-
import { readFileSync as
|
|
22503
|
+
import { readFileSync as readFileSync27 } from "node:fs";
|
|
21972
22504
|
import { hostname as hostname12 } from "node:os";
|
|
21973
22505
|
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>]";
|
|
21974
22506
|
var USAGE = "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]";
|
|
@@ -22141,7 +22673,7 @@ async function answerCmd(o, argv) {
|
|
|
22141
22673
|
const words = argv.filter((a) => a !== "--json" && a !== "--stdin");
|
|
22142
22674
|
const ref = words[0] ?? "";
|
|
22143
22675
|
const slash = ref.lastIndexOf("/");
|
|
22144
|
-
const answer = (fromStdin ? (o.stdin ?? (() =>
|
|
22676
|
+
const answer = (fromStdin ? (o.stdin ?? (() => readFileSync27(0, "utf8")))() : words.slice(1).join(" ")).trim();
|
|
22145
22677
|
if (slash <= 0 || slash === ref.length - 1 || fromStdin && words.length > 1) {
|
|
22146
22678
|
error(ANSWER_USAGE);
|
|
22147
22679
|
return 2;
|
|
@@ -22338,7 +22870,7 @@ async function decisionCmd(o, argv) {
|
|
|
22338
22870
|
error(DECISION_USAGE);
|
|
22339
22871
|
return 2;
|
|
22340
22872
|
}
|
|
22341
|
-
const md = seen || routeAlone ? "" : (fromStdin ? (o.stdin ?? (() =>
|
|
22873
|
+
const md = seen || routeAlone ? "" : (fromStdin ? (o.stdin ?? (() => readFileSync27(0, "utf8")))() : rest.join(" ")).trim();
|
|
22342
22874
|
if (!seen && !routeAlone && (!md || fromStdin && rest.length > 0)) {
|
|
22343
22875
|
error(md ? DECISION_USAGE : `${answer ? "an answer" : "an overrule"} needs some text`);
|
|
22344
22876
|
return 2;
|
|
@@ -22479,7 +23011,7 @@ function approvedLine(key, j) {
|
|
|
22479
23011
|
function readNodes(o, a) {
|
|
22480
23012
|
let text;
|
|
22481
23013
|
try {
|
|
22482
|
-
text = a.file ? (o.readFile ?? ((p) =>
|
|
23014
|
+
text = a.file ? (o.readFile ?? ((p) => readFileSync27(p, "utf8")))(a.file) : (o.stdin ?? (() => readFileSync27(0, "utf8")))();
|
|
22483
23015
|
} catch (e) {
|
|
22484
23016
|
return { error: e instanceof Error ? e.message : String(e) };
|
|
22485
23017
|
}
|
|
@@ -22790,14 +23322,14 @@ var seatEvent = async (o) => {
|
|
|
22790
23322
|
|
|
22791
23323
|
// src/commands/seatSync.ts
|
|
22792
23324
|
import { execFileSync as execFileSync4 } from "node:child_process";
|
|
22793
|
-
import { existsSync as
|
|
23325
|
+
import { existsSync as existsSync26, mkdirSync as mkdirSync22, readFileSync as readFileSync28, readdirSync as readdirSync5, rmSync as rmSync16, writeFileSync as writeFileSync25 } from "node:fs";
|
|
22794
23326
|
import { platform as platform2 } from "node:os";
|
|
22795
|
-
import { dirname as dirname17, join as
|
|
23327
|
+
import { dirname as dirname17, join as join26 } from "node:path";
|
|
22796
23328
|
|
|
22797
23329
|
// src/cookieDb.ts
|
|
22798
23330
|
import { copyFileSync, rmSync as rmSync15 } from "node:fs";
|
|
22799
23331
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
22800
|
-
import { join as
|
|
23332
|
+
import { join as join25 } from "node:path";
|
|
22801
23333
|
var QUERY = "SELECT name, hex(encrypted_value) AS ev FROM cookies WHERE host_key LIKE '%claude.ai%'";
|
|
22802
23334
|
var isBun = () => typeof globalThis.Bun !== "undefined";
|
|
22803
23335
|
async function queryRows(dbPath) {
|
|
@@ -22826,7 +23358,7 @@ function nodeSqlite() {
|
|
|
22826
23358
|
return mod;
|
|
22827
23359
|
}
|
|
22828
23360
|
async function readCookieRows(dbPath) {
|
|
22829
|
-
const copy =
|
|
23361
|
+
const copy = join25(tmpdir2(), `sleds-cookies-${process.pid}-${Date.now()}.sqlite`);
|
|
22830
23362
|
copyFileSync(dbPath, copy);
|
|
22831
23363
|
try {
|
|
22832
23364
|
const rows = await queryRows(copy);
|
|
@@ -23110,7 +23642,7 @@ function findFile(root, name) {
|
|
|
23110
23642
|
const stack = [root];
|
|
23111
23643
|
while (stack.length > 0) {
|
|
23112
23644
|
const dir = stack.pop();
|
|
23113
|
-
if (!dir || !
|
|
23645
|
+
if (!dir || !existsSync26(dir))
|
|
23114
23646
|
continue;
|
|
23115
23647
|
let entries;
|
|
23116
23648
|
try {
|
|
@@ -23119,7 +23651,7 @@ function findFile(root, name) {
|
|
|
23119
23651
|
continue;
|
|
23120
23652
|
}
|
|
23121
23653
|
for (const e of entries) {
|
|
23122
|
-
const p =
|
|
23654
|
+
const p = join26(dir, e.name);
|
|
23123
23655
|
if (e.isDirectory())
|
|
23124
23656
|
stack.push(p);
|
|
23125
23657
|
else if (e.name === name)
|
|
@@ -23130,8 +23662,8 @@ function findFile(root, name) {
|
|
|
23130
23662
|
}
|
|
23131
23663
|
function findCookieDb(home, source) {
|
|
23132
23664
|
const b = BROWSERS[source.browser];
|
|
23133
|
-
const root =
|
|
23134
|
-
if (!
|
|
23665
|
+
const root = join26(home, APP_SUPPORT, b.appDir);
|
|
23666
|
+
if (!existsSync26(root))
|
|
23135
23667
|
throw new Error(`no ${source.browser} data at ${root} (is it installed?)`);
|
|
23136
23668
|
if (!b.profiled) {
|
|
23137
23669
|
const db = findFile(root, "Cookies");
|
|
@@ -23139,15 +23671,15 @@ function findCookieDb(home, source) {
|
|
|
23139
23671
|
throw new Error(`no cookie store under ${root}`);
|
|
23140
23672
|
return db;
|
|
23141
23673
|
}
|
|
23142
|
-
const localStatePath =
|
|
23143
|
-
const localState =
|
|
23674
|
+
const localStatePath = join26(root, "Local State");
|
|
23675
|
+
const localState = existsSync26(localStatePath) ? readFileSync28(localStatePath, "utf8") : null;
|
|
23144
23676
|
const wanted = source.profile ?? "Default";
|
|
23145
23677
|
const { dir, ambiguous } = resolveProfileDir(localState, wanted);
|
|
23146
23678
|
if (ambiguous)
|
|
23147
23679
|
throw new Error(`"${wanted}" names ${ambiguous.length} ${source.browser} profiles (${ambiguous.join(", ")}) — pass the directory or the account email instead`);
|
|
23148
23680
|
if (!dir)
|
|
23149
23681
|
throw new Error(`no ${source.browser} profile named "${wanted}" (check chrome://version)`);
|
|
23150
|
-
const db = cookieDbCandidates(
|
|
23682
|
+
const db = cookieDbCandidates(join26(root, dir)).find((p) => existsSync26(p));
|
|
23151
23683
|
if (!db)
|
|
23152
23684
|
throw new Error(`profile "${dir}" has no cookie store yet (open claude.ai in it once)`);
|
|
23153
23685
|
return db;
|
|
@@ -23191,7 +23723,7 @@ function fetchSeatUsage(jar, org, now, run) {
|
|
|
23191
23723
|
}
|
|
23192
23724
|
function launchAgentPlist(bun, script, home, source) {
|
|
23193
23725
|
const label = launchLabel(source);
|
|
23194
|
-
const log =
|
|
23726
|
+
const log = join26(home, ".config", "sleds", `${label.slice("ai.sleds.".length)}.log`);
|
|
23195
23727
|
const esc = (s) => s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
23196
23728
|
const args = [bun, script, "seat-sync", ...sourceArgs(source)].map((a) => ` <string>${esc(a)}</string>`).join(`
|
|
23197
23729
|
`);
|
|
@@ -23213,7 +23745,7 @@ ${args}
|
|
|
23213
23745
|
`;
|
|
23214
23746
|
}
|
|
23215
23747
|
function plistPath2(home, source) {
|
|
23216
|
-
return
|
|
23748
|
+
return join26(home, "Library", "LaunchAgents", `${launchLabel(source)}.plist`);
|
|
23217
23749
|
}
|
|
23218
23750
|
function install(home, argv, source, log) {
|
|
23219
23751
|
const bun = argv[0] ?? process.execPath;
|
|
@@ -23222,7 +23754,7 @@ function install(home, argv, source, log) {
|
|
|
23222
23754
|
log("cannot resolve the sleds script path to install the timer");
|
|
23223
23755
|
return 1;
|
|
23224
23756
|
}
|
|
23225
|
-
const dir =
|
|
23757
|
+
const dir = join26(home, "Library", "LaunchAgents");
|
|
23226
23758
|
mkdirSync22(dir, { recursive: true });
|
|
23227
23759
|
const path = plistPath2(home, source);
|
|
23228
23760
|
const tokenPath = seatTokenPath(home, source);
|
|
@@ -23246,7 +23778,7 @@ function uninstall(home, source, log) {
|
|
|
23246
23778
|
execFileSync4("launchctl", ["unload", "-w", path], { stdio: "ignore" });
|
|
23247
23779
|
} catch {}
|
|
23248
23780
|
rmSync16(seatTokenPath(home, source), { force: true });
|
|
23249
|
-
log(
|
|
23781
|
+
log(existsSync26(path) ? `seat-sync timer removed (${path} can be deleted)` : "no seat-sync timer");
|
|
23250
23782
|
return 0;
|
|
23251
23783
|
}
|
|
23252
23784
|
var seatSync = async (o) => {
|
|
@@ -23320,7 +23852,7 @@ var seatSync = async (o) => {
|
|
|
23320
23852
|
};
|
|
23321
23853
|
function readSeatToken(home, source) {
|
|
23322
23854
|
try {
|
|
23323
|
-
return
|
|
23855
|
+
return readFileSync28(seatTokenPath(home, source), "utf8").trim() || undefined;
|
|
23324
23856
|
} catch {
|
|
23325
23857
|
return;
|
|
23326
23858
|
}
|
|
@@ -23403,7 +23935,7 @@ var sessionEvent = async (o) => {
|
|
|
23403
23935
|
|
|
23404
23936
|
// src/commands/specEvent.ts
|
|
23405
23937
|
init_meter();
|
|
23406
|
-
import { readFileSync as
|
|
23938
|
+
import { readFileSync as readFileSync29, statSync as statSync2 } from "node:fs";
|
|
23407
23939
|
import { basename as basename6, isAbsolute as isAbsolute4, relative } from "node:path";
|
|
23408
23940
|
var HOOK_STDIN_TIMEOUT_MS3 = 1000;
|
|
23409
23941
|
var ESTIMATE_TIMEOUT_MS = 4000;
|
|
@@ -23459,7 +23991,7 @@ function readFileOrNull(path) {
|
|
|
23459
23991
|
try {
|
|
23460
23992
|
if (statSync2(path).size > 2000000)
|
|
23461
23993
|
return null;
|
|
23462
|
-
return
|
|
23994
|
+
return readFileSync29(path, "utf8");
|
|
23463
23995
|
} catch {
|
|
23464
23996
|
return null;
|
|
23465
23997
|
}
|
|
@@ -23518,9 +24050,9 @@ var specEvent = async (o) => {
|
|
|
23518
24050
|
};
|
|
23519
24051
|
|
|
23520
24052
|
// src/commands/status.ts
|
|
24053
|
+
import { existsSync as existsSync27 } from "node:fs";
|
|
23521
24054
|
init_config();
|
|
23522
24055
|
init_settings();
|
|
23523
|
-
import { existsSync as existsSync26 } from "node:fs";
|
|
23524
24056
|
var HEALTH_TIMEOUT_MS2 = 4000;
|
|
23525
24057
|
var checkHealth2 = async (url, fetchFn = fetch, timeoutMs = HEALTH_TIMEOUT_MS2) => {
|
|
23526
24058
|
try {
|
|
@@ -23550,6 +24082,13 @@ var fetchProxyStatus = async (url, fetchFn = fetch, timeoutMs = HEALTH_TIMEOUT_M
|
|
|
23550
24082
|
}
|
|
23551
24083
|
};
|
|
23552
24084
|
var row2 = (label, value) => `${label.padEnd(12)}${value}`;
|
|
24085
|
+
var claudeLine = (c, seatEmail) => {
|
|
24086
|
+
if (!c)
|
|
24087
|
+
return "no Claude login found (signed out, or on an API key)";
|
|
24088
|
+
const org = c.orgName && (c.plan === "Team" || c.plan === "Enterprise") ? ` (${c.orgName})` : "";
|
|
24089
|
+
const other = seatEmail && seatEmail.toLowerCase() !== c.email.toLowerCase() ? " — not the seat's account" : "";
|
|
24090
|
+
return `${c.email}${c.plan ? ` · ${c.plan}` : ""}${org}${other}`;
|
|
24091
|
+
};
|
|
23553
24092
|
var formatStatus = (s) => {
|
|
23554
24093
|
const lines = [];
|
|
23555
24094
|
const stale = s.mode === "on" && !s.pointed;
|
|
@@ -23582,6 +24121,8 @@ var formatStatus = (s) => {
|
|
|
23582
24121
|
lines.push(row2("Seat:", `${s.email}${s.agentKind ? ` · ${s.agentKind}` : ""}`));
|
|
23583
24122
|
else if (s.agentKind)
|
|
23584
24123
|
lines.push(row2("Agent:", s.agentKind));
|
|
24124
|
+
if (s.claude !== undefined)
|
|
24125
|
+
lines.push(row2("Claude:", claudeLine(s.claude, s.email)));
|
|
23585
24126
|
if (s.org)
|
|
23586
24127
|
lines.push(row2("Org:", `${s.org.name}${s.org.slug ? ` (${s.org.slug})` : ""} · ${s.org.plan} · ${s.org.upstream === "none" ? "no platform key (walls: hold or hand off)" : s.org.upstream === "own" ? "platform key" : "shared key"}${s.orgFrom === "env" ? " · from SLEDS_ORG" : ""}`));
|
|
23587
24128
|
if (s.otherOrgs)
|
|
@@ -23597,7 +24138,7 @@ var status3 = async (o) => {
|
|
|
23597
24138
|
const log = o.log ?? console.log;
|
|
23598
24139
|
const nowMs = (o.now ?? Date.now)();
|
|
23599
24140
|
const cfg = readConfig(o.home);
|
|
23600
|
-
const settings =
|
|
24141
|
+
const settings = existsSync27(settingsPath(o.home)) ? readSettings(o.home) : {};
|
|
23601
24142
|
const inSettings = readProxy(settings);
|
|
23602
24143
|
const picked = selectOrg(cfg, undefined, o.env ?? process.env);
|
|
23603
24144
|
const viaEnv = picked.from === "env";
|
|
@@ -23627,6 +24168,8 @@ var status3 = async (o) => {
|
|
|
23627
24168
|
info.agentKind = cfg.agentKind;
|
|
23628
24169
|
if (cfg.wiring)
|
|
23629
24170
|
info.wiring = cfg.wiring;
|
|
24171
|
+
if (cfg.agentKind !== "codex")
|
|
24172
|
+
info.claude = readClaudeAccount(o.home, o.env ?? process.env);
|
|
23630
24173
|
if (org)
|
|
23631
24174
|
info.org = {
|
|
23632
24175
|
name: org.name,
|
|
@@ -23748,7 +24291,7 @@ var rememberSeat = (o, key, window, session, now, input) => {
|
|
|
23748
24291
|
const posts = [];
|
|
23749
24292
|
if (src && shouldPostSeat(prior, window, now)) {
|
|
23750
24293
|
state = markPosted(state, window, now);
|
|
23751
|
-
posts.push(postSeatWindow({ ...src, fetchFn: o.fetchFn, timeoutMs: o.timeoutMs ?? SEAT_POST_TIMEOUT_MS }, window));
|
|
24294
|
+
posts.push(postSeatWindow({ ...src, fetchFn: o.fetchFn, timeoutMs: o.timeoutMs ?? SEAT_POST_TIMEOUT_MS }, window, readClaudeAccount(o.home, o.env ?? process.env)));
|
|
23752
24295
|
}
|
|
23753
24296
|
const events = seatWallEvents(prior, window);
|
|
23754
24297
|
const credits = creditsEvent(o, prior, input, now);
|
|
@@ -23788,7 +24331,7 @@ var announce = (o, events) => {
|
|
|
23788
24331
|
|
|
23789
24332
|
// src/commands/stopEvent.ts
|
|
23790
24333
|
init_meter();
|
|
23791
|
-
import { existsSync as
|
|
24334
|
+
import { existsSync as existsSync28, readFileSync as readFileSync30 } from "node:fs";
|
|
23792
24335
|
|
|
23793
24336
|
// src/transcript.ts
|
|
23794
24337
|
var isObject16 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
@@ -23884,7 +24427,7 @@ async function postTranscript(o, payload, now) {
|
|
|
23884
24427
|
const src = meterSource(o.home);
|
|
23885
24428
|
if (!src)
|
|
23886
24429
|
return false;
|
|
23887
|
-
const read = o.readFile ?? ((p) =>
|
|
24430
|
+
const read = o.readFile ?? ((p) => existsSync28(p) ? readFileSync30(p, "utf8") : null);
|
|
23888
24431
|
let text;
|
|
23889
24432
|
try {
|
|
23890
24433
|
text = read(path);
|
|
@@ -23962,7 +24505,7 @@ var stopEvent = async (o) => {
|
|
|
23962
24505
|
|
|
23963
24506
|
// src/commands/uninstall.ts
|
|
23964
24507
|
import { spawnSync as spawnSync9 } from "node:child_process";
|
|
23965
|
-
import { existsSync as
|
|
24508
|
+
import { existsSync as existsSync29, rmSync as rmSync17 } from "node:fs";
|
|
23966
24509
|
init_config();
|
|
23967
24510
|
init_meter();
|
|
23968
24511
|
init_settings();
|
|
@@ -23975,7 +24518,7 @@ var uninstall2 = (o) => {
|
|
|
23975
24518
|
(o.log ?? console.log)(` ${codexLine}`);
|
|
23976
24519
|
const log = o.log ?? console.log;
|
|
23977
24520
|
const removed = [];
|
|
23978
|
-
if (
|
|
24521
|
+
if (existsSync29(settingsPath(o.home))) {
|
|
23979
24522
|
let settings = readSettings(o.home);
|
|
23980
24523
|
let changed = false;
|
|
23981
24524
|
if (hasEitherKey(settings)) {
|
|
@@ -24022,18 +24565,18 @@ var uninstall2 = (o) => {
|
|
|
24022
24565
|
writeSettings(o.home, settings);
|
|
24023
24566
|
}
|
|
24024
24567
|
for (const file of [meterCachePath(o.home), ...allSeatStatePaths(o.home)]) {
|
|
24025
|
-
if (
|
|
24568
|
+
if (existsSync29(file)) {
|
|
24026
24569
|
rmSync17(file, { force: true });
|
|
24027
24570
|
removed.push(file);
|
|
24028
24571
|
}
|
|
24029
24572
|
}
|
|
24030
24573
|
const skill = skillDir(o.home);
|
|
24031
|
-
if (
|
|
24574
|
+
if (existsSync29(skill)) {
|
|
24032
24575
|
rmSync17(skill, { recursive: true, force: true });
|
|
24033
24576
|
removed.push(skill);
|
|
24034
24577
|
}
|
|
24035
24578
|
const plist = musherPlistPath(o.home);
|
|
24036
|
-
if (
|
|
24579
|
+
if (existsSync29(plist)) {
|
|
24037
24580
|
(o.unloadAgent ?? defaultUnload)(plist);
|
|
24038
24581
|
rmSync17(plist, { force: true });
|
|
24039
24582
|
removed.push(`launchd agent ${musherLabel()}`);
|
|
@@ -24105,7 +24648,7 @@ var verify = async (o) => {
|
|
|
24105
24648
|
|
|
24106
24649
|
// src/commands/webhooks.ts
|
|
24107
24650
|
var USAGE2 = "usage: sleds webhooks [--json] | sleds webhooks deliveries <id or name> [--json]";
|
|
24108
|
-
var
|
|
24651
|
+
var str14 = (x) => typeof x === "string" ? x : "";
|
|
24109
24652
|
function ago3(at, now) {
|
|
24110
24653
|
const m = Math.max(0, Math.floor((now - at) / 60000));
|
|
24111
24654
|
if (m < 1)
|
|
@@ -24121,7 +24664,7 @@ function webhookLines(raw, now) {
|
|
|
24121
24664
|
if (!rows.length)
|
|
24122
24665
|
return ["no webhooks — add one in the app: Settings › Integrations › Webhooks"];
|
|
24123
24666
|
return rows.map((h) => {
|
|
24124
|
-
let host =
|
|
24667
|
+
let host = str14(h.url);
|
|
24125
24668
|
try {
|
|
24126
24669
|
host = new URL(host.replace(/\{[a-z.]+\}/g, "x")).host;
|
|
24127
24670
|
} catch {}
|
|
@@ -24129,21 +24672,21 @@ function webhookLines(raw, now) {
|
|
|
24129
24672
|
const last = h.lastDelivery;
|
|
24130
24673
|
const lastText = last?.at ? `last ${last.status ?? "—"} ${last.state ?? ""} ${ago3(last.at, now)}`.replace(/\s+/g, " ") : "nothing sent yet";
|
|
24131
24674
|
const off = h.enabled ? "on " : "off";
|
|
24132
|
-
const why = !h.enabled && h.disabledWhy ? ` (${
|
|
24133
|
-
return `${off} ${
|
|
24675
|
+
const why = !h.enabled && h.disabledWhy ? ` (${str14(h.disabledWhy)})` : "";
|
|
24676
|
+
return `${off} ${str14(h.name)} ${host} ${events.includes("*") ? "all events" : events.join(",")} ${lastText}${why} [${str14(h.id)}]`;
|
|
24134
24677
|
});
|
|
24135
24678
|
}
|
|
24136
24679
|
function deliveryLines(raw, now) {
|
|
24137
24680
|
const j = clean(raw);
|
|
24138
24681
|
const rows = Array.isArray(j.deliveries) ? j.deliveries : [];
|
|
24139
|
-
const name =
|
|
24682
|
+
const name = str14(j.webhook?.name);
|
|
24140
24683
|
if (!rows.length)
|
|
24141
24684
|
return [`${name}: nothing sent yet`];
|
|
24142
24685
|
return [
|
|
24143
24686
|
`${name} — the last ${rows.length}`,
|
|
24144
24687
|
...rows.map((d) => {
|
|
24145
|
-
const result = d.status !== null && d.status !== undefined ? String(d.status) :
|
|
24146
|
-
return `${
|
|
24688
|
+
const result = d.status !== null && d.status !== undefined ? String(d.status) : str14(d.error) || "—";
|
|
24689
|
+
return `${str14(d.state).padEnd(9)} ${result.padEnd(5)} ${str14(d.event).padEnd(20)} ${str14(d.key) || "—"} ${ago3(Number(d.at), now)} tries ${d.attempts} ${str14(d.deliveryId)}`;
|
|
24147
24690
|
})
|
|
24148
24691
|
];
|
|
24149
24692
|
}
|
|
@@ -24174,7 +24717,7 @@ var webhooksCmd = async (o) => {
|
|
|
24174
24717
|
return 1;
|
|
24175
24718
|
}
|
|
24176
24719
|
if (r.status !== 200 || !r.json) {
|
|
24177
|
-
const why =
|
|
24720
|
+
const why = str14(r.json?.error) || `HTTP ${r.status}`;
|
|
24178
24721
|
error(r.status === 404 && !r.json ? "SLEDS does not know this seat — run `sleds init`" : why);
|
|
24179
24722
|
return 1;
|
|
24180
24723
|
}
|
|
@@ -24188,13 +24731,13 @@ var webhooksCmd = async (o) => {
|
|
|
24188
24731
|
|
|
24189
24732
|
// src/config.ts
|
|
24190
24733
|
init_settings();
|
|
24191
|
-
import { chmodSync as chmodSync7, existsSync as
|
|
24192
|
-
import { join as
|
|
24734
|
+
import { chmodSync as chmodSync7, existsSync as existsSync30, mkdirSync as mkdirSync23, readFileSync as readFileSync31, renameSync as renameSync9, writeFileSync as writeFileSync26 } from "node:fs";
|
|
24735
|
+
import { join as join27, resolve as resolve11 } from "node:path";
|
|
24193
24736
|
var configDir2 = (home, env = process.env) => {
|
|
24194
24737
|
const o = env.SLEDS_HOME?.trim();
|
|
24195
|
-
return o ?
|
|
24738
|
+
return o ? resolve11(home, o) : join27(home, ".config", "sleds");
|
|
24196
24739
|
};
|
|
24197
|
-
var configPath2 = (home) =>
|
|
24740
|
+
var configPath2 = (home) => join27(configDir2(home), "config.json");
|
|
24198
24741
|
var isObject17 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
24199
24742
|
var optString2 = (v) => typeof v === "string" ? v : undefined;
|
|
24200
24743
|
var parsePriorEnv2 = (v) => {
|
|
@@ -24326,9 +24869,9 @@ var parseOrgInfo2 = (raw) => {
|
|
|
24326
24869
|
};
|
|
24327
24870
|
var readConfig2 = (home) => {
|
|
24328
24871
|
const p = configPath2(home);
|
|
24329
|
-
if (!
|
|
24872
|
+
if (!existsSync30(p))
|
|
24330
24873
|
return {};
|
|
24331
|
-
return parseConfig2(JSON.parse(
|
|
24874
|
+
return parseConfig2(JSON.parse(readFileSync31(p, "utf8")));
|
|
24332
24875
|
};
|
|
24333
24876
|
|
|
24334
24877
|
// src/runners/exec.ts
|
|
@@ -24380,15 +24923,15 @@ var defaultExec5 = (cmd, args) => {
|
|
|
24380
24923
|
};
|
|
24381
24924
|
|
|
24382
24925
|
// src/settings.ts
|
|
24383
|
-
import { dirname as dirname18, join as
|
|
24926
|
+
import { dirname as dirname18, join as join28, resolve as resolve12 } from "node:path";
|
|
24384
24927
|
var withSeatEnv2 = (command, home, env = process.env, org) => {
|
|
24385
24928
|
const parts = [];
|
|
24386
24929
|
const sh = env.SLEDS_HOME?.trim();
|
|
24387
24930
|
const cc = env.CLAUDE_CONFIG_DIR?.trim();
|
|
24388
24931
|
if (sh)
|
|
24389
|
-
parts.push(`SLEDS_HOME=${shellWord2(
|
|
24932
|
+
parts.push(`SLEDS_HOME=${shellWord2(resolve12(home, sh))}`);
|
|
24390
24933
|
if (cc)
|
|
24391
|
-
parts.push(`CLAUDE_CONFIG_DIR=${shellWord2(
|
|
24934
|
+
parts.push(`CLAUDE_CONFIG_DIR=${shellWord2(resolve12(home, cc))}`);
|
|
24392
24935
|
parts.push(command);
|
|
24393
24936
|
return pinOrg4(parts.join(" "), org);
|
|
24394
24937
|
};
|
|
@@ -24688,8 +25231,12 @@ var main = async (argv) => {
|
|
|
24688
25231
|
return engineLogCmd({ home, argv: argv.slice(2) });
|
|
24689
25232
|
if (cmd === "engine" && argv[1] === "list")
|
|
24690
25233
|
return engineListCmd({ home, argv: argv.slice(2) });
|
|
24691
|
-
if (cmd === "engine" && argv[1] === "install")
|
|
25234
|
+
if (cmd === "engine" && argv[1] === "install") {
|
|
25235
|
+
const ask = process.stdin.isTTY ? promptStdin : undefined;
|
|
25236
|
+
if (!await ensureReposDir({ home, argv: argv.slice(2), ...ask ? { ask } : {} }))
|
|
25237
|
+
return 1;
|
|
24692
25238
|
return engineInstall2({ home, argv: argv.slice(2), sledsArgv: sledsArgv() });
|
|
25239
|
+
}
|
|
24693
25240
|
if (cmd === "engine" && argv[1] === "uninstall")
|
|
24694
25241
|
return engineUninstall2({ home, argv: argv.slice(2), sledsArgv: sledsArgv() });
|
|
24695
25242
|
if (cmd === "engine" && argv[1] === "profile")
|
|
@@ -24875,7 +25422,7 @@ ${renderHelpText(argv[1])}`);
|
|
|
24875
25422
|
case "version":
|
|
24876
25423
|
case "--version":
|
|
24877
25424
|
case "-v":
|
|
24878
|
-
console.log("sleds 0.1.
|
|
25425
|
+
console.log("sleds 0.1.44");
|
|
24879
25426
|
return 0;
|
|
24880
25427
|
default:
|
|
24881
25428
|
console.error(`unknown command: ${cmd}
|