@sleds/cli 0.1.60 → 0.1.61
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 +1329 -454
- package/package.json +1 -1
package/dist/sleds.mjs
CHANGED
|
@@ -797,9 +797,9 @@ var init_verbs = __esm(() => {
|
|
|
797
797
|
id: "attach",
|
|
798
798
|
group: "meter",
|
|
799
799
|
want: "Put the spec, the prototype, a screenshot or a link on the item so the team can open it",
|
|
800
|
-
mcp: "attach {project, name, content, mime?, kind?} · attach {project, url, title?} — intake takes the same as `attachments` when filing",
|
|
800
|
+
mcp: "attach {project, name, content, mime?, kind?, mock?} · attach {project, url, title?} — intake takes the same as `attachments` when filing",
|
|
801
801
|
app: "Work › the item › Links",
|
|
802
|
-
how: "The file itself lives in SLEDS, not only in the chat that made it: a spec as markdown, a prototype as one HTML page (opens and renders in its own tab), a screenshot — or a link to a doc, a Figma, a Loom. Up to 4 MB; the same name again replaces. Anyone on the board opens it from the item; the person who added it, or an admin, can remove it."
|
|
802
|
+
how: "The file itself lives in SLEDS, not only in the chat that made it: a spec as markdown, a prototype as one HTML page (opens and renders in its own tab), a screenshot — or a link to a doc, a Figma, a Loom. Up to 4 MB; the same name again replaces. Mark the prototype or screenshot that shows the change with `mock: true` — the item's Overview shows it as the proposal. Anyone on the board opens it from the item; the person who added it, or an admin, can remove it."
|
|
803
803
|
},
|
|
804
804
|
{
|
|
805
805
|
id: "update",
|
|
@@ -1149,6 +1149,8 @@ function readForm(text) {
|
|
|
1149
1149
|
const val = (f) => plain(f.value.join(" "));
|
|
1150
1150
|
const one = (slot) => found.find((f) => f.slot === slot && val(f));
|
|
1151
1151
|
const ask = one("ask");
|
|
1152
|
+
const summary = one("summary");
|
|
1153
|
+
const expected = one("expected");
|
|
1152
1154
|
const users = one("users");
|
|
1153
1155
|
const sid = one("sid");
|
|
1154
1156
|
const loc = one("location");
|
|
@@ -1172,6 +1174,8 @@ function readForm(text) {
|
|
|
1172
1174
|
return {
|
|
1173
1175
|
form: def.name,
|
|
1174
1176
|
...ask ? { ask: val(ask) } : {},
|
|
1177
|
+
...summary ? { summary: val(summary) } : {},
|
|
1178
|
+
...expected ? { expected: val(expected) } : {},
|
|
1175
1179
|
...details ? { details } : {},
|
|
1176
1180
|
...touches ? { touches } : {},
|
|
1177
1181
|
...demand ? { demand } : {},
|
|
@@ -1196,6 +1200,8 @@ var init_forms = __esm(() => {
|
|
|
1196
1200
|
[/^sids?\b/, "sid"],
|
|
1197
1201
|
[/^location ids?\b/, "location"],
|
|
1198
1202
|
[/^issue description\b/, "ask"],
|
|
1203
|
+
[/^summary\b|^title\b/, "summary"],
|
|
1204
|
+
[/^expected (?:behaviou?r|result|outcome)\b/, "expected"],
|
|
1199
1205
|
[/^(?:number of )?clients? affected\b|^number of clients\b/, "clients"],
|
|
1200
1206
|
[/\bimpacting revenue\b|\brevenue impact\b/, "revenue"]
|
|
1201
1207
|
],
|
|
@@ -1281,8 +1287,12 @@ function draftBrief(input) {
|
|
|
1281
1287
|
const form = readForm(text);
|
|
1282
1288
|
const parsed = sections(text);
|
|
1283
1289
|
const preamble = parsed.preamble;
|
|
1284
|
-
const byKey = form ? {
|
|
1285
|
-
|
|
1290
|
+
const byKey = form ? {
|
|
1291
|
+
touches: form.touches,
|
|
1292
|
+
demand: form.demand,
|
|
1293
|
+
...form.expected ? { done: form.expected } : {}
|
|
1294
|
+
} : parsed.byKey;
|
|
1295
|
+
const fromText = form?.summary ? form.summary : form?.ask ? form.ask : askFrom(text, preamble);
|
|
1286
1296
|
const askText = a.ask?.trim() || (fromText ? titleOf(fromText) : undefined);
|
|
1287
1297
|
if (!askText)
|
|
1288
1298
|
return null;
|
|
@@ -3280,7 +3290,8 @@ function parseAttachments(raw) {
|
|
|
3280
3290
|
mime,
|
|
3281
3291
|
content,
|
|
3282
3292
|
...str5(a.kind) ? { kind: str5(a.kind) } : {},
|
|
3283
|
-
...a.encoding === "base64" ? { encoding: "base64" } : {}
|
|
3293
|
+
...a.encoding === "base64" ? { encoding: "base64" } : {},
|
|
3294
|
+
...typeof a.mock === "boolean" ? { mock: a.mock } : {}
|
|
3284
3295
|
});
|
|
3285
3296
|
}
|
|
3286
3297
|
return out.slice(0, 12);
|
|
@@ -4777,7 +4788,11 @@ var init_server = __esm(() => {
|
|
|
4777
4788
|
},
|
|
4778
4789
|
mime: { type: "string", description: "text/markdown, text/html, image/png…" },
|
|
4779
4790
|
content: { type: "string", description: "The file: text as is, or base64." },
|
|
4780
|
-
encoding: { type: "string", enum: ["text", "base64"] }
|
|
4791
|
+
encoding: { type: "string", enum: ["text", "base64"] },
|
|
4792
|
+
mock: {
|
|
4793
|
+
type: "boolean",
|
|
4794
|
+
description: "true on the prototype or screenshot that shows the change — the item's Overview shows it as the proposal."
|
|
4795
|
+
}
|
|
4781
4796
|
},
|
|
4782
4797
|
required: ["name", "content"]
|
|
4783
4798
|
}
|
|
@@ -5160,7 +5175,7 @@ var init_server = __esm(() => {
|
|
|
5160
5175
|
},
|
|
5161
5176
|
{
|
|
5162
5177
|
name: "attach",
|
|
5163
|
-
description: "Put a file or a link on an item so anyone on the board can open it — a spec or PRD as markdown, a prototype you made as one HTML page (it opens and renders in its own tab), a screenshot; or a URL (a doc, a Figma, a Loom) with `url` instead of a file. Pass the item's key (QW-142, from status, or what intake said). Files up to 4 MB; the same name again replaces the earlier one. `intake` takes the same files as `attachments` when filing.",
|
|
5178
|
+
description: "Put a file or a link on an item so anyone on the board can open it — a spec or PRD as markdown, a prototype you made as one HTML page (it opens and renders in its own tab), a screenshot; or a URL (a doc, a Figma, a Loom) with `url` instead of a file. Pass the item's key (QW-142, from status, or what intake said). Files up to 4 MB; the same name again replaces the earlier one. Mark the prototype or screenshot that shows the change with `mock: true` — the item's Overview shows it as the proposal. `intake` takes the same files as `attachments` when filing.",
|
|
5164
5179
|
inputSchema: {
|
|
5165
5180
|
type: "object",
|
|
5166
5181
|
properties: {
|
|
@@ -5184,7 +5199,11 @@ var init_server = __esm(() => {
|
|
|
5184
5199
|
type: "string",
|
|
5185
5200
|
description: "The file: text as is, or base64 with encoding base64."
|
|
5186
5201
|
},
|
|
5187
|
-
encoding: { type: "string", enum: ["text", "base64"] }
|
|
5202
|
+
encoding: { type: "string", enum: ["text", "base64"] },
|
|
5203
|
+
mock: {
|
|
5204
|
+
type: "boolean",
|
|
5205
|
+
description: "true on the prototype or screenshot that shows the change — the item's Overview shows it as the proposal. false clears the mark; omitted, a replaced file keeps its mark."
|
|
5206
|
+
}
|
|
5188
5207
|
},
|
|
5189
5208
|
required: ["project"]
|
|
5190
5209
|
}
|
|
@@ -5708,7 +5727,7 @@ var init_agent = __esm(() => {
|
|
|
5708
5727
|
REPLY_RULES = [
|
|
5709
5728
|
"How to answer this reply:",
|
|
5710
5729
|
'- If it answers nothing and changes nothing — thanks, "noted", "copy", "will do", or people talking to each other — set quiet to true and post nothing.',
|
|
5711
|
-
"- Never ask a question the thread already asked
|
|
5730
|
+
"- Never ask a question the thread already asked — by you or by anyone — and never ask one the thread has already answered, in anyone's reply. Read every reply in the thread as an answer first.",
|
|
5712
5731
|
"- If someone in the thread already gave the cause or a fix, do not ask the poster for details it makes moot. Say in one line what is left, or set quiet.",
|
|
5713
5732
|
"- Keep it to what changed and what is next. No recap of the ask."
|
|
5714
5733
|
].join(`
|
|
@@ -6731,12 +6750,14 @@ var init_sentry = () => {};
|
|
|
6731
6750
|
var init_outside = () => {};
|
|
6732
6751
|
|
|
6733
6752
|
// ../../packages/shared/src/pack/pack.ts
|
|
6753
|
+
var LANDING_GAP_MS;
|
|
6734
6754
|
var init_pack = __esm(() => {
|
|
6735
6755
|
init_flags();
|
|
6736
6756
|
init_profile();
|
|
6737
6757
|
init_sentry();
|
|
6738
6758
|
init_outside();
|
|
6739
6759
|
init_setup();
|
|
6760
|
+
LANDING_GAP_MS = 10 * 60000;
|
|
6740
6761
|
});
|
|
6741
6762
|
// ../../packages/shared/src/pack/prHeader.ts
|
|
6742
6763
|
var PR_HEADER_OPEN = "<!-- sleds:header -->", PR_HEADER_CLOSE = "<!-- /sleds:header -->";
|
|
@@ -6819,6 +6840,11 @@ var init_musher = __esm(() => {
|
|
|
6819
6840
|
init_plan4();
|
|
6820
6841
|
});
|
|
6821
6842
|
|
|
6843
|
+
// ../../packages/shared/src/route/handoff.ts
|
|
6844
|
+
var init_handoff = __esm(() => {
|
|
6845
|
+
init_route();
|
|
6846
|
+
});
|
|
6847
|
+
|
|
6822
6848
|
// ../../packages/shared/src/route/doneChecks.ts
|
|
6823
6849
|
function shellWords(s) {
|
|
6824
6850
|
const out = [];
|
|
@@ -7034,6 +7060,55 @@ var init_doneChecks = __esm(() => {
|
|
|
7034
7060
|
// ../../packages/shared/src/route/routeFix.ts
|
|
7035
7061
|
var init_routeFix = () => {};
|
|
7036
7062
|
|
|
7063
|
+
// ../../packages/shared/src/route/reshape.ts
|
|
7064
|
+
var init_reshape = __esm(() => {
|
|
7065
|
+
init_route();
|
|
7066
|
+
});
|
|
7067
|
+
|
|
7068
|
+
// ../../packages/shared/src/route/say.ts
|
|
7069
|
+
var VERB_MODAL, ORDER_VERB, each = (a, b, f) => a.flatMap((x) => b.map((y) => f(x, y))), FORMS2;
|
|
7070
|
+
var init_say = __esm(() => {
|
|
7071
|
+
init_reshape();
|
|
7072
|
+
init_route();
|
|
7073
|
+
VERB_MODAL = String.raw`(?:(?:should|must|can|has\s+to|have\s+to|needs?\s+to)\s+)?`;
|
|
7074
|
+
ORDER_VERB = String.raw`(?:${VERB_MODAL}(?:comes?|runs?|go|goes|starts?|be|is|are)\s+)?`;
|
|
7075
|
+
FORMS2 = [
|
|
7076
|
+
{
|
|
7077
|
+
kind: "pair",
|
|
7078
|
+
re: /^(?:merge|fold|combine)\s+(.+?)\s+into\s+(.+)$/i,
|
|
7079
|
+
ops: (a, b) => each(a, b, (absorb, into) => ({ op: "merge", absorb, into }))
|
|
7080
|
+
},
|
|
7081
|
+
{ kind: "split", re: /^split\s+(.+?)(?:\s*,?\s+(?:moving|move|taking|with|off)\s+(.+))?$/i },
|
|
7082
|
+
{ kind: "rename", re: /^(?:rename|retitle)\s+(.+?)\s+(?:to|as)\s+(.+)$/i },
|
|
7083
|
+
{ kind: "move", re: /^move\s+(.+?)\s+from\s+(.+?)\s+(?:to|into|onto)\s+(.+)$/i },
|
|
7084
|
+
{
|
|
7085
|
+
kind: "pair",
|
|
7086
|
+
re: /^unblock\s+(.+?)\s+from\s+(.+)$/i,
|
|
7087
|
+
ops: (a, b) => each(a, b, (node, on) => ({ op: "unwait", node, on }))
|
|
7088
|
+
},
|
|
7089
|
+
{
|
|
7090
|
+
kind: "pair",
|
|
7091
|
+
re: /^(.+?)\s+(?:no\s+longer|doesn't|does\s+not|don't|do\s+not|shouldn't|should\s+not|needn't|need\s+not|(?:should\s+|can\s+)?stops?)\s+(?:waits?|waiting|depends?|depending)\s+(?:on|for|upon)\s+(.+)$/i,
|
|
7092
|
+
ops: (a, b) => each(a, b, (node, on) => ({ op: "unwait", node, on }))
|
|
7093
|
+
},
|
|
7094
|
+
{
|
|
7095
|
+
kind: "pair",
|
|
7096
|
+
re: new RegExp(String.raw`^(?:make\s+)?(.+?)\s+${VERB_MODAL}(?:waits?|depends?)\s+(?:on|for|upon)\s+(.+)$`, "i"),
|
|
7097
|
+
ops: (a, b) => each(a, b, (node, on) => ({ op: "wait", node, on }))
|
|
7098
|
+
},
|
|
7099
|
+
{
|
|
7100
|
+
kind: "pair",
|
|
7101
|
+
re: new RegExp(String.raw`^(.+?)\s+${ORDER_VERB}after\s+(.+)$`, "i"),
|
|
7102
|
+
ops: (a, b) => each(a, b, (node, on) => ({ op: "wait", node, on }))
|
|
7103
|
+
},
|
|
7104
|
+
{
|
|
7105
|
+
kind: "pair",
|
|
7106
|
+
re: new RegExp(String.raw`^(.+?)\s+${ORDER_VERB}before\s+(.+)$`, "i"),
|
|
7107
|
+
ops: (a, b) => each(b, a, (node, on) => ({ op: "wait", node, on }))
|
|
7108
|
+
}
|
|
7109
|
+
];
|
|
7110
|
+
});
|
|
7111
|
+
|
|
7037
7112
|
// ../../packages/shared/src/route/engine.ts
|
|
7038
7113
|
function seatSlots(atOnce) {
|
|
7039
7114
|
if (typeof atOnce !== "number" || !Number.isFinite(atOnce))
|
|
@@ -7278,6 +7353,26 @@ function draftPrompt(o) {
|
|
|
7278
7353
|
${claim.pack.trim()}
|
|
7279
7354
|
`;
|
|
7280
7355
|
}
|
|
7356
|
+
function mockPrompt(o) {
|
|
7357
|
+
const base = o.base ?? "main";
|
|
7358
|
+
const lines = [
|
|
7359
|
+
`You are drawing a mock for ${o.item} — "${o.title}"${o.engine ? ` — started by ${o.engine}` : ""}. Nobody is at the keyboard: a person signs the mock before the build gets its nodes.`,
|
|
7360
|
+
"",
|
|
7361
|
+
"- Draw one self-contained HTML file: CSS and JS inline, no other files, nothing loaded from the network. Build it from the repo's real design tokens and components (read them in the code: colors, type, spacing, radii) and data shaped like the real thing.",
|
|
7362
|
+
"- Draw it on the screen the change lives on: find that page or component in this checkout and show it as it is today, with the change in place, so a person can compare it with the product. If the change has no screen of its own, draw the one a person sees it from.",
|
|
7363
|
+
`- Write the HTML only to \`${o.out}\` — that one file, outside this checkout. Write nothing anywhere else.`,
|
|
7364
|
+
`- Change no code: this checkout is \`origin/${base}\`, for reading. Do not edit, create or delete a file in it, and make no branch, commit, push or PR. The engine checks: a checkout left changed, or a commit, fails the run and nothing is attached.`,
|
|
7365
|
+
"- Ask nobody: a mock run cannot park. Where the brief leaves something open, draw the plainest reading and say it in a short note inside the HTML.",
|
|
7366
|
+
"- Once the file is written, stop. The engine reads it and attaches it to the item as its mock, marked as the next version."
|
|
7367
|
+
];
|
|
7368
|
+
return `${lines.join(`
|
|
7369
|
+
`)}
|
|
7370
|
+
|
|
7371
|
+
---
|
|
7372
|
+
|
|
7373
|
+
${o.pack.trim()}
|
|
7374
|
+
`;
|
|
7375
|
+
}
|
|
7281
7376
|
function quote(text) {
|
|
7282
7377
|
return text.trim().split(`
|
|
7283
7378
|
`).map((l) => l ? `> ${l}` : ">");
|
|
@@ -7542,7 +7637,7 @@ function eventPrompt(o) {
|
|
|
7542
7637
|
return reviewPrompt({ ...o, event });
|
|
7543
7638
|
return ciTwicePrompt({ ...o, event });
|
|
7544
7639
|
}
|
|
7545
|
-
var DEFAULT_SEAT = "\x00default", MAX_SEAT_AT_ONCE = 16, HOLD_DEFAULTS, DRAFT_NODE = "draft", BRANCH_FIELDS, PR_TITLE_FIELDS, ENGINE_MCP_SERVER = "sleds", RUN_TOKEN_ENV = "SLEDS_RUN_TOKEN", NO_RUN_TOKEN = "no run token from SLEDS: this session acts as the seat", SANDBOX_DOMAIN, SANDBOX_DEFAULT_ALLOW, toolName = (name, engineMcp) => engineMcp ? `mcp__${ENGINE_MCP_SERVER}__${name}` : name, ENGINE_MARK = "<!-- sleds:engine -->", VISUAL_PATH, APPROACH_SHAPE, AUTONOMY_SAYS, IRREVERSIBLE = "deleting or migrating data in place; deploying outside the playbook's window; anything that leaves the org (a message to a customer, a publish, a release, an email); spend past the band or the allowance; force-pushing or deleting a branch someone else holds; granting or removing access; changing the brief. When in doubt, it is irreversible.", oneLine = (s, max) => s.replace(/\s+/g, " ").trim().slice(0, max), VERDICT_SHAPE, DECIDER_RULES, codes = (xs) => xs.map((x) => `\`${x}\``).join(", "), RESOLVE_MAX_FILES = 8;
|
|
7640
|
+
var DEFAULT_SEAT = "\x00default", MAX_SEAT_AT_ONCE = 16, HOLD_DEFAULTS, DRAFT_NODE = "draft", MOCK_NODE = "mock", BRANCH_FIELDS, PR_TITLE_FIELDS, ENGINE_MCP_SERVER = "sleds", RUN_TOKEN_ENV = "SLEDS_RUN_TOKEN", NO_RUN_TOKEN = "no run token from SLEDS: this session acts as the seat", SANDBOX_DOMAIN, SANDBOX_DEFAULT_ALLOW, toolName = (name, engineMcp) => engineMcp ? `mcp__${ENGINE_MCP_SERVER}__${name}` : name, ENGINE_MARK = "<!-- sleds:engine -->", VISUAL_PATH, APPROACH_SHAPE, AUTONOMY_SAYS, IRREVERSIBLE = "deleting or migrating data in place; deploying outside the playbook's window; anything that leaves the org (a message to a customer, a publish, a release, an email); spend past the band or the allowance; force-pushing or deleting a branch someone else holds; granting or removing access; changing the brief. When in doubt, it is irreversible.", oneLine = (s, max) => s.replace(/\s+/g, " ").trim().slice(0, max), VERDICT_SHAPE, DECIDER_RULES, codes = (xs) => xs.map((x) => `\`${x}\``).join(", "), RESOLVE_MAX_FILES = 8;
|
|
7546
7641
|
var init_engine = __esm(() => {
|
|
7547
7642
|
init_prHeader();
|
|
7548
7643
|
init_route();
|
|
@@ -7727,6 +7822,18 @@ var BASE_URL_KEY = "ANTHROPIC_BASE_URL", AUTH_TOKEN_KEY = "ANTHROPIC_AUTH_TOKEN"
|
|
|
7727
7822
|
return { ...settings };
|
|
7728
7823
|
const { [BASE_URL_KEY]: _url, [AUTH_TOKEN_KEY]: _token, ...rest } = settings.env;
|
|
7729
7824
|
return { ...settings, env: rest };
|
|
7825
|
+
}, bareUrl = (u) => u.trim().replace(/[./]+$/, "").toLowerCase(), isSledsProxyEnv = (settings, proxyUrls = []) => {
|
|
7826
|
+
const env = envOf(settings);
|
|
7827
|
+
const url = env[BASE_URL_KEY];
|
|
7828
|
+
const token = env[AUTH_TOKEN_KEY];
|
|
7829
|
+
if (typeof token === "string" && isSeatToken(token.trim()))
|
|
7830
|
+
return true;
|
|
7831
|
+
if (typeof url !== "string")
|
|
7832
|
+
return false;
|
|
7833
|
+
return proxyUrls.some((p) => p !== undefined && bareUrl(p) === bareUrl(url));
|
|
7834
|
+
}, hasGatewayEnv = (settings) => {
|
|
7835
|
+
const env = envOf(settings);
|
|
7836
|
+
return BASE_URL_KEY in env || AUTH_TOKEN_KEY in env;
|
|
7730
7837
|
}, readProxy = (settings) => {
|
|
7731
7838
|
const env = envOf(settings);
|
|
7732
7839
|
const url = env[BASE_URL_KEY];
|
|
@@ -7734,7 +7841,7 @@ var BASE_URL_KEY = "ANTHROPIC_BASE_URL", AUTH_TOKEN_KEY = "ANTHROPIC_AUTH_TOKEN"
|
|
|
7734
7841
|
if (typeof url !== "string" || typeof token !== "string")
|
|
7735
7842
|
return null;
|
|
7736
7843
|
return { url, token };
|
|
7737
|
-
}, STATUS_LINE_KEY = "statusLine", statusLineEntry = (command) => ({ type: "command", command }), isSledsStatusLine = (value) => isObject5(value) && value.type === "command" && typeof value.command === "string" && /\bstatusline\b/.test(value.command), applyStatusLine = (settings, command) => {
|
|
7844
|
+
}, STATUS_LINE_KEY = "statusLine", statusLineEntry = (command) => ({ type: "command", command }), invokesSleds = (command) => /\bsleds\b/.test(command), isSledsStatusLine = (value) => isObject5(value) && value.type === "command" && typeof value.command === "string" && invokesSleds(value.command) && /\bstatusline\b/.test(value.command), applyStatusLine = (settings, command) => {
|
|
7738
7845
|
const current = settings[STATUS_LINE_KEY];
|
|
7739
7846
|
const prior = current === undefined || isSledsStatusLine(current) ? undefined : current;
|
|
7740
7847
|
return { settings: { ...settings, [STATUS_LINE_KEY]: statusLineEntry(command) }, prior };
|
|
@@ -7743,11 +7850,12 @@ var BASE_URL_KEY = "ANTHROPIC_BASE_URL", AUTH_TOKEN_KEY = "ANTHROPIC_AUTH_TOKEN"
|
|
|
7743
7850
|
return { ...settings };
|
|
7744
7851
|
const { [STATUS_LINE_KEY]: _ours, ...rest } = settings;
|
|
7745
7852
|
return prior === undefined ? rest : { ...rest, [STATUS_LINE_KEY]: prior };
|
|
7746
|
-
}, HOOKS_KEY = "hooks", NOTIFICATION_EVENT = "Notification", SEAT_EVENT_MATCHER = "quota_auto_resume_fired|quota_auto_resume_stale|quota_auto_resume_disabled", SEAT_EVENT_TIMEOUT_S = 10,
|
|
7853
|
+
}, HOOKS_KEY = "hooks", NOTIFICATION_EVENT = "Notification", SEAT_EVENT_MATCHER = "quota_auto_resume_fired|quota_auto_resume_stale|quota_auto_resume_disabled", SEAT_EVENT_TIMEOUT_S = 10, isSledsHookFor = (sub) => (entry) => {
|
|
7747
7854
|
if (!isObject5(entry) || !Array.isArray(entry.hooks))
|
|
7748
7855
|
return false;
|
|
7749
|
-
|
|
7750
|
-
|
|
7856
|
+
const re = new RegExp(`\\b${sub}\\b`);
|
|
7857
|
+
return entry.hooks.some((h) => isObject5(h) && h.type === "command" && typeof h.command === "string" && invokesSleds(h.command) && re.test(h.command));
|
|
7858
|
+
}, isSledsSeatHook, seatHookEntry = (command) => ({
|
|
7751
7859
|
matcher: SEAT_EVENT_MATCHER,
|
|
7752
7860
|
hooks: [{ type: "command", command, timeout: SEAT_EVENT_TIMEOUT_S }]
|
|
7753
7861
|
}), notificationHooks = (settings) => {
|
|
@@ -7778,11 +7886,7 @@ var BASE_URL_KEY = "ANTHROPIC_BASE_URL", AUTH_TOKEN_KEY = "ANTHROPIC_AUTH_TOKEN"
|
|
|
7778
7886
|
if (!list.some(isSledsSeatHook))
|
|
7779
7887
|
return { ...settings };
|
|
7780
7888
|
return withNotificationHooks(settings, list.filter((e) => !isSledsSeatHook(e)));
|
|
7781
|
-
}, hasSeatHook = (settings) => notificationHooks(settings).some(isSledsSeatHook), POST_TOOL_EVENT = "PostToolUse", SPEC_EVENT_MATCHER = "Write|Edit|MultiEdit", SPEC_EVENT_TIMEOUT_S = 5, isSledsSpecHook = (
|
|
7782
|
-
if (!isObject5(entry) || !Array.isArray(entry.hooks))
|
|
7783
|
-
return false;
|
|
7784
|
-
return entry.hooks.some((h) => isObject5(h) && h.type === "command" && typeof h.command === "string" && /\bspec-event\b/.test(h.command));
|
|
7785
|
-
}, specHookEntry = (command) => ({
|
|
7889
|
+
}, hasSeatHook = (settings) => notificationHooks(settings).some(isSledsSeatHook), POST_TOOL_EVENT = "PostToolUse", SPEC_EVENT_MATCHER = "Write|Edit|MultiEdit", SPEC_EVENT_TIMEOUT_S = 5, isSledsSpecHook, specHookEntry = (command) => ({
|
|
7786
7890
|
matcher: SPEC_EVENT_MATCHER,
|
|
7787
7891
|
hooks: [{ type: "command", command, timeout: SPEC_EVENT_TIMEOUT_S }]
|
|
7788
7892
|
}), eventHooks = (settings, event) => {
|
|
@@ -7813,11 +7917,7 @@ var BASE_URL_KEY = "ANTHROPIC_BASE_URL", AUTH_TOKEN_KEY = "ANTHROPIC_AUTH_TOKEN"
|
|
|
7813
7917
|
if (!list.some(isSledsSpecHook))
|
|
7814
7918
|
return { ...settings };
|
|
7815
7919
|
return withEventHooks(settings, POST_TOOL_EVENT, list.filter((e) => !isSledsSpecHook(e)));
|
|
7816
|
-
}, hasSpecHook = (settings) => eventHooks(settings, POST_TOOL_EVENT).some(isSledsSpecHook), PROMPT_EVENT = "UserPromptSubmit", PROMPT_EVENT_TIMEOUT_S = 3, isSledsPromptHook = (
|
|
7817
|
-
if (!isObject5(entry) || !Array.isArray(entry.hooks))
|
|
7818
|
-
return false;
|
|
7819
|
-
return entry.hooks.some((h) => isObject5(h) && h.type === "command" && typeof h.command === "string" && /\bprompt-event\b/.test(h.command));
|
|
7820
|
-
}, promptHookEntry = (command) => ({
|
|
7920
|
+
}, hasSpecHook = (settings) => eventHooks(settings, POST_TOOL_EVENT).some(isSledsSpecHook), PROMPT_EVENT = "UserPromptSubmit", PROMPT_EVENT_TIMEOUT_S = 3, isSledsPromptHook, promptHookEntry = (command) => ({
|
|
7821
7921
|
hooks: [{ type: "command", command, timeout: PROMPT_EVENT_TIMEOUT_S }]
|
|
7822
7922
|
}), applyPromptHook = (settings, command) => {
|
|
7823
7923
|
const list = eventHooks(settings, PROMPT_EVENT);
|
|
@@ -7830,12 +7930,7 @@ var BASE_URL_KEY = "ANTHROPIC_BASE_URL", AUTH_TOKEN_KEY = "ANTHROPIC_AUTH_TOKEN"
|
|
|
7830
7930
|
if (!list.some(isSledsPromptHook))
|
|
7831
7931
|
return { ...settings };
|
|
7832
7932
|
return withEventHooks(settings, PROMPT_EVENT, list.filter((e) => !isSledsPromptHook(e)));
|
|
7833
|
-
}, hasPromptHook = (settings) => eventHooks(settings, PROMPT_EVENT).some(isSledsPromptHook), SESSION_EVENT = "SessionStart", SESSION_EVENT_TIMEOUT_S = 3, STOP_EVENT = "Stop", STOP_EVENT_TIMEOUT_S = 3,
|
|
7834
|
-
if (!isObject5(entry) || !Array.isArray(entry.hooks))
|
|
7835
|
-
return false;
|
|
7836
|
-
const re = new RegExp(`\\b${sub}\\b`);
|
|
7837
|
-
return entry.hooks.some((h) => isObject5(h) && h.type === "command" && typeof h.command === "string" && re.test(h.command));
|
|
7838
|
-
}, isSledsSessionHook, isSledsStopHook, sessionHookEntry = (command) => ({
|
|
7933
|
+
}, hasPromptHook = (settings) => eventHooks(settings, PROMPT_EVENT).some(isSledsPromptHook), SESSION_EVENT = "SessionStart", SESSION_EVENT_TIMEOUT_S = 3, STOP_EVENT = "Stop", STOP_EVENT_TIMEOUT_S = 3, isSledsSessionHook, isSledsStopHook, sessionHookEntry = (command) => ({
|
|
7839
7934
|
hooks: [{ type: "command", command, timeout: SESSION_EVENT_TIMEOUT_S }]
|
|
7840
7935
|
}), stopHookEntry = (command) => ({
|
|
7841
7936
|
hooks: [{ type: "command", command, timeout: STOP_EVENT_TIMEOUT_S }]
|
|
@@ -7890,6 +7985,9 @@ var BASE_URL_KEY = "ANTHROPIC_BASE_URL", AUTH_TOKEN_KEY = "ANTHROPIC_AUTH_TOKEN"
|
|
|
7890
7985
|
var init_settings = __esm(() => {
|
|
7891
7986
|
LEADING_EXPORTS = /^(?:[A-Za-z_][A-Za-z0-9_]*=(?:'[^']*'|\S+)\s+)*/;
|
|
7892
7987
|
ORG_EXPORT = /(?:^|\s)SLEDS_ORG=(?:'[^']*'|\S+)(?=\s)/;
|
|
7988
|
+
isSledsSeatHook = isSledsHookFor("seat-event");
|
|
7989
|
+
isSledsSpecHook = isSledsHookFor("spec-event");
|
|
7990
|
+
isSledsPromptHook = isSledsHookFor("prompt-event");
|
|
7893
7991
|
isSledsSessionHook = isSledsHookFor("session-event");
|
|
7894
7992
|
isSledsStopHook = isSledsHookFor("stop-event");
|
|
7895
7993
|
isSledsPermissionHook = isSledsHookFor("permission-event");
|
|
@@ -7946,6 +8044,9 @@ var configDir = (home, env = process.env) => {
|
|
|
7946
8044
|
cfg.schedule = schedule;
|
|
7947
8045
|
if (raw.priorStatusLine !== undefined)
|
|
7948
8046
|
cfg.priorStatusLine = raw.priorStatusLine;
|
|
8047
|
+
const priorGatewayEnv = parsePriorEnv(raw.priorGatewayEnv);
|
|
8048
|
+
if (priorGatewayEnv !== undefined)
|
|
8049
|
+
cfg.priorGatewayEnv = priorGatewayEnv;
|
|
7949
8050
|
if (raw.wiring === "seat-first" || raw.wiring === "proxy-always")
|
|
7950
8051
|
cfg.wiring = raw.wiring;
|
|
7951
8052
|
if (raw.agentKind === "claude-code" || raw.agentKind === "codex")
|
|
@@ -8114,9 +8215,11 @@ var init_config = __esm(() => {
|
|
|
8114
8215
|
});
|
|
8115
8216
|
|
|
8116
8217
|
// src/meter.ts
|
|
8218
|
+
import { createHash } from "node:crypto";
|
|
8117
8219
|
import { existsSync as existsSync6, mkdirSync as mkdirSync6, readFileSync as readFileSync6, renameSync as renameSync3, writeFileSync as writeFileSync6 } from "node:fs";
|
|
8118
8220
|
import { join as join6 } from "node:path";
|
|
8119
|
-
var METER_CACHE_TTL_MS = 1e4, METER_TIMEOUT_MS = 1500, isObject7 = (v) => typeof v === "object" && v !== null && !Array.isArray(v), num4 = (v) => typeof v === "number" && Number.isFinite(v) ? v : undefined, meterCachePath = (home) => join6(configDir(home), "meter.json"),
|
|
8221
|
+
var METER_CACHE_TTL_MS = 1e4, METER_TIMEOUT_MS = 1500, isObject7 = (v) => typeof v === "object" && v !== null && !Array.isArray(v), num4 = (v) => typeof v === "number" && Number.isFinite(v) ? v : undefined, meterCachePath = (home) => join6(configDir(home), "meter.json"), meterCacheKey = (src) => createHash("sha256").update(`${src.proxyUrl}
|
|
8222
|
+
${src.token}`).digest("hex").slice(0, 16), readMeterCache = (home, now, source) => {
|
|
8120
8223
|
const p = meterCachePath(home);
|
|
8121
8224
|
if (!existsSync6(p))
|
|
8122
8225
|
return null;
|
|
@@ -8124,6 +8227,8 @@ var METER_CACHE_TTL_MS = 1e4, METER_TIMEOUT_MS = 1500, isObject7 = (v) => typeof
|
|
|
8124
8227
|
const raw = JSON.parse(readFileSync6(p, "utf8"));
|
|
8125
8228
|
if (!isObject7(raw))
|
|
8126
8229
|
return null;
|
|
8230
|
+
if (source !== undefined && raw.source !== source)
|
|
8231
|
+
return null;
|
|
8127
8232
|
const fetchedAt = num4(raw.fetchedAt);
|
|
8128
8233
|
if (fetchedAt === undefined || now - fetchedAt >= METER_CACHE_TTL_MS || fetchedAt > now)
|
|
8129
8234
|
return null;
|
|
@@ -8131,12 +8236,12 @@ var METER_CACHE_TTL_MS = 1e4, METER_TIMEOUT_MS = 1500, isObject7 = (v) => typeof
|
|
|
8131
8236
|
} catch {
|
|
8132
8237
|
return null;
|
|
8133
8238
|
}
|
|
8134
|
-
}, writeMeterCache = (home, meter, now) => {
|
|
8239
|
+
}, writeMeterCache = (home, meter, now, source) => {
|
|
8135
8240
|
const dir = configDir(home);
|
|
8136
8241
|
mkdirSync6(dir, { recursive: true, mode: 448 });
|
|
8137
8242
|
const target = meterCachePath(home);
|
|
8138
8243
|
const tmp = `${target}.${process.pid}.tmp`;
|
|
8139
|
-
const file = { fetchedAt: now, meter };
|
|
8244
|
+
const file = { fetchedAt: now, meter, ...source ? { source } : {} };
|
|
8140
8245
|
writeFileSync6(tmp, `${JSON.stringify(file)}
|
|
8141
8246
|
`, { mode: 384 });
|
|
8142
8247
|
renameSync3(tmp, target);
|
|
@@ -8167,16 +8272,17 @@ var METER_CACHE_TTL_MS = 1e4, METER_TIMEOUT_MS = 1500, isObject7 = (v) => typeof
|
|
|
8167
8272
|
return proxyUrl && token ? { proxyUrl, token } : null;
|
|
8168
8273
|
}, loadMeter = async (o) => {
|
|
8169
8274
|
const now = o.now ?? Date.now();
|
|
8170
|
-
const
|
|
8171
|
-
if (cached)
|
|
8172
|
-
return cached;
|
|
8173
|
-
const src = meterSource(o.home);
|
|
8275
|
+
const src = meterSource(o.home, undefined, { env: o.env });
|
|
8174
8276
|
if (!src)
|
|
8175
8277
|
return null;
|
|
8278
|
+
const key = meterCacheKey(src);
|
|
8279
|
+
const cached = readMeterCache(o.home, now, key);
|
|
8280
|
+
if (cached)
|
|
8281
|
+
return cached;
|
|
8176
8282
|
const fresh = await fetchMeter({ ...src, fetchFn: o.fetchFn, timeoutMs: o.timeoutMs });
|
|
8177
8283
|
if (fresh) {
|
|
8178
8284
|
try {
|
|
8179
|
-
writeMeterCache(o.home, fresh, now);
|
|
8285
|
+
writeMeterCache(o.home, fresh, now, key);
|
|
8180
8286
|
} catch {}
|
|
8181
8287
|
}
|
|
8182
8288
|
return fresh;
|
|
@@ -8241,8 +8347,11 @@ var init_src = __esm(() => {
|
|
|
8241
8347
|
init_newrelic();
|
|
8242
8348
|
init_musher();
|
|
8243
8349
|
init_route();
|
|
8350
|
+
init_handoff();
|
|
8244
8351
|
init_doneChecks();
|
|
8245
8352
|
init_routeFix();
|
|
8353
|
+
init_reshape();
|
|
8354
|
+
init_say();
|
|
8246
8355
|
init_engine();
|
|
8247
8356
|
init_sources();
|
|
8248
8357
|
init_otlp();
|
|
@@ -8640,7 +8749,15 @@ function parseRun(v) {
|
|
|
8640
8749
|
item,
|
|
8641
8750
|
node,
|
|
8642
8751
|
title: typeof title === "string" ? title : "",
|
|
8643
|
-
...v.kind === "draft" ? { kind:
|
|
8752
|
+
...v.kind === "draft" || v.kind === "mock" ? { kind: v.kind } : {},
|
|
8753
|
+
...isObject8(v.mock) && typeof v.mock.out === "string" && typeof v.mock.base === "string" ? {
|
|
8754
|
+
mock: {
|
|
8755
|
+
out: v.mock.out,
|
|
8756
|
+
base: v.mock.base,
|
|
8757
|
+
...typeof v.mock.model === "string" ? { model: v.mock.model } : {},
|
|
8758
|
+
...typeof v.mock.drawn === "string" ? { drawn: v.mock.drawn } : {}
|
|
8759
|
+
}
|
|
8760
|
+
} : {},
|
|
8644
8761
|
...typeof v.seatId === "string" ? { seatId: v.seatId } : {},
|
|
8645
8762
|
...typeof v.runId === "string" ? { runId: v.runId } : {},
|
|
8646
8763
|
...typeof v.leaseUntil === "number" ? { leaseUntil: v.leaseUntil } : {},
|
|
@@ -8688,6 +8805,7 @@ function parseRun(v) {
|
|
|
8688
8805
|
}
|
|
8689
8806
|
} : {},
|
|
8690
8807
|
...v.pr.clear === true ? { clear: true } : {},
|
|
8808
|
+
...v.pr.queued === true ? { queued: true } : {},
|
|
8691
8809
|
...typeof v.pr.ci === "string" ? { ci: v.pr.ci } : {},
|
|
8692
8810
|
...typeof v.pr.review === "string" ? { review: v.pr.review } : {},
|
|
8693
8811
|
...typeof v.pr.posted === "string" ? { posted: v.pr.posted } : {},
|
|
@@ -8698,13 +8816,15 @@ function parseRun(v) {
|
|
|
8698
8816
|
...conflicted ? { conflicted } : {},
|
|
8699
8817
|
...typeof v.pr.reviewed === "string" ? { reviewed: v.pr.reviewed } : {},
|
|
8700
8818
|
...typeof v.pr.look === "string" ? { look: v.pr.look } : {},
|
|
8819
|
+
...typeof v.pr.instructAt === "number" ? { instructAt: v.pr.instructAt } : {},
|
|
8701
8820
|
...isObject8(v.pr.instructed) && Array.isArray(v.pr.instructed.ids) && (v.pr.instructed.how === "told" || v.pr.instructed.how === "restarted") ? {
|
|
8702
8821
|
instructed: {
|
|
8703
8822
|
ids: v.pr.instructed.ids.filter((x) => typeof x === "string"),
|
|
8704
8823
|
how: v.pr.instructed.how
|
|
8705
8824
|
}
|
|
8706
8825
|
} : {},
|
|
8707
|
-
...typeof v.pr.findings === "string" ? { findings: v.pr.findings } : {}
|
|
8826
|
+
...typeof v.pr.findings === "string" ? { findings: v.pr.findings } : {},
|
|
8827
|
+
...v.pr.live === true ? { live: true } : {}
|
|
8708
8828
|
}
|
|
8709
8829
|
} : {}
|
|
8710
8830
|
};
|
|
@@ -8996,8 +9116,11 @@ init_sentry();
|
|
|
8996
9116
|
init_newrelic();
|
|
8997
9117
|
init_musher();
|
|
8998
9118
|
init_route();
|
|
9119
|
+
init_handoff();
|
|
8999
9120
|
init_doneChecks();
|
|
9000
9121
|
init_routeFix();
|
|
9122
|
+
init_reshape();
|
|
9123
|
+
init_say();
|
|
9001
9124
|
init_engine();
|
|
9002
9125
|
init_sources();
|
|
9003
9126
|
init_otlp();
|
|
@@ -9276,6 +9399,14 @@ import { basename, dirname as dirname5, join as join7 } from "node:path";
|
|
|
9276
9399
|
var MUSHER_LABEL = "ai.sleds.musher";
|
|
9277
9400
|
var MUSHER_INTERVAL_S = 300;
|
|
9278
9401
|
var DEFAULT_PERMISSION_MODE = "acceptEdits";
|
|
9402
|
+
var PERMISSION_MODES = [
|
|
9403
|
+
"acceptEdits",
|
|
9404
|
+
"auto",
|
|
9405
|
+
"bypassPermissions",
|
|
9406
|
+
"default",
|
|
9407
|
+
"dontAsk",
|
|
9408
|
+
"plan"
|
|
9409
|
+
];
|
|
9279
9410
|
var DEFAULT_TIMEOUT_MINUTES = 90;
|
|
9280
9411
|
var SUMMARY_MAX = 2000;
|
|
9281
9412
|
var musherLabel = (env = process.env) => {
|
|
@@ -9394,11 +9525,12 @@ var defaultExecCapture = (cmd, args, o) => {
|
|
|
9394
9525
|
stdio: ["ignore", "pipe", "pipe"],
|
|
9395
9526
|
env: { ...process.env, SLEDS_MUSHER: "1" }
|
|
9396
9527
|
});
|
|
9528
|
+
const timedOut = r.error?.code === "ETIMEDOUT";
|
|
9397
9529
|
return {
|
|
9398
9530
|
stdout: r.stdout ?? "",
|
|
9399
9531
|
stderr: r.stderr ?? "",
|
|
9400
9532
|
status: r.status,
|
|
9401
|
-
...r.error ? { error: r.error.message } : {}
|
|
9533
|
+
...timedOut ? { timedOut } : r.error ? { error: r.error.message } : {}
|
|
9402
9534
|
};
|
|
9403
9535
|
};
|
|
9404
9536
|
var musherClient = (o) => {
|
|
@@ -9474,8 +9606,10 @@ var runJob = async (o) => {
|
|
|
9474
9606
|
const r = exec(o.claudePath, args, { cwd, timeoutMs: o.timeoutMs });
|
|
9475
9607
|
const durationMs = Date.now() - t0;
|
|
9476
9608
|
const result = r.error ? { ok: false, summary: `could not run ${o.claudePath}: ${r.error}` } : parseClaudeOutput(r.stdout, r.status);
|
|
9477
|
-
if (!r.error && r.status === null)
|
|
9609
|
+
if (!r.error && (r.timedOut || r.status === null)) {
|
|
9610
|
+
result.ok = false;
|
|
9478
9611
|
result.summary = `timed out after ${Math.round(o.timeoutMs / 60000)} min. ${result.summary}`.trim();
|
|
9612
|
+
}
|
|
9479
9613
|
const outputs = collectOutputs(gitNewCommits(cwd, headBefore, gitHead(cwd, exec), exec), result.summary);
|
|
9480
9614
|
await o.client.finish({
|
|
9481
9615
|
jobId: o.job.jobId,
|
|
@@ -9910,8 +10044,32 @@ var markWarned = (state, events) => {
|
|
|
9910
10044
|
}
|
|
9911
10045
|
return { ...state, warned, ...lastWallResetsAt !== undefined ? { lastWallResetsAt } : {} };
|
|
9912
10046
|
};
|
|
9913
|
-
var
|
|
9914
|
-
var
|
|
10047
|
+
var CREDITS_LINE = /^\s*you(?:'|’)re out of usage credits\b/i;
|
|
10048
|
+
var entryText = (v) => {
|
|
10049
|
+
if (typeof v === "string")
|
|
10050
|
+
return v;
|
|
10051
|
+
if (!Array.isArray(v))
|
|
10052
|
+
return "";
|
|
10053
|
+
return v.map((b) => isObject9(b) && b.type === "text" && typeof b.text === "string" ? b.text : "").join(`
|
|
10054
|
+
`);
|
|
10055
|
+
};
|
|
10056
|
+
var isCreditsLine = (line) => {
|
|
10057
|
+
if (!line.includes("usage credits"))
|
|
10058
|
+
return false;
|
|
10059
|
+
let raw;
|
|
10060
|
+
try {
|
|
10061
|
+
raw = JSON.parse(line);
|
|
10062
|
+
} catch {
|
|
10063
|
+
return false;
|
|
10064
|
+
}
|
|
10065
|
+
if (!isObject9(raw))
|
|
10066
|
+
return false;
|
|
10067
|
+
const message = isObject9(raw.message) ? raw.message : undefined;
|
|
10068
|
+
const texts = raw.type === "system" ? [entryText(raw.content), entryText(raw.text)] : raw.type === "assistant" && raw.isApiErrorMessage === true ? [entryText(message?.content)] : [];
|
|
10069
|
+
return texts.some((t) => CREDITS_LINE.test(t));
|
|
10070
|
+
};
|
|
10071
|
+
var creditsHitIn = (text) => text.split(`
|
|
10072
|
+
`).some(isCreditsLine);
|
|
9915
10073
|
var transcriptPathFromStdin = (text) => {
|
|
9916
10074
|
try {
|
|
9917
10075
|
const raw = JSON.parse(text.trim());
|
|
@@ -9934,12 +10092,14 @@ var scanTranscriptForCredits = (path, prior) => {
|
|
|
9934
10092
|
try {
|
|
9935
10093
|
const buf = Buffer.alloc(size - start);
|
|
9936
10094
|
readSync(fd, buf, 0, buf.length, start);
|
|
9937
|
-
const
|
|
10095
|
+
const lastNewline = buf.lastIndexOf(10);
|
|
10096
|
+
const whole = lastNewline >= 0 ? buf.subarray(0, lastNewline + 1) : Buffer.alloc(0);
|
|
10097
|
+
const hit = creditsHitIn(whole.toString("utf8"));
|
|
9938
10098
|
return {
|
|
9939
10099
|
hit,
|
|
9940
10100
|
next: {
|
|
9941
10101
|
path,
|
|
9942
|
-
offset:
|
|
10102
|
+
offset: lastNewline >= 0 ? start + lastNewline + 1 : start,
|
|
9943
10103
|
...hit ? { hitAt: Date.now() } : prior?.hitAt ? { hitAt: prior.hitAt } : {}
|
|
9944
10104
|
}
|
|
9945
10105
|
};
|
|
@@ -10033,10 +10193,7 @@ var switchOff = (settings, prior) => {
|
|
|
10033
10193
|
}
|
|
10034
10194
|
return { ...stripped, env };
|
|
10035
10195
|
};
|
|
10036
|
-
var
|
|
10037
|
-
const env = envOf2(settings);
|
|
10038
|
-
return BASE_URL_KEY in env || AUTH_TOKEN_KEY in env;
|
|
10039
|
-
};
|
|
10196
|
+
var envToRestore = (priors, proxyUrls) => priors.find((p) => p !== undefined && !isSledsProxyEnv({ env: p }, proxyUrls)) ?? {};
|
|
10040
10197
|
var RELATIVE = /^\+(?:(\d+)h)?(?:(\d+)m)?$/;
|
|
10041
10198
|
var parseUntilReset = (input, nowMs) => {
|
|
10042
10199
|
const trimmed = input.trim();
|
|
@@ -10274,7 +10431,7 @@ var deployCmd = async (o) => {
|
|
|
10274
10431
|
return 0;
|
|
10275
10432
|
}
|
|
10276
10433
|
const why = typeof r.json?.error === "string" ? r.json.error : `HTTP ${r.status}`;
|
|
10277
|
-
if (r.status >= 400 && r.status < 500) {
|
|
10434
|
+
if (r.status >= 400 && r.status < 500 && r.status !== 429 && r.status !== 408) {
|
|
10278
10435
|
error(r.status === 401 ? `SLEDS refused the deploy token (${why})` : why);
|
|
10279
10436
|
return 1;
|
|
10280
10437
|
}
|
|
@@ -10290,13 +10447,13 @@ import {
|
|
|
10290
10447
|
existsSync as existsSync21,
|
|
10291
10448
|
lstatSync as lstatSync2,
|
|
10292
10449
|
mkdtempSync,
|
|
10293
|
-
readFileSync as
|
|
10450
|
+
readFileSync as readFileSync20,
|
|
10294
10451
|
readlinkSync as readlinkSync4,
|
|
10295
10452
|
rmSync as rmSync13,
|
|
10296
10453
|
writeFileSync as writeFileSync19
|
|
10297
10454
|
} from "node:fs";
|
|
10298
|
-
import { tmpdir, userInfo as userInfo2 } from "node:os";
|
|
10299
|
-
import { delimiter as delimiter3, dirname as
|
|
10455
|
+
import { tmpdir as tmpdir2, userInfo as userInfo2 } from "node:os";
|
|
10456
|
+
import { delimiter as delimiter3, dirname as dirname18, join as join27, resolve as resolve10 } from "node:path";
|
|
10300
10457
|
|
|
10301
10458
|
// src/agentMcp.ts
|
|
10302
10459
|
import { execFileSync as execFileSync3 } from "node:child_process";
|
|
@@ -10402,6 +10559,7 @@ async function offerMcp2(o) {
|
|
|
10402
10559
|
}
|
|
10403
10560
|
|
|
10404
10561
|
// src/channels/cockpit.ts
|
|
10562
|
+
import { execFile } from "node:child_process";
|
|
10405
10563
|
var detectCockpit = (env) => {
|
|
10406
10564
|
if (env.CMUX_SOCKET_PATH || env.CMUX_WORKSPACE_ID || env.CMUX_SURFACE_ID) {
|
|
10407
10565
|
const c = { kind: "cmux" };
|
|
@@ -10650,23 +10808,19 @@ var fetchFeed = async (o, now) => {
|
|
|
10650
10808
|
return null;
|
|
10651
10809
|
}
|
|
10652
10810
|
};
|
|
10811
|
+
var execCockpit = (bin, args, timeoutMs) => new Promise((resolve) => {
|
|
10812
|
+
try {
|
|
10813
|
+
const child = execFile(bin, [...args], { timeout: timeoutMs }, () => resolve());
|
|
10814
|
+
child.stdin?.end();
|
|
10815
|
+
} catch {
|
|
10816
|
+
resolve();
|
|
10817
|
+
}
|
|
10818
|
+
});
|
|
10653
10819
|
var runCockpit = async (cockpit, argv, opts = {}) => {
|
|
10654
|
-
const
|
|
10820
|
+
const exec = opts.exec ?? execCockpit;
|
|
10655
10821
|
const timeoutMs = opts.timeoutMs ?? 300;
|
|
10656
10822
|
const bin = cockpit.kind === "cmux" ? "cmux" : cockpit.kind;
|
|
10657
|
-
await Promise.allSettled(argv.map(
|
|
10658
|
-
const proc = spawn([bin, ...a], { stdout: "ignore", stderr: "ignore", stdin: "ignore" });
|
|
10659
|
-
const timer = setTimeout(() => {
|
|
10660
|
-
try {
|
|
10661
|
-
proc.kill();
|
|
10662
|
-
} catch {}
|
|
10663
|
-
}, timeoutMs);
|
|
10664
|
-
try {
|
|
10665
|
-
await proc.exited;
|
|
10666
|
-
} finally {
|
|
10667
|
-
clearTimeout(timer);
|
|
10668
|
-
}
|
|
10669
|
-
}));
|
|
10823
|
+
await Promise.allSettled(argv.map((a) => exec(bin, a, timeoutMs)));
|
|
10670
10824
|
};
|
|
10671
10825
|
|
|
10672
10826
|
// src/commands/doctor.ts
|
|
@@ -10678,7 +10832,14 @@ import { join as join11 } from "node:path";
|
|
|
10678
10832
|
var API_KEY_VAR = "ANTHROPIC_API_KEY";
|
|
10679
10833
|
var SEAT_TOKEN_VAR = "CLAUDE_CODE_OAUTH_TOKEN";
|
|
10680
10834
|
function namesVar(text, name) {
|
|
10681
|
-
|
|
10835
|
+
const assign = new RegExp(`^[ \\t]*(?:export[ \\t]+)?${name}[ \\t]*=(.*)$`);
|
|
10836
|
+
return text.split(/\r?\n/).some((line) => {
|
|
10837
|
+
const value = assign.exec(line)?.[1]?.trim();
|
|
10838
|
+
if (!value)
|
|
10839
|
+
return false;
|
|
10840
|
+
const quoted = /^(["'])(.*)\1$/.exec(value);
|
|
10841
|
+
return (quoted ? (quoted[2] ?? "").trim() : value) !== "";
|
|
10842
|
+
});
|
|
10682
10843
|
}
|
|
10683
10844
|
function loginPath(env, home = homedir()) {
|
|
10684
10845
|
return join11(env.CLAUDE_CONFIG_DIR?.trim() || join11(home, ".claude"), ".credentials.json");
|
|
@@ -10758,12 +10919,18 @@ var readCmuxPassword = (exec) => {
|
|
|
10758
10919
|
return null;
|
|
10759
10920
|
return r.stdout.trim() || null;
|
|
10760
10921
|
};
|
|
10922
|
+
var securityQuote = (s) => `"${s.replace(/[\\"]/g, (c) => `\\${c}`)}"`;
|
|
10761
10923
|
var writeCmuxPassword = (exec, pw) => {
|
|
10762
|
-
|
|
10763
|
-
|
|
10764
|
-
|
|
10765
|
-
|
|
10766
|
-
|
|
10924
|
+
if (/[\r\n\0]/.test(pw))
|
|
10925
|
+
throw new Error("could not store the cmux password in the Keychain: it has a line break");
|
|
10926
|
+
const command = ["add-generic-password", "-U", ...item, "-w"].join(" ");
|
|
10927
|
+
const r = exec("security", ["-i"], { input: `${command} ${securityQuote(pw)}
|
|
10928
|
+
` });
|
|
10929
|
+
const why = r.stderr.split(pw).join("…").trim();
|
|
10930
|
+
if (r.status !== 0)
|
|
10931
|
+
throw new Error(`could not store the cmux password in the Keychain: ${why || `exit ${r.status}`}`);
|
|
10932
|
+
if (readCmuxPassword(exec) !== pw.trim())
|
|
10933
|
+
throw new Error(`could not store the cmux password in the Keychain: ${why || "it did not read back"}`);
|
|
10767
10934
|
};
|
|
10768
10935
|
var clearCmuxPassword = (exec) => exec("security", ["delete-generic-password", ...item]).status === 0;
|
|
10769
10936
|
|
|
@@ -11595,14 +11762,14 @@ import {
|
|
|
11595
11762
|
appendFileSync as appendFileSync2,
|
|
11596
11763
|
chmodSync as chmodSync5,
|
|
11597
11764
|
mkdirSync as mkdirSync18,
|
|
11598
|
-
readFileSync as
|
|
11765
|
+
readFileSync as readFileSync16,
|
|
11599
11766
|
existsSync as realExists,
|
|
11600
11767
|
rmSync as rmSync10,
|
|
11601
|
-
statSync as
|
|
11768
|
+
statSync as statSync6,
|
|
11602
11769
|
writeFileSync as writeFileSync16
|
|
11603
11770
|
} from "node:fs";
|
|
11604
|
-
import { hostname as hostname2, userInfo } from "node:os";
|
|
11605
|
-
import { dirname as
|
|
11771
|
+
import { hostname as hostname2, tmpdir, userInfo } from "node:os";
|
|
11772
|
+
import { dirname as dirname16, isAbsolute as isAbsolute3, join as join25, resolve as resolve9 } from "node:path";
|
|
11606
11773
|
|
|
11607
11774
|
// src/engine/beat.ts
|
|
11608
11775
|
var BEAT_EVERY_MS = 60000;
|
|
@@ -11614,7 +11781,7 @@ function beatBody(state, flags, host = {}, cap = {}) {
|
|
|
11614
11781
|
name: flags.name,
|
|
11615
11782
|
os: host.os ?? process.platform,
|
|
11616
11783
|
runner: flags.runner,
|
|
11617
|
-
version: host.version ?? "0.1.
|
|
11784
|
+
version: host.version ?? "0.1.61" ?? "dev",
|
|
11618
11785
|
running: live.length,
|
|
11619
11786
|
items: live.slice(0, BEAT_ITEMS).map((r) => `${r.item}/${r.node}`),
|
|
11620
11787
|
...cap.atOnce !== undefined ? { atOnce: cap.atOnce } : {},
|
|
@@ -12054,17 +12221,19 @@ function parsePrView(stdout) {
|
|
|
12054
12221
|
const name = String(r.name ?? r.context ?? "");
|
|
12055
12222
|
if (!name)
|
|
12056
12223
|
continue;
|
|
12057
|
-
latest.set(name
|
|
12224
|
+
latest.set(`${String(r.workflowName ?? "")}\x00${name}`, {
|
|
12225
|
+
name,
|
|
12058
12226
|
status: String(r.status ?? "COMPLETED"),
|
|
12059
12227
|
conclusion: String(r.conclusion ?? r.state ?? "")
|
|
12060
12228
|
});
|
|
12061
12229
|
}
|
|
12062
12230
|
const failing = [];
|
|
12063
12231
|
let pending = 0;
|
|
12064
|
-
for (const
|
|
12065
|
-
if (FAILED.has(c.conclusion))
|
|
12066
|
-
failing.
|
|
12067
|
-
|
|
12232
|
+
for (const c of latest.values()) {
|
|
12233
|
+
if (FAILED.has(c.conclusion)) {
|
|
12234
|
+
if (!failing.includes(c.name))
|
|
12235
|
+
failing.push(c.name);
|
|
12236
|
+
} else if (c.status !== "COMPLETED" && c.conclusion !== "SUCCESS")
|
|
12068
12237
|
pending++;
|
|
12069
12238
|
else if (c.conclusion === "PENDING" || c.conclusion === "EXPECTED")
|
|
12070
12239
|
pending++;
|
|
@@ -12283,6 +12452,7 @@ function prComment(exec, repoPath, url, body) {
|
|
|
12283
12452
|
|
|
12284
12453
|
// src/engine/jiraSource.ts
|
|
12285
12454
|
var JIRA_LOG_MAX = 1000;
|
|
12455
|
+
var JIRA_MAX_PAGES = 20;
|
|
12286
12456
|
function nodeKeyOf(summary) {
|
|
12287
12457
|
return /^\s*\[([A-Za-z]{1,3})\]/.exec(summary)?.[1]?.toUpperCase();
|
|
12288
12458
|
}
|
|
@@ -12428,10 +12598,20 @@ function jiraSource(o) {
|
|
|
12428
12598
|
};
|
|
12429
12599
|
const read = async () => {
|
|
12430
12600
|
const jql = encodeURIComponent(`parent = ${epic} ORDER BY key`);
|
|
12431
|
-
const
|
|
12432
|
-
|
|
12433
|
-
|
|
12434
|
-
|
|
12601
|
+
const issues = [];
|
|
12602
|
+
let token;
|
|
12603
|
+
for (let page = 0;; page++) {
|
|
12604
|
+
if (page >= JIRA_MAX_PAGES)
|
|
12605
|
+
throw new Error(`Jira: ${epic} has more than ${JIRA_MAX_PAGES} pages of children`);
|
|
12606
|
+
const r = await call(`/rest/api/3/search/jql?jql=${jql}&fields=summary,description,status,issuelinks&maxResults=100${token ? `&nextPageToken=${encodeURIComponent(token)}` : ""}`);
|
|
12607
|
+
if (!r.ok)
|
|
12608
|
+
throw new Error(`Jira: could not read ${epic}'s children — ${errorOf(r)}`);
|
|
12609
|
+
if (Array.isArray(r.json?.issues))
|
|
12610
|
+
issues.push(...r.json.issues.filter(isObject8));
|
|
12611
|
+
token = str6(r.json?.nextPageToken);
|
|
12612
|
+
if (!token || r.json?.isLast === true)
|
|
12613
|
+
break;
|
|
12614
|
+
}
|
|
12435
12615
|
const nodes = issues.map(jiraNode);
|
|
12436
12616
|
byNode.clear();
|
|
12437
12617
|
for (const n of nodes)
|
|
@@ -12763,6 +12943,26 @@ function instructionsOf(s) {
|
|
|
12763
12943
|
const i = s;
|
|
12764
12944
|
return i && typeof i.instructions === "function" ? s : null;
|
|
12765
12945
|
}
|
|
12946
|
+
function mocksOf(s) {
|
|
12947
|
+
const m = s;
|
|
12948
|
+
return m && typeof m.finishMock === "function" && typeof m.failMock === "function" ? s : null;
|
|
12949
|
+
}
|
|
12950
|
+
function mockRunOf(row) {
|
|
12951
|
+
const m = isObject8(row.mockRun) ? row.mockRun : {};
|
|
12952
|
+
return { asked: isObject8(m.asked), running: isObject8(m.running) };
|
|
12953
|
+
}
|
|
12954
|
+
function askedMocks(r) {
|
|
12955
|
+
const mine = Array.isArray(r.mine) ? r.mine.filter(isObject8) : [];
|
|
12956
|
+
return mine.filter((row) => {
|
|
12957
|
+
const m = mockRunOf(row);
|
|
12958
|
+
return m.asked && !m.running;
|
|
12959
|
+
});
|
|
12960
|
+
}
|
|
12961
|
+
function mockBandText(b) {
|
|
12962
|
+
const lo = num5(b.lowMin);
|
|
12963
|
+
const hi = num5(b.highMin);
|
|
12964
|
+
return lo !== undefined && hi !== undefined ? `${lo}–${hi} min` : undefined;
|
|
12965
|
+
}
|
|
12766
12966
|
function instructionsFrom(waiting) {
|
|
12767
12967
|
if (!Array.isArray(waiting))
|
|
12768
12968
|
return [];
|
|
@@ -13049,6 +13249,8 @@ function sledRunning(r) {
|
|
|
13049
13249
|
const route = row.route;
|
|
13050
13250
|
if (isObject8(route) && route.drafting === true)
|
|
13051
13251
|
running++;
|
|
13252
|
+
if (mockRunOf(row).running)
|
|
13253
|
+
running++;
|
|
13052
13254
|
if (!isObject8(route) || !isObject8(route.armed))
|
|
13053
13255
|
continue;
|
|
13054
13256
|
running += keysIn(isObject8(route.states) ? route.states : {}, "running").length;
|
|
@@ -13084,14 +13286,12 @@ function routeText(route, states) {
|
|
|
13084
13286
|
}
|
|
13085
13287
|
function sledsSource(client, o) {
|
|
13086
13288
|
const routeOf = (q) => client.get(`/proxy/route?q=${encodeURIComponent(q)}`);
|
|
13087
|
-
|
|
13289
|
+
const mockRefused = new Map;
|
|
13290
|
+
async function pickup(sled, drafted, profile) {
|
|
13088
13291
|
const engine = o.engine;
|
|
13089
|
-
|
|
13090
|
-
if (profile?.pickup !== "auto")
|
|
13292
|
+
if (!autoHere(profile))
|
|
13091
13293
|
return null;
|
|
13092
|
-
|
|
13093
|
-
return null;
|
|
13094
|
-
const atOnce = (o.atOnce ?? profile.atOnce ?? 1) - drafted;
|
|
13294
|
+
const atOnce = (o.atOnce ?? profile?.atOnce ?? 1) - drafted;
|
|
13095
13295
|
const row = nextPickup(sled, { atOnce, engine });
|
|
13096
13296
|
if (!row)
|
|
13097
13297
|
return null;
|
|
@@ -13171,8 +13371,94 @@ function sledsSource(client, o) {
|
|
|
13171
13371
|
};
|
|
13172
13372
|
return { item, ready: [], running: [], landed: false, seatIds: [], draft: { claim, why } };
|
|
13173
13373
|
}
|
|
13374
|
+
function autoHere(profile) {
|
|
13375
|
+
if (profile?.pickup !== "auto")
|
|
13376
|
+
return false;
|
|
13377
|
+
return !profile.engine || sameEngine(profile.engine, o.engine);
|
|
13378
|
+
}
|
|
13379
|
+
async function mocks(sled, held, profile) {
|
|
13380
|
+
const waiting = askedMocks(sled);
|
|
13381
|
+
if (waiting.length === 0 || !autoHere(profile))
|
|
13382
|
+
return [];
|
|
13383
|
+
let room = (o.atOnce ?? profile?.atOnce ?? 1) - sledRunning(sled) - held;
|
|
13384
|
+
const out = [];
|
|
13385
|
+
for (const row of waiting) {
|
|
13386
|
+
if (room <= 0)
|
|
13387
|
+
break;
|
|
13388
|
+
const claim = await takeMock(row);
|
|
13389
|
+
if (!claim)
|
|
13390
|
+
continue;
|
|
13391
|
+
out.push(claim);
|
|
13392
|
+
room--;
|
|
13393
|
+
}
|
|
13394
|
+
return out;
|
|
13395
|
+
}
|
|
13396
|
+
async function takeMock(row) {
|
|
13397
|
+
const project = str6(row.project) ?? str6(row.key);
|
|
13398
|
+
if (!project)
|
|
13399
|
+
return null;
|
|
13400
|
+
const item = str6(row.key) ?? project;
|
|
13401
|
+
const runner = typeof o.runner === "function" ? o.runner() : o.runner;
|
|
13402
|
+
const r = await client.postFull("/proxy/mock", {
|
|
13403
|
+
project,
|
|
13404
|
+
take: true,
|
|
13405
|
+
engine: o.engine,
|
|
13406
|
+
...runner ? { runner } : {},
|
|
13407
|
+
...o.leaseMin !== undefined ? { leaseMin: o.leaseMin } : {}
|
|
13408
|
+
});
|
|
13409
|
+
if (!r || r.status >= 300 || !r.json || r.json.ok === false || !str6(r.json.runId)) {
|
|
13410
|
+
const why = r ? serverMessage(r) : UNREACHABLE3;
|
|
13411
|
+
if (mockRefused.get(item) !== why)
|
|
13412
|
+
o.log?.(`Pickup: Auto could not take ${item}'s mock — ${why}`);
|
|
13413
|
+
mockRefused.set(item, why);
|
|
13414
|
+
return null;
|
|
13415
|
+
}
|
|
13416
|
+
mockRefused.delete(item);
|
|
13417
|
+
const j = r.json;
|
|
13418
|
+
const key = str6(j.key) ?? item;
|
|
13419
|
+
const band = isObject8(j.band) ? mockBandText(j.band) : undefined;
|
|
13420
|
+
o.log?.(`Pickup: Auto is drawing ${key}'s mock — a no-code session${band ? `, sized ${band}` : ""}`);
|
|
13421
|
+
const leaseUntil = num5(j.leaseUntil);
|
|
13422
|
+
return {
|
|
13423
|
+
item: key,
|
|
13424
|
+
node: MOCK_NODE,
|
|
13425
|
+
title: str6(j.title) ?? key,
|
|
13426
|
+
pack: str6(j.pack) ?? "",
|
|
13427
|
+
repos: strings(j.repos),
|
|
13428
|
+
kind: "mock",
|
|
13429
|
+
runId: str6(j.runId),
|
|
13430
|
+
...leaseUntil !== undefined ? { leaseUntil } : {},
|
|
13431
|
+
...str6(j.seatId) ? { seatId: str6(j.seatId) } : {},
|
|
13432
|
+
...str6(j.runToken) ? { runToken: str6(j.runToken) } : {}
|
|
13433
|
+
};
|
|
13434
|
+
}
|
|
13435
|
+
async function postMock(body, what) {
|
|
13436
|
+
const r = await client.postFull("/proxy/mock", body);
|
|
13437
|
+
if (!r)
|
|
13438
|
+
throw new Error(`${UNREACHABLE3} — ${what} not recorded`);
|
|
13439
|
+
if (r.status < 300 && r.json?.ok !== false)
|
|
13440
|
+
return { ok: true };
|
|
13441
|
+
if (r.status === 401 || r.status === 403 || r.status === 404 && !r.json)
|
|
13442
|
+
throw new SeatTokenError(r.status);
|
|
13443
|
+
if (r.status === 409)
|
|
13444
|
+
return { gone: serverMessage(r) };
|
|
13445
|
+
if (r.status === 400 || r.status === 413)
|
|
13446
|
+
return { refused: serverMessage(r) };
|
|
13447
|
+
throw new Error(`${what}: ${serverMessage(r)}`);
|
|
13448
|
+
}
|
|
13449
|
+
async function failMock(claim, log, engine) {
|
|
13450
|
+
if (!claim.runId)
|
|
13451
|
+
return { gone: "no run" };
|
|
13452
|
+
return postMock({ runId: claim.runId, engine, fail: { log: log.slice(-FAIL_LOG_MAX) } }, `${claim.item}/${claim.node} fail`);
|
|
13453
|
+
}
|
|
13174
13454
|
return {
|
|
13175
13455
|
name: "sleds",
|
|
13456
|
+
async finishMock(claim, html, f) {
|
|
13457
|
+
if (!claim.runId)
|
|
13458
|
+
return { gone: "no run" };
|
|
13459
|
+
return postMock({ runId: claim.runId, engine: f.engine, html, ...f.model ? { model: f.model } : {} }, `${claim.item}/${claim.node} mock`);
|
|
13460
|
+
},
|
|
13461
|
+
failMock,
|
|
13176
13462
|
async read() {
|
|
13177
13463
|
if (o.only) {
|
|
13178
13464
|
const q = o.only;
|
|
@@ -13183,6 +13469,11 @@ function sledsSource(client, o) {
|
|
|
13183
13469
|
const r = await readOrSay(client, "/proxy/sled", "the sled");
|
|
13184
13470
|
const items = sledItems(r, o.armedOnly === true, o.engine);
|
|
13185
13471
|
if (o.armedOnly && o.engine) {
|
|
13472
|
+
let profileRead;
|
|
13473
|
+
const profileNow = () => {
|
|
13474
|
+
profileRead ??= readProfile(client);
|
|
13475
|
+
return profileRead;
|
|
13476
|
+
};
|
|
13186
13477
|
const mine = Array.isArray(r.mine) ? r.mine.filter(isObject8) : [];
|
|
13187
13478
|
for (const row of mine) {
|
|
13188
13479
|
const route = row.route;
|
|
@@ -13197,7 +13488,7 @@ function sledsSource(client, o) {
|
|
|
13197
13488
|
const waiting = armedDrafts(r, o.engine);
|
|
13198
13489
|
let drafted = 0;
|
|
13199
13490
|
if (waiting.length > 0) {
|
|
13200
|
-
const cap = o.atOnce ?? (await
|
|
13491
|
+
const cap = o.atOnce ?? (await profileNow())?.atOnce ?? 1;
|
|
13201
13492
|
let room = cap - sledRunning(r);
|
|
13202
13493
|
for (const row of waiting) {
|
|
13203
13494
|
const item = str6(row.key) ?? String(row.project);
|
|
@@ -13215,7 +13506,22 @@ function sledsSource(client, o) {
|
|
|
13215
13506
|
}
|
|
13216
13507
|
}
|
|
13217
13508
|
}
|
|
13218
|
-
const
|
|
13509
|
+
const asked = askedMocks(r).length > 0 ? await mocks(r, drafted, await profileNow()) : [];
|
|
13510
|
+
for (const claim of asked) {
|
|
13511
|
+
const it = items.find((x) => x.item === claim.item);
|
|
13512
|
+
if (it)
|
|
13513
|
+
it.mock = { claim };
|
|
13514
|
+
else
|
|
13515
|
+
items.push({
|
|
13516
|
+
item: claim.item,
|
|
13517
|
+
ready: [],
|
|
13518
|
+
running: [],
|
|
13519
|
+
landed: false,
|
|
13520
|
+
seatIds: [],
|
|
13521
|
+
mock: { claim }
|
|
13522
|
+
});
|
|
13523
|
+
}
|
|
13524
|
+
const picked = await pickup(r, drafted + asked.length, await profileNow());
|
|
13219
13525
|
if (picked)
|
|
13220
13526
|
items.push(picked);
|
|
13221
13527
|
}
|
|
@@ -13283,6 +13589,8 @@ function sledsSource(client, o) {
|
|
|
13283
13589
|
});
|
|
13284
13590
|
if (r?.status !== 409)
|
|
13285
13591
|
return "running";
|
|
13592
|
+
if (claim.kind === "mock")
|
|
13593
|
+
return "lost";
|
|
13286
13594
|
let route;
|
|
13287
13595
|
try {
|
|
13288
13596
|
route = await routeOf(claim.item);
|
|
@@ -13308,6 +13616,15 @@ function sledsSource(client, o) {
|
|
|
13308
13616
|
return "lost";
|
|
13309
13617
|
},
|
|
13310
13618
|
async report(claim, outcome, by) {
|
|
13619
|
+
if (claim.kind === "mock") {
|
|
13620
|
+
if (outcome.kind === "pr" || outcome.kind === "merged")
|
|
13621
|
+
return;
|
|
13622
|
+
const log = outcome.kind === "park" ? outcome.question : outcome.kind === "release" ? `released: ${outcome.why}` : outcome.log;
|
|
13623
|
+
const r = await failMock(claim, log, by);
|
|
13624
|
+
if ("refused" in r)
|
|
13625
|
+
throw new ReportRefusedError(400, `${claim.item}/${claim.node} fail: ${r.refused}`, true);
|
|
13626
|
+
return;
|
|
13627
|
+
}
|
|
13311
13628
|
let path;
|
|
13312
13629
|
let body;
|
|
13313
13630
|
if (outcome.kind === "pr" || outcome.kind === "merged") {
|
|
@@ -13823,6 +14140,24 @@ function fastForwardUntouched(exec, path, base, log) {
|
|
|
13823
14140
|
var INSTALL_TIMEOUT_MS = 15 * 60000;
|
|
13824
14141
|
var MANAGERS = new Set(["pnpm", "npm", "yarn", "bun"]);
|
|
13825
14142
|
var INSTALL_VERBS = new Set(["install", "i", "ci"]);
|
|
14143
|
+
var INSTALL_FLAGS = new Set([
|
|
14144
|
+
"--frozen-lockfile",
|
|
14145
|
+
"--immutable",
|
|
14146
|
+
"--prefer-offline",
|
|
14147
|
+
"--offline",
|
|
14148
|
+
"--ignore-scripts",
|
|
14149
|
+
"--prod",
|
|
14150
|
+
"--production",
|
|
14151
|
+
"--no-optional",
|
|
14152
|
+
"--no-audit",
|
|
14153
|
+
"--no-fund",
|
|
14154
|
+
"--non-interactive",
|
|
14155
|
+
"--silent",
|
|
14156
|
+
"--reporter=silent",
|
|
14157
|
+
"--reporter=append-only",
|
|
14158
|
+
"--loglevel=error",
|
|
14159
|
+
"--loglevel=warn"
|
|
14160
|
+
]);
|
|
13826
14161
|
function installArgv(command) {
|
|
13827
14162
|
const line = command?.trim().replace(/^`|`$/g, "") ?? "";
|
|
13828
14163
|
if (!line || /[;&|<>$`()\n]/.test(line))
|
|
@@ -13838,7 +14173,7 @@ function installArgv(command) {
|
|
|
13838
14173
|
rest = argv.slice(2);
|
|
13839
14174
|
else
|
|
13840
14175
|
return null;
|
|
13841
|
-
return rest.every((a) =>
|
|
14176
|
+
return rest.every((a) => INSTALL_FLAGS.has(a)) ? argv : null;
|
|
13842
14177
|
}
|
|
13843
14178
|
function lockfileInstall(path, exists) {
|
|
13844
14179
|
if (exists(join17(path, "pnpm-lock.yaml")))
|
|
@@ -14047,6 +14382,7 @@ function mcpConfigsOf(state) {
|
|
|
14047
14382
|
}
|
|
14048
14383
|
async function tick(deps) {
|
|
14049
14384
|
deps.setupNoted ??= new Set;
|
|
14385
|
+
deps.noClone ??= new Map;
|
|
14050
14386
|
const made = new Set(mcpConfigsOf(deps.readState()));
|
|
14051
14387
|
const write = deps.writeMcpConfig;
|
|
14052
14388
|
const d = write ? {
|
|
@@ -14136,6 +14472,11 @@ async function tickOnce(d) {
|
|
|
14136
14472
|
keep.push(run);
|
|
14137
14473
|
continue;
|
|
14138
14474
|
}
|
|
14475
|
+
if (run.kind === "mock") {
|
|
14476
|
+
if (await settleMock(d, run, claim, runner, finished))
|
|
14477
|
+
keep.push(run);
|
|
14478
|
+
continue;
|
|
14479
|
+
}
|
|
14139
14480
|
if (run.parked) {
|
|
14140
14481
|
if (liveAnswerer(run) && await settleAnswerer(d, run) === "released")
|
|
14141
14482
|
continue;
|
|
@@ -14208,6 +14549,7 @@ async function tickOnce(d) {
|
|
|
14208
14549
|
try {
|
|
14209
14550
|
await d.source.report(claim, outcome, d.engine);
|
|
14210
14551
|
finished.push({ item: run.item, node: run.node, outcome });
|
|
14552
|
+
endNodeSession(d, runner, run.handle, run.repoPath, ref(run));
|
|
14211
14553
|
} catch (e) {
|
|
14212
14554
|
d.log(`${ref(run)}: exited with no PR; the fail report failed, retrying next tick: ${message(e)}`);
|
|
14213
14555
|
keep.push(run);
|
|
@@ -14240,10 +14582,11 @@ async function tickOnce(d) {
|
|
|
14240
14582
|
run.seenTicks = (run.seenTicks ?? 0) + 1;
|
|
14241
14583
|
if (!ghDown) {
|
|
14242
14584
|
const quiet = now - (run.seenAt ?? run.startedAt);
|
|
14585
|
+
const provider = providerFailure(shown);
|
|
14243
14586
|
if (quiet >= PROVIDER_RETRY_MIN * 60000)
|
|
14244
|
-
failing =
|
|
14245
|
-
asked = !
|
|
14246
|
-
idle = !
|
|
14587
|
+
failing = provider;
|
|
14588
|
+
asked = !provider && run.seenTicks >= STILL_TICKS && askedAndStopped(shown);
|
|
14589
|
+
idle = !provider && (asked || quiet >= d.idleMin * 60000);
|
|
14247
14590
|
}
|
|
14248
14591
|
}
|
|
14249
14592
|
}
|
|
@@ -14263,6 +14606,11 @@ async function tickOnce(d) {
|
|
|
14263
14606
|
continue;
|
|
14264
14607
|
}
|
|
14265
14608
|
if (beat === "done") {
|
|
14609
|
+
if (ghDown) {
|
|
14610
|
+
d.log(`${ref(run)}: moved on, but its PR is not seen yet — kept until GitHub answers`);
|
|
14611
|
+
keep.push(run);
|
|
14612
|
+
continue;
|
|
14613
|
+
}
|
|
14266
14614
|
d.log(`${ref(run)}: moved on without the engine (PR or landed) — the session is left open (${runner.watchHint(run.handle)})`);
|
|
14267
14615
|
continue;
|
|
14268
14616
|
}
|
|
@@ -14374,6 +14722,9 @@ async function tickOnce(d) {
|
|
|
14374
14722
|
for (const it of items)
|
|
14375
14723
|
if (it.draft && !takeNone && !startsHeld(d))
|
|
14376
14724
|
await startDraft(d, it.draft.claim, it.draft.why, state, started, finished);
|
|
14725
|
+
for (const it of items)
|
|
14726
|
+
if (it.mock && !takeNone && !startsHeld(d))
|
|
14727
|
+
await startMock(d, it.mock.claim, state, started, finished);
|
|
14377
14728
|
const live = new Map(items.map((i) => [i.item, i]));
|
|
14378
14729
|
const unheld = state.runs.filter((r) => r.parked && (live.get(r.item)?.landed ?? true));
|
|
14379
14730
|
if (unheld.length) {
|
|
@@ -14400,8 +14751,14 @@ async function tickOnce(d) {
|
|
|
14400
14751
|
await rereadAtOnce(d);
|
|
14401
14752
|
const cap = await machineCap(d);
|
|
14402
14753
|
const atOnce = startsHeld(d) ? 0 : cap;
|
|
14754
|
+
for (const [k, c] of d.noClone ?? [])
|
|
14755
|
+
if (!alive(c) || typeof d.resolveRepo(c) === "string")
|
|
14756
|
+
d.noClone?.delete(k);
|
|
14403
14757
|
const takes = planTakes({
|
|
14404
|
-
items
|
|
14758
|
+
items: d.noClone?.size ? items.map((i) => ({
|
|
14759
|
+
...i,
|
|
14760
|
+
ready: i.ready.filter((n) => !d.noClone?.has(ref({ item: i.item, node: n })))
|
|
14761
|
+
})) : items,
|
|
14405
14762
|
tracked: [
|
|
14406
14763
|
...state.runs.filter((r) => !r.pr && (!r.parked || liveAnswerer(r))).map((r) => ({
|
|
14407
14764
|
item: r.item,
|
|
@@ -14474,6 +14831,7 @@ async function tickOnce(d) {
|
|
|
14474
14831
|
d.setupNoted?.add(repoPath.error);
|
|
14475
14832
|
d.log(`${at}: released, no try spent — ${repoPath.error}`);
|
|
14476
14833
|
}
|
|
14834
|
+
d.noClone?.set(at, claim);
|
|
14477
14835
|
await report({ kind: "release", why: repoPath.error });
|
|
14478
14836
|
continue;
|
|
14479
14837
|
}
|
|
@@ -14496,10 +14854,17 @@ async function tickOnce(d) {
|
|
|
14496
14854
|
}
|
|
14497
14855
|
let start;
|
|
14498
14856
|
let mcpConfig;
|
|
14857
|
+
let made;
|
|
14858
|
+
const dropMade = () => {
|
|
14859
|
+
if (made)
|
|
14860
|
+
removeEngineWorktree(d, repoPath, made, at);
|
|
14861
|
+
};
|
|
14499
14862
|
try {
|
|
14500
14863
|
const base = d.defaultBranch(d.exec, repoPath);
|
|
14501
14864
|
const { install, gate } = claimRunOf(claim.run, claim.repos[0]);
|
|
14502
14865
|
const worktree = d.runner.makesWorktree ? repoPath : d.ensureWorktree(d.exec, repoPath, branch, worktreePath(repoPath, name), base, install) ?? worktreePath(repoPath, name);
|
|
14866
|
+
if (!d.runner.makesWorktree)
|
|
14867
|
+
made = worktree;
|
|
14503
14868
|
const prTitle = d.prTitleOf?.(claim);
|
|
14504
14869
|
mcpConfig = engineMcpConfigFor(d, name, claim.runToken, at, worktree);
|
|
14505
14870
|
const allow = gateAllowRules(gate);
|
|
@@ -14535,6 +14900,7 @@ async function tickOnce(d) {
|
|
|
14535
14900
|
};
|
|
14536
14901
|
} catch (e) {
|
|
14537
14902
|
d.log(`${at}: could not start: ${message(e)}`);
|
|
14903
|
+
dropMade();
|
|
14538
14904
|
await report({ kind: "fail", log: `The engine could not start the session: ${message(e)}` });
|
|
14539
14905
|
continue;
|
|
14540
14906
|
}
|
|
@@ -14545,6 +14911,7 @@ async function tickOnce(d) {
|
|
|
14545
14911
|
const down = probeRunner(d);
|
|
14546
14912
|
if (down) {
|
|
14547
14913
|
d.log(`${at}: released — the runner could not start it: ${message(e)}`);
|
|
14914
|
+
dropMade();
|
|
14548
14915
|
await report({ kind: "release", why: `runner unavailable: ${down}` });
|
|
14549
14916
|
continue;
|
|
14550
14917
|
}
|
|
@@ -14563,6 +14930,8 @@ async function tickOnce(d) {
|
|
|
14563
14930
|
if (slow)
|
|
14564
14931
|
d.log(`${at}: ${slow} ${inRow} times in a row — failed, not released again`);
|
|
14565
14932
|
d.log(`${at}: could not start: ${message(e)}`);
|
|
14933
|
+
if (!slow)
|
|
14934
|
+
dropMade();
|
|
14566
14935
|
await report({ kind: "fail", log: `The engine could not start the session: ${message(e)}` });
|
|
14567
14936
|
continue;
|
|
14568
14937
|
}
|
|
@@ -14591,18 +14960,9 @@ async function tickOnce(d) {
|
|
|
14591
14960
|
setLane(d, d.runner, handle, "working");
|
|
14592
14961
|
try {
|
|
14593
14962
|
const beat = await d.source.heartbeat(claim, watch);
|
|
14594
|
-
if (beat === "parked" || beat === "done") {
|
|
14595
|
-
|
|
14596
|
-
|
|
14597
|
-
continue;
|
|
14598
|
-
}
|
|
14599
|
-
if (beat === "lost") {
|
|
14600
|
-
d.log(`${at}: the claim was lost as the session started — stopping it`);
|
|
14601
|
-
try {
|
|
14602
|
-
d.runner.stop(handle);
|
|
14603
|
-
} catch (e) {
|
|
14604
|
-
d.log(`${at}: could not stop the session: ${message(e)}`);
|
|
14605
|
-
}
|
|
14963
|
+
if (beat === "parked" || beat === "done" || beat === "lost") {
|
|
14964
|
+
d.log(beat === "lost" ? `${at}: the claim was lost as the session started — stopping it` : `${at}: the node moved on as the session started (${beat}) — stopping it`);
|
|
14965
|
+
endNodeSession(d, d.runner, handle, repoPath, at);
|
|
14606
14966
|
state.runs = state.runs.filter((r) => r !== run);
|
|
14607
14967
|
save();
|
|
14608
14968
|
continue;
|
|
@@ -15732,26 +16092,250 @@ ${shown}`;
|
|
|
15732
16092
|
endDraft(d, run, runner);
|
|
15733
16093
|
return false;
|
|
15734
16094
|
}
|
|
15735
|
-
function endDraft(d, run, runner) {
|
|
16095
|
+
function endDraft(d, run, runner, who = "drafter") {
|
|
16096
|
+
try {
|
|
16097
|
+
if (runner.status(run.handle) === "running")
|
|
16098
|
+
runner.stop(run.handle);
|
|
16099
|
+
} catch (e) {
|
|
16100
|
+
d.log(`${ref(run)}: could not close the session: ${message(e)}`);
|
|
16101
|
+
}
|
|
16102
|
+
const wt = run.handle.cwd;
|
|
16103
|
+
if (wt === run.repoPath)
|
|
16104
|
+
return;
|
|
16105
|
+
try {
|
|
16106
|
+
if (runner.makesWorktree) {
|
|
16107
|
+
const note = runner.removeWorktree?.(run.handle, run.repoPath);
|
|
16108
|
+
if (note)
|
|
16109
|
+
d.log(`${ref(run)}: ${note}`);
|
|
16110
|
+
} else if (wt.startsWith(`${run.repoPath}/.worktrees/`)) {
|
|
16111
|
+
d.removeWorktree(d.exec, run.repoPath, wt);
|
|
16112
|
+
}
|
|
16113
|
+
} catch (e) {
|
|
16114
|
+
d.log(`${ref(run)}: kept the ${who}'s checkout ${wt}: ${message(e)}`);
|
|
16115
|
+
}
|
|
16116
|
+
}
|
|
16117
|
+
async function startMock(d, claim, state, started, finished) {
|
|
16118
|
+
const at = ref(claim);
|
|
16119
|
+
const fail = async (log) => {
|
|
16120
|
+
d.log(`${at}: the mock was not drawn — ${log}`);
|
|
16121
|
+
try {
|
|
16122
|
+
await d.source.report(claim, { kind: "fail", log }, d.engine);
|
|
16123
|
+
} catch (e) {
|
|
16124
|
+
d.log(`${at}: fail report failed: ${message(e)}`);
|
|
16125
|
+
}
|
|
16126
|
+
finished.push({ item: claim.item, node: claim.node, outcome: { kind: "fail", log } });
|
|
16127
|
+
};
|
|
16128
|
+
if (!mocksOf(d.source) || !d.mockOut || !d.readMock) {
|
|
16129
|
+
await fail("this engine cannot draw mocks (its source or its files do not carry them)");
|
|
16130
|
+
return;
|
|
16131
|
+
}
|
|
16132
|
+
const repoPath = d.resolveRepo(claim);
|
|
16133
|
+
if (typeof repoPath !== "string") {
|
|
16134
|
+
await fail(repoPath.error);
|
|
16135
|
+
return;
|
|
16136
|
+
}
|
|
16137
|
+
const name = sessionName(claim.item, MOCK_NODE);
|
|
16138
|
+
const branch = nodeBranch(claim.item, MOCK_NODE);
|
|
16139
|
+
let handle;
|
|
16140
|
+
let out;
|
|
16141
|
+
let base;
|
|
16142
|
+
let launch;
|
|
16143
|
+
try {
|
|
16144
|
+
base = d.defaultBranch(d.exec, repoPath);
|
|
16145
|
+
const worktree = d.runner.makesWorktree ? repoPath : d.ensureDraftWorktree(d.exec, repoPath, worktreePath(repoPath, name), base) ?? worktreePath(repoPath, name);
|
|
16146
|
+
out = d.mockOut(name);
|
|
16147
|
+
const prompt = mockPrompt({
|
|
16148
|
+
item: claim.item,
|
|
16149
|
+
title: claim.title,
|
|
16150
|
+
pack: claim.pack,
|
|
16151
|
+
out,
|
|
16152
|
+
base,
|
|
16153
|
+
engine: d.engine
|
|
16154
|
+
});
|
|
16155
|
+
launch = nodeLaunchFor(d, at);
|
|
16156
|
+
const allow = [`Edit(/${out})`];
|
|
16157
|
+
handle = d.runner.start({
|
|
16158
|
+
name,
|
|
16159
|
+
repoPath,
|
|
16160
|
+
worktree,
|
|
16161
|
+
branch,
|
|
16162
|
+
base,
|
|
16163
|
+
promptFile: d.writePrompt(name, prompt),
|
|
16164
|
+
permissionMode: d.permissionMode,
|
|
16165
|
+
...launchOptions(launch),
|
|
16166
|
+
...sandboxFor(d, name, worktree, allow),
|
|
16167
|
+
...hooksFor(d, name, allow),
|
|
16168
|
+
...mainCheckoutFor(d, worktree)
|
|
16169
|
+
});
|
|
16170
|
+
} catch (e) {
|
|
16171
|
+
await fail(`the engine could not start the session: ${message(e)}`);
|
|
16172
|
+
return;
|
|
16173
|
+
}
|
|
16174
|
+
const watch = safeWatch(d.runner, handle);
|
|
16175
|
+
const run = {
|
|
16176
|
+
item: claim.item,
|
|
16177
|
+
node: MOCK_NODE,
|
|
16178
|
+
title: claim.title,
|
|
16179
|
+
kind: "mock",
|
|
16180
|
+
mock: { out, base, ...launch.model ? { model: launch.model } : {} },
|
|
16181
|
+
...claim.seatId ? { seatId: claim.seatId } : {},
|
|
16182
|
+
...claim.runId ? { runId: claim.runId } : {},
|
|
16183
|
+
...claim.leaseUntil !== undefined ? { leaseUntil: claim.leaseUntil } : {},
|
|
16184
|
+
branch,
|
|
16185
|
+
repoPath,
|
|
16186
|
+
handle,
|
|
16187
|
+
startedAt: d.now(),
|
|
16188
|
+
seenAt: d.now()
|
|
16189
|
+
};
|
|
16190
|
+
state.runs.push(run);
|
|
16191
|
+
d.writeState(state);
|
|
16192
|
+
d.log(`${at}: drawing a mock — no code; the HTML goes to ${out}`);
|
|
16193
|
+
try {
|
|
16194
|
+
await d.source.heartbeat(claim, watch);
|
|
16195
|
+
} catch (e) {
|
|
16196
|
+
d.log(`${at}: started, but the watch handle was not recorded: ${message(e)}`);
|
|
16197
|
+
}
|
|
16198
|
+
started.push({ item: claim.item, node: MOCK_NODE, watch, watchTarget: watchTarget(handle) });
|
|
16199
|
+
}
|
|
16200
|
+
function mockChangedCode(exec, cwd, base) {
|
|
16201
|
+
const status = exec("git", ["-C", cwd, "status", "--porcelain"]);
|
|
16202
|
+
if (status.status !== 0)
|
|
16203
|
+
return `git could not read the checkout: ${status.stderr.trim()}`;
|
|
16204
|
+
const changed = status.stdout.split(`
|
|
16205
|
+
`).filter((l) => l.trim());
|
|
16206
|
+
if (changed.length)
|
|
16207
|
+
return `${changed.length} file${changed.length === 1 ? "" : "s"} changed (${changed.slice(0, 5).map((l) => l.slice(3).trim()).join(", ")}${changed.length > 5 ? ", …" : ""})`;
|
|
16208
|
+
const ahead = exec("git", ["-C", cwd, "rev-list", "--count", `origin/${base}..HEAD`]);
|
|
16209
|
+
if (ahead.status !== 0)
|
|
16210
|
+
return `git could not count the commits: ${ahead.stderr.trim()}`;
|
|
16211
|
+
const n = Number.parseInt(ahead.stdout.trim(), 10);
|
|
16212
|
+
if (!Number.isFinite(n))
|
|
16213
|
+
return "git could not count the commits";
|
|
16214
|
+
return n > 0 ? `${n} commit${n === 1 ? "" : "s"} past origin/${base}` : null;
|
|
16215
|
+
}
|
|
16216
|
+
async function settleMock(d, run, claim, runner, finished) {
|
|
16217
|
+
let beat;
|
|
16218
|
+
try {
|
|
16219
|
+
beat = await d.source.heartbeat(claim, safeWatch(runner, run.handle));
|
|
16220
|
+
} catch (e) {
|
|
16221
|
+
d.log(`${ref(run)}: heartbeat failed, retrying next tick: ${message(e)}`);
|
|
16222
|
+
return true;
|
|
16223
|
+
}
|
|
16224
|
+
if (beat !== "running") {
|
|
16225
|
+
d.log(`${ref(run)}: the mock claim was lost — stopping the session`);
|
|
16226
|
+
endDraft(d, run, runner, "mock session");
|
|
16227
|
+
return false;
|
|
16228
|
+
}
|
|
16229
|
+
if (run.runId)
|
|
16230
|
+
run.leaseUntil = d.now() + d.leaseMin * 60000;
|
|
16231
|
+
let status;
|
|
16232
|
+
try {
|
|
16233
|
+
status = runner.status(run.handle);
|
|
16234
|
+
} catch (e) {
|
|
16235
|
+
d.log(`${ref(run)}: could not read the session's status: ${message(e)}`);
|
|
16236
|
+
return true;
|
|
16237
|
+
}
|
|
16238
|
+
const out = run.mock?.out;
|
|
16239
|
+
const read = out ? d.readMock?.(out) ?? null : null;
|
|
16240
|
+
const drawn = read?.trim() ? read : null;
|
|
16241
|
+
if (status === "running") {
|
|
16242
|
+
if (drawn) {
|
|
16243
|
+
const print = fingerprint(drawn);
|
|
16244
|
+
if (run.mock && run.mock.drawn !== print) {
|
|
16245
|
+
run.mock.drawn = print;
|
|
16246
|
+
return true;
|
|
16247
|
+
}
|
|
16248
|
+
} else {
|
|
16249
|
+
if (!runner.readsSession)
|
|
16250
|
+
return true;
|
|
16251
|
+
let shown = "";
|
|
16252
|
+
try {
|
|
16253
|
+
shown = runner.read(run.handle);
|
|
16254
|
+
} catch {
|
|
16255
|
+
shown = "";
|
|
16256
|
+
}
|
|
16257
|
+
const now = d.now();
|
|
16258
|
+
const print = fingerprint(shown);
|
|
16259
|
+
if (run.seen !== print) {
|
|
16260
|
+
run.seen = print;
|
|
16261
|
+
run.seenAt = now;
|
|
16262
|
+
return true;
|
|
16263
|
+
}
|
|
16264
|
+
if (now - (run.seenAt ?? run.startedAt) < d.idleMin * 60000)
|
|
16265
|
+
return true;
|
|
16266
|
+
}
|
|
16267
|
+
}
|
|
16268
|
+
const changed = mockChangedCode(d.exec, run.handle.cwd, run.mock?.base ?? "main");
|
|
16269
|
+
let log = "";
|
|
16270
|
+
if (changed)
|
|
16271
|
+
log = `the mock run changed code: ${changed}. Nothing was attached.`;
|
|
16272
|
+
else if (!drawn) {
|
|
16273
|
+
let tail = "";
|
|
16274
|
+
try {
|
|
16275
|
+
tail = runner.read(run.handle);
|
|
16276
|
+
} catch (e) {
|
|
16277
|
+
tail = `(the session's output could not be read: ${message(e)})`;
|
|
16278
|
+
}
|
|
16279
|
+
log = status === "exited" ? `The session exited with no HTML at ${out ?? "its file"}.${tail ? `
|
|
16280
|
+
${tail}` : ""}` : `Idle for ${d.idleMin} min with no HTML written.${tail ? ` Last output:
|
|
16281
|
+
${tail}` : ""}`;
|
|
16282
|
+
}
|
|
16283
|
+
const mocks = mocksOf(d.source);
|
|
16284
|
+
if (!log && mocks && drawn) {
|
|
16285
|
+
let posted;
|
|
16286
|
+
try {
|
|
16287
|
+
posted = await mocks.finishMock(claim, drawn, {
|
|
16288
|
+
engine: d.engine,
|
|
16289
|
+
...run.mock?.model ? { model: run.mock.model } : {}
|
|
16290
|
+
});
|
|
16291
|
+
} catch (e) {
|
|
16292
|
+
d.log(`${ref(run)}: the mock is drawn; posting it failed, retrying next tick: ${message(e)}`);
|
|
16293
|
+
return true;
|
|
16294
|
+
}
|
|
16295
|
+
if ("ok" in posted) {
|
|
16296
|
+
d.log(`${ref(run)}: mock drawn — attached to ${run.item} as its mock, for a person to sign`);
|
|
16297
|
+
endDraft(d, run, runner, "mock session");
|
|
16298
|
+
return false;
|
|
16299
|
+
}
|
|
16300
|
+
if ("gone" in posted) {
|
|
16301
|
+
d.log(`${ref(run)}: the mock is drawn, but SLEDS no longer runs it (${posted.gone}) — not attached`);
|
|
16302
|
+
endDraft(d, run, runner, "mock session");
|
|
16303
|
+
return false;
|
|
16304
|
+
}
|
|
16305
|
+
log = `SLEDS refused the HTML: ${posted.refused}`;
|
|
16306
|
+
}
|
|
16307
|
+
if (!log)
|
|
16308
|
+
log = "this engine cannot post mocks";
|
|
16309
|
+
const outcome = { kind: "fail", log };
|
|
16310
|
+
try {
|
|
16311
|
+
await d.source.report(claim, outcome, d.engine);
|
|
16312
|
+
} catch (e) {
|
|
16313
|
+
d.log(`${ref(run)}: the mock run failed; the fail report failed, retrying next tick: ${message(e)}`);
|
|
16314
|
+
return true;
|
|
16315
|
+
}
|
|
16316
|
+
d.log(`${ref(run)}: the mock was not drawn — ${log.split(`
|
|
16317
|
+
`)[0]}`);
|
|
16318
|
+
finished.push({ item: run.item, node: run.node, outcome });
|
|
16319
|
+
endDraft(d, run, runner, "mock session");
|
|
16320
|
+
return false;
|
|
16321
|
+
}
|
|
16322
|
+
function endNodeSession(d, runner, h, repoPath, at) {
|
|
15736
16323
|
try {
|
|
15737
|
-
if (runner.status(
|
|
15738
|
-
runner.stop(
|
|
16324
|
+
if (runner.status(h) === "running")
|
|
16325
|
+
runner.stop(h);
|
|
15739
16326
|
} catch (e) {
|
|
15740
|
-
d.log(`${
|
|
16327
|
+
d.log(`${at}: could not close the session: ${message(e)}`);
|
|
15741
16328
|
}
|
|
15742
|
-
|
|
15743
|
-
|
|
16329
|
+
if (!runner.makesWorktree)
|
|
16330
|
+
removeEngineWorktree(d, repoPath, h.cwd, at);
|
|
16331
|
+
}
|
|
16332
|
+
function removeEngineWorktree(d, repoPath, wt, at) {
|
|
16333
|
+
if (wt === repoPath || !wt.startsWith(`${repoPath}/.worktrees/`))
|
|
15744
16334
|
return;
|
|
15745
16335
|
try {
|
|
15746
|
-
|
|
15747
|
-
const note = runner.removeWorktree?.(run.handle, run.repoPath);
|
|
15748
|
-
if (note)
|
|
15749
|
-
d.log(`${ref(run)}: ${note}`);
|
|
15750
|
-
} else if (wt.startsWith(`${run.repoPath}/.worktrees/`)) {
|
|
15751
|
-
d.removeWorktree(d.exec, run.repoPath, wt);
|
|
15752
|
-
}
|
|
16336
|
+
d.removeWorktree(d.exec, repoPath, wt);
|
|
15753
16337
|
} catch (e) {
|
|
15754
|
-
d.log(`${
|
|
16338
|
+
d.log(`${at}: kept the worktree ${wt}: ${message(e)}`);
|
|
15755
16339
|
}
|
|
15756
16340
|
}
|
|
15757
16341
|
function ciNudge(url, failing) {
|
|
@@ -15842,6 +16426,11 @@ async function follow(d, state, run, claim, runner, finished, attention, changes
|
|
|
15842
16426
|
stopOpen(d, run, runner);
|
|
15843
16427
|
return false;
|
|
15844
16428
|
}
|
|
16429
|
+
try {
|
|
16430
|
+
pr.live = runner.status(run.handle) === "running" || undefined;
|
|
16431
|
+
} catch {
|
|
16432
|
+
pr.live = undefined;
|
|
16433
|
+
}
|
|
15845
16434
|
const at = { item: run.item, node: run.node, url: pr.url };
|
|
15846
16435
|
const ci = ciLine(view);
|
|
15847
16436
|
if (ci !== pr.ci)
|
|
@@ -16791,16 +17380,60 @@ init_state();
|
|
|
16791
17380
|
init_meter();
|
|
16792
17381
|
|
|
16793
17382
|
// src/runners/gitGuard.ts
|
|
16794
|
-
import {
|
|
17383
|
+
import { statSync as statSync2 } from "node:fs";
|
|
17384
|
+
import { basename as basename4, dirname as dirname12, join as join18, resolve as resolve8 } from "node:path";
|
|
16795
17385
|
var GIT_GUARD_ENV = {
|
|
16796
|
-
GIT_CONFIG_COUNT: "
|
|
17386
|
+
GIT_CONFIG_COUNT: "4",
|
|
16797
17387
|
GIT_CONFIG_KEY_0: "core.fsmonitor",
|
|
16798
17388
|
GIT_CONFIG_VALUE_0: "false",
|
|
16799
17389
|
GIT_CONFIG_KEY_1: "core.hooksPath",
|
|
16800
|
-
GIT_CONFIG_VALUE_1: "/dev/null"
|
|
17390
|
+
GIT_CONFIG_VALUE_1: "/dev/null",
|
|
17391
|
+
GIT_CONFIG_KEY_2: "remote.origin.uploadpack",
|
|
17392
|
+
GIT_CONFIG_VALUE_2: "git-upload-pack",
|
|
17393
|
+
GIT_CONFIG_KEY_3: "remote.origin.receivepack",
|
|
17394
|
+
GIT_CONFIG_VALUE_3: "git-receive-pack",
|
|
17395
|
+
GIT_ALLOW_PROTOCOL: "file:http:https:ssh",
|
|
17396
|
+
GIT_TERMINAL_PROMPT: "0"
|
|
17397
|
+
};
|
|
17398
|
+
function trustedSshCommand(env = process.env, exec = defaultExec) {
|
|
17399
|
+
if (env.GIT_SSH_COMMAND?.trim())
|
|
17400
|
+
return env.GIT_SSH_COMMAND;
|
|
17401
|
+
const r = exec("git", ["config", "--get", "core.sshCommand"], {
|
|
17402
|
+
cwd: "/",
|
|
17403
|
+
env: { GIT_CEILING_DIRECTORIES: "/" },
|
|
17404
|
+
timeoutMs: 5000
|
|
17405
|
+
});
|
|
17406
|
+
return r.status === 0 && r.stdout.trim() || "ssh";
|
|
17407
|
+
}
|
|
17408
|
+
var ssh;
|
|
17409
|
+
function gitGuardEnv() {
|
|
17410
|
+
ssh ??= trustedSshCommand();
|
|
17411
|
+
return { ...GIT_GUARD_ENV, GIT_SSH_COMMAND: ssh };
|
|
17412
|
+
}
|
|
17413
|
+
var isFile = (p) => statSync2(p, { throwIfNoEntry: false })?.isFile() ?? false;
|
|
17414
|
+
var isDir = (p) => statSync2(p, { throwIfNoEntry: false })?.isDirectory() ?? false;
|
|
17415
|
+
function ownGitDirs(dir, stat = { isFile, isDir }) {
|
|
17416
|
+
const wt = resolve8(dir);
|
|
17417
|
+
if (basename4(dirname12(wt)) !== ".worktrees")
|
|
17418
|
+
return null;
|
|
17419
|
+
const common = join18(dirname12(dirname12(wt)), ".git");
|
|
17420
|
+
if (!stat.isFile(join18(wt, ".git")) || !stat.isDir(common))
|
|
17421
|
+
return null;
|
|
17422
|
+
return {
|
|
17423
|
+
GIT_DIR: join18(common, "worktrees", basename4(wt)),
|
|
17424
|
+
GIT_COMMON_DIR: common,
|
|
17425
|
+
GIT_WORK_TREE: wt
|
|
17426
|
+
};
|
|
17427
|
+
}
|
|
17428
|
+
var callDir = (args, cwd) => {
|
|
17429
|
+
const base = cwd ?? process.cwd();
|
|
17430
|
+
return args[0] === "-C" && args[1] ? resolve8(base, args[1]) : base;
|
|
16801
17431
|
};
|
|
16802
17432
|
var GUARDED = new Set(["git", "gh"]);
|
|
16803
|
-
var guardGit = (exec) => (cmd, args, o) => GUARDED.has(basename4(cmd)) ? exec(cmd, args, {
|
|
17433
|
+
var guardGit = (exec, guard = gitGuardEnv) => (cmd, args, o) => GUARDED.has(basename4(cmd)) ? exec(cmd, args, {
|
|
17434
|
+
...o,
|
|
17435
|
+
env: { ...o?.env, ...guard(), ...ownGitDirs(callDir(args, o?.cwd)) }
|
|
17436
|
+
}) : exec(cmd, args, o);
|
|
16804
17437
|
|
|
16805
17438
|
// src/runners/command.ts
|
|
16806
17439
|
var expandCommandTemplate = (template, o) => {
|
|
@@ -17500,7 +18133,7 @@ function tmuxRunner(exec) {
|
|
|
17500
18133
|
return r.status === 0 ? r.stdout : "";
|
|
17501
18134
|
},
|
|
17502
18135
|
send(h, text) {
|
|
17503
|
-
const typed = exec("tmux", ["send-keys", "-t", tmuxPane(h.id), "-l", text]);
|
|
18136
|
+
const typed = exec("tmux", ["send-keys", "-t", tmuxPane(h.id), "-l", "--", text]);
|
|
17504
18137
|
if (typed.status !== 0) {
|
|
17505
18138
|
throw new Error(`tmux send-keys ${h.id} failed: ${typed.stderr.trim() || `exit ${typed.status}`}`);
|
|
17506
18139
|
}
|
|
@@ -17568,9 +18201,20 @@ function restoreRunner(name, exec = defaultExec) {
|
|
|
17568
18201
|
init_settings();
|
|
17569
18202
|
|
|
17570
18203
|
// src/update/drain.ts
|
|
17571
|
-
import {
|
|
18204
|
+
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
18205
|
+
import {
|
|
18206
|
+
closeSync as closeSync4,
|
|
18207
|
+
existsSync as existsSync18,
|
|
18208
|
+
mkdirSync as mkdirSync17,
|
|
18209
|
+
openSync as openSync4,
|
|
18210
|
+
readFileSync as readFileSync15,
|
|
18211
|
+
rmSync as rmSync9,
|
|
18212
|
+
statSync as statSync5,
|
|
18213
|
+
utimesSync,
|
|
18214
|
+
writeSync as writeSync2
|
|
18215
|
+
} from "node:fs";
|
|
17572
18216
|
import { homedir as homedir7 } from "node:os";
|
|
17573
|
-
import { join as
|
|
18217
|
+
import { join as join24 } from "node:path";
|
|
17574
18218
|
|
|
17575
18219
|
// ../../packages/shared/src/updates/manifest.ts
|
|
17576
18220
|
var CLI_PLATFORMS = [
|
|
@@ -17665,7 +18309,7 @@ function belowMinimum(version, minimum) {
|
|
|
17665
18309
|
init_state();
|
|
17666
18310
|
|
|
17667
18311
|
// src/update/fetch.ts
|
|
17668
|
-
import { createHash } from "node:crypto";
|
|
18312
|
+
import { createHash as createHash2, randomUUID as randomUUID2 } from "node:crypto";
|
|
17669
18313
|
import {
|
|
17670
18314
|
chmodSync as chmodSync4,
|
|
17671
18315
|
closeSync as closeSync3,
|
|
@@ -17709,16 +18353,16 @@ import {
|
|
|
17709
18353
|
readlinkSync as readlinkSync2,
|
|
17710
18354
|
renameSync as renameSync7,
|
|
17711
18355
|
rmSync as rmSync4,
|
|
17712
|
-
statSync as
|
|
18356
|
+
statSync as statSync3,
|
|
17713
18357
|
writeFileSync as writeFileSync13
|
|
17714
18358
|
} from "node:fs";
|
|
17715
18359
|
import { homedir as homedir3 } from "node:os";
|
|
17716
|
-
import { dirname as
|
|
18360
|
+
import { dirname as dirname13, join as join19, win32 as win322 } from "node:path";
|
|
17717
18361
|
var UPDATE_LOCK_STALE_MS = 10 * 60000;
|
|
17718
18362
|
function sledsRoot(home = homedir3(), platform = process.platform) {
|
|
17719
|
-
return platform === "win32" ? win322.join(process.env.LOCALAPPDATA ?? win322.join(home, "AppData", "Local"), "sleds") :
|
|
18363
|
+
return platform === "win32" ? win322.join(process.env.LOCALAPPDATA ?? win322.join(home, "AppData", "Local"), "sleds") : join19(home, ".sleds");
|
|
17720
18364
|
}
|
|
17721
|
-
var pathFor = (platform) => platform === "win32" ? win322 : { join:
|
|
18365
|
+
var pathFor = (platform) => platform === "win32" ? win322 : { join: join19 };
|
|
17722
18366
|
function versionDir(home, v, platform = process.platform) {
|
|
17723
18367
|
return pathFor(platform).join(sledsRoot(home, platform), "versions", v);
|
|
17724
18368
|
}
|
|
@@ -17791,36 +18435,87 @@ function readUpdateState(home) {
|
|
|
17791
18435
|
}
|
|
17792
18436
|
function writeUpdateState(home, s) {
|
|
17793
18437
|
const path = updateStatePath(home);
|
|
17794
|
-
mkdirSync12(
|
|
18438
|
+
mkdirSync12(dirname13(path), { recursive: true, mode: 448 });
|
|
17795
18439
|
const tmp = `${path}.${process.pid}.${randomUUID()}.tmp`;
|
|
17796
18440
|
writeFileSync13(tmp, `${JSON.stringify(s, null, 2)}
|
|
17797
18441
|
`, { mode: 384 });
|
|
17798
18442
|
renameSync7(tmp, path);
|
|
17799
18443
|
}
|
|
17800
|
-
|
|
18444
|
+
var BREAK_STALE_MS = 30000;
|
|
18445
|
+
var readText = (path) => {
|
|
18446
|
+
try {
|
|
18447
|
+
return readFileSync13(path, "utf8");
|
|
18448
|
+
} catch {
|
|
18449
|
+
return null;
|
|
18450
|
+
}
|
|
18451
|
+
};
|
|
18452
|
+
function parseLockBody(text) {
|
|
18453
|
+
if (text === null)
|
|
18454
|
+
return null;
|
|
17801
18455
|
try {
|
|
17802
|
-
const v = JSON.parse(
|
|
18456
|
+
const v = JSON.parse(text);
|
|
17803
18457
|
if (isObj3(v) && num7(v.pid) && num7(v.at))
|
|
17804
18458
|
return { pid: v.pid, at: v.at, ...str11(v.token) ? { token: v.token } : {} };
|
|
17805
18459
|
} catch {}
|
|
17806
18460
|
return null;
|
|
17807
18461
|
}
|
|
17808
|
-
|
|
17809
|
-
|
|
18462
|
+
var readLockBody = (path) => parseLockBody(readText(path));
|
|
18463
|
+
function lockStale(path, text, now, maxMs) {
|
|
17810
18464
|
try {
|
|
17811
|
-
const at =
|
|
17812
|
-
return now - at >
|
|
18465
|
+
const at = parseLockBody(text)?.at ?? statSync3(path).mtimeMs;
|
|
18466
|
+
return now - at > maxMs;
|
|
17813
18467
|
} catch {
|
|
17814
18468
|
return false;
|
|
17815
18469
|
}
|
|
17816
18470
|
}
|
|
18471
|
+
function removeIfSame(path, text, aside) {
|
|
18472
|
+
try {
|
|
18473
|
+
renameSync7(path, aside);
|
|
18474
|
+
} catch {
|
|
18475
|
+
return;
|
|
18476
|
+
}
|
|
18477
|
+
if (readText(aside) !== text) {
|
|
18478
|
+
try {
|
|
18479
|
+
linkSync2(aside, path);
|
|
18480
|
+
} catch {}
|
|
18481
|
+
}
|
|
18482
|
+
rmSync4(aside, { force: true });
|
|
18483
|
+
}
|
|
18484
|
+
function breakStale(path, seen, token) {
|
|
18485
|
+
const brk = `${path}.break`;
|
|
18486
|
+
const tmp = `${brk}.${token}.tmp`;
|
|
18487
|
+
const aside = `${path}.${token}.stale`;
|
|
18488
|
+
const body = `${JSON.stringify({ pid: process.pid, at: Date.now(), token })}
|
|
18489
|
+
`;
|
|
18490
|
+
writeFileSync13(tmp, body, { mode: 384 });
|
|
18491
|
+
try {
|
|
18492
|
+
try {
|
|
18493
|
+
linkSync2(tmp, brk);
|
|
18494
|
+
} catch (e) {
|
|
18495
|
+
if (e.code !== "EEXIST")
|
|
18496
|
+
throw e;
|
|
18497
|
+
const held = readText(brk);
|
|
18498
|
+
if (held !== null && lockStale(brk, held, Date.now(), BREAK_STALE_MS))
|
|
18499
|
+
removeIfSame(brk, held, aside);
|
|
18500
|
+
return false;
|
|
18501
|
+
}
|
|
18502
|
+
try {
|
|
18503
|
+
if (readText(path) === seen && lockStale(path, seen, Date.now(), UPDATE_LOCK_STALE_MS))
|
|
18504
|
+
removeIfSame(path, seen, aside);
|
|
18505
|
+
return true;
|
|
18506
|
+
} finally {
|
|
18507
|
+
removeIfSame(brk, body, aside);
|
|
18508
|
+
}
|
|
18509
|
+
} finally {
|
|
18510
|
+
rmSync4(tmp, { force: true });
|
|
18511
|
+
}
|
|
18512
|
+
}
|
|
17817
18513
|
async function withUpdateLock(home, fn, o = {}) {
|
|
17818
18514
|
const waitMs = o.waitMs ?? 30000;
|
|
17819
18515
|
const path = updateLockPath(home);
|
|
17820
|
-
mkdirSync12(
|
|
18516
|
+
mkdirSync12(dirname13(path), { recursive: true, mode: 448 });
|
|
17821
18517
|
const token = randomUUID();
|
|
17822
18518
|
const tmp = `${path}.${token}.tmp`;
|
|
17823
|
-
const aside = `${path}.${token}.stale`;
|
|
17824
18519
|
writeFileSync13(tmp, `${JSON.stringify({ pid: process.pid, at: Date.now(), token })}
|
|
17825
18520
|
`, {
|
|
17826
18521
|
mode: 384
|
|
@@ -17835,22 +18530,9 @@ async function withUpdateLock(home, fn, o = {}) {
|
|
|
17835
18530
|
if (e.code !== "EEXIST")
|
|
17836
18531
|
throw e;
|
|
17837
18532
|
}
|
|
17838
|
-
|
|
17839
|
-
|
|
17840
|
-
renameSync7(path, aside);
|
|
17841
|
-
} catch (e) {
|
|
17842
|
-
if (e.code === "ENOENT")
|
|
17843
|
-
continue;
|
|
17844
|
-
throw e;
|
|
17845
|
-
}
|
|
17846
|
-
if (!lockStale(aside, Date.now())) {
|
|
17847
|
-
try {
|
|
17848
|
-
linkSync2(aside, path);
|
|
17849
|
-
} catch {}
|
|
17850
|
-
}
|
|
17851
|
-
rmSync4(aside, { force: true });
|
|
18533
|
+
const seen = readText(path);
|
|
18534
|
+
if (seen !== null && lockStale(path, seen, Date.now(), UPDATE_LOCK_STALE_MS) && breakStale(path, seen, token))
|
|
17852
18535
|
continue;
|
|
17853
|
-
}
|
|
17854
18536
|
if (Date.now() - start >= waitMs) {
|
|
17855
18537
|
const held = readLockBody(path);
|
|
17856
18538
|
throw new Error(`update.json is locked${held ? ` by pid ${held.pid}` : ""} (${path})`);
|
|
@@ -17897,7 +18579,7 @@ async function getText(fetchFn, url) {
|
|
|
17897
18579
|
return res.text();
|
|
17898
18580
|
}
|
|
17899
18581
|
async function sha256File(path) {
|
|
17900
|
-
const hash =
|
|
18582
|
+
const hash = createHash2("sha256");
|
|
17901
18583
|
let size = 0;
|
|
17902
18584
|
for await (const chunk of createReadStream(path)) {
|
|
17903
18585
|
hash.update(chunk);
|
|
@@ -17910,7 +18592,7 @@ async function fetchVersion(o) {
|
|
|
17910
18592
|
const platform = o.platform ?? process.platform;
|
|
17911
18593
|
const arch = o.arch ?? process.arch;
|
|
17912
18594
|
const publicKey = o.publicKey ?? RELEASE_PUBLIC_KEY;
|
|
17913
|
-
const running = o.running ?? "0.1.
|
|
18595
|
+
const running = o.running ?? "0.1.61";
|
|
17914
18596
|
if (!publicKey)
|
|
17915
18597
|
return { ok: false, why: "no release key yet" };
|
|
17916
18598
|
if (!VERSION_RE2.test(o.version))
|
|
@@ -17949,12 +18631,12 @@ async function fetchVersion(o) {
|
|
|
17949
18631
|
} catch {}
|
|
17950
18632
|
}
|
|
17951
18633
|
mkdirSync13(versionDir(o.home, o.version, platform), { recursive: true, mode: 493 });
|
|
17952
|
-
const part = `${bin}.part`;
|
|
18634
|
+
const part = `${bin}.${process.pid}.${randomUUID2()}.part`;
|
|
17953
18635
|
try {
|
|
17954
18636
|
const res = await fetchFn(asset.url);
|
|
17955
18637
|
if (!res.ok || !res.body)
|
|
17956
18638
|
return { ok: false, why: `${asset.url}: HTTP ${res.status}` };
|
|
17957
|
-
const hash =
|
|
18639
|
+
const hash = createHash2("sha256");
|
|
17958
18640
|
let size = 0;
|
|
17959
18641
|
const fd = openSync3(part, "w", 384);
|
|
17960
18642
|
try {
|
|
@@ -17964,6 +18646,7 @@ async function fetchVersion(o) {
|
|
|
17964
18646
|
break;
|
|
17965
18647
|
hash.update(chunk);
|
|
17966
18648
|
writeSync(fd, chunk);
|
|
18649
|
+
o.progress?.();
|
|
17967
18650
|
}
|
|
17968
18651
|
} finally {
|
|
17969
18652
|
closeSync3(fd);
|
|
@@ -17999,24 +18682,24 @@ import {
|
|
|
17999
18682
|
readlinkSync as readlinkSync3,
|
|
18000
18683
|
renameSync as renameSync9,
|
|
18001
18684
|
rmSync as rmSync7,
|
|
18002
|
-
statSync as
|
|
18685
|
+
statSync as statSync4,
|
|
18003
18686
|
symlinkSync,
|
|
18004
18687
|
writeFileSync as writeFileSync15
|
|
18005
18688
|
} from "node:fs";
|
|
18006
18689
|
import { homedir as homedir4 } from "node:os";
|
|
18007
|
-
import { basename as basename5, delimiter as delimiter2, dirname as
|
|
18690
|
+
import { basename as basename5, delimiter as delimiter2, dirname as dirname14, isAbsolute as isAbsolute2, join as join22, relative } from "node:path";
|
|
18008
18691
|
|
|
18009
18692
|
// src/commands/engineSystemd.ts
|
|
18010
|
-
import { join as
|
|
18693
|
+
import { join as join20 } from "node:path";
|
|
18011
18694
|
var BOX_ENV_UNIT_PATH = "%h/.config/sleds/box.env";
|
|
18012
18695
|
function boxEnvPath(home) {
|
|
18013
|
-
return
|
|
18696
|
+
return join20(home, ".config", "sleds", "box.env");
|
|
18014
18697
|
}
|
|
18015
18698
|
function engineUnitDir(home) {
|
|
18016
|
-
return
|
|
18699
|
+
return join20(home, ".config", "systemd", "user");
|
|
18017
18700
|
}
|
|
18018
18701
|
function engineUnitPath(home, label) {
|
|
18019
|
-
return
|
|
18702
|
+
return join20(engineUnitDir(home), `${label}.service`);
|
|
18020
18703
|
}
|
|
18021
18704
|
function systemdQuote(s) {
|
|
18022
18705
|
const body = s.replace(/\\/g, "\\\\").replace(/"/g, "\\\"").replace(/\n/g, "\\n").replace(/%/g, "%%").replace(/\$/g, "$$$$");
|
|
@@ -18058,7 +18741,7 @@ function parseEngineUnit(text) {
|
|
|
18058
18741
|
// src/schedule.ts
|
|
18059
18742
|
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
18060
18743
|
import { existsSync as existsSync15, mkdirSync as mkdirSync14, rmSync as rmSync6, writeFileSync as writeFileSync14 } from "node:fs";
|
|
18061
|
-
import { join as
|
|
18744
|
+
import { join as join21 } from "node:path";
|
|
18062
18745
|
var REVERT_LABEL = "ai.sleds.revert";
|
|
18063
18746
|
var defaultExec2 = (cmd, args) => {
|
|
18064
18747
|
const r = spawnSync4(cmd, [...args], { stdio: ["ignore", "ignore", "pipe"], encoding: "utf8" });
|
|
@@ -18066,7 +18749,7 @@ var defaultExec2 = (cmd, args) => {
|
|
|
18066
18749
|
return { ok: false, stderr: r.error.message };
|
|
18067
18750
|
return { ok: r.status === 0, stderr: r.stderr ?? "" };
|
|
18068
18751
|
};
|
|
18069
|
-
var plistPath = (home) =>
|
|
18752
|
+
var plistPath = (home) => join21(home, "Library", "LaunchAgents", `${REVERT_LABEL}.plist`);
|
|
18070
18753
|
var xmlEscape2 = (s) => s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
18071
18754
|
var buildLaunchdPlist = (sledsArgv, deadline) => {
|
|
18072
18755
|
const args = [...sledsArgv, "off"].map((a) => ` <string>${xmlEscape2(a)}</string>`).join(`
|
|
@@ -18105,7 +18788,7 @@ var scheduleRevert = (d, deadlineMs) => {
|
|
|
18105
18788
|
if (d.platform === "darwin") {
|
|
18106
18789
|
const p = plistPath(d.home);
|
|
18107
18790
|
try {
|
|
18108
|
-
mkdirSync14(
|
|
18791
|
+
mkdirSync14(join21(d.home, "Library", "LaunchAgents"), { recursive: true });
|
|
18109
18792
|
d.exec("launchctl", ["unload", p]);
|
|
18110
18793
|
writeFileSync14(p, buildLaunchdPlist(d.sledsArgv, deadline));
|
|
18111
18794
|
const r = d.exec("launchctl", ["load", p]);
|
|
@@ -18157,8 +18840,8 @@ var HOOK_SUBS = [
|
|
|
18157
18840
|
"stop-event",
|
|
18158
18841
|
"permission-event"
|
|
18159
18842
|
];
|
|
18160
|
-
var migrateBakDir = (home) =>
|
|
18161
|
-
var manifestPath = (home) =>
|
|
18843
|
+
var migrateBakDir = (home) => join22(sledsRoot(home), "migrate.bak");
|
|
18844
|
+
var manifestPath = (home) => join22(migrateBakDir(home), "manifest.json");
|
|
18162
18845
|
var xmlEscape3 = (s) => s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
18163
18846
|
var xmlUnescape = (s) => s.replace(/</g, "<").replace(/>/g, ">").replace(/&/g, "&");
|
|
18164
18847
|
var shQuote = (s) => /^[\w./~-]+$/.test(s) ? s : `'${s.replace(/'/g, "'\\''")}'`;
|
|
@@ -18258,13 +18941,13 @@ function rewriteSettings(text, link) {
|
|
|
18258
18941
|
}
|
|
18259
18942
|
var listDir = (dir, keep) => {
|
|
18260
18943
|
try {
|
|
18261
|
-
return readdirSync2(dir).filter(keep).sort().map((n) =>
|
|
18944
|
+
return readdirSync2(dir).filter(keep).sort().map((n) => join22(dir, n));
|
|
18262
18945
|
} catch {
|
|
18263
18946
|
return [];
|
|
18264
18947
|
}
|
|
18265
18948
|
};
|
|
18266
18949
|
function unitFiles(home) {
|
|
18267
|
-
const agents = listDir(
|
|
18950
|
+
const agents = listDir(join22(home, "Library", "LaunchAgents"), (n) => n.startsWith("ai.sleds.") && n.endsWith(".plist")).map((path) => ({
|
|
18268
18951
|
path,
|
|
18269
18952
|
...basename5(path).startsWith("ai.sleds.revert") ? {} : { reload: "launchd" }
|
|
18270
18953
|
}));
|
|
@@ -18276,7 +18959,7 @@ function unitClaudeDir(text) {
|
|
|
18276
18959
|
return plist ? xmlUnescape(plist) : undefined;
|
|
18277
18960
|
}
|
|
18278
18961
|
function claudeDirs(home, env) {
|
|
18279
|
-
const dirs = [
|
|
18962
|
+
const dirs = [join22(home, ".claude"), claudeDir(home, env)];
|
|
18280
18963
|
for (const u of unitFiles(home)) {
|
|
18281
18964
|
try {
|
|
18282
18965
|
const d = unitClaudeDir(readFileSync14(u.path, "utf8"));
|
|
@@ -18300,7 +18983,7 @@ function migrationPlan(home, link, env = process.env) {
|
|
|
18300
18983
|
out.push({ ...u, text: next });
|
|
18301
18984
|
}
|
|
18302
18985
|
for (const dir of claudeDirs(home, env)) {
|
|
18303
|
-
const path =
|
|
18986
|
+
const path = join22(dir, "settings.json");
|
|
18304
18987
|
let text;
|
|
18305
18988
|
try {
|
|
18306
18989
|
text = readFileSync14(path, "utf8");
|
|
@@ -18315,12 +18998,12 @@ function migrationPlan(home, link, env = process.env) {
|
|
|
18315
18998
|
}
|
|
18316
18999
|
var backupFor = (home, path) => {
|
|
18317
19000
|
const rel = relative(home, path);
|
|
18318
|
-
return rel.startsWith("..") || isAbsolute2(rel) ?
|
|
19001
|
+
return rel.startsWith("..") || isAbsolute2(rel) ? join22(migrateBakDir(home), "abs", path) : join22(migrateBakDir(home), rel);
|
|
18319
19002
|
};
|
|
18320
19003
|
function writeKeepingMode(path, text) {
|
|
18321
19004
|
let mode = 420;
|
|
18322
19005
|
try {
|
|
18323
|
-
mode =
|
|
19006
|
+
mode = statSync4(path).mode & 511;
|
|
18324
19007
|
} catch {}
|
|
18325
19008
|
const tmp = `${path}.${process.pid}.migrate.tmp`;
|
|
18326
19009
|
writeFileSync15(tmp, text, { mode });
|
|
@@ -18332,7 +19015,7 @@ function copyInto(from, to) {
|
|
|
18332
19015
|
renameSync9(tmp, to);
|
|
18333
19016
|
}
|
|
18334
19017
|
function pointLink(link, bin) {
|
|
18335
|
-
mkdirSync15(
|
|
19018
|
+
mkdirSync15(dirname14(link), { recursive: true, mode: 448 });
|
|
18336
19019
|
const tmp = `${link}.tmp`;
|
|
18337
19020
|
rmSync7(tmp, { force: true });
|
|
18338
19021
|
symlinkSync(bin, tmp);
|
|
@@ -18357,9 +19040,7 @@ function reloadScript(lock, files) {
|
|
|
18357
19040
|
for (const a of agents)
|
|
18358
19041
|
lines.push(`launchctl unload ${shQuote(a.path)} 2>/dev/null; launchctl load ${shQuote(a.path)}`);
|
|
18359
19042
|
if (units.length)
|
|
18360
|
-
lines.push("systemctl --user daemon-reload");
|
|
18361
|
-
for (const u of units)
|
|
18362
|
-
lines.push(`systemctl --user restart ${shQuote(basename5(u.path))}`);
|
|
19043
|
+
lines.push("systemctl --user daemon-reload", `systemctl --user --no-block restart ${units.map((u) => shQuote(basename5(u.path))).join(" ")}`);
|
|
18363
19044
|
if (!lines.length)
|
|
18364
19045
|
return null;
|
|
18365
19046
|
return [
|
|
@@ -18453,7 +19134,7 @@ async function migrateToBinary(o) {
|
|
|
18453
19134
|
rmSync7(migrateBakDir(home), { recursive: true, force: true });
|
|
18454
19135
|
for (const p of plan) {
|
|
18455
19136
|
const backup = backupFor(home, p.path);
|
|
18456
|
-
mkdirSync15(
|
|
19137
|
+
mkdirSync15(dirname14(backup), { recursive: true, mode: 448 });
|
|
18457
19138
|
copyFileSync(p.path, backup);
|
|
18458
19139
|
manifest.files.push({ path: p.path, backup, ...p.reload ? { reload: p.reload } : {} });
|
|
18459
19140
|
}
|
|
@@ -18487,7 +19168,7 @@ async function migrateToBinary(o) {
|
|
|
18487
19168
|
log(`sleds stays on the npm install: ${result.why}`);
|
|
18488
19169
|
return { ok: false, why: result.why };
|
|
18489
19170
|
}
|
|
18490
|
-
const binDir =
|
|
19171
|
+
const binDir = dirname14(link);
|
|
18491
19172
|
if (!(env.PATH ?? "").split(delimiter2).includes(binDir))
|
|
18492
19173
|
log(`${binDir} is not on your PATH — add it in your shell profile to run this sleds from a terminal (sleds never edits it)`);
|
|
18493
19174
|
reload(home, result.files, detach);
|
|
@@ -18507,10 +19188,10 @@ import {
|
|
|
18507
19188
|
symlinkSync as symlinkSync2
|
|
18508
19189
|
} from "node:fs";
|
|
18509
19190
|
import { homedir as homedir5 } from "node:os";
|
|
18510
|
-
import { dirname as
|
|
19191
|
+
import { dirname as dirname15, join as join23 } from "node:path";
|
|
18511
19192
|
var NO_PREVIOUS = "no previous version on this machine";
|
|
18512
19193
|
function allStateFiles(home = homedir5(), env = process.env) {
|
|
18513
|
-
const roots = [...new Set([configDir(home, env),
|
|
19194
|
+
const roots = [...new Set([configDir(home, env), join23(home, ".config", "sleds")])];
|
|
18514
19195
|
const out = [];
|
|
18515
19196
|
const walk = (dir) => {
|
|
18516
19197
|
let names;
|
|
@@ -18521,20 +19202,20 @@ function allStateFiles(home = homedir5(), env = process.env) {
|
|
|
18521
19202
|
}
|
|
18522
19203
|
for (const n of names.sort())
|
|
18523
19204
|
if (n.endsWith(".json") && n !== "removed-engines.json")
|
|
18524
|
-
out.push(
|
|
19205
|
+
out.push(join23(dir, n));
|
|
18525
19206
|
for (const sub of ["orgs", "seats"]) {
|
|
18526
19207
|
let kids;
|
|
18527
19208
|
try {
|
|
18528
|
-
kids = readdirSync3(
|
|
19209
|
+
kids = readdirSync3(join23(dir, sub));
|
|
18529
19210
|
} catch {
|
|
18530
19211
|
continue;
|
|
18531
19212
|
}
|
|
18532
19213
|
for (const k of kids.sort())
|
|
18533
|
-
walk(
|
|
19214
|
+
walk(join23(dir, sub, k));
|
|
18534
19215
|
}
|
|
18535
19216
|
};
|
|
18536
19217
|
for (const r of roots)
|
|
18537
|
-
walk(
|
|
19218
|
+
walk(join23(r, "engine"));
|
|
18538
19219
|
return out;
|
|
18539
19220
|
}
|
|
18540
19221
|
var backupPath2 = (file, v) => `${file}.${v}.bak`;
|
|
@@ -18548,7 +19229,7 @@ function pointBinAt(home, v, platform) {
|
|
|
18548
19229
|
const target = versionBin(home, v, platform);
|
|
18549
19230
|
if (!existsSync16(target))
|
|
18550
19231
|
throw new Error(`${target} is missing — sleds ${v} is not downloaded`);
|
|
18551
|
-
mkdirSync16(
|
|
19232
|
+
mkdirSync16(dirname15(bin), { recursive: true, mode: 448 });
|
|
18552
19233
|
if (platform === "win32") {
|
|
18553
19234
|
replaceAside(bin, target);
|
|
18554
19235
|
return;
|
|
@@ -18620,7 +19301,7 @@ function applyConfirm(s, home, o = {}) {
|
|
|
18620
19301
|
if (!s.current)
|
|
18621
19302
|
return;
|
|
18622
19303
|
for (const f of o.stateFiles ?? allStateFiles(home)) {
|
|
18623
|
-
const dir =
|
|
19304
|
+
const dir = dirname15(f);
|
|
18624
19305
|
const prefix = `${f.slice(dir.length + 1)}.`;
|
|
18625
19306
|
let names;
|
|
18626
19307
|
try {
|
|
@@ -18630,10 +19311,10 @@ function applyConfirm(s, home, o = {}) {
|
|
|
18630
19311
|
}
|
|
18631
19312
|
for (const n of names)
|
|
18632
19313
|
if (n.startsWith(prefix) && n.endsWith(".bak"))
|
|
18633
|
-
rmSync8(
|
|
19314
|
+
rmSync8(join23(dir, n), { force: true });
|
|
18634
19315
|
}
|
|
18635
19316
|
const keep = new Set([s.current, s.previous, s.drain?.to].filter(Boolean));
|
|
18636
|
-
const versions =
|
|
19317
|
+
const versions = dirname15(versionDir(home, s.current, platform));
|
|
18637
19318
|
let names = [];
|
|
18638
19319
|
try {
|
|
18639
19320
|
names = readdirSync3(versions);
|
|
@@ -18684,6 +19365,7 @@ function reconcile(s, home, o) {
|
|
|
18684
19365
|
// src/update/guard.ts
|
|
18685
19366
|
var UPDATE_EXIT = 4;
|
|
18686
19367
|
var FIRST_BEAT_MS = 180000;
|
|
19368
|
+
var FIRE_RETRY_MS = 15000;
|
|
18687
19369
|
var STARTS_PER_ENGINE = 3;
|
|
18688
19370
|
function guarded(home, version, platform) {
|
|
18689
19371
|
return !!version && version !== "dev" && existsSync17(sledsRoot(home, platform));
|
|
@@ -18767,9 +19449,16 @@ function firstBeatWatchdog(o) {
|
|
|
18767
19449
|
const clearTimer = o.clearTimer ?? ((t) => clearTimeout(t));
|
|
18768
19450
|
let done = false;
|
|
18769
19451
|
let confirming = false;
|
|
19452
|
+
const setTimer = o.setTimer ?? ((fn, ms) => {
|
|
19453
|
+
const t = setTimeout(fn, ms);
|
|
19454
|
+
t.unref?.();
|
|
19455
|
+
return t;
|
|
19456
|
+
});
|
|
19457
|
+
let timer;
|
|
18770
19458
|
const fire = () => {
|
|
18771
19459
|
if (done)
|
|
18772
19460
|
return;
|
|
19461
|
+
const lock = o.lockWaitMs !== undefined ? { waitMs: o.lockWaitMs } : {};
|
|
18773
19462
|
withUpdateLock(home, (s) => {
|
|
18774
19463
|
if (s.pending?.to === version)
|
|
18775
19464
|
return rollBackHere(s, home, version, `sleds ${version} did not check in within 3 minutes`, now(), o);
|
|
@@ -18781,20 +19470,20 @@ function firstBeatWatchdog(o) {
|
|
|
18781
19470
|
why: `sleds ${version} was rolled back on this machine — restarting onto ${at}`
|
|
18782
19471
|
};
|
|
18783
19472
|
return null;
|
|
18784
|
-
}).then((g) => {
|
|
19473
|
+
}, lock).then((g) => {
|
|
18785
19474
|
done = true;
|
|
18786
19475
|
if (g && !g.go) {
|
|
18787
19476
|
say(g.why);
|
|
18788
19477
|
exit(g.exit);
|
|
18789
19478
|
}
|
|
18790
|
-
}, (e) =>
|
|
19479
|
+
}, (e) => {
|
|
19480
|
+
if (done)
|
|
19481
|
+
return;
|
|
19482
|
+
say(`the first-beat check could not run: ${e instanceof Error ? e.message : String(e)} — trying again in ${FIRE_RETRY_MS / 1000} s`);
|
|
19483
|
+
timer = setTimer(fire, FIRE_RETRY_MS);
|
|
19484
|
+
});
|
|
18791
19485
|
};
|
|
18792
|
-
|
|
18793
|
-
const t = setTimeout(fn, ms);
|
|
18794
|
-
t.unref?.();
|
|
18795
|
-
return t;
|
|
18796
|
-
});
|
|
18797
|
-
const timer = setTimer(fire, Math.max(0, pending.at + FIRST_BEAT_MS - now()));
|
|
19486
|
+
timer = setTimer(fire, Math.max(0, pending.at + FIRST_BEAT_MS - now()));
|
|
18798
19487
|
return {
|
|
18799
19488
|
beat: async () => {
|
|
18800
19489
|
if (done || confirming)
|
|
@@ -18891,9 +19580,10 @@ async function machineAnswer(client, tokens) {
|
|
|
18891
19580
|
return null;
|
|
18892
19581
|
}
|
|
18893
19582
|
}
|
|
19583
|
+
var FETCH_LOCK_TOUCH_MS = 30000;
|
|
18894
19584
|
async function fetchOnce(o) {
|
|
18895
19585
|
const dir = versionDir(o.home, o.version, o.platform);
|
|
18896
|
-
const lock =
|
|
19586
|
+
const lock = join24(dir, "fetch.lock");
|
|
18897
19587
|
mkdirSync17(dir, { recursive: true, mode: 493 });
|
|
18898
19588
|
let fd;
|
|
18899
19589
|
try {
|
|
@@ -18902,7 +19592,7 @@ async function fetchOnce(o) {
|
|
|
18902
19592
|
if (e.code !== "EEXIST")
|
|
18903
19593
|
throw e;
|
|
18904
19594
|
try {
|
|
18905
|
-
if ((o.now ?? Date.now()) -
|
|
19595
|
+
if ((o.now ?? Date.now()) - statSync5(lock).mtimeMs <= FETCH_LOCK_STALE_MS)
|
|
18906
19596
|
return null;
|
|
18907
19597
|
} catch {
|
|
18908
19598
|
return null;
|
|
@@ -18910,14 +19600,28 @@ async function fetchOnce(o) {
|
|
|
18910
19600
|
rmSync9(lock, { force: true });
|
|
18911
19601
|
return fetchOnce(o);
|
|
18912
19602
|
}
|
|
19603
|
+
const mine = `${process.pid} ${randomUUID3()}
|
|
19604
|
+
`;
|
|
18913
19605
|
try {
|
|
18914
|
-
writeSync2(fd,
|
|
18915
|
-
`);
|
|
19606
|
+
writeSync2(fd, mine);
|
|
18916
19607
|
closeSync4(fd);
|
|
18917
19608
|
const { fetch: f = fetchVersion, now: _n, ...rest } = o;
|
|
18918
|
-
|
|
19609
|
+
let touchedAt = 0;
|
|
19610
|
+
const progress = () => {
|
|
19611
|
+
const t = Date.now();
|
|
19612
|
+
if (t - touchedAt < FETCH_LOCK_TOUCH_MS)
|
|
19613
|
+
return;
|
|
19614
|
+
touchedAt = t;
|
|
19615
|
+
try {
|
|
19616
|
+
utimesSync(lock, t / 1000, t / 1000);
|
|
19617
|
+
} catch {}
|
|
19618
|
+
};
|
|
19619
|
+
return await f({ ...rest, progress });
|
|
18919
19620
|
} finally {
|
|
18920
|
-
|
|
19621
|
+
try {
|
|
19622
|
+
if (readFileSync15(lock, "utf8") === mine)
|
|
19623
|
+
rmSync9(lock, { force: true });
|
|
19624
|
+
} catch {}
|
|
18921
19625
|
}
|
|
18922
19626
|
}
|
|
18923
19627
|
function machineVersion(home, platform) {
|
|
@@ -19400,7 +20104,7 @@ function flagsFromProfile(flags, given, profile, home) {
|
|
|
19400
20104
|
}
|
|
19401
20105
|
}
|
|
19402
20106
|
if (profile.reposDir && !has("repos-dir") && !has("repo")) {
|
|
19403
|
-
out.reposDir = splitReposDirs(profile.reposDir).map((d) => home && /^~(\/|$)/.test(d) ?
|
|
20107
|
+
out.reposDir = splitReposDirs(profile.reposDir).map((d) => home && /^~(\/|$)/.test(d) ? join25(home, d.slice(1)) : d).join(":");
|
|
19404
20108
|
used.push(`repos ${profile.reposDir}`);
|
|
19405
20109
|
}
|
|
19406
20110
|
if (profile.mcpStrict !== undefined && !has("strict-mcp") && !has("no-strict-mcp")) {
|
|
@@ -19495,7 +20199,7 @@ function engineClient(o, org, breaker) {
|
|
|
19495
20199
|
...src,
|
|
19496
20200
|
...breaker ? { fetchFn: breaker.fetch } : {},
|
|
19497
20201
|
timeoutMs: ENGINE_TIMEOUT_MS,
|
|
19498
|
-
version: "0.1.
|
|
20202
|
+
version: "0.1.61"
|
|
19499
20203
|
});
|
|
19500
20204
|
const asked = org?.trim() || env.SLEDS_ORG?.trim();
|
|
19501
20205
|
if (asked && !selectOrg(readConfig(o.home), org, env).seat)
|
|
@@ -19528,19 +20232,19 @@ function realPidAlive2(pid) {
|
|
|
19528
20232
|
function memberMcpConfigs(o, cwd) {
|
|
19529
20233
|
const read = o.readJson ?? ((p) => {
|
|
19530
20234
|
try {
|
|
19531
|
-
return JSON.parse(
|
|
20235
|
+
return JSON.parse(readFileSync16(p, "utf8"));
|
|
19532
20236
|
} catch {
|
|
19533
20237
|
return null;
|
|
19534
20238
|
}
|
|
19535
20239
|
});
|
|
19536
20240
|
const claudeDir = (o.env ?? process.env).CLAUDE_CONFIG_DIR;
|
|
19537
20241
|
return [
|
|
19538
|
-
read(claudeDir ?
|
|
19539
|
-
...cwd ? [read(
|
|
20242
|
+
read(claudeDir ? join25(claudeDir, ".claude.json") : join25(o.home, ".claude.json")),
|
|
20243
|
+
...cwd ? [read(join25(cwd, ".mcp.json"))] : []
|
|
19540
20244
|
];
|
|
19541
20245
|
}
|
|
19542
20246
|
function engineStateDir(o, org, seat) {
|
|
19543
|
-
return seatStateDir(orgStateDir(o.stateDir ??
|
|
20247
|
+
return seatStateDir(orgStateDir(o.stateDir ?? join25(configDir(o.home, o.env ?? process.env), "engine"), org), seat);
|
|
19544
20248
|
}
|
|
19545
20249
|
function engineSeat(o, org) {
|
|
19546
20250
|
try {
|
|
@@ -19567,11 +20271,11 @@ function engineDeps(o, flags, source, runner, stateName) {
|
|
|
19567
20271
|
const stateDir = engineStateDir(o, flags.org, flags.seat);
|
|
19568
20272
|
const file = statePath(stateDir, stateName);
|
|
19569
20273
|
const mcpOrg = flags.org && engineOrg(o, flags.org).seated ? flags.org : undefined;
|
|
19570
|
-
const repo = flags.repo ?
|
|
19571
|
-
const reposDirs = splitReposDirs(flags.reposDir).map((d) =>
|
|
20274
|
+
const repo = flags.repo ? resolve9(cwd, flags.repo) : undefined;
|
|
20275
|
+
const reposDirs = splitReposDirs(flags.reposDir).map((d) => resolve9(cwd, d));
|
|
19572
20276
|
const { branch, prTitle } = flags;
|
|
19573
|
-
const hooksDir =
|
|
19574
|
-
const permissionMark = (name) =>
|
|
20277
|
+
const hooksDir = join25(stateDir, "hooks");
|
|
20278
|
+
const permissionMark = (name) => join25(hooksDir, `${name}.permission.json`);
|
|
19575
20279
|
const withHook = (name, settings) => applyPermissionHook(settings, [...o.sledsArgv ?? ["sleds"], "permission-event", "--mark", permissionMark(name)].map(shq2).join(" "));
|
|
19576
20280
|
return {
|
|
19577
20281
|
source,
|
|
@@ -19596,9 +20300,9 @@ function engineDeps(o, flags, source, runner, stateName) {
|
|
|
19596
20300
|
sandbox: true,
|
|
19597
20301
|
writeSandboxSettings: o.writeSandboxSettings ?? ((name, cwd, allow) => {
|
|
19598
20302
|
const gitCommonDir = gitCommonDirOf(exec, cwd);
|
|
19599
|
-
const dir =
|
|
20303
|
+
const dir = join25(stateDir, "sandbox");
|
|
19600
20304
|
mkdirSync18(dir, { recursive: true, mode: 448 });
|
|
19601
|
-
const p =
|
|
20305
|
+
const p = join25(dir, `${name}.json`);
|
|
19602
20306
|
const walls = engineSandboxSettings(o.home, o.env ?? process.env, gitCommonDir, [
|
|
19603
20307
|
...SANDBOX_DEFAULT_ALLOW,
|
|
19604
20308
|
...flags.sandboxAllow ?? []
|
|
@@ -19615,7 +20319,7 @@ function engineDeps(o, flags, source, runner, stateName) {
|
|
|
19615
20319
|
} : {},
|
|
19616
20320
|
writeHookSettings: (name, allow) => {
|
|
19617
20321
|
mkdirSync18(hooksDir, { recursive: true, mode: 448 });
|
|
19618
|
-
const p =
|
|
20322
|
+
const p = join25(hooksDir, `${name}.json`);
|
|
19619
20323
|
const base = allow?.length ? { permissions: { allow: [...allow] } } : {};
|
|
19620
20324
|
writeFileSync16(p, `${JSON.stringify(withHook(name, base), null, 2)}
|
|
19621
20325
|
`, { mode: 384 });
|
|
@@ -19643,9 +20347,9 @@ function engineDeps(o, flags, source, runner, stateName) {
|
|
|
19643
20347
|
exists
|
|
19644
20348
|
}),
|
|
19645
20349
|
writeMcpConfig: o.writeMcpConfig ?? ((name, runToken, worktree) => {
|
|
19646
|
-
const dir =
|
|
20350
|
+
const dir = join25(stateDir, "mcp");
|
|
19647
20351
|
mkdirSync18(dir, { recursive: true, mode: 448 });
|
|
19648
|
-
const p =
|
|
20352
|
+
const p = join25(dir, `${name}.json`);
|
|
19649
20353
|
let others = {};
|
|
19650
20354
|
if (flags.strictMcp && flags.mcpAllow?.length) {
|
|
19651
20355
|
const r = mcpPassThrough(flags.mcpAllow, memberMcpConfigs(o, worktree));
|
|
@@ -19661,12 +20365,26 @@ function engineDeps(o, flags, source, runner, stateName) {
|
|
|
19661
20365
|
}),
|
|
19662
20366
|
removeMcpConfig: o.removeMcpConfig ?? ((p) => rmSync10(p, { force: true })),
|
|
19663
20367
|
writePrompt: o.writePrompt ?? ((name, text) => {
|
|
19664
|
-
const dir =
|
|
20368
|
+
const dir = join25(stateDir, "prompts");
|
|
19665
20369
|
mkdirSync18(dir, { recursive: true, mode: 448 });
|
|
19666
|
-
const p =
|
|
20370
|
+
const p = join25(dir, `${name}.md`);
|
|
19667
20371
|
writeFileSync16(p, text, { mode: 384 });
|
|
19668
20372
|
return p;
|
|
19669
20373
|
}),
|
|
20374
|
+
mockOut: (name) => {
|
|
20375
|
+
const dir = join25(tmpdir(), "sleds-mocks");
|
|
20376
|
+
mkdirSync18(dir, { recursive: true, mode: 448 });
|
|
20377
|
+
const p = join25(dir, `${name}.html`);
|
|
20378
|
+
rmSync10(p, { force: true });
|
|
20379
|
+
return p;
|
|
20380
|
+
},
|
|
20381
|
+
readMock: (p) => {
|
|
20382
|
+
try {
|
|
20383
|
+
return readFileSync16(p, "utf8");
|
|
20384
|
+
} catch {
|
|
20385
|
+
return null;
|
|
20386
|
+
}
|
|
20387
|
+
},
|
|
19670
20388
|
ensureWorktree: (e, repoPath, branch, path, base, install) => ensureWorktree(e, repoPath, branch, path, base, o.worktreeFs ?? { ...realWorktreeFs, exists }, { ...install ? { command: install } : {}, log: o.log ?? console.log }),
|
|
19671
20389
|
ensureDraftWorktree: (e, repoPath, path, base) => ensureDetachedWorktree(e, repoPath, path, base, o.worktreeFs ?? { ...realWorktreeFs, exists }),
|
|
19672
20390
|
defaultBranch,
|
|
@@ -19691,14 +20409,14 @@ function engineDeps(o, flags, source, runner, stateName) {
|
|
|
19691
20409
|
}
|
|
19692
20410
|
function sandboxDenyRead(home, env) {
|
|
19693
20411
|
return {
|
|
19694
|
-
dirs: [...new Set([configDir(home, env),
|
|
20412
|
+
dirs: [...new Set([configDir(home, env), join25(home, ".config", "sleds")])],
|
|
19695
20413
|
files: [
|
|
19696
20414
|
...new Set([
|
|
19697
20415
|
tokenPath(home, env),
|
|
19698
|
-
|
|
20416
|
+
join25(home, ".sleds-token"),
|
|
19699
20417
|
settingsPath(home, env),
|
|
19700
20418
|
backupPath(home, env),
|
|
19701
|
-
|
|
20419
|
+
join25(claudeDir(home, env), "settings.local.json")
|
|
19702
20420
|
])
|
|
19703
20421
|
]
|
|
19704
20422
|
};
|
|
@@ -19715,21 +20433,21 @@ function gitCommonDirOf(exec, cwd) {
|
|
|
19715
20433
|
const dir = r.status === 0 ? r.stdout.trim() : "";
|
|
19716
20434
|
if (!dir)
|
|
19717
20435
|
throw new Error(`git has no common dir for ${cwd}, so the sandbox has nowhere to commit`);
|
|
19718
|
-
return
|
|
20436
|
+
return resolve9(cwd, dir);
|
|
19719
20437
|
}
|
|
19720
20438
|
var LENT_NEEDS_SANDBOX = "a lent engine runs every session sandboxed (spec 48 rule 10) — add --sandbox to --lent-by";
|
|
19721
20439
|
function lenderHome(accountHome, lentBy) {
|
|
19722
|
-
return isAbsolute3(lentBy) ?
|
|
20440
|
+
return isAbsolute3(lentBy) ? resolve9(lentBy) : join25(dirname16(accountHome), lentBy);
|
|
19723
20441
|
}
|
|
19724
20442
|
function lentHomeProblems(accountHome, lentBy, modeOf) {
|
|
19725
20443
|
const lender = lenderHome(accountHome, lentBy);
|
|
19726
|
-
if (lender ===
|
|
20444
|
+
if (lender === resolve9(accountHome))
|
|
19727
20445
|
return [
|
|
19728
20446
|
`--lent-by ${lentBy} is this account — a lent engine runs as the borrower's own OS user, not the lender's`
|
|
19729
20447
|
];
|
|
19730
20448
|
const problems = [];
|
|
19731
20449
|
for (const [who, home] of [
|
|
19732
|
-
["the borrower's",
|
|
20450
|
+
["the borrower's", resolve9(accountHome)],
|
|
19733
20451
|
["the lender's", lender]
|
|
19734
20452
|
]) {
|
|
19735
20453
|
const mode = modeOf(home);
|
|
@@ -19742,7 +20460,7 @@ function lentHomeProblems(accountHome, lentBy, modeOf) {
|
|
|
19742
20460
|
}
|
|
19743
20461
|
var homeMode = (path) => {
|
|
19744
20462
|
try {
|
|
19745
|
-
return
|
|
20463
|
+
return statSync6(path).mode & 4095;
|
|
19746
20464
|
} catch {
|
|
19747
20465
|
return null;
|
|
19748
20466
|
}
|
|
@@ -19787,7 +20505,7 @@ async function driveEngine(o, flags, deps, until = {}) {
|
|
|
19787
20505
|
runner: deps.runner.name,
|
|
19788
20506
|
atOnce: flags.atOnce,
|
|
19789
20507
|
interval: flags.interval,
|
|
19790
|
-
cliVersion: "0.1.
|
|
20508
|
+
cliVersion: "0.1.61"
|
|
19791
20509
|
};
|
|
19792
20510
|
const beat = (checkedAt) => {
|
|
19793
20511
|
const m = deps.machine;
|
|
@@ -20135,14 +20853,14 @@ init_config();
|
|
|
20135
20853
|
import {
|
|
20136
20854
|
existsSync as existsSync19,
|
|
20137
20855
|
mkdirSync as mkdirSync19,
|
|
20138
|
-
readFileSync as
|
|
20856
|
+
readFileSync as readFileSync17,
|
|
20139
20857
|
readdirSync as readdirSync4,
|
|
20140
20858
|
realpathSync,
|
|
20141
20859
|
rmSync as rmSync11,
|
|
20142
20860
|
writeFileSync as writeFileSync17
|
|
20143
20861
|
} from "node:fs";
|
|
20144
20862
|
import { hostname as hostname3 } from "node:os";
|
|
20145
|
-
import { basename as basename6, dirname as
|
|
20863
|
+
import { basename as basename6, dirname as dirname17, isAbsolute as isAbsolute4, join as join26 } from "node:path";
|
|
20146
20864
|
init_state();
|
|
20147
20865
|
var ENGINE_LABEL = "ai.sleds.engine";
|
|
20148
20866
|
var ENGINE_INSTALL_USAGE = `usage: sleds engine install [engine flags] | sleds engine uninstall [--name <n>] [--org <slug>]
|
|
@@ -20163,13 +20881,13 @@ function engineLabel(name, org, seat) {
|
|
|
20163
20881
|
return [ENGINE_LABEL, org, seat?.trim(), name].filter((p) => Boolean(p)).map((p, i) => i === 0 ? p : engineSlug(p)).join(".");
|
|
20164
20882
|
}
|
|
20165
20883
|
function enginePlistPath(home, name, org, seat) {
|
|
20166
|
-
return
|
|
20884
|
+
return join26(home, "Library", "LaunchAgents", `${engineLabel(name, org, seat)}.plist`);
|
|
20167
20885
|
}
|
|
20168
20886
|
function engineLogPath(stateDir, name) {
|
|
20169
|
-
return
|
|
20887
|
+
return join26(stateDir, `${engineSlug(name)}.log`);
|
|
20170
20888
|
}
|
|
20171
20889
|
function engineEventsPath(stateDir, name) {
|
|
20172
|
-
return
|
|
20890
|
+
return join26(stateDir, `${engineSlug(name)}.events.jsonl`);
|
|
20173
20891
|
}
|
|
20174
20892
|
var xmlEscape4 = (s) => s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
20175
20893
|
function buildEnginePlist(o) {
|
|
@@ -20266,10 +20984,10 @@ function readEngines(dir, suffix, manager, parse) {
|
|
|
20266
20984
|
}
|
|
20267
20985
|
const out = [];
|
|
20268
20986
|
for (const name of names.filter((n) => n.startsWith(`${ENGINE_LABEL}.`) && n.endsWith(suffix))) {
|
|
20269
|
-
const path =
|
|
20987
|
+
const path = join26(dir, name);
|
|
20270
20988
|
let parsed;
|
|
20271
20989
|
try {
|
|
20272
|
-
parsed = parse(
|
|
20990
|
+
parsed = parse(readFileSync17(path, "utf8"), name);
|
|
20273
20991
|
} catch {
|
|
20274
20992
|
parsed = null;
|
|
20275
20993
|
}
|
|
@@ -20286,7 +21004,7 @@ function readEngines(dir, suffix, manager, parse) {
|
|
|
20286
21004
|
}
|
|
20287
21005
|
function installedEngines(home) {
|
|
20288
21006
|
return [
|
|
20289
|
-
...readEngines(
|
|
21007
|
+
...readEngines(join26(home, "Library", "LaunchAgents"), ".plist", "launchd", parseEnginePlist),
|
|
20290
21008
|
...readEngines(engineUnitDir(home), ".service", "systemd", (text, file) => {
|
|
20291
21009
|
const parsed = parseEngineUnit(text);
|
|
20292
21010
|
return parsed && { label: basename6(file, ".service"), ...parsed };
|
|
@@ -20336,7 +21054,7 @@ function engineInstall(o) {
|
|
|
20336
21054
|
if (platform === "linux")
|
|
20337
21055
|
return installUnit({ o, parsed, label, sledsArgv, env: agentEnv, logPath, exec });
|
|
20338
21056
|
const plist = enginePlistPath(o.home, parsed.name, parsed.org, seat);
|
|
20339
|
-
mkdirSync19(
|
|
21057
|
+
mkdirSync19(dirname17(plist), { recursive: true });
|
|
20340
21058
|
if (existsSync19(plist))
|
|
20341
21059
|
exec("launchctl", ["unload", plist]);
|
|
20342
21060
|
for (const other of installedEngines(o.home)) {
|
|
@@ -20378,7 +21096,7 @@ function installUnit(a) {
|
|
|
20378
21096
|
const unit = engineUnitPath(o.home, label);
|
|
20379
21097
|
const unitName = basename6(unit);
|
|
20380
21098
|
const systemctl = (...args) => exec("systemctl", ["--user", ...args]);
|
|
20381
|
-
mkdirSync19(
|
|
21099
|
+
mkdirSync19(dirname17(unit), { recursive: true });
|
|
20382
21100
|
if (existsSync19(unit))
|
|
20383
21101
|
systemctl("stop", unitName);
|
|
20384
21102
|
for (const other of installedEngines(o.home)) {
|
|
@@ -20456,7 +21174,7 @@ function engineUninstall(o) {
|
|
|
20456
21174
|
}
|
|
20457
21175
|
|
|
20458
21176
|
// src/commands/musher.ts
|
|
20459
|
-
import { existsSync as existsSync20, mkdirSync as mkdirSync20, readFileSync as
|
|
21177
|
+
import { existsSync as existsSync20, mkdirSync as mkdirSync20, readFileSync as readFileSync18, rmSync as rmSync12, writeFileSync as writeFileSync18 } from "node:fs";
|
|
20460
21178
|
init_config();
|
|
20461
21179
|
init_meter();
|
|
20462
21180
|
var defaultWhich = (cmd) => {
|
|
@@ -20484,12 +21202,14 @@ var findClaude = (home, configured, which) => {
|
|
|
20484
21202
|
return p;
|
|
20485
21203
|
return null;
|
|
20486
21204
|
};
|
|
21205
|
+
var MUSHER_SWITCHES = new Set(["install", "uninstall", "status"]);
|
|
21206
|
+
var MUSHER_SETTINGS = new Set(["claude", "mode", "timeout"]);
|
|
20487
21207
|
var musherAgentState = (home, env = process.env) => {
|
|
20488
21208
|
const plist = musherPlistPath(home, env);
|
|
20489
21209
|
if (!existsSync20(plist))
|
|
20490
21210
|
return { installed: false, thisSeat: false };
|
|
20491
21211
|
try {
|
|
20492
|
-
const text =
|
|
21212
|
+
const text = readFileSync18(plist, "utf8");
|
|
20493
21213
|
const want = env.SLEDS_HOME?.trim();
|
|
20494
21214
|
const has = /<key>SLEDS_HOME<\/key>\s*<string>([^<]*)<\/string>/.exec(text)?.[1];
|
|
20495
21215
|
return { installed: true, thisSeat: (want ?? undefined) === (has ?? undefined) };
|
|
@@ -20499,7 +21219,7 @@ var musherAgentState = (home, env = process.env) => {
|
|
|
20499
21219
|
};
|
|
20500
21220
|
|
|
20501
21221
|
// src/claudeAccount.ts
|
|
20502
|
-
import { readFileSync as
|
|
21222
|
+
import { readFileSync as readFileSync19 } from "node:fs";
|
|
20503
21223
|
var PLANS = {
|
|
20504
21224
|
claude_max: "Max",
|
|
20505
21225
|
claude_pro: "Pro",
|
|
@@ -20528,7 +21248,7 @@ var parseClaudeAccount = (raw) => {
|
|
|
20528
21248
|
};
|
|
20529
21249
|
var readClaudeAccount = (home, env = process.env) => {
|
|
20530
21250
|
try {
|
|
20531
|
-
return parseClaudeAccount(JSON.parse(
|
|
21251
|
+
return parseClaudeAccount(JSON.parse(readFileSync19(claudeUserConfigPath2(home, env), "utf8")));
|
|
20532
21252
|
} catch {
|
|
20533
21253
|
return null;
|
|
20534
21254
|
}
|
|
@@ -20665,7 +21385,7 @@ async function runDoctor(o) {
|
|
|
20665
21385
|
detail: `seat-event ${seat ? "on" : "missing"} · spec-event ${spec ? "on" : "missing"} · session-event ${session ? "on" : "missing"} · stop-event ${stop ? "on" : "missing"}`,
|
|
20666
21386
|
fix: all ? undefined : "sleds hooks"
|
|
20667
21387
|
});
|
|
20668
|
-
const want = env.SLEDS_HOME?.trim() ?
|
|
21388
|
+
const want = env.SLEDS_HOME?.trim() ? resolve10(home, env.SLEDS_HOME.trim()) : undefined;
|
|
20669
21389
|
const cmds = sledsCommandsIn(settings);
|
|
20670
21390
|
const wrong = cmds.filter((c) => {
|
|
20671
21391
|
const got = bakedHome(c);
|
|
@@ -20754,7 +21474,7 @@ async function runDoctor(o) {
|
|
|
20754
21474
|
checks.push({ level: "ok", name: "path", detail: which[0] ?? "" });
|
|
20755
21475
|
const link = sledsLink(home, o.platform);
|
|
20756
21476
|
if (existsSync21(link)) {
|
|
20757
|
-
const binDir =
|
|
21477
|
+
const binDir = dirname18(link);
|
|
20758
21478
|
const onPath = (env.PATH ?? "").split(delimiter3).includes(binDir);
|
|
20759
21479
|
const first = which[0];
|
|
20760
21480
|
const fix = `echo 'export PATH="${binDir}:$PATH"' >> ~/.zshrc`;
|
|
@@ -20943,12 +21663,12 @@ function sandboxChecks(o, env) {
|
|
|
20943
21663
|
}
|
|
20944
21664
|
];
|
|
20945
21665
|
const nonce = o.nonce ?? `sleds-probe-${randomBytes(16).toString("hex")}`;
|
|
20946
|
-
const probe =
|
|
20947
|
-
const work = mkdtempSync(
|
|
21666
|
+
const probe = join27(dir, SANDBOX_PROBE);
|
|
21667
|
+
const work = mkdtempSync(join27(tmpdir2(), "sleds-sandbox-"));
|
|
20948
21668
|
try {
|
|
20949
21669
|
writeFileSync19(probe, `${nonce}
|
|
20950
21670
|
`, { mode: 384 });
|
|
20951
|
-
const settingsFile =
|
|
21671
|
+
const settingsFile = join27(work, "settings.json");
|
|
20952
21672
|
const settings = engineSandboxSettings(o.home, env, work, SANDBOX_DEFAULT_ALLOW);
|
|
20953
21673
|
const claudeSettings = settingsPath(o.home, env);
|
|
20954
21674
|
writeFileSync19(settingsFile, JSON.stringify(settings, null, 2), { mode: 384 });
|
|
@@ -21122,7 +21842,7 @@ function boxChecks(o, env, which, need) {
|
|
|
21122
21842
|
const file = boxEnvPath(o.home);
|
|
21123
21843
|
let boxEnvText = null;
|
|
21124
21844
|
try {
|
|
21125
|
-
boxEnvText =
|
|
21845
|
+
boxEnvText = readFileSync20(file, "utf8");
|
|
21126
21846
|
} catch {
|
|
21127
21847
|
boxEnvText = null;
|
|
21128
21848
|
}
|
|
@@ -21240,14 +21960,14 @@ import {
|
|
|
21240
21960
|
appendFileSync as appendFileSync3,
|
|
21241
21961
|
chmodSync as chmodSync6,
|
|
21242
21962
|
mkdirSync as mkdirSync21,
|
|
21243
|
-
readFileSync as
|
|
21963
|
+
readFileSync as readFileSync21,
|
|
21244
21964
|
existsSync as realExists2,
|
|
21245
21965
|
rmSync as rmSync14,
|
|
21246
|
-
statSync as
|
|
21966
|
+
statSync as statSync7,
|
|
21247
21967
|
writeFileSync as writeFileSync20
|
|
21248
21968
|
} from "node:fs";
|
|
21249
|
-
import { hostname as hostname4, userInfo as userInfo3 } from "node:os";
|
|
21250
|
-
import { dirname as
|
|
21969
|
+
import { hostname as hostname4, tmpdir as tmpdir3, userInfo as userInfo3 } from "node:os";
|
|
21970
|
+
import { dirname as dirname19, isAbsolute as isAbsolute5, join as join28, resolve as resolve11 } from "node:path";
|
|
21251
21971
|
init_machine();
|
|
21252
21972
|
init_state();
|
|
21253
21973
|
init_meter();
|
|
@@ -21507,7 +22227,7 @@ function flagsFromProfile2(flags, given, profile, home) {
|
|
|
21507
22227
|
}
|
|
21508
22228
|
}
|
|
21509
22229
|
if (profile.reposDir && !has("repos-dir") && !has("repo")) {
|
|
21510
|
-
out.reposDir = splitReposDirs(profile.reposDir).map((d) => home && /^~(\/|$)/.test(d) ?
|
|
22230
|
+
out.reposDir = splitReposDirs(profile.reposDir).map((d) => home && /^~(\/|$)/.test(d) ? join28(home, d.slice(1)) : d).join(":");
|
|
21511
22231
|
used.push(`repos ${profile.reposDir}`);
|
|
21512
22232
|
}
|
|
21513
22233
|
if (profile.mcpStrict !== undefined && !has("strict-mcp") && !has("no-strict-mcp")) {
|
|
@@ -21654,7 +22374,7 @@ function engineClient2(o, org, breaker) {
|
|
|
21654
22374
|
...src,
|
|
21655
22375
|
...breaker ? { fetchFn: breaker.fetch } : {},
|
|
21656
22376
|
timeoutMs: ENGINE_TIMEOUT_MS2,
|
|
21657
|
-
version: "0.1.
|
|
22377
|
+
version: "0.1.61"
|
|
21658
22378
|
});
|
|
21659
22379
|
const asked = org?.trim() || env.SLEDS_ORG?.trim();
|
|
21660
22380
|
if (asked && !selectOrg(readConfig(o.home), org, env).seat)
|
|
@@ -21696,19 +22416,19 @@ function realPidAlive3(pid) {
|
|
|
21696
22416
|
function memberMcpConfigs2(o, cwd) {
|
|
21697
22417
|
const read = o.readJson ?? ((p) => {
|
|
21698
22418
|
try {
|
|
21699
|
-
return JSON.parse(
|
|
22419
|
+
return JSON.parse(readFileSync21(p, "utf8"));
|
|
21700
22420
|
} catch {
|
|
21701
22421
|
return null;
|
|
21702
22422
|
}
|
|
21703
22423
|
});
|
|
21704
22424
|
const claudeDir = (o.env ?? process.env).CLAUDE_CONFIG_DIR;
|
|
21705
22425
|
return [
|
|
21706
|
-
read(claudeDir ?
|
|
21707
|
-
...cwd ? [read(
|
|
22426
|
+
read(claudeDir ? join28(claudeDir, ".claude.json") : join28(o.home, ".claude.json")),
|
|
22427
|
+
...cwd ? [read(join28(cwd, ".mcp.json"))] : []
|
|
21708
22428
|
];
|
|
21709
22429
|
}
|
|
21710
22430
|
function engineStateDir2(o, org, seat) {
|
|
21711
|
-
return seatStateDir(orgStateDir(o.stateDir ??
|
|
22431
|
+
return seatStateDir(orgStateDir(o.stateDir ?? join28(configDir(o.home, o.env ?? process.env), "engine"), org), seat);
|
|
21712
22432
|
}
|
|
21713
22433
|
function engineSeat2(o, org) {
|
|
21714
22434
|
try {
|
|
@@ -21731,7 +22451,7 @@ function engineCredential(o) {
|
|
|
21731
22451
|
const file = boxEnvPath(o.home);
|
|
21732
22452
|
let boxEnvText = null;
|
|
21733
22453
|
try {
|
|
21734
|
-
boxEnvText =
|
|
22454
|
+
boxEnvText = readFileSync21(file, "utf8");
|
|
21735
22455
|
} catch {}
|
|
21736
22456
|
return detectCredential(o.env ?? process.env, {
|
|
21737
22457
|
home: o.home,
|
|
@@ -21748,11 +22468,11 @@ function engineDeps2(o, flags, source, runner, stateName) {
|
|
|
21748
22468
|
const stateDir = engineStateDir2(o, flags.org, flags.seat);
|
|
21749
22469
|
const file = statePath(stateDir, stateName);
|
|
21750
22470
|
const mcpOrg = flags.org && engineOrg2(o, flags.org).seated ? flags.org : undefined;
|
|
21751
|
-
const repo = flags.repo ?
|
|
21752
|
-
const reposDirs = splitReposDirs(flags.reposDir).map((d) =>
|
|
22471
|
+
const repo = flags.repo ? resolve11(cwd, flags.repo) : undefined;
|
|
22472
|
+
const reposDirs = splitReposDirs(flags.reposDir).map((d) => resolve11(cwd, d));
|
|
21753
22473
|
const { branch, prTitle } = flags;
|
|
21754
|
-
const hooksDir =
|
|
21755
|
-
const permissionMark = (name) =>
|
|
22474
|
+
const hooksDir = join28(stateDir, "hooks");
|
|
22475
|
+
const permissionMark = (name) => join28(hooksDir, `${name}.permission.json`);
|
|
21756
22476
|
const withHook = (name, settings) => applyPermissionHook(settings, [...o.sledsArgv ?? ["sleds"], "permission-event", "--mark", permissionMark(name)].map(shq2).join(" "));
|
|
21757
22477
|
return {
|
|
21758
22478
|
source,
|
|
@@ -21777,9 +22497,9 @@ function engineDeps2(o, flags, source, runner, stateName) {
|
|
|
21777
22497
|
sandbox: true,
|
|
21778
22498
|
writeSandboxSettings: o.writeSandboxSettings ?? ((name, cwd, allow) => {
|
|
21779
22499
|
const gitCommonDir = gitCommonDirOf2(exec, cwd);
|
|
21780
|
-
const dir =
|
|
22500
|
+
const dir = join28(stateDir, "sandbox");
|
|
21781
22501
|
mkdirSync21(dir, { recursive: true, mode: 448 });
|
|
21782
|
-
const p =
|
|
22502
|
+
const p = join28(dir, `${name}.json`);
|
|
21783
22503
|
const walls = engineSandboxSettings2(o.home, o.env ?? process.env, gitCommonDir, [
|
|
21784
22504
|
...SANDBOX_DEFAULT_ALLOW,
|
|
21785
22505
|
...flags.sandboxAllow ?? []
|
|
@@ -21796,7 +22516,7 @@ function engineDeps2(o, flags, source, runner, stateName) {
|
|
|
21796
22516
|
} : {},
|
|
21797
22517
|
writeHookSettings: (name, allow) => {
|
|
21798
22518
|
mkdirSync21(hooksDir, { recursive: true, mode: 448 });
|
|
21799
|
-
const p =
|
|
22519
|
+
const p = join28(hooksDir, `${name}.json`);
|
|
21800
22520
|
const base = allow?.length ? { permissions: { allow: [...allow] } } : {};
|
|
21801
22521
|
writeFileSync20(p, `${JSON.stringify(withHook(name, base), null, 2)}
|
|
21802
22522
|
`, { mode: 384 });
|
|
@@ -21824,9 +22544,9 @@ function engineDeps2(o, flags, source, runner, stateName) {
|
|
|
21824
22544
|
exists
|
|
21825
22545
|
}),
|
|
21826
22546
|
writeMcpConfig: o.writeMcpConfig ?? ((name, runToken, worktree) => {
|
|
21827
|
-
const dir =
|
|
22547
|
+
const dir = join28(stateDir, "mcp");
|
|
21828
22548
|
mkdirSync21(dir, { recursive: true, mode: 448 });
|
|
21829
|
-
const p =
|
|
22549
|
+
const p = join28(dir, `${name}.json`);
|
|
21830
22550
|
let others = {};
|
|
21831
22551
|
if (flags.strictMcp && flags.mcpAllow?.length) {
|
|
21832
22552
|
const r = mcpPassThrough(flags.mcpAllow, memberMcpConfigs2(o, worktree));
|
|
@@ -21842,12 +22562,26 @@ function engineDeps2(o, flags, source, runner, stateName) {
|
|
|
21842
22562
|
}),
|
|
21843
22563
|
removeMcpConfig: o.removeMcpConfig ?? ((p) => rmSync14(p, { force: true })),
|
|
21844
22564
|
writePrompt: o.writePrompt ?? ((name, text) => {
|
|
21845
|
-
const dir =
|
|
22565
|
+
const dir = join28(stateDir, "prompts");
|
|
21846
22566
|
mkdirSync21(dir, { recursive: true, mode: 448 });
|
|
21847
|
-
const p =
|
|
22567
|
+
const p = join28(dir, `${name}.md`);
|
|
21848
22568
|
writeFileSync20(p, text, { mode: 384 });
|
|
21849
22569
|
return p;
|
|
21850
22570
|
}),
|
|
22571
|
+
mockOut: (name) => {
|
|
22572
|
+
const dir = join28(tmpdir3(), "sleds-mocks");
|
|
22573
|
+
mkdirSync21(dir, { recursive: true, mode: 448 });
|
|
22574
|
+
const p = join28(dir, `${name}.html`);
|
|
22575
|
+
rmSync14(p, { force: true });
|
|
22576
|
+
return p;
|
|
22577
|
+
},
|
|
22578
|
+
readMock: (p) => {
|
|
22579
|
+
try {
|
|
22580
|
+
return readFileSync21(p, "utf8");
|
|
22581
|
+
} catch {
|
|
22582
|
+
return null;
|
|
22583
|
+
}
|
|
22584
|
+
},
|
|
21851
22585
|
ensureWorktree: (e, repoPath, branch, path, base, install) => ensureWorktree(e, repoPath, branch, path, base, o.worktreeFs ?? { ...realWorktreeFs, exists }, { ...install ? { command: install } : {}, log: o.log ?? console.log }),
|
|
21852
22586
|
ensureDraftWorktree: (e, repoPath, path, base) => ensureDetachedWorktree(e, repoPath, path, base, o.worktreeFs ?? { ...realWorktreeFs, exists }),
|
|
21853
22587
|
defaultBranch,
|
|
@@ -21872,14 +22606,14 @@ function engineDeps2(o, flags, source, runner, stateName) {
|
|
|
21872
22606
|
}
|
|
21873
22607
|
function sandboxDenyRead2(home, env) {
|
|
21874
22608
|
return {
|
|
21875
|
-
dirs: [...new Set([configDir(home, env),
|
|
22609
|
+
dirs: [...new Set([configDir(home, env), join28(home, ".config", "sleds")])],
|
|
21876
22610
|
files: [
|
|
21877
22611
|
...new Set([
|
|
21878
22612
|
tokenPath(home, env),
|
|
21879
|
-
|
|
22613
|
+
join28(home, ".sleds-token"),
|
|
21880
22614
|
settingsPath(home, env),
|
|
21881
22615
|
backupPath(home, env),
|
|
21882
|
-
|
|
22616
|
+
join28(claudeDir(home, env), "settings.local.json")
|
|
21883
22617
|
])
|
|
21884
22618
|
]
|
|
21885
22619
|
};
|
|
@@ -21896,7 +22630,7 @@ function gitCommonDirOf2(exec, cwd) {
|
|
|
21896
22630
|
const dir = r.status === 0 ? r.stdout.trim() : "";
|
|
21897
22631
|
if (!dir)
|
|
21898
22632
|
throw new Error(`git has no common dir for ${cwd}, so the sandbox has nowhere to commit`);
|
|
21899
|
-
return
|
|
22633
|
+
return resolve11(cwd, dir);
|
|
21900
22634
|
}
|
|
21901
22635
|
function sandboxRefusal(flags) {
|
|
21902
22636
|
if (flags.lentBy && !flags.sandbox)
|
|
@@ -21913,17 +22647,17 @@ function sandboxRefusal(flags) {
|
|
|
21913
22647
|
}
|
|
21914
22648
|
var LENT_NEEDS_SANDBOX2 = "a lent engine runs every session sandboxed (spec 48 rule 10) — add --sandbox to --lent-by";
|
|
21915
22649
|
function lenderHome2(accountHome, lentBy) {
|
|
21916
|
-
return isAbsolute5(lentBy) ?
|
|
22650
|
+
return isAbsolute5(lentBy) ? resolve11(lentBy) : join28(dirname19(accountHome), lentBy);
|
|
21917
22651
|
}
|
|
21918
22652
|
function lentHomeProblems2(accountHome, lentBy, modeOf) {
|
|
21919
22653
|
const lender = lenderHome2(accountHome, lentBy);
|
|
21920
|
-
if (lender ===
|
|
22654
|
+
if (lender === resolve11(accountHome))
|
|
21921
22655
|
return [
|
|
21922
22656
|
`--lent-by ${lentBy} is this account — a lent engine runs as the borrower's own OS user, not the lender's`
|
|
21923
22657
|
];
|
|
21924
22658
|
const problems = [];
|
|
21925
22659
|
for (const [who, home] of [
|
|
21926
|
-
["the borrower's",
|
|
22660
|
+
["the borrower's", resolve11(accountHome)],
|
|
21927
22661
|
["the lender's", lender]
|
|
21928
22662
|
]) {
|
|
21929
22663
|
const mode = modeOf(home);
|
|
@@ -21936,7 +22670,7 @@ function lentHomeProblems2(accountHome, lentBy, modeOf) {
|
|
|
21936
22670
|
}
|
|
21937
22671
|
var homeMode2 = (path) => {
|
|
21938
22672
|
try {
|
|
21939
|
-
return
|
|
22673
|
+
return statSync7(path).mode & 4095;
|
|
21940
22674
|
} catch {
|
|
21941
22675
|
return null;
|
|
21942
22676
|
}
|
|
@@ -21981,7 +22715,7 @@ async function driveEngine2(o, flags, deps, until = {}) {
|
|
|
21981
22715
|
runner: deps.runner.name,
|
|
21982
22716
|
atOnce: flags.atOnce,
|
|
21983
22717
|
interval: flags.interval,
|
|
21984
|
-
cliVersion: "0.1.
|
|
22718
|
+
cliVersion: "0.1.61"
|
|
21985
22719
|
};
|
|
21986
22720
|
const beat = (checkedAt) => {
|
|
21987
22721
|
const m = deps.machine;
|
|
@@ -22611,14 +23345,14 @@ init_config();
|
|
|
22611
23345
|
import {
|
|
22612
23346
|
existsSync as existsSync22,
|
|
22613
23347
|
mkdirSync as mkdirSync22,
|
|
22614
|
-
readFileSync as
|
|
23348
|
+
readFileSync as readFileSync22,
|
|
22615
23349
|
readdirSync as readdirSync6,
|
|
22616
23350
|
realpathSync as realpathSync2,
|
|
22617
23351
|
rmSync as rmSync15,
|
|
22618
23352
|
writeFileSync as writeFileSync21
|
|
22619
23353
|
} from "node:fs";
|
|
22620
23354
|
import { hostname as hostname5 } from "node:os";
|
|
22621
|
-
import { basename as basename7, dirname as
|
|
23355
|
+
import { basename as basename7, dirname as dirname20, isAbsolute as isAbsolute6, join as join29 } from "node:path";
|
|
22622
23356
|
init_state();
|
|
22623
23357
|
var ENGINE_LABEL2 = "ai.sleds.engine";
|
|
22624
23358
|
var ENGINE_INSTALL_USAGE2 = `usage: sleds engine install [engine flags] | sleds engine uninstall [--name <n>] [--org <slug>]
|
|
@@ -22639,13 +23373,13 @@ function engineLabel2(name, org, seat) {
|
|
|
22639
23373
|
return [ENGINE_LABEL2, org, seat?.trim(), name].filter((p) => Boolean(p)).map((p, i) => i === 0 ? p : engineSlug(p)).join(".");
|
|
22640
23374
|
}
|
|
22641
23375
|
function enginePlistPath2(home, name, org, seat) {
|
|
22642
|
-
return
|
|
23376
|
+
return join29(home, "Library", "LaunchAgents", `${engineLabel2(name, org, seat)}.plist`);
|
|
22643
23377
|
}
|
|
22644
23378
|
function engineLogPath2(stateDir, name) {
|
|
22645
|
-
return
|
|
23379
|
+
return join29(stateDir, `${engineSlug(name)}.log`);
|
|
22646
23380
|
}
|
|
22647
23381
|
function engineEventsPath2(stateDir, name) {
|
|
22648
|
-
return
|
|
23382
|
+
return join29(stateDir, `${engineSlug(name)}.events.jsonl`);
|
|
22649
23383
|
}
|
|
22650
23384
|
var xmlEscape5 = (s) => s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
22651
23385
|
function buildEnginePlist2(o) {
|
|
@@ -22742,10 +23476,10 @@ function readEngines2(dir, suffix, manager, parse) {
|
|
|
22742
23476
|
}
|
|
22743
23477
|
const out = [];
|
|
22744
23478
|
for (const name of names.filter((n) => n.startsWith(`${ENGINE_LABEL2}.`) && n.endsWith(suffix))) {
|
|
22745
|
-
const path =
|
|
23479
|
+
const path = join29(dir, name);
|
|
22746
23480
|
let parsed;
|
|
22747
23481
|
try {
|
|
22748
|
-
parsed = parse(
|
|
23482
|
+
parsed = parse(readFileSync22(path, "utf8"), name);
|
|
22749
23483
|
} catch {
|
|
22750
23484
|
parsed = null;
|
|
22751
23485
|
}
|
|
@@ -22762,7 +23496,7 @@ function readEngines2(dir, suffix, manager, parse) {
|
|
|
22762
23496
|
}
|
|
22763
23497
|
function installedEngines2(home) {
|
|
22764
23498
|
return [
|
|
22765
|
-
...readEngines2(
|
|
23499
|
+
...readEngines2(join29(home, "Library", "LaunchAgents"), ".plist", "launchd", parseEnginePlist2),
|
|
22766
23500
|
...readEngines2(engineUnitDir(home), ".service", "systemd", (text, file) => {
|
|
22767
23501
|
const parsed = parseEngineUnit(text);
|
|
22768
23502
|
return parsed && { label: basename7(file, ".service"), ...parsed };
|
|
@@ -22800,7 +23534,7 @@ async function ensureReposDir(o) {
|
|
|
22800
23534
|
return false;
|
|
22801
23535
|
}
|
|
22802
23536
|
const exists = o.exists ?? existsSync22;
|
|
22803
|
-
const missing = dirs.filter((d) => !exists(/^~(\/|$)/.test(d) ?
|
|
23537
|
+
const missing = dirs.filter((d) => !exists(/^~(\/|$)/.test(d) ? join29(o.home, d.slice(1)) : d));
|
|
22804
23538
|
if (missing.length) {
|
|
22805
23539
|
error(`Not a folder on this machine: ${missing.join(", ")}. ${REPOS_DIR_NEEDED}`);
|
|
22806
23540
|
return false;
|
|
@@ -22856,7 +23590,7 @@ function engineInstall2(o) {
|
|
|
22856
23590
|
if (platform === "linux")
|
|
22857
23591
|
return installUnit2({ o, parsed, label, sledsArgv, env: agentEnv, logPath, exec });
|
|
22858
23592
|
const plist = enginePlistPath2(o.home, parsed.name, parsed.org, seat);
|
|
22859
|
-
mkdirSync22(
|
|
23593
|
+
mkdirSync22(dirname20(plist), { recursive: true });
|
|
22860
23594
|
if (existsSync22(plist))
|
|
22861
23595
|
exec("launchctl", ["unload", plist]);
|
|
22862
23596
|
for (const other of installedEngines2(o.home)) {
|
|
@@ -22898,7 +23632,7 @@ function installUnit2(a) {
|
|
|
22898
23632
|
const unit = engineUnitPath(o.home, label);
|
|
22899
23633
|
const unitName = basename7(unit);
|
|
22900
23634
|
const systemctl = (...args) => exec("systemctl", ["--user", ...args]);
|
|
22901
|
-
mkdirSync22(
|
|
23635
|
+
mkdirSync22(dirname20(unit), { recursive: true });
|
|
22902
23636
|
if (existsSync22(unit))
|
|
22903
23637
|
systemctl("stop", unitName);
|
|
22904
23638
|
for (const other of installedEngines2(o.home)) {
|
|
@@ -23099,7 +23833,7 @@ import { closeSync as closeSync5, fstatSync as fstatSync2, openSync as openSync5
|
|
|
23099
23833
|
import { hostname as hostname6 } from "node:os";
|
|
23100
23834
|
|
|
23101
23835
|
// src/commands/engineStatus.ts
|
|
23102
|
-
import { existsSync as existsSync23, readFileSync as
|
|
23836
|
+
import { existsSync as existsSync23, readFileSync as readFileSync23 } from "node:fs";
|
|
23103
23837
|
init_state();
|
|
23104
23838
|
|
|
23105
23839
|
// src/engine/status.ts
|
|
@@ -23181,7 +23915,10 @@ function engineStatus(i) {
|
|
|
23181
23915
|
...opt("review", r.pr.review || undefined),
|
|
23182
23916
|
mergeable: r.pr.clear === true,
|
|
23183
23917
|
queued: r.pr.queued === true,
|
|
23184
|
-
...opt("conflict", conflictOf(r, i.state.decisions, i.watchOf))
|
|
23918
|
+
...opt("conflict", conflictOf(r, i.state.decisions, i.watchOf)),
|
|
23919
|
+
runner: r.handle.runner,
|
|
23920
|
+
...r.pr.live ? { watch: i.watchOf(r), watchTarget: watchTarget(r.handle) } : {},
|
|
23921
|
+
...opt("worktree", r.handle.cwd !== r.repoPath ? r.handle.cwd : undefined)
|
|
23185
23922
|
});
|
|
23186
23923
|
continue;
|
|
23187
23924
|
}
|
|
@@ -23255,10 +23992,14 @@ function statusLines(s, now) {
|
|
|
23255
23992
|
}
|
|
23256
23993
|
lines.push(`running ${s.running.length}`);
|
|
23257
23994
|
for (const r of s.running)
|
|
23258
|
-
lines.push(` ${r.item}/${r.node} ${r.title} · ${r.answerer ? `parked, the decider answers${r.answerer.model ? ` on ${r.answerer.model}` : ""}${r.answerer.effort ? ` · ${r.answerer.effort}` : ""}` : r.parked ? "parked, waiting for your answer" : ago(r.startedAt)} · watch: ${r.watch}`);
|
|
23995
|
+
lines.push(` ${r.item}/${r.node} ${r.title} · ${r.answerer ? `parked, the decider answers${r.answerer.model ? ` on ${r.answerer.model}` : ""}${r.answerer.effort ? ` · ${r.answerer.effort}` : ""}` : r.parked ? "parked, waiting for your answer" : `${r.kind === "mock" ? "drawing a mock · " : ""}${ago(r.startedAt)}`} · watch: ${r.watch}`);
|
|
23259
23996
|
lines.push(`following ${s.following.length}`);
|
|
23260
23997
|
for (const f of s.following) {
|
|
23261
23998
|
lines.push(` ${f.item}/${f.node} ${f.pr}${f.checks ? ` checks ${f.checks}` : ""}${f.review ? ` · ${f.review}` : ""}${f.mergeable ? " · ready for your merge" : ""}${f.queued ? " · queued to merge" : ""}`);
|
|
23999
|
+
if (f.watch)
|
|
24000
|
+
lines.push(` watch: ${f.watch}`);
|
|
24001
|
+
else if (f.worktree)
|
|
24002
|
+
lines.push(` worktree: ${f.worktree}`);
|
|
23262
24003
|
const c = f.conflict;
|
|
23263
24004
|
if (c)
|
|
23264
24005
|
lines.push(` conflict on ${c.files.join(", ")}${c.cause ? ` · ${c.cause}` : ""} · ${CONFLICT_STAGE[c.stage]} · since ${ago(c.since)} ago${c.watch ? ` · watch: ${c.watch}` : ""}`);
|
|
@@ -23390,7 +24131,7 @@ var engineLogCmd = (o) => {
|
|
|
23390
24131
|
|
|
23391
24132
|
// src/commands/engineProfile.ts
|
|
23392
24133
|
init_src();
|
|
23393
|
-
import { existsSync as existsSync24, readFileSync as
|
|
24134
|
+
import { existsSync as existsSync24, readFileSync as readFileSync24 } from "node:fs";
|
|
23394
24135
|
import { hostname as hostname7 } from "node:os";
|
|
23395
24136
|
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]";
|
|
23396
24137
|
var PROFILE_FIELDS = [
|
|
@@ -23661,7 +24402,7 @@ async function engineProfileCmd(o) {
|
|
|
23661
24402
|
const rest = words.slice(1);
|
|
23662
24403
|
let stdin = null;
|
|
23663
24404
|
if (fromStdin) {
|
|
23664
|
-
const taken = cmuxPasswordFromJson((o.stdin ?? (() =>
|
|
24405
|
+
const taken = cmuxPasswordFromJson((o.stdin ?? (() => readFileSync24(0, "utf8")))());
|
|
23665
24406
|
if ("error" in taken || taken.password !== undefined && password !== undefined) {
|
|
23666
24407
|
error("error" in taken ? taken.error : "the cmux password is given once");
|
|
23667
24408
|
error(PROFILE_USAGE);
|
|
@@ -23746,7 +24487,7 @@ var keychainLine = (k) => k === "set" ? "cmuxPassword: set (Keychain)" : k === "
|
|
|
23746
24487
|
|
|
23747
24488
|
// src/commands/engineStatus.ts
|
|
23748
24489
|
init_state();
|
|
23749
|
-
import { existsSync as existsSync25, readFileSync as
|
|
24490
|
+
import { existsSync as existsSync25, readFileSync as readFileSync25 } from "node:fs";
|
|
23750
24491
|
import { hostname as hostname8 } from "node:os";
|
|
23751
24492
|
var ENGINE_STATUS_USAGE = "usage: sleds engine status [--name <n>] [--org <slug>] [--json]";
|
|
23752
24493
|
function parseStatusArgs(argv, host) {
|
|
@@ -23802,7 +24543,7 @@ function readEngineStatus(stateDir, engine, host, pidAlive, seat) {
|
|
|
23802
24543
|
}
|
|
23803
24544
|
function safeRead(path) {
|
|
23804
24545
|
try {
|
|
23805
|
-
return
|
|
24546
|
+
return readFileSync25(path, "utf8");
|
|
23806
24547
|
} catch {
|
|
23807
24548
|
return "";
|
|
23808
24549
|
}
|
|
@@ -23923,10 +24664,10 @@ init_meter();
|
|
|
23923
24664
|
|
|
23924
24665
|
// src/owed.ts
|
|
23925
24666
|
init_config();
|
|
23926
|
-
import { existsSync as existsSync26, mkdirSync as mkdirSync23, readFileSync as
|
|
23927
|
-
import { dirname as
|
|
23928
|
-
var owedPath = (home) =>
|
|
23929
|
-
var sessionPath = (home) =>
|
|
24667
|
+
import { existsSync as existsSync26, mkdirSync as mkdirSync23, readFileSync as readFileSync26, renameSync as renameSync11, writeFileSync as writeFileSync22 } from "node:fs";
|
|
24668
|
+
import { dirname as dirname21, join as join30 } from "node:path";
|
|
24669
|
+
var owedPath = (home) => join30(configDir(home), "owed.json");
|
|
24670
|
+
var sessionPath = (home) => join30(configDir(home), "session.json");
|
|
23930
24671
|
var OWED_TTL_MS = 3 * 24 * 60 * 60000;
|
|
23931
24672
|
var str14 = (v) => typeof v === "string" ? v : undefined;
|
|
23932
24673
|
function sourceKey(s) {
|
|
@@ -23944,13 +24685,13 @@ var readJson = (p) => {
|
|
|
23944
24685
|
if (!existsSync26(p))
|
|
23945
24686
|
return null;
|
|
23946
24687
|
try {
|
|
23947
|
-
return JSON.parse(
|
|
24688
|
+
return JSON.parse(readFileSync26(p, "utf8"));
|
|
23948
24689
|
} catch {
|
|
23949
24690
|
return null;
|
|
23950
24691
|
}
|
|
23951
24692
|
};
|
|
23952
24693
|
var writeJson = (p, v) => {
|
|
23953
|
-
mkdirSync23(
|
|
24694
|
+
mkdirSync23(dirname21(p), { recursive: true, mode: 448 });
|
|
23954
24695
|
const tmp = `${p}.${process.pid}.tmp`;
|
|
23955
24696
|
writeFileSync22(tmp, `${JSON.stringify(v, null, 2)}
|
|
23956
24697
|
`, { mode: 384 });
|
|
@@ -24084,7 +24825,7 @@ function contextFor(deps) {
|
|
|
24084
24825
|
...src,
|
|
24085
24826
|
fetchFn: deps.fetchFn,
|
|
24086
24827
|
timeoutMs: deps.timeoutMs,
|
|
24087
|
-
version: "0.1.
|
|
24828
|
+
version: "0.1.61"
|
|
24088
24829
|
}), deps.home, now) : null;
|
|
24089
24830
|
return {
|
|
24090
24831
|
channel: "code",
|
|
@@ -24788,19 +25529,27 @@ var init = async (o) => {
|
|
|
24788
25529
|
const wrote = [];
|
|
24789
25530
|
const seatInfo = await fetchSeatInfo(url, resolved.token, o.fetchFn);
|
|
24790
25531
|
const org = seatInfo.org;
|
|
24791
|
-
const
|
|
25532
|
+
const current = readSettings(o.home, env);
|
|
25533
|
+
const prior = readConfig(o.home);
|
|
25534
|
+
const proxyUrls = [url, prior.proxyUrl, DEFAULT_PROXY_URL];
|
|
25535
|
+
const ours = isSledsProxyEnv(current, proxyUrls);
|
|
25536
|
+
const gateway = hasGatewayEnv(current) && !ours;
|
|
25537
|
+
const kept = ours ? "proxy-always" : "seat-first";
|
|
25538
|
+
if (!org && !o.proxyAlways && !o.seatFirst)
|
|
25539
|
+
error(`Could not read this seat's org from ${url}/proxy/meter; kept Claude Code's wiring as it was (${kept}). Re-run \`sleds init\` once the proxy answers.`);
|
|
25540
|
+
const wiring = o.proxyAlways ? "proxy-always" : o.seatFirst ? "seat-first" : org?.mode ?? kept;
|
|
24792
25541
|
if (wiring === "proxy-always" && org && !org.capabilities.upstreamKey) {
|
|
24793
25542
|
error(`${org.name} has no platform key for the proxy to forward with; --proxy-always would break every request. Using seat-first.`);
|
|
24794
25543
|
}
|
|
24795
25544
|
const finalWiring = wiring === "proxy-always" && org && !org.capabilities.upstreamKey ? "seat-first" : wiring;
|
|
24796
25545
|
if (backupSettings(o.home, env))
|
|
24797
25546
|
wrote.push(`backed up settings to ${backupPath(o.home, env)}`);
|
|
24798
|
-
const
|
|
24799
|
-
const
|
|
25547
|
+
const wired = finalWiring === "proxy-always" ? applyProxy(current, { url, token: resolved.token }) : ours ? switchOff(current, envToRestore([prior.priorEnv, prior.priorGatewayEnv], proxyUrls)) : current;
|
|
25548
|
+
const priorGatewayEnv = finalWiring !== "proxy-always" ? undefined : gateway ? capturePriorEnv(current) : prior.priorGatewayEnv;
|
|
24800
25549
|
const slug = org?.slug ?? resolved.approved?.org.slug;
|
|
24801
25550
|
const statusLine = applyStatusLine(wired, pinOrg(o.statusLineCommand ?? sledsCommand("statusline", o.home, o.env), slug));
|
|
24802
25551
|
writeSettings(o.home, statusLine.settings, env);
|
|
24803
|
-
wrote.push(finalWiring === "proxy-always" ? `set env.ANTHROPIC_BASE_URL and env.ANTHROPIC_AUTH_TOKEN in ${settingsPath(o.home, env)} (everything through the proxy)` : `left Claude Code on your seat (no ANTHROPIC_BASE_URL in ${settingsPath(o.home, env)}); \`sleds on\` switches to the proxy at a wall`);
|
|
25552
|
+
wrote.push(finalWiring === "proxy-always" ? `set env.ANTHROPIC_BASE_URL and env.ANTHROPIC_AUTH_TOKEN in ${settingsPath(o.home, env)} (everything through the proxy${gateway ? "; your own gateway is kept for `sleds off` and uninstall" : ""})` : gateway ? `left your own ANTHROPIC_BASE_URL / ANTHROPIC_AUTH_TOKEN in ${settingsPath(o.home, env)} as they were; \`sleds on\` switches to the proxy at a wall` : `left Claude Code on your seat (no ANTHROPIC_BASE_URL in ${settingsPath(o.home, env)}); \`sleds on\` switches to the proxy at a wall`);
|
|
24804
25553
|
wrote.push(statusLine.prior === undefined ? `set statusLine in ${settingsPath(o.home, env)}` : `set statusLine in ${settingsPath(o.home, env)} (previous one kept for uninstall)`);
|
|
24805
25554
|
hooks2({
|
|
24806
25555
|
home: o.home,
|
|
@@ -24819,12 +25568,12 @@ var init = async (o) => {
|
|
|
24819
25568
|
writeTokenFile(o.home, resolved.token);
|
|
24820
25569
|
wrote.push(`saved token to ${tokenPath(o.home)} (0600)`);
|
|
24821
25570
|
}
|
|
24822
|
-
const prior = readConfig(o.home);
|
|
24823
25571
|
const base = {
|
|
24824
25572
|
...prior,
|
|
24825
25573
|
proxyUrl: url,
|
|
24826
25574
|
agentKind: "claude-code",
|
|
24827
|
-
...statusLine.prior !== undefined ? { priorStatusLine: statusLine.prior } : {}
|
|
25575
|
+
...statusLine.prior !== undefined ? { priorStatusLine: statusLine.prior } : {},
|
|
25576
|
+
priorGatewayEnv
|
|
24828
25577
|
};
|
|
24829
25578
|
const email = seatInfo.email ?? resolved.approved?.email;
|
|
24830
25579
|
const asked = orgChoice(prior, o.org, env);
|
|
@@ -24978,8 +25727,9 @@ var login = async (o) => {
|
|
|
24978
25727
|
// src/commands/logout.ts
|
|
24979
25728
|
init_config();
|
|
24980
25729
|
init_state();
|
|
24981
|
-
import { existsSync as existsSync27, readFileSync as
|
|
25730
|
+
import { existsSync as existsSync27, readFileSync as readFileSync27, rmSync as rmSync16 } from "node:fs";
|
|
24982
25731
|
import { hostname as hostname10 } from "node:os";
|
|
25732
|
+
import { basename as basename8 } from "node:path";
|
|
24983
25733
|
init_settings();
|
|
24984
25734
|
var LOGOUT_USAGE = "usage: sleds logout [--org <slug>] [--json]";
|
|
24985
25735
|
function parseLogoutArgs(argv) {
|
|
@@ -25101,8 +25851,15 @@ async function logout(o) {
|
|
|
25101
25851
|
if (!name)
|
|
25102
25852
|
continue;
|
|
25103
25853
|
if (!e.org) {
|
|
25104
|
-
|
|
25105
|
-
|
|
25854
|
+
const exec = o.exec ?? defaultExec2;
|
|
25855
|
+
if (e.manager === "systemd") {
|
|
25856
|
+
exec("systemctl", ["--user", "disable", "--now", basename8(e.plist)]);
|
|
25857
|
+
rmSync16(e.plist, { force: true });
|
|
25858
|
+
exec("systemctl", ["--user", "daemon-reload"]);
|
|
25859
|
+
} else {
|
|
25860
|
+
exec("launchctl", ["unload", e.plist]);
|
|
25861
|
+
rmSync16(e.plist, { force: true });
|
|
25862
|
+
}
|
|
25106
25863
|
continue;
|
|
25107
25864
|
}
|
|
25108
25865
|
engineUninstall({
|
|
@@ -25135,7 +25892,7 @@ async function logout(o) {
|
|
|
25135
25892
|
};
|
|
25136
25893
|
writeConfig(o.home, next);
|
|
25137
25894
|
const tp = tokenPath(o.home, env);
|
|
25138
|
-
if (existsSync27(tp) &&
|
|
25895
|
+
if (existsSync27(tp) && readFileSync27(tp, "utf8").trim() === seat.token)
|
|
25139
25896
|
rmSync16(tp, { force: true });
|
|
25140
25897
|
const active = next.activeOrg ?? next.org?.slug ?? null;
|
|
25141
25898
|
const result = { ok: true, email, org: slug, paused, active };
|
|
@@ -25213,9 +25970,9 @@ var meter = async (o) => {
|
|
|
25213
25970
|
init_config();
|
|
25214
25971
|
init_meter();
|
|
25215
25972
|
import { spawnSync as spawnSync7 } from "node:child_process";
|
|
25216
|
-
import { existsSync as existsSync28, mkdirSync as mkdirSync25, readFileSync as
|
|
25973
|
+
import { existsSync as existsSync28, mkdirSync as mkdirSync25, readFileSync as readFileSync28, rmSync as rmSync17, writeFileSync as writeFileSync24 } from "node:fs";
|
|
25217
25974
|
import { hostname as hostname11 } from "node:os";
|
|
25218
|
-
import { dirname as
|
|
25975
|
+
import { dirname as dirname22 } from "node:path";
|
|
25219
25976
|
var defaultWhich3 = (cmd) => {
|
|
25220
25977
|
const path = process.env.PATH ?? "";
|
|
25221
25978
|
for (const dir of path.split(":").filter(Boolean)) {
|
|
@@ -25253,7 +26010,7 @@ var musherInstall = (o) => {
|
|
|
25253
26010
|
const plist = musherPlistPath(o.home, env);
|
|
25254
26011
|
const label = musherLabel(env);
|
|
25255
26012
|
const dir = configDir(o.home, env);
|
|
25256
|
-
mkdirSync25(
|
|
26013
|
+
mkdirSync25(dirname22(plist), { recursive: true });
|
|
25257
26014
|
mkdirSync25(dir, { recursive: true, mode: 448 });
|
|
25258
26015
|
const execPath = o.sledsArgv[0] ?? process.execPath;
|
|
25259
26016
|
const agentEnv = {
|
|
@@ -25373,16 +26130,22 @@ var musherTick = async (o) => {
|
|
|
25373
26130
|
};
|
|
25374
26131
|
var musherConfigure = (o, flags) => {
|
|
25375
26132
|
const log = o.log ?? console.log;
|
|
26133
|
+
const error = o.error ?? console.error;
|
|
25376
26134
|
const cfg = readConfig(o.home);
|
|
25377
26135
|
const musher = { ...cfg.musher ?? {} };
|
|
25378
26136
|
if (flags.claude)
|
|
25379
26137
|
musher.claudePath = flags.claude;
|
|
25380
|
-
if (flags.mode)
|
|
26138
|
+
if (flags.mode) {
|
|
26139
|
+
if (!PERMISSION_MODES.includes(flags.mode)) {
|
|
26140
|
+
error(`--mode takes one of ${PERMISSION_MODES.join(", ")} (got ${flags.mode})`);
|
|
26141
|
+
return 1;
|
|
26142
|
+
}
|
|
25381
26143
|
musher.permissionMode = flags.mode;
|
|
26144
|
+
}
|
|
25382
26145
|
if (flags.timeout) {
|
|
25383
|
-
const n = Number
|
|
25384
|
-
if (!Number.
|
|
25385
|
-
|
|
26146
|
+
const n = /^\d+$/.test(flags.timeout) ? Number(flags.timeout) : Number.NaN;
|
|
26147
|
+
if (!Number.isSafeInteger(n) || n <= 0) {
|
|
26148
|
+
error("--timeout takes minutes, a positive whole number");
|
|
25386
26149
|
return 1;
|
|
25387
26150
|
}
|
|
25388
26151
|
musher.timeoutMinutes = n;
|
|
@@ -25397,22 +26160,38 @@ var defaultExecSync = (cmd, args) => {
|
|
|
25397
26160
|
return { ok: false, stderr: r.error.message };
|
|
25398
26161
|
return { ok: r.status === 0, stderr: r.stderr ?? "" };
|
|
25399
26162
|
};
|
|
25400
|
-
var
|
|
25401
|
-
|
|
26163
|
+
var MUSHER_SWITCHES2 = new Set(["install", "uninstall", "status"]);
|
|
26164
|
+
var MUSHER_SETTINGS2 = new Set(["claude", "mode", "timeout"]);
|
|
26165
|
+
var parseMusherArgs = (argv) => {
|
|
25402
26166
|
const flags = {};
|
|
25403
26167
|
const positional = [];
|
|
25404
|
-
for (let i = 0;i <
|
|
25405
|
-
const a =
|
|
25406
|
-
if (a.startsWith("--")) {
|
|
25407
|
-
const next = args[i + 1];
|
|
25408
|
-
if (next !== undefined && !next.startsWith("--")) {
|
|
25409
|
-
flags[a.slice(2)] = next;
|
|
25410
|
-
i++;
|
|
25411
|
-
} else
|
|
25412
|
-
flags[a.slice(2)] = "true";
|
|
25413
|
-
} else
|
|
26168
|
+
for (let i = 0;i < argv.length; i++) {
|
|
26169
|
+
const a = argv[i] ?? "";
|
|
26170
|
+
if (!a.startsWith("--")) {
|
|
25414
26171
|
positional.push(a);
|
|
26172
|
+
continue;
|
|
26173
|
+
}
|
|
26174
|
+
const eq = a.indexOf("=");
|
|
26175
|
+
const name = a.slice(2, eq < 0 ? undefined : eq);
|
|
26176
|
+
if (MUSHER_SWITCHES2.has(name) && eq < 0)
|
|
26177
|
+
flags[name] = "true";
|
|
26178
|
+
else if (MUSHER_SETTINGS2.has(name)) {
|
|
26179
|
+
const v = eq < 0 ? argv[++i] : a.slice(eq + 1);
|
|
26180
|
+
if (!v?.trim() || v.startsWith("--"))
|
|
26181
|
+
return { error: `--${name} needs a value` };
|
|
26182
|
+
flags[name] = v.trim();
|
|
26183
|
+
} else
|
|
26184
|
+
return { error: `unknown argument ${a}` };
|
|
26185
|
+
}
|
|
26186
|
+
return { flags, positional };
|
|
26187
|
+
};
|
|
26188
|
+
var musher3 = async (o) => {
|
|
26189
|
+
const parsed = parseMusherArgs(o.argv);
|
|
26190
|
+
if ("error" in parsed) {
|
|
26191
|
+
(o.error ?? console.error)(`sleds musher: ${parsed.error}`);
|
|
26192
|
+
return 1;
|
|
25415
26193
|
}
|
|
26194
|
+
const { flags, positional } = parsed;
|
|
25416
26195
|
if (flags.install)
|
|
25417
26196
|
return musherInstall(o);
|
|
25418
26197
|
if (flags.uninstall)
|
|
@@ -25430,13 +26209,24 @@ var musher3 = async (o) => {
|
|
|
25430
26209
|
// src/commands/off.ts
|
|
25431
26210
|
init_config();
|
|
25432
26211
|
init_settings();
|
|
26212
|
+
|
|
26213
|
+
// src/commands/init.ts
|
|
26214
|
+
init_config();
|
|
26215
|
+
init_state();
|
|
26216
|
+
init_settings();
|
|
26217
|
+
var DEFAULT_PROXY_URL2 = "https://proxy.sleds.ai";
|
|
26218
|
+
var lineReaders2 = new WeakMap;
|
|
26219
|
+
|
|
26220
|
+
// src/commands/off.ts
|
|
25433
26221
|
var off = (o) => {
|
|
25434
26222
|
const log = o.log ?? console.log;
|
|
25435
26223
|
const cfg = readConfig(o.home);
|
|
25436
26224
|
const settings = readSettings(o.home);
|
|
25437
|
-
const
|
|
25438
|
-
|
|
25439
|
-
|
|
26225
|
+
const proxyUrls = [cfg.proxyUrl, DEFAULT_PROXY_URL2];
|
|
26226
|
+
const pointed = isSledsProxyEnv(settings, proxyUrls);
|
|
26227
|
+
const wasOn = cfg.mode === "on" || pointed;
|
|
26228
|
+
if (pointed)
|
|
26229
|
+
writeSettings(o.home, switchOff(settings, cfg.priorEnv ?? cfg.priorGatewayEnv ?? {}));
|
|
25440
26230
|
if (o.scheduler)
|
|
25441
26231
|
cancelRevert(o.scheduler, cfg.schedule);
|
|
25442
26232
|
const next = {
|
|
@@ -25446,6 +26236,7 @@ var off = (o) => {
|
|
|
25446
26236
|
untilReset: undefined,
|
|
25447
26237
|
reason: undefined,
|
|
25448
26238
|
priorEnv: undefined,
|
|
26239
|
+
priorGatewayEnv: pointed && !cfg.priorEnv ? undefined : cfg.priorGatewayEnv,
|
|
25449
26240
|
schedule: undefined
|
|
25450
26241
|
};
|
|
25451
26242
|
writeConfig(o.home, next);
|
|
@@ -25460,15 +26251,6 @@ var off = (o) => {
|
|
|
25460
26251
|
// src/commands/on.ts
|
|
25461
26252
|
init_config();
|
|
25462
26253
|
init_settings();
|
|
25463
|
-
|
|
25464
|
-
// src/commands/init.ts
|
|
25465
|
-
init_config();
|
|
25466
|
-
init_state();
|
|
25467
|
-
init_settings();
|
|
25468
|
-
var DEFAULT_PROXY_URL2 = "https://proxy.sleds.ai";
|
|
25469
|
-
var lineReaders2 = new WeakMap;
|
|
25470
|
-
|
|
25471
|
-
// src/commands/on.ts
|
|
25472
26254
|
var on = (o) => {
|
|
25473
26255
|
const log = o.log ?? console.log;
|
|
25474
26256
|
const error = o.error ?? console.error;
|
|
@@ -25618,7 +26400,7 @@ var orgUse = (o) => {
|
|
|
25618
26400
|
writeTokenFile(o.home, seat.token);
|
|
25619
26401
|
if (existsSync29(settingsPath(o.home)) && cfg.mode !== "on") {
|
|
25620
26402
|
const settings = readSettings(o.home);
|
|
25621
|
-
const pointed = readProxy(settings);
|
|
26403
|
+
const pointed = isSledsProxyEnv(settings, [cfg.proxyUrl]) ? readProxy(settings) : null;
|
|
25622
26404
|
const url = pointed?.url ?? cfg.proxyUrl;
|
|
25623
26405
|
if (seat.wiring === "proxy-always" && url)
|
|
25624
26406
|
writeSettings(o.home, applyProxy(settings, { url, token: seat.token }));
|
|
@@ -25653,7 +26435,7 @@ var orgCmd = (o) => {
|
|
|
25653
26435
|
init_src();
|
|
25654
26436
|
import { spawnSync as spawnSync8 } from "node:child_process";
|
|
25655
26437
|
import { writeFileSync as writeFileSync25 } from "node:fs";
|
|
25656
|
-
import { join as
|
|
26438
|
+
import { join as join31 } from "node:path";
|
|
25657
26439
|
function packFileName(key) {
|
|
25658
26440
|
return `pack-${slugify(key, 80) || "item"}.md`;
|
|
25659
26441
|
}
|
|
@@ -25681,7 +26463,7 @@ var packCmd = async (o) => {
|
|
|
25681
26463
|
log(r.text);
|
|
25682
26464
|
return 0;
|
|
25683
26465
|
}
|
|
25684
|
-
const path =
|
|
26466
|
+
const path = join31(o.cwd ?? process.cwd(), packFileName(key));
|
|
25685
26467
|
(o.write ?? ((p, t) => writeFileSync25(p, t.endsWith(`
|
|
25686
26468
|
`) ? t : `${t}
|
|
25687
26469
|
`)))(path, r.text);
|
|
@@ -25690,8 +26472,8 @@ var packCmd = async (o) => {
|
|
|
25690
26472
|
};
|
|
25691
26473
|
|
|
25692
26474
|
// src/commands/permissionEvent.ts
|
|
25693
|
-
import { chmodSync as chmodSync7, mkdirSync as mkdirSync26, readFileSync as
|
|
25694
|
-
import { dirname as
|
|
26475
|
+
import { chmodSync as chmodSync7, mkdirSync as mkdirSync26, readFileSync as readFileSync29, renameSync as renameSync12, rmSync as rmSync19, writeFileSync as writeFileSync26 } from "node:fs";
|
|
26476
|
+
import { dirname as dirname23 } from "node:path";
|
|
25695
26477
|
var isObject13 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
25696
26478
|
var messageOf = (text) => {
|
|
25697
26479
|
try {
|
|
@@ -25715,7 +26497,7 @@ var permissionEvent = async (o) => {
|
|
|
25715
26497
|
if (!isToolPermission2(message))
|
|
25716
26498
|
return 0;
|
|
25717
26499
|
const mark = { message, at: (o.now ?? Date.now)() };
|
|
25718
|
-
mkdirSync26(
|
|
26500
|
+
mkdirSync26(dirname23(o.mark), { recursive: true, mode: 448 });
|
|
25719
26501
|
const tmp = `${o.mark}.${process.pid}.tmp`;
|
|
25720
26502
|
writeFileSync26(tmp, `${JSON.stringify(mark)}
|
|
25721
26503
|
`, { mode: 384 });
|
|
@@ -25762,6 +26544,7 @@ var exitsLine = (hasUpstreamKey) => hasUpstreamKey ? "`/sled handoff <what you w
|
|
|
25762
26544
|
var approachingContext = (bar, usedPct, resetsAt, hasUpstreamKey, now) => `[SLEDS] This seat is at ${usedPct !== undefined ? `${Math.round(usedPct)}%` : "90%+"} of its ${LABEL[bar]} limit; it resets in ${fmtIn(resetsAt - now)}. Tell the user in one short line before you answer, and offer the exits: ${exitsLine(hasUpstreamKey)} Do not mention this again unless asked.`;
|
|
25763
26545
|
var hitContext = (bar, resetsAt, hasUpstreamKey, now) => `[SLEDS] This seat hit its ${LABEL[bar]} limit; it resets in ${fmtIn(resetsAt - now)}. If this reply fails or the user is stuck, the exits are: ${exitsLine(hasUpstreamKey)} Say so in one line.`;
|
|
25764
26546
|
var creditsBlockText = (hasUpstreamKey) => `SLEDS: this seat is out of usage credits — the next reply would fail, so this prompt was not sent. ${hasUpstreamKey ? "Exits: /sled handoff <task> (a buddy picks it up), sleds on (continue on the workspace's platform key), or /model to a cheaper model." : "Exits: /sled handoff <task> (a buddy picks it up), sleds defer (resume when credits are topped up), or /model to a cheaper model."} Re-send your prompt after choosing; SLEDS will not block again.`;
|
|
26547
|
+
var creditsContext = (hasUpstreamKey) => `[SLEDS] This seat is out of usage credits; this reply may fail. Tell the user in one short line, and offer the exits: ${exitsLine(hasUpstreamKey)}`;
|
|
25765
26548
|
var promptEvent = async (o) => {
|
|
25766
26549
|
const log = o.log ?? console.log;
|
|
25767
26550
|
const error = o.error ?? console.error;
|
|
@@ -25784,27 +26567,35 @@ var promptEvent = async (o) => {
|
|
|
25784
26567
|
const mark = (bar, stage, at) => {
|
|
25785
26568
|
told[bar] = { ...told[bar] ?? {}, [stage]: at };
|
|
25786
26569
|
};
|
|
25787
|
-
|
|
26570
|
+
for (const e of pending)
|
|
26571
|
+
mark(e.bar, e.stage, e.resetsAt);
|
|
26572
|
+
let saved = true;
|
|
26573
|
+
try {
|
|
26574
|
+
writeSeatState(o.home, { ...state, told }, key);
|
|
26575
|
+
} catch {
|
|
26576
|
+
saved = false;
|
|
26577
|
+
}
|
|
26578
|
+
let block = false;
|
|
25788
26579
|
const lines = [];
|
|
25789
26580
|
for (const e of pending) {
|
|
25790
|
-
mark(e.bar, e.stage, e.resetsAt);
|
|
25791
26581
|
if (e.bar === "credits") {
|
|
25792
|
-
if (
|
|
25793
|
-
|
|
25794
|
-
|
|
25795
|
-
|
|
26582
|
+
if (saved)
|
|
26583
|
+
block = true;
|
|
26584
|
+
else
|
|
26585
|
+
lines.push(creditsContext(hasKey));
|
|
25796
26586
|
continue;
|
|
25797
26587
|
}
|
|
25798
26588
|
const pct = state.window?.[e.bar]?.usedPct;
|
|
25799
26589
|
lines.push(e.stage === "approaching" ? approachingContext(e.bar, pct, e.resetsAt, hasKey, now) : hitContext(e.bar, e.resetsAt, hasKey, now));
|
|
25800
26590
|
}
|
|
25801
|
-
|
|
25802
|
-
|
|
25803
|
-
|
|
25804
|
-
|
|
26591
|
+
if (block) {
|
|
26592
|
+
error(creditsBlockText(hasKey));
|
|
26593
|
+
return 2;
|
|
26594
|
+
}
|
|
26595
|
+
if (lines.length > 0)
|
|
25805
26596
|
log(lines.join(`
|
|
25806
26597
|
`));
|
|
25807
|
-
return
|
|
26598
|
+
return 0;
|
|
25808
26599
|
};
|
|
25809
26600
|
|
|
25810
26601
|
// src/commands/reopen.ts
|
|
@@ -25878,13 +26669,14 @@ async function reopenCmd(o) {
|
|
|
25878
26669
|
|
|
25879
26670
|
// src/commands/route.ts
|
|
25880
26671
|
init_src();
|
|
25881
|
-
import { readFileSync as
|
|
26672
|
+
import { readFileSync as readFileSync30 } from "node:fs";
|
|
25882
26673
|
import { hostname as hostname12 } from "node:os";
|
|
25883
26674
|
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>]";
|
|
25884
|
-
var USAGE2 = "usage: sleds route <key> [--long | --dot | --json] | sleds route arm <key> [--off] [--json] | sleds route answer <key>/<node> --stdin | sleds route propose <key> --file <nodes.json> | sleds route approve <key> | sleds route redraft <key> | sleds route single <key> | sleds route elsewhere <key> [--note <text>] | 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]";
|
|
26675
|
+
var USAGE2 = "usage: sleds route <key> [--long | --dot | --json] | sleds route arm <key> [--off] [--json] | sleds route answer <key>/<node> --stdin | sleds route instruct <key>/<node> --stdin [--voice] | sleds route propose <key> --file <nodes.json> | sleds route approve <key> [--voice --stdin] | sleds route redraft <key> | sleds route single <key> | sleds route elsewhere <key> [--note <text>] | 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]";
|
|
25885
26676
|
var ANSWER_USAGE = "usage: sleds route answer <key>/<node> <answer…> | sleds route answer <key>/<node> --stdin [--json]";
|
|
26677
|
+
var INSTRUCT_USAGE = "usage: sleds route instruct <key>/<node> <instruction…> | sleds route instruct <key>/<node> --stdin [--voice] [--json]";
|
|
25886
26678
|
var PROPOSE_USAGE = "usage: sleds route propose <key> (--file <nodes.json> | --stdin) [--name <engine>] [--json]";
|
|
25887
|
-
var APPROVE_USAGE = "usage: sleds route approve <key> [--file <nodes.json>] [--json]";
|
|
26679
|
+
var APPROVE_USAGE = "usage: sleds route approve <key> [--file <nodes.json>] [--voice --stdin] [--json]";
|
|
25888
26680
|
var REDRAFT_USAGE = "usage: sleds route redraft <key> [--json]";
|
|
25889
26681
|
var SINGLE_USAGE = "usage: sleds route single <key> [--json]";
|
|
25890
26682
|
var ELSEWHERE_USAGE = "usage: sleds route elsewhere <key> [--note <text>] [--json]";
|
|
@@ -25933,6 +26725,10 @@ function parseVerbArgs(argv, allowed) {
|
|
|
25933
26725
|
out[flag] = true;
|
|
25934
26726
|
continue;
|
|
25935
26727
|
}
|
|
26728
|
+
if (flag === "voice") {
|
|
26729
|
+
out.voice = true;
|
|
26730
|
+
continue;
|
|
26731
|
+
}
|
|
25936
26732
|
const value = argv[i + 1];
|
|
25937
26733
|
if (value === undefined || value.startsWith("--"))
|
|
25938
26734
|
return { error: `${a} needs a value` };
|
|
@@ -26056,7 +26852,7 @@ async function answerCmd(o, argv) {
|
|
|
26056
26852
|
const words = argv.filter((a) => a !== "--json" && a !== "--stdin");
|
|
26057
26853
|
const ref = words[0] ?? "";
|
|
26058
26854
|
const slash = ref.lastIndexOf("/");
|
|
26059
|
-
const answer = (fromStdin ? (o.stdin ?? (() =>
|
|
26855
|
+
const answer = (fromStdin ? (o.stdin ?? (() => readFileSync30(0, "utf8")))() : words.slice(1).join(" ")).trim();
|
|
26060
26856
|
if (slash <= 0 || slash === ref.length - 1 || fromStdin && words.length > 1) {
|
|
26061
26857
|
error(ANSWER_USAGE);
|
|
26062
26858
|
return 2;
|
|
@@ -26088,6 +26884,59 @@ async function answerCmd(o, argv) {
|
|
|
26088
26884
|
log(r.json?.state === "answered" ? `${ref}: its landing is answered — the nodes it held are released` : `${ref} answered — back to ready; the engine takes it on its next check`);
|
|
26089
26885
|
return 0;
|
|
26090
26886
|
}
|
|
26887
|
+
async function instructCmd(o, argv) {
|
|
26888
|
+
const log = o.log ?? console.log;
|
|
26889
|
+
const error = o.error ?? console.error;
|
|
26890
|
+
const json = argv.includes("--json");
|
|
26891
|
+
const fromStdin = argv.includes("--stdin");
|
|
26892
|
+
const voice = argv.includes("--voice");
|
|
26893
|
+
const words = argv.filter((a) => a !== "--json" && a !== "--stdin" && a !== "--voice");
|
|
26894
|
+
const ref = words[0] ?? "";
|
|
26895
|
+
const slash = ref.lastIndexOf("/");
|
|
26896
|
+
if (slash <= 0 || slash === ref.length - 1 || fromStdin && words.length > 1 || voice && !fromStdin || words.some((w) => w.startsWith("--"))) {
|
|
26897
|
+
error(INSTRUCT_USAGE);
|
|
26898
|
+
return 2;
|
|
26899
|
+
}
|
|
26900
|
+
const text = fromStdin ? (o.stdin ?? (() => readFileSync30(0, "utf8")))() : words.slice(1).join(" ");
|
|
26901
|
+
const { md, heard } = voice ? splitVoiceStdin(text) : { md: text.trim(), heard: undefined };
|
|
26902
|
+
if (!md) {
|
|
26903
|
+
error("an instruction needs some text");
|
|
26904
|
+
return 2;
|
|
26905
|
+
}
|
|
26906
|
+
const client = engineClient(o);
|
|
26907
|
+
if ("error" in client) {
|
|
26908
|
+
error(client.error);
|
|
26909
|
+
return 1;
|
|
26910
|
+
}
|
|
26911
|
+
const r = await client.postFull("/proxy/route/instruct", {
|
|
26912
|
+
project: ref,
|
|
26913
|
+
md,
|
|
26914
|
+
...heard !== undefined ? { voice: { heard } } : {}
|
|
26915
|
+
});
|
|
26916
|
+
if (!r) {
|
|
26917
|
+
error(`${ref}: SLEDS unreachable — nothing sent`);
|
|
26918
|
+
return 1;
|
|
26919
|
+
}
|
|
26920
|
+
const ok = r.status < 300 && r.json?.ok !== false;
|
|
26921
|
+
if (json)
|
|
26922
|
+
log(JSON.stringify(r.json ?? { ok: false, error: `HTTP ${r.status}` }));
|
|
26923
|
+
if (!ok) {
|
|
26924
|
+
const why = typeof r.json?.error === "string" ? r.json.error : `HTTP ${r.status}`;
|
|
26925
|
+
if (!json)
|
|
26926
|
+
error(`${ref}: ${why}`);
|
|
26927
|
+
return 1;
|
|
26928
|
+
}
|
|
26929
|
+
if (!json)
|
|
26930
|
+
log(`${ref} instructed — the engine following its PR hands it to the session`);
|
|
26931
|
+
return 0;
|
|
26932
|
+
}
|
|
26933
|
+
function splitVoiceStdin(text) {
|
|
26934
|
+
const nl = text.indexOf(`
|
|
26935
|
+
`);
|
|
26936
|
+
const md = (nl < 0 ? text : text.slice(0, nl)).trim();
|
|
26937
|
+
const rest = nl < 0 ? "" : text.slice(nl + 1).trim();
|
|
26938
|
+
return { md, heard: rest || md };
|
|
26939
|
+
}
|
|
26091
26940
|
async function proposeCmd(o, argv) {
|
|
26092
26941
|
const log = o.log ?? console.log;
|
|
26093
26942
|
const error = o.error ?? console.error;
|
|
@@ -26135,12 +26984,13 @@ async function proposeCmd(o, argv) {
|
|
|
26135
26984
|
async function approveCmd(o, argv) {
|
|
26136
26985
|
const log = o.log ?? console.log;
|
|
26137
26986
|
const error = o.error ?? console.error;
|
|
26138
|
-
const a = parseVerbArgs(argv, ["file", "json"]);
|
|
26139
|
-
if ("error" in a) {
|
|
26140
|
-
error(a.error);
|
|
26987
|
+
const a = parseVerbArgs(argv, ["file", "json", "voice", "stdin"]);
|
|
26988
|
+
if ("error" in a || Boolean(a.voice) !== a.stdin) {
|
|
26989
|
+
error("error" in a ? a.error : "--voice and --stdin go together: stdin is what was heard");
|
|
26141
26990
|
error(APPROVE_USAGE);
|
|
26142
26991
|
return 2;
|
|
26143
26992
|
}
|
|
26993
|
+
const heard = a.voice ? (o.stdin ?? (() => readFileSync30(0, "utf8")))().trim() : undefined;
|
|
26144
26994
|
let nodes;
|
|
26145
26995
|
let approach;
|
|
26146
26996
|
if (a.file) {
|
|
@@ -26160,7 +27010,8 @@ async function approveCmd(o, argv) {
|
|
|
26160
27010
|
const r = await client.postFull("/proxy/route/approve", {
|
|
26161
27011
|
project: a.key,
|
|
26162
27012
|
...nodes ? { nodes } : {},
|
|
26163
|
-
...approach ? { approach } : {}
|
|
27013
|
+
...approach ? { approach } : {},
|
|
27014
|
+
...heard !== undefined ? { voice: { heard } } : {}
|
|
26164
27015
|
});
|
|
26165
27016
|
if (!r) {
|
|
26166
27017
|
error(`${a.key}: SLEDS unreachable — nothing approved`);
|
|
@@ -26262,7 +27113,7 @@ async function decisionCmd(o, argv) {
|
|
|
26262
27113
|
error(DECISION_USAGE);
|
|
26263
27114
|
return 2;
|
|
26264
27115
|
}
|
|
26265
|
-
const md = seen || routeAlone ? "" : (fromStdin ? (o.stdin ?? (() =>
|
|
27116
|
+
const md = seen || routeAlone ? "" : (fromStdin ? (o.stdin ?? (() => readFileSync30(0, "utf8")))() : rest.join(" ")).trim();
|
|
26266
27117
|
if (!seen && !routeAlone && (!md || fromStdin && rest.length > 0)) {
|
|
26267
27118
|
error(md ? DECISION_USAGE : `${answer ? "an answer" : "an overrule"} needs some text`);
|
|
26268
27119
|
return 2;
|
|
@@ -26441,7 +27292,7 @@ function approvedLine(key, j) {
|
|
|
26441
27292
|
function readNodes(o, a) {
|
|
26442
27293
|
let text;
|
|
26443
27294
|
try {
|
|
26444
|
-
text = a.file ? (o.readFile ?? ((p) =>
|
|
27295
|
+
text = a.file ? (o.readFile ?? ((p) => readFileSync30(p, "utf8")))(a.file) : (o.stdin ?? (() => readFileSync30(0, "utf8")))();
|
|
26445
27296
|
} catch (e) {
|
|
26446
27297
|
return { error: e instanceof Error ? e.message : String(e) };
|
|
26447
27298
|
}
|
|
@@ -26470,6 +27321,8 @@ var routeCmd = async (o) => {
|
|
|
26470
27321
|
return armCmd(o, o.argv.slice(1));
|
|
26471
27322
|
if (o.argv[0] === "answer")
|
|
26472
27323
|
return answerCmd(o, o.argv.slice(1));
|
|
27324
|
+
if (o.argv[0] === "instruct")
|
|
27325
|
+
return instructCmd(o, o.argv.slice(1));
|
|
26473
27326
|
if (o.argv[0] === "propose")
|
|
26474
27327
|
return proposeCmd(o, o.argv.slice(1));
|
|
26475
27328
|
if (o.argv[0] === "approve")
|
|
@@ -26796,8 +27649,11 @@ var seatEvent = async (o) => {
|
|
|
26796
27649
|
if (week && week.resetsAt > now)
|
|
26797
27650
|
writeSeatState(o.home, { ...state, lastWallResetsAt: week.resetsAt }, key);
|
|
26798
27651
|
} catch {}
|
|
26799
|
-
|
|
26800
|
-
|
|
27652
|
+
let hasKey = true;
|
|
27653
|
+
try {
|
|
27654
|
+
hasKey = readConfig(o.home).org?.capabilities.upstreamKey !== false;
|
|
27655
|
+
} catch {}
|
|
27656
|
+
log(seatWallLine(hasKey));
|
|
26801
27657
|
return 0;
|
|
26802
27658
|
};
|
|
26803
27659
|
|
|
@@ -26873,14 +27729,14 @@ async function seatRevokeCmd(o) {
|
|
|
26873
27729
|
|
|
26874
27730
|
// src/commands/seatSync.ts
|
|
26875
27731
|
import { execFileSync as execFileSync5 } from "node:child_process";
|
|
26876
|
-
import { existsSync as existsSync30, mkdirSync as mkdirSync27, readFileSync as
|
|
27732
|
+
import { existsSync as existsSync30, mkdirSync as mkdirSync27, readFileSync as readFileSync31, readdirSync as readdirSync7, rmSync as rmSync21, writeFileSync as writeFileSync27 } from "node:fs";
|
|
26877
27733
|
import { platform as platform2 } from "node:os";
|
|
26878
|
-
import { dirname as
|
|
27734
|
+
import { dirname as dirname24, join as join33 } from "node:path";
|
|
26879
27735
|
|
|
26880
27736
|
// src/cookieDb.ts
|
|
26881
27737
|
import { copyFileSync as copyFileSync3, rmSync as rmSync20 } from "node:fs";
|
|
26882
|
-
import { tmpdir as
|
|
26883
|
-
import { join as
|
|
27738
|
+
import { tmpdir as tmpdir4 } from "node:os";
|
|
27739
|
+
import { join as join32 } from "node:path";
|
|
26884
27740
|
var QUERY = "SELECT name, hex(encrypted_value) AS ev FROM cookies WHERE host_key LIKE '%claude.ai%'";
|
|
26885
27741
|
var isBun = () => typeof globalThis.Bun !== "undefined";
|
|
26886
27742
|
async function queryRows(dbPath) {
|
|
@@ -26909,7 +27765,7 @@ function nodeSqlite() {
|
|
|
26909
27765
|
return mod;
|
|
26910
27766
|
}
|
|
26911
27767
|
async function readCookieRows(dbPath) {
|
|
26912
|
-
const copy =
|
|
27768
|
+
const copy = join32(tmpdir4(), `sleds-cookies-${process.pid}-${Date.now()}.sqlite`);
|
|
26913
27769
|
copyFileSync3(dbPath, copy);
|
|
26914
27770
|
try {
|
|
26915
27771
|
const rows = await queryRows(copy);
|
|
@@ -27202,7 +28058,7 @@ function findFile(root, name) {
|
|
|
27202
28058
|
continue;
|
|
27203
28059
|
}
|
|
27204
28060
|
for (const e of entries) {
|
|
27205
|
-
const p =
|
|
28061
|
+
const p = join33(dir, e.name);
|
|
27206
28062
|
if (e.isDirectory())
|
|
27207
28063
|
stack.push(p);
|
|
27208
28064
|
else if (e.name === name)
|
|
@@ -27213,7 +28069,7 @@ function findFile(root, name) {
|
|
|
27213
28069
|
}
|
|
27214
28070
|
function findCookieDb(home, source) {
|
|
27215
28071
|
const b = BROWSERS[source.browser];
|
|
27216
|
-
const root =
|
|
28072
|
+
const root = join33(home, APP_SUPPORT, b.appDir);
|
|
27217
28073
|
if (!existsSync30(root))
|
|
27218
28074
|
throw new Error(`no ${source.browser} data at ${root} (is it installed?)`);
|
|
27219
28075
|
if (!b.profiled) {
|
|
@@ -27222,15 +28078,15 @@ function findCookieDb(home, source) {
|
|
|
27222
28078
|
throw new Error(`no cookie store under ${root}`);
|
|
27223
28079
|
return db;
|
|
27224
28080
|
}
|
|
27225
|
-
const localStatePath =
|
|
27226
|
-
const localState = existsSync30(localStatePath) ?
|
|
28081
|
+
const localStatePath = join33(root, "Local State");
|
|
28082
|
+
const localState = existsSync30(localStatePath) ? readFileSync31(localStatePath, "utf8") : null;
|
|
27227
28083
|
const wanted = source.profile ?? "Default";
|
|
27228
28084
|
const { dir, ambiguous } = resolveProfileDir(localState, wanted);
|
|
27229
28085
|
if (ambiguous)
|
|
27230
28086
|
throw new Error(`"${wanted}" names ${ambiguous.length} ${source.browser} profiles (${ambiguous.join(", ")}) — pass the directory or the account email instead`);
|
|
27231
28087
|
if (!dir)
|
|
27232
28088
|
throw new Error(`no ${source.browser} profile named "${wanted}" (check chrome://version)`);
|
|
27233
|
-
const db = cookieDbCandidates(
|
|
28089
|
+
const db = cookieDbCandidates(join33(root, dir)).find((p) => existsSync30(p));
|
|
27234
28090
|
if (!db)
|
|
27235
28091
|
throw new Error(`profile "${dir}" has no cookie store yet (open claude.ai in it once)`);
|
|
27236
28092
|
return db;
|
|
@@ -27274,7 +28130,7 @@ function fetchSeatUsage(jar, org, now, run) {
|
|
|
27274
28130
|
}
|
|
27275
28131
|
function launchAgentPlist(argv, home, source) {
|
|
27276
28132
|
const label = launchLabel(source);
|
|
27277
|
-
const log =
|
|
28133
|
+
const log = join33(home, ".config", "sleds", `${label.slice("ai.sleds.".length)}.log`);
|
|
27278
28134
|
const esc = (s) => s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
27279
28135
|
const args = [...argv, "seat-sync", ...sourceArgs(source)].map((a) => ` <string>${esc(a)}</string>`).join(`
|
|
27280
28136
|
`);
|
|
@@ -27296,20 +28152,20 @@ ${args}
|
|
|
27296
28152
|
`;
|
|
27297
28153
|
}
|
|
27298
28154
|
function plistPath2(home, source) {
|
|
27299
|
-
return
|
|
28155
|
+
return join33(home, "Library", "LaunchAgents", `${launchLabel(source)}.plist`);
|
|
27300
28156
|
}
|
|
27301
28157
|
function install(home, argv, source, log, launchctl) {
|
|
27302
28158
|
if (!argv[0]) {
|
|
27303
28159
|
log("cannot resolve the sleds command to install the timer");
|
|
27304
28160
|
return 1;
|
|
27305
28161
|
}
|
|
27306
|
-
const dir =
|
|
28162
|
+
const dir = join33(home, "Library", "LaunchAgents");
|
|
27307
28163
|
mkdirSync27(dir, { recursive: true });
|
|
27308
28164
|
const path = plistPath2(home, source);
|
|
27309
28165
|
const tokenPath = seatTokenPath(home, source);
|
|
27310
28166
|
rmSync21(tokenPath, { force: true });
|
|
27311
28167
|
if (source.token) {
|
|
27312
|
-
mkdirSync27(
|
|
28168
|
+
mkdirSync27(dirname24(tokenPath), { recursive: true, mode: 448 });
|
|
27313
28169
|
writeFileSync27(tokenPath, `${source.token}
|
|
27314
28170
|
`, { mode: 384 });
|
|
27315
28171
|
}
|
|
@@ -27390,10 +28246,12 @@ var seatSync = async (o) => {
|
|
|
27390
28246
|
log(`seat-sync: usage read failed — ${why}`);
|
|
27391
28247
|
return 2;
|
|
27392
28248
|
}
|
|
27393
|
-
|
|
27394
|
-
|
|
27395
|
-
|
|
27396
|
-
|
|
28249
|
+
if (token === saved?.token) {
|
|
28250
|
+
try {
|
|
28251
|
+
const key = seatKeyFor(o.home, process.env);
|
|
28252
|
+
writeSeatState(o.home, { ...readSeatState(o.home, key), window: usageAsWindow(usage) }, key);
|
|
28253
|
+
} catch {}
|
|
28254
|
+
}
|
|
27397
28255
|
const posted = await postSeat(proxyUrl, token, seatSyncBody(usage), o.fetchFn ?? fetch);
|
|
27398
28256
|
const wk = usage.sevenDay ? `wk ${Math.round(usage.sevenDay.usedPct)}%` : "wk —";
|
|
27399
28257
|
const models = Object.keys(usage.models).length;
|
|
@@ -27403,7 +28261,7 @@ var seatSync = async (o) => {
|
|
|
27403
28261
|
};
|
|
27404
28262
|
function readSeatToken(home, source) {
|
|
27405
28263
|
try {
|
|
27406
|
-
return
|
|
28264
|
+
return readFileSync31(seatTokenPath(home, source), "utf8").trim() || undefined;
|
|
27407
28265
|
} catch {
|
|
27408
28266
|
return;
|
|
27409
28267
|
}
|
|
@@ -27486,8 +28344,8 @@ var sessionEvent = async (o) => {
|
|
|
27486
28344
|
|
|
27487
28345
|
// src/commands/specEvent.ts
|
|
27488
28346
|
init_meter();
|
|
27489
|
-
import { readFileSync as
|
|
27490
|
-
import { basename as
|
|
28347
|
+
import { readFileSync as readFileSync32, statSync as statSync8 } from "node:fs";
|
|
28348
|
+
import { basename as basename9, isAbsolute as isAbsolute7, relative as relative2 } from "node:path";
|
|
27491
28349
|
var HOOK_STDIN_TIMEOUT_MS3 = 1000;
|
|
27492
28350
|
var ESTIMATE_TIMEOUT_MS = 4000;
|
|
27493
28351
|
var MIN_SPEC_CHARS = 200;
|
|
@@ -27500,12 +28358,12 @@ function isSpecPath(path) {
|
|
|
27500
28358
|
return false;
|
|
27501
28359
|
if (NOT_SPEC.test(path))
|
|
27502
28360
|
return false;
|
|
27503
|
-
return SPEC_DIRS.test(path) || SPEC_NAMES.test(
|
|
28361
|
+
return SPEC_DIRS.test(path) || SPEC_NAMES.test(basename9(path));
|
|
27504
28362
|
}
|
|
27505
28363
|
function specTitle(body, path) {
|
|
27506
28364
|
const m = /^#\s+(.+?)\s*$/m.exec(body);
|
|
27507
28365
|
const t = m?.[1]?.trim();
|
|
27508
|
-
return (t && t.length > 0 ? t :
|
|
28366
|
+
return (t && t.length > 0 ? t : basename9(path).replace(/\.(md|mdx)$/i, "")).slice(0, 120);
|
|
27509
28367
|
}
|
|
27510
28368
|
function looksLikeSpec(body) {
|
|
27511
28369
|
if (body.trim().length < MIN_SPEC_CHARS)
|
|
@@ -27540,9 +28398,9 @@ function projectKey(filePath, cwd) {
|
|
|
27540
28398
|
}
|
|
27541
28399
|
function readFileOrNull(path) {
|
|
27542
28400
|
try {
|
|
27543
|
-
if (
|
|
28401
|
+
if (statSync8(path).size > 2000000)
|
|
27544
28402
|
return null;
|
|
27545
|
-
return
|
|
28403
|
+
return readFileSync32(path, "utf8");
|
|
27546
28404
|
} catch {
|
|
27547
28405
|
return null;
|
|
27548
28406
|
}
|
|
@@ -27553,7 +28411,12 @@ var specEvent = async (o) => {
|
|
|
27553
28411
|
const input = raw ? parseHookInput(raw) : null;
|
|
27554
28412
|
if (!input || !isSpecPath(input.filePath))
|
|
27555
28413
|
return 0;
|
|
27556
|
-
|
|
28414
|
+
let src;
|
|
28415
|
+
try {
|
|
28416
|
+
src = meterSource(o.home);
|
|
28417
|
+
} catch {
|
|
28418
|
+
return 0;
|
|
28419
|
+
}
|
|
27557
28420
|
if (!src)
|
|
27558
28421
|
return 0;
|
|
27559
28422
|
const body = (o.readFile ?? readFileOrNull)(input.filePath);
|
|
@@ -27563,7 +28426,7 @@ var specEvent = async (o) => {
|
|
|
27563
28426
|
const spec = {
|
|
27564
28427
|
title: specTitle(body, input.filePath),
|
|
27565
28428
|
body: body.slice(0, MAX_SPEC_CHARS),
|
|
27566
|
-
repoHint: input.cwd ?
|
|
28429
|
+
repoHint: input.cwd ? basename9(input.cwd) : undefined
|
|
27567
28430
|
};
|
|
27568
28431
|
try {
|
|
27569
28432
|
const res = await (o.fetchFn ?? fetch)(`${src.proxyUrl}/proxy/estimate`, {
|
|
@@ -27759,7 +28622,6 @@ var status3 = async (o) => {
|
|
|
27759
28622
|
};
|
|
27760
28623
|
|
|
27761
28624
|
// src/commands/statusline.ts
|
|
27762
|
-
init_config();
|
|
27763
28625
|
init_meter();
|
|
27764
28626
|
var statusline = async (o) => {
|
|
27765
28627
|
const log = o.log ?? console.log;
|
|
@@ -27798,7 +28660,8 @@ var statusline = async (o) => {
|
|
|
27798
28660
|
home: o.home,
|
|
27799
28661
|
now,
|
|
27800
28662
|
fetchFn: o.fetchFn,
|
|
27801
|
-
timeoutMs: o.timeoutMs
|
|
28663
|
+
timeoutMs: o.timeoutMs,
|
|
28664
|
+
env: o.env
|
|
27802
28665
|
});
|
|
27803
28666
|
line = formatMeterLine(meter, now, seatFragment);
|
|
27804
28667
|
} catch {
|
|
@@ -27817,9 +28680,9 @@ var speakToCockpit = async (o, key, window, events, now) => {
|
|
|
27817
28680
|
try {
|
|
27818
28681
|
const st = readSeatState(o.home, key);
|
|
27819
28682
|
let feed;
|
|
27820
|
-
const src = meterSource(o.home);
|
|
28683
|
+
const src = meterSource(o.home, undefined, { env: o.env });
|
|
27821
28684
|
if (src && now - (st.cockpit?.feed?.at ?? 0) >= FEED_MIN_INTERVAL_MS) {
|
|
27822
|
-
const got = await fetchFeed({ ...src, email:
|
|
28685
|
+
const got = await fetchFeed({ ...src, email: key.email ?? undefined, fetchFn: o.fetchFn, timeoutMs: o.timeoutMs }, now);
|
|
27823
28686
|
if (got)
|
|
27824
28687
|
feed = got;
|
|
27825
28688
|
}
|
|
@@ -27832,13 +28695,13 @@ var speakToCockpit = async (o, key, window, events, now) => {
|
|
|
27832
28695
|
return;
|
|
27833
28696
|
writeSeatState(o.home, { ...readSeatState(o.home, key), cockpit: r.state }, key);
|
|
27834
28697
|
if (r.argv.length)
|
|
27835
|
-
await runCockpit(cockpit, r.argv, {
|
|
28698
|
+
await runCockpit(cockpit, r.argv, { exec: o.exec });
|
|
27836
28699
|
} catch {}
|
|
27837
28700
|
};
|
|
27838
28701
|
var rememberSeat = (o, key, window, session, now, input) => {
|
|
27839
28702
|
const prior = readSeatState(o.home, key);
|
|
27840
28703
|
let state = { ...prior, window, ...session ? { session } : {} };
|
|
27841
|
-
const src = meterSource(o.home);
|
|
28704
|
+
const src = meterSource(o.home, undefined, { env: o.env });
|
|
27842
28705
|
const posts = [];
|
|
27843
28706
|
if (src && shouldPostSeat(prior, window, now)) {
|
|
27844
28707
|
state = markPosted(state, window, now);
|
|
@@ -27868,7 +28731,7 @@ var creditsEvent = (o, prior, input, now) => {
|
|
|
27868
28731
|
var announce = (o, events) => {
|
|
27869
28732
|
if (events.length === 0)
|
|
27870
28733
|
return [];
|
|
27871
|
-
const src = meterSource(o.home);
|
|
28734
|
+
const src = meterSource(o.home, undefined, { env: o.env });
|
|
27872
28735
|
if (!src)
|
|
27873
28736
|
return [];
|
|
27874
28737
|
return events.map((e) => postSeatWall({ ...src, fetchFn: o.fetchFn, timeoutMs: o.timeoutMs ?? SEAT_POST_TIMEOUT_MS }, {
|
|
@@ -27882,7 +28745,7 @@ var announce = (o, events) => {
|
|
|
27882
28745
|
|
|
27883
28746
|
// src/commands/stopEvent.ts
|
|
27884
28747
|
init_meter();
|
|
27885
|
-
import { existsSync as existsSync32, readFileSync as
|
|
28748
|
+
import { existsSync as existsSync32, readFileSync as readFileSync33 } from "node:fs";
|
|
27886
28749
|
|
|
27887
28750
|
// src/transcript.ts
|
|
27888
28751
|
var isObject16 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
@@ -27975,10 +28838,15 @@ async function postTranscript(o, payload, now) {
|
|
|
27975
28838
|
const path = payload.transcript_path?.trim();
|
|
27976
28839
|
if (!sessionId || !path)
|
|
27977
28840
|
return false;
|
|
27978
|
-
|
|
28841
|
+
let src;
|
|
28842
|
+
try {
|
|
28843
|
+
src = meterSource(o.home);
|
|
28844
|
+
} catch {
|
|
28845
|
+
return false;
|
|
28846
|
+
}
|
|
27979
28847
|
if (!src)
|
|
27980
28848
|
return false;
|
|
27981
|
-
const read = o.readFile ?? ((p) => existsSync32(p) ?
|
|
28849
|
+
const read = o.readFile ?? ((p) => existsSync32(p) ? readFileSync33(p, "utf8") : null);
|
|
27982
28850
|
let text;
|
|
27983
28851
|
try {
|
|
27984
28852
|
text = read(path);
|
|
@@ -28072,21 +28940,27 @@ var uninstall2 = (o) => {
|
|
|
28072
28940
|
if (existsSync33(settingsPath(o.home))) {
|
|
28073
28941
|
let settings = readSettings(o.home);
|
|
28074
28942
|
let changed = false;
|
|
28075
|
-
|
|
28076
|
-
|
|
28943
|
+
const cfg = readConfig(o.home);
|
|
28944
|
+
const proxyUrls = [cfg.proxyUrl, DEFAULT_PROXY_URL2];
|
|
28945
|
+
const restoredEnv = isSledsProxyEnv(settings, proxyUrls);
|
|
28946
|
+
if (restoredEnv) {
|
|
28947
|
+
const restore = envToRestore([cfg.priorEnv, cfg.priorGatewayEnv], proxyUrls);
|
|
28948
|
+
settings = switchOff(settings, restore);
|
|
28077
28949
|
changed = true;
|
|
28078
|
-
removed.push(`env.ANTHROPIC_BASE_URL and env.ANTHROPIC_AUTH_TOKEN from ${settingsPath(o.home)}`);
|
|
28950
|
+
removed.push(hasGatewayEnv(settings) ? `env.ANTHROPIC_BASE_URL and env.ANTHROPIC_AUTH_TOKEN from ${settingsPath(o.home)} (your own gateway restored)` : `env.ANTHROPIC_BASE_URL and env.ANTHROPIC_AUTH_TOKEN from ${settingsPath(o.home)}`);
|
|
28079
28951
|
}
|
|
28080
|
-
|
|
28081
|
-
|
|
28952
|
+
const restoredLine = isSledsStatusLine(settings[STATUS_LINE_KEY]);
|
|
28953
|
+
if (restoredLine) {
|
|
28082
28954
|
settings = removeStatusLine(settings, cfg.priorStatusLine);
|
|
28083
28955
|
changed = true;
|
|
28084
28956
|
removed.push(cfg.priorStatusLine === undefined ? `statusLine from ${settingsPath(o.home)}` : `statusLine from ${settingsPath(o.home)} (previous one restored)`);
|
|
28085
|
-
if (cfg.priorStatusLine !== undefined) {
|
|
28086
|
-
const { priorStatusLine: _restored, ...rest } = cfg;
|
|
28087
|
-
writeConfig(o.home, rest);
|
|
28088
|
-
}
|
|
28089
28957
|
}
|
|
28958
|
+
if (restoredLine && cfg.priorStatusLine !== undefined || restoredEnv && (cfg.priorEnv !== undefined || cfg.priorGatewayEnv !== undefined))
|
|
28959
|
+
writeConfig(o.home, {
|
|
28960
|
+
...cfg,
|
|
28961
|
+
...restoredLine ? { priorStatusLine: undefined } : {},
|
|
28962
|
+
...restoredEnv ? { priorEnv: undefined, priorGatewayEnv: undefined } : {}
|
|
28963
|
+
});
|
|
28090
28964
|
if (hasSeatHook(settings)) {
|
|
28091
28965
|
settings = removeSeatHook(settings);
|
|
28092
28966
|
changed = true;
|
|
@@ -28142,18 +29016,12 @@ var uninstall2 = (o) => {
|
|
|
28142
29016
|
log(`Kept ${tokenPath(o.home)} (delete it yourself if you want the token gone).`);
|
|
28143
29017
|
return 0;
|
|
28144
29018
|
};
|
|
28145
|
-
var hasEitherKey = (settings) => {
|
|
28146
|
-
const env = settings.env;
|
|
28147
|
-
if (typeof env !== "object" || env === null)
|
|
28148
|
-
return false;
|
|
28149
|
-
return BASE_URL_KEY in env || AUTH_TOKEN_KEY in env;
|
|
28150
|
-
};
|
|
28151
29019
|
|
|
28152
29020
|
// src/commands/update.ts
|
|
28153
29021
|
init_server();
|
|
28154
29022
|
import { existsSync as existsSync34 } from "node:fs";
|
|
28155
29023
|
import { homedir as homedir8, userInfo as userInfo4 } from "node:os";
|
|
28156
|
-
import { basename as
|
|
29024
|
+
import { basename as basename10 } from "node:path";
|
|
28157
29025
|
init_config();
|
|
28158
29026
|
init_state();
|
|
28159
29027
|
init_meter();
|
|
@@ -28218,7 +29086,7 @@ var realPidAlive4 = (pid) => {
|
|
|
28218
29086
|
};
|
|
28219
29087
|
function machineClient(home, env) {
|
|
28220
29088
|
const src = meterSource(home, undefined, { env });
|
|
28221
|
-
return src ? fetchClient({ ...src, version: "0.1.
|
|
29089
|
+
return src ? fetchClient({ ...src, version: "0.1.61" }) : null;
|
|
28222
29090
|
}
|
|
28223
29091
|
async function noteAnswer2(home, a, now) {
|
|
28224
29092
|
try {
|
|
@@ -28230,7 +29098,7 @@ async function noteAnswer2(home, a, now) {
|
|
|
28230
29098
|
}
|
|
28231
29099
|
function busyEngines(files) {
|
|
28232
29100
|
return files.map((f) => ({
|
|
28233
|
-
name:
|
|
29101
|
+
name: basename10(f, ".json"),
|
|
28234
29102
|
running: readState(f).runs.filter(isRunning).length
|
|
28235
29103
|
})).filter((e) => e.running > 0);
|
|
28236
29104
|
}
|
|
@@ -28543,13 +29411,13 @@ var webhooksCmd = async (o) => {
|
|
|
28543
29411
|
|
|
28544
29412
|
// src/config.ts
|
|
28545
29413
|
init_settings();
|
|
28546
|
-
import { chmodSync as chmodSync8, existsSync as existsSync35, mkdirSync as mkdirSync28, readFileSync as
|
|
28547
|
-
import { join as
|
|
29414
|
+
import { chmodSync as chmodSync8, existsSync as existsSync35, mkdirSync as mkdirSync28, readFileSync as readFileSync34, renameSync as renameSync13, writeFileSync as writeFileSync28 } from "node:fs";
|
|
29415
|
+
import { join as join34, resolve as resolve12 } from "node:path";
|
|
28548
29416
|
var configDir2 = (home, env = process.env) => {
|
|
28549
29417
|
const o = env.SLEDS_HOME?.trim();
|
|
28550
|
-
return o ?
|
|
29418
|
+
return o ? resolve12(home, o) : join34(home, ".config", "sleds");
|
|
28551
29419
|
};
|
|
28552
|
-
var configPath2 = (home) =>
|
|
29420
|
+
var configPath2 = (home) => join34(configDir2(home), "config.json");
|
|
28553
29421
|
var isObject17 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
28554
29422
|
var optString2 = (v) => typeof v === "string" ? v : undefined;
|
|
28555
29423
|
var parsePriorEnv2 = (v) => {
|
|
@@ -28599,6 +29467,9 @@ var parseConfig2 = (raw) => {
|
|
|
28599
29467
|
cfg.schedule = schedule;
|
|
28600
29468
|
if (raw.priorStatusLine !== undefined)
|
|
28601
29469
|
cfg.priorStatusLine = raw.priorStatusLine;
|
|
29470
|
+
const priorGatewayEnv = parsePriorEnv2(raw.priorGatewayEnv);
|
|
29471
|
+
if (priorGatewayEnv !== undefined)
|
|
29472
|
+
cfg.priorGatewayEnv = priorGatewayEnv;
|
|
28602
29473
|
if (raw.wiring === "seat-first" || raw.wiring === "proxy-always")
|
|
28603
29474
|
cfg.wiring = raw.wiring;
|
|
28604
29475
|
if (raw.agentKind === "claude-code" || raw.agentKind === "codex")
|
|
@@ -28683,7 +29554,7 @@ var readConfig2 = (home) => {
|
|
|
28683
29554
|
const p = configPath2(home);
|
|
28684
29555
|
if (!existsSync35(p))
|
|
28685
29556
|
return {};
|
|
28686
|
-
return parseConfig2(JSON.parse(
|
|
29557
|
+
return parseConfig2(JSON.parse(readFileSync34(p, "utf8")));
|
|
28687
29558
|
};
|
|
28688
29559
|
|
|
28689
29560
|
// src/runners/exec.ts
|
|
@@ -28741,9 +29612,9 @@ function runnerFor3(name, exec = defaultExec, o = {}) {
|
|
|
28741
29612
|
|
|
28742
29613
|
// src/runtime.ts
|
|
28743
29614
|
import { homedir as homedir9 } from "node:os";
|
|
28744
|
-
import { join as
|
|
29615
|
+
import { join as join35, win32 as win323 } from "node:path";
|
|
28745
29616
|
var isCompiled2 = (argv1 = process.argv[1]) => Boolean(argv1 && (argv1.startsWith("/$bunfs/") || argv1.startsWith("B:\\~BUN\\")));
|
|
28746
|
-
var sledsLink2 = (home = homedir9(), platform = process.platform) => platform === "win32" ? win323.join(process.env.LOCALAPPDATA ?? win323.join(home, "AppData", "Local"), "sleds", "bin", "sleds.exe") :
|
|
29617
|
+
var sledsLink2 = (home = homedir9(), platform = process.platform) => platform === "win32" ? win323.join(process.env.LOCALAPPDATA ?? win323.join(home, "AppData", "Local"), "sleds", "bin", "sleds.exe") : join35(home, ".sleds", "bin", "sleds");
|
|
28747
29618
|
|
|
28748
29619
|
// src/schedule.ts
|
|
28749
29620
|
import { spawnSync as spawnSync11 } from "node:child_process";
|
|
@@ -28755,15 +29626,15 @@ var defaultExec5 = (cmd, args) => {
|
|
|
28755
29626
|
};
|
|
28756
29627
|
|
|
28757
29628
|
// src/settings.ts
|
|
28758
|
-
import { dirname as
|
|
29629
|
+
import { dirname as dirname25, join as join36, resolve as resolve13 } from "node:path";
|
|
28759
29630
|
var withSeatEnv2 = (command, home, env = process.env, org) => {
|
|
28760
29631
|
const parts = [];
|
|
28761
29632
|
const sh = env.SLEDS_HOME?.trim();
|
|
28762
29633
|
const cc = env.CLAUDE_CONFIG_DIR?.trim();
|
|
28763
29634
|
if (sh)
|
|
28764
|
-
parts.push(`SLEDS_HOME=${shellWord2(
|
|
29635
|
+
parts.push(`SLEDS_HOME=${shellWord2(resolve13(home, sh))}`);
|
|
28765
29636
|
if (cc)
|
|
28766
|
-
parts.push(`CLAUDE_CONFIG_DIR=${shellWord2(
|
|
29637
|
+
parts.push(`CLAUDE_CONFIG_DIR=${shellWord2(resolve13(home, cc))}`);
|
|
28767
29638
|
parts.push(command);
|
|
28768
29639
|
return pinOrg4(parts.join(" "), org);
|
|
28769
29640
|
};
|
|
@@ -28779,12 +29650,16 @@ var pinOrg4 = (command, org) => {
|
|
|
28779
29650
|
};
|
|
28780
29651
|
var shellWord2 = (v) => /^[\w./~-]+$/.test(v) ? v : `'${v.replace(/'/g, "'\\''")}'`;
|
|
28781
29652
|
var isObject18 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
29653
|
+
var invokesSleds2 = (command) => /\bsleds\b/.test(command);
|
|
28782
29654
|
var isSledsHookFor2 = (sub) => (entry) => {
|
|
28783
29655
|
if (!isObject18(entry) || !Array.isArray(entry.hooks))
|
|
28784
29656
|
return false;
|
|
28785
29657
|
const re = new RegExp(`\\b${sub}\\b`);
|
|
28786
|
-
return entry.hooks.some((h) => isObject18(h) && h.type === "command" && typeof h.command === "string" && re.test(h.command));
|
|
29658
|
+
return entry.hooks.some((h) => isObject18(h) && h.type === "command" && typeof h.command === "string" && invokesSleds2(h.command) && re.test(h.command));
|
|
28787
29659
|
};
|
|
29660
|
+
var isSledsSeatHook2 = isSledsHookFor2("seat-event");
|
|
29661
|
+
var isSledsSpecHook2 = isSledsHookFor2("spec-event");
|
|
29662
|
+
var isSledsPromptHook2 = isSledsHookFor2("prompt-event");
|
|
28788
29663
|
var isSledsSessionHook2 = isSledsHookFor2("session-event");
|
|
28789
29664
|
var isSledsStopHook2 = isSledsHookFor2("stop-event");
|
|
28790
29665
|
var isSledsPermissionHook2 = isSledsHookFor2("permission-event");
|
|
@@ -29081,7 +29956,7 @@ var main = async (argv) => {
|
|
|
29081
29956
|
now: Date.now(),
|
|
29082
29957
|
stderr: (line) => console.error(line),
|
|
29083
29958
|
isTTY: !!process.stderr.isTTY,
|
|
29084
|
-
version: "0.1.
|
|
29959
|
+
version: "0.1.61"
|
|
29085
29960
|
});
|
|
29086
29961
|
switch (cmd) {
|
|
29087
29962
|
case "update":
|
|
@@ -29314,7 +30189,7 @@ ${renderHelpText(argv[1])}`);
|
|
|
29314
30189
|
case "version":
|
|
29315
30190
|
case "--version":
|
|
29316
30191
|
case "-v":
|
|
29317
|
-
console.log("sleds 0.1.
|
|
30192
|
+
console.log("sleds 0.1.61");
|
|
29318
30193
|
return 0;
|
|
29319
30194
|
default:
|
|
29320
30195
|
console.error(`unknown command: ${cmd}
|