@sleds/cli 0.1.60 → 0.1.62

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.
Files changed (2) hide show
  1. package/dist/sleds.mjs +1338 -458
  2. 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 ? { touches: form.touches, demand: form.demand } : parsed.byKey;
1285
- const fromText = form?.ask ? form.ask : askFrom(text, preamble);
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);
@@ -3621,7 +3632,7 @@ An engine (or you) takes a ready node with \`sled {take: "${key}/<key>"}\`; Run
3621
3632
  text: "Show the person the graph as text and ask for a yes; then call route again with the same nodes and confirm: true. With nobody to ask (a planning session), call again with propose: true instead.",
3622
3633
  isError: true
3623
3634
  };
3624
- const r = await ctx.client.get(`/proxy/route?q=${encodeURIComponent(q)}`);
3635
+ const r = await ctx.client.get(`/proxy/route?q=${encodeURIComponent(q)}&draft=1`);
3625
3636
  if (!r)
3626
3637
  return UNREACHABLE;
3627
3638
  if (r.ok === false)
@@ -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. If it is still open, say only what is still needed, once, in a few words.",
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, isSledsSeatHook = (entry) => {
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
- return entry.hooks.some((h) => isObject5(h) && h.type === "command" && typeof h.command === "string" && /\bseat-event\b/.test(h.command));
7750
- }, seatHookEntry = (command) => ({
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 = (entry) => {
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 = (entry) => {
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, isSledsHookFor = (sub) => (entry) => {
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"), readMeterCache = (home, now) => {
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 cached = readMeterCache(o.home, now);
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: "draft" } : {},
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 CREDITS_PATTERN = /out of usage credits|cc_cli_limit_message/i;
9914
- var creditsHitIn = (text) => CREDITS_PATTERN.test(text);
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 hit = creditsHitIn(buf.toString("utf8"));
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: size,
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 isPointedAtProxy = (settings) => {
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 readFileSync19,
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 dirname17, join as join26, resolve as resolve9 } from "node:path";
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 spawn = opts.spawn ?? Bun.spawn;
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(async (a) => {
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
- return new RegExp(`^\\s*(?:export\\s+)?${name}\\s*=\\s*\\S`, "m").test(text);
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
- const r = exec("security", ["add-generic-password", "-U", ...item, "-w", pw]);
10763
- if (r.status !== 0) {
10764
- const why = r.stderr.split(pw).join("…").trim() || `exit ${r.status}`;
10765
- throw new Error(`could not store the cmux password in the Keychain: ${why}`);
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 readFileSync15,
11765
+ readFileSync as readFileSync16,
11599
11766
  existsSync as realExists,
11600
11767
  rmSync as rmSync10,
11601
- statSync as statSync5,
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 dirname15, isAbsolute as isAbsolute3, join as join24, resolve as resolve8 } from "node:path";
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.60" ?? "dev",
11784
+ version: host.version ?? "0.1.62" ?? "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 [name, c] of latest) {
12065
- if (FAILED.has(c.conclusion))
12066
- failing.push(name);
12067
- else if (c.status !== "COMPLETED" && c.conclusion !== "SUCCESS")
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 r = await call(`/rest/api/3/search/jql?jql=${jql}&fields=summary,description,status,issuelinks&maxResults=100`);
12432
- if (!r.ok)
12433
- throw new Error(`Jira: could not read ${epic}'s children — ${errorOf(r)}`);
12434
- const issues = Array.isArray(r.json?.issues) ? r.json.issues.filter(isObject8) : [];
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
- async function pickup(sled, drafted) {
13289
+ const mockRefused = new Map;
13290
+ async function pickup(sled, drafted, profile) {
13088
13291
  const engine = o.engine;
13089
- const profile = await readProfile(client);
13090
- if (profile?.pickup !== "auto")
13091
- return null;
13092
- if (profile.engine && !sameEngine(profile.engine, engine))
13292
+ if (!autoHere(profile))
13093
13293
  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;
@@ -13139,7 +13339,7 @@ function sledsSource(client, o) {
13139
13339
  let pack = "";
13140
13340
  let brief = null;
13141
13341
  try {
13142
- brief = await routeOf(project);
13342
+ brief = await client.get(`/proxy/route?q=${encodeURIComponent(project)}&draft=1`);
13143
13343
  pack = (isObject8(brief?.draft) ? str6(brief.draft.pack) : undefined) ?? "";
13144
13344
  } catch {
13145
13345
  pack = "";
@@ -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 readProfile(client))?.atOnce ?? 1;
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 picked = await pickup(r, drafted);
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) => a.startsWith("-")) ? argv : null;
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 = providerFailure(shown);
14245
- asked = !failing && run.seenTicks >= STILL_TICKS && !providerFailure(shown) && askedAndStopped(shown);
14246
- idle = !failing && (asked || quiet >= d.idleMin * 60000);
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
- state.runs = state.runs.filter((r) => r !== run);
14596
- save();
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") {
15736
16096
  try {
15737
16097
  if (runner.status(run.handle) === "running")
15738
16098
  runner.stop(run.handle);
15739
16099
  } catch (e) {
15740
- d.log(`${ref(run)}: could not close the session: ${message(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) {
16323
+ try {
16324
+ if (runner.status(h) === "running")
16325
+ runner.stop(h);
16326
+ } catch (e) {
16327
+ d.log(`${at}: could not close the session: ${message(e)}`);
15741
16328
  }
15742
- const wt = run.handle.cwd;
15743
- if (wt === run.repoPath)
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
- if (runner.makesWorktree) {
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(`${ref(run)}: kept the drafter's checkout ${wt}: ${message(e)}`);
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 { basename as basename4 } from "node:path";
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: "2",
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, { ...o, env: { ...o?.env, ...GIT_GUARD_ENV } }) : exec(cmd, args, o);
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 { closeSync as closeSync4, existsSync as existsSync18, mkdirSync as mkdirSync17, openSync as openSync4, rmSync as rmSync9, statSync as statSync4, writeSync as writeSync2 } from "node:fs";
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 join23 } from "node:path";
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 statSync2,
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 dirname12, join as join18, win32 as win322 } from "node:path";
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") : join18(home, ".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: join18 };
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(dirname12(path), { recursive: true, mode: 448 });
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
- function readLockBody(path) {
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(readFileSync13(path, "utf8"));
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
- function lockStale(path, now) {
17809
- const body = readLockBody(path);
18462
+ var readLockBody = (path) => parseLockBody(readText(path));
18463
+ function lockStale(path, text, now, maxMs) {
17810
18464
  try {
17811
- const at = body?.at ?? statSync2(path).mtimeMs;
17812
- return now - at > UPDATE_LOCK_STALE_MS;
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(dirname12(path), { recursive: true, mode: 448 });
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
- if (lockStale(path, Date.now())) {
17839
- try {
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 = createHash("sha256");
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.60";
18595
+ const running = o.running ?? "0.1.62";
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 = createHash("sha256");
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 statSync3,
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 dirname13, isAbsolute as isAbsolute2, join as join21, relative } from "node:path";
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 join19 } from "node:path";
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 join19(home, ".config", "sleds", "box.env");
18696
+ return join20(home, ".config", "sleds", "box.env");
18014
18697
  }
18015
18698
  function engineUnitDir(home) {
18016
- return join19(home, ".config", "systemd", "user");
18699
+ return join20(home, ".config", "systemd", "user");
18017
18700
  }
18018
18701
  function engineUnitPath(home, label) {
18019
- return join19(engineUnitDir(home), `${label}.service`);
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 join20 } from "node:path";
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) => join20(home, "Library", "LaunchAgents", `${REVERT_LABEL}.plist`);
18752
+ var plistPath = (home) => join21(home, "Library", "LaunchAgents", `${REVERT_LABEL}.plist`);
18070
18753
  var xmlEscape2 = (s) => s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
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(join20(d.home, "Library", "LaunchAgents"), { recursive: true });
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) => join21(sledsRoot(home), "migrate.bak");
18161
- var manifestPath = (home) => join21(migrateBakDir(home), "manifest.json");
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, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
18163
18846
  var xmlUnescape = (s) => s.replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&amp;/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) => join21(dir, 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(join21(home, "Library", "LaunchAgents"), (n) => n.startsWith("ai.sleds.") && n.endsWith(".plist")).map((path) => ({
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 = [join21(home, ".claude"), claudeDir(home, env)];
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 = join21(dir, "settings.json");
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) ? join21(migrateBakDir(home), "abs", path) : join21(migrateBakDir(home), 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 = statSync3(path).mode & 511;
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(dirname13(link), { recursive: true, mode: 448 });
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(dirname13(backup), { recursive: true, mode: 448 });
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 = dirname13(link);
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 dirname14, join as join22 } from "node:path";
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), join22(home, ".config", "sleds")])];
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(join22(dir, n));
19205
+ out.push(join23(dir, n));
18525
19206
  for (const sub of ["orgs", "seats"]) {
18526
19207
  let kids;
18527
19208
  try {
18528
- kids = readdirSync3(join22(dir, sub));
19209
+ kids = readdirSync3(join23(dir, sub));
18529
19210
  } catch {
18530
19211
  continue;
18531
19212
  }
18532
19213
  for (const k of kids.sort())
18533
- walk(join22(dir, sub, k));
19214
+ walk(join23(dir, sub, k));
18534
19215
  }
18535
19216
  };
18536
19217
  for (const r of roots)
18537
- walk(join22(r, "engine"));
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(dirname14(bin), { recursive: true, mode: 448 });
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 = dirname14(f);
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(join22(dir, n), { force: true });
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 = dirname14(versionDir(home, s.current, platform));
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) => say(`the first-beat check could not run: ${e instanceof Error ? e.message : String(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
- const setTimer = o.setTimer ?? ((fn, ms) => {
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 = join23(dir, "fetch.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()) - statSync4(lock).mtimeMs <= FETCH_LOCK_STALE_MS)
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, `${process.pid}
18915
- `);
19606
+ writeSync2(fd, mine);
18916
19607
  closeSync4(fd);
18917
19608
  const { fetch: f = fetchVersion, now: _n, ...rest } = o;
18918
- return await f(rest);
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
- rmSync9(lock, { force: true });
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) {
@@ -19112,6 +19816,11 @@ function engineUpdater(o) {
19112
19816
  };
19113
19817
  }
19114
19818
 
19819
+ // src/version.ts
19820
+ function cliVersion(env) {
19821
+ return "0.1.62";
19822
+ }
19823
+
19115
19824
  // src/commands/update.ts
19116
19825
  init_server();
19117
19826
  init_config();
@@ -19400,7 +20109,7 @@ function flagsFromProfile(flags, given, profile, home) {
19400
20109
  }
19401
20110
  }
19402
20111
  if (profile.reposDir && !has("repos-dir") && !has("repo")) {
19403
- out.reposDir = splitReposDirs(profile.reposDir).map((d) => home && /^~(\/|$)/.test(d) ? join24(home, d.slice(1)) : d).join(":");
20112
+ out.reposDir = splitReposDirs(profile.reposDir).map((d) => home && /^~(\/|$)/.test(d) ? join25(home, d.slice(1)) : d).join(":");
19404
20113
  used.push(`repos ${profile.reposDir}`);
19405
20114
  }
19406
20115
  if (profile.mcpStrict !== undefined && !has("strict-mcp") && !has("no-strict-mcp")) {
@@ -19495,7 +20204,7 @@ function engineClient(o, org, breaker) {
19495
20204
  ...src,
19496
20205
  ...breaker ? { fetchFn: breaker.fetch } : {},
19497
20206
  timeoutMs: ENGINE_TIMEOUT_MS,
19498
- version: "0.1.60"
20207
+ version: "0.1.62"
19499
20208
  });
19500
20209
  const asked = org?.trim() || env.SLEDS_ORG?.trim();
19501
20210
  if (asked && !selectOrg(readConfig(o.home), org, env).seat)
@@ -19528,19 +20237,19 @@ function realPidAlive2(pid) {
19528
20237
  function memberMcpConfigs(o, cwd) {
19529
20238
  const read = o.readJson ?? ((p) => {
19530
20239
  try {
19531
- return JSON.parse(readFileSync15(p, "utf8"));
20240
+ return JSON.parse(readFileSync16(p, "utf8"));
19532
20241
  } catch {
19533
20242
  return null;
19534
20243
  }
19535
20244
  });
19536
20245
  const claudeDir = (o.env ?? process.env).CLAUDE_CONFIG_DIR;
19537
20246
  return [
19538
- read(claudeDir ? join24(claudeDir, ".claude.json") : join24(o.home, ".claude.json")),
19539
- ...cwd ? [read(join24(cwd, ".mcp.json"))] : []
20247
+ read(claudeDir ? join25(claudeDir, ".claude.json") : join25(o.home, ".claude.json")),
20248
+ ...cwd ? [read(join25(cwd, ".mcp.json"))] : []
19540
20249
  ];
19541
20250
  }
19542
20251
  function engineStateDir(o, org, seat) {
19543
- return seatStateDir(orgStateDir(o.stateDir ?? join24(configDir(o.home, o.env ?? process.env), "engine"), org), seat);
20252
+ return seatStateDir(orgStateDir(o.stateDir ?? join25(configDir(o.home, o.env ?? process.env), "engine"), org), seat);
19544
20253
  }
19545
20254
  function engineSeat(o, org) {
19546
20255
  try {
@@ -19567,11 +20276,11 @@ function engineDeps(o, flags, source, runner, stateName) {
19567
20276
  const stateDir = engineStateDir(o, flags.org, flags.seat);
19568
20277
  const file = statePath(stateDir, stateName);
19569
20278
  const mcpOrg = flags.org && engineOrg(o, flags.org).seated ? flags.org : undefined;
19570
- const repo = flags.repo ? resolve8(cwd, flags.repo) : undefined;
19571
- const reposDirs = splitReposDirs(flags.reposDir).map((d) => resolve8(cwd, d));
20279
+ const repo = flags.repo ? resolve9(cwd, flags.repo) : undefined;
20280
+ const reposDirs = splitReposDirs(flags.reposDir).map((d) => resolve9(cwd, d));
19572
20281
  const { branch, prTitle } = flags;
19573
- const hooksDir = join24(stateDir, "hooks");
19574
- const permissionMark = (name) => join24(hooksDir, `${name}.permission.json`);
20282
+ const hooksDir = join25(stateDir, "hooks");
20283
+ const permissionMark = (name) => join25(hooksDir, `${name}.permission.json`);
19575
20284
  const withHook = (name, settings) => applyPermissionHook(settings, [...o.sledsArgv ?? ["sleds"], "permission-event", "--mark", permissionMark(name)].map(shq2).join(" "));
19576
20285
  return {
19577
20286
  source,
@@ -19596,9 +20305,9 @@ function engineDeps(o, flags, source, runner, stateName) {
19596
20305
  sandbox: true,
19597
20306
  writeSandboxSettings: o.writeSandboxSettings ?? ((name, cwd, allow) => {
19598
20307
  const gitCommonDir = gitCommonDirOf(exec, cwd);
19599
- const dir = join24(stateDir, "sandbox");
20308
+ const dir = join25(stateDir, "sandbox");
19600
20309
  mkdirSync18(dir, { recursive: true, mode: 448 });
19601
- const p = join24(dir, `${name}.json`);
20310
+ const p = join25(dir, `${name}.json`);
19602
20311
  const walls = engineSandboxSettings(o.home, o.env ?? process.env, gitCommonDir, [
19603
20312
  ...SANDBOX_DEFAULT_ALLOW,
19604
20313
  ...flags.sandboxAllow ?? []
@@ -19615,7 +20324,7 @@ function engineDeps(o, flags, source, runner, stateName) {
19615
20324
  } : {},
19616
20325
  writeHookSettings: (name, allow) => {
19617
20326
  mkdirSync18(hooksDir, { recursive: true, mode: 448 });
19618
- const p = join24(hooksDir, `${name}.json`);
20327
+ const p = join25(hooksDir, `${name}.json`);
19619
20328
  const base = allow?.length ? { permissions: { allow: [...allow] } } : {};
19620
20329
  writeFileSync16(p, `${JSON.stringify(withHook(name, base), null, 2)}
19621
20330
  `, { mode: 384 });
@@ -19643,9 +20352,9 @@ function engineDeps(o, flags, source, runner, stateName) {
19643
20352
  exists
19644
20353
  }),
19645
20354
  writeMcpConfig: o.writeMcpConfig ?? ((name, runToken, worktree) => {
19646
- const dir = join24(stateDir, "mcp");
20355
+ const dir = join25(stateDir, "mcp");
19647
20356
  mkdirSync18(dir, { recursive: true, mode: 448 });
19648
- const p = join24(dir, `${name}.json`);
20357
+ const p = join25(dir, `${name}.json`);
19649
20358
  let others = {};
19650
20359
  if (flags.strictMcp && flags.mcpAllow?.length) {
19651
20360
  const r = mcpPassThrough(flags.mcpAllow, memberMcpConfigs(o, worktree));
@@ -19661,12 +20370,26 @@ function engineDeps(o, flags, source, runner, stateName) {
19661
20370
  }),
19662
20371
  removeMcpConfig: o.removeMcpConfig ?? ((p) => rmSync10(p, { force: true })),
19663
20372
  writePrompt: o.writePrompt ?? ((name, text) => {
19664
- const dir = join24(stateDir, "prompts");
20373
+ const dir = join25(stateDir, "prompts");
19665
20374
  mkdirSync18(dir, { recursive: true, mode: 448 });
19666
- const p = join24(dir, `${name}.md`);
20375
+ const p = join25(dir, `${name}.md`);
19667
20376
  writeFileSync16(p, text, { mode: 384 });
19668
20377
  return p;
19669
20378
  }),
20379
+ mockOut: (name) => {
20380
+ const dir = join25(tmpdir(), "sleds-mocks");
20381
+ mkdirSync18(dir, { recursive: true, mode: 448 });
20382
+ const p = join25(dir, `${name}.html`);
20383
+ rmSync10(p, { force: true });
20384
+ return p;
20385
+ },
20386
+ readMock: (p) => {
20387
+ try {
20388
+ return readFileSync16(p, "utf8");
20389
+ } catch {
20390
+ return null;
20391
+ }
20392
+ },
19670
20393
  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
20394
  ensureDraftWorktree: (e, repoPath, path, base) => ensureDetachedWorktree(e, repoPath, path, base, o.worktreeFs ?? { ...realWorktreeFs, exists }),
19672
20395
  defaultBranch,
@@ -19691,14 +20414,14 @@ function engineDeps(o, flags, source, runner, stateName) {
19691
20414
  }
19692
20415
  function sandboxDenyRead(home, env) {
19693
20416
  return {
19694
- dirs: [...new Set([configDir(home, env), join24(home, ".config", "sleds")])],
20417
+ dirs: [...new Set([configDir(home, env), join25(home, ".config", "sleds")])],
19695
20418
  files: [
19696
20419
  ...new Set([
19697
20420
  tokenPath(home, env),
19698
- join24(home, ".sleds-token"),
20421
+ join25(home, ".sleds-token"),
19699
20422
  settingsPath(home, env),
19700
20423
  backupPath(home, env),
19701
- join24(claudeDir(home, env), "settings.local.json")
20424
+ join25(claudeDir(home, env), "settings.local.json")
19702
20425
  ])
19703
20426
  ]
19704
20427
  };
@@ -19715,21 +20438,21 @@ function gitCommonDirOf(exec, cwd) {
19715
20438
  const dir = r.status === 0 ? r.stdout.trim() : "";
19716
20439
  if (!dir)
19717
20440
  throw new Error(`git has no common dir for ${cwd}, so the sandbox has nowhere to commit`);
19718
- return resolve8(cwd, dir);
20441
+ return resolve9(cwd, dir);
19719
20442
  }
19720
20443
  var LENT_NEEDS_SANDBOX = "a lent engine runs every session sandboxed (spec 48 rule 10) — add --sandbox to --lent-by";
19721
20444
  function lenderHome(accountHome, lentBy) {
19722
- return isAbsolute3(lentBy) ? resolve8(lentBy) : join24(dirname15(accountHome), lentBy);
20445
+ return isAbsolute3(lentBy) ? resolve9(lentBy) : join25(dirname16(accountHome), lentBy);
19723
20446
  }
19724
20447
  function lentHomeProblems(accountHome, lentBy, modeOf) {
19725
20448
  const lender = lenderHome(accountHome, lentBy);
19726
- if (lender === resolve8(accountHome))
20449
+ if (lender === resolve9(accountHome))
19727
20450
  return [
19728
20451
  `--lent-by ${lentBy} is this account — a lent engine runs as the borrower's own OS user, not the lender's`
19729
20452
  ];
19730
20453
  const problems = [];
19731
20454
  for (const [who, home] of [
19732
- ["the borrower's", resolve8(accountHome)],
20455
+ ["the borrower's", resolve9(accountHome)],
19733
20456
  ["the lender's", lender]
19734
20457
  ]) {
19735
20458
  const mode = modeOf(home);
@@ -19742,7 +20465,7 @@ function lentHomeProblems(accountHome, lentBy, modeOf) {
19742
20465
  }
19743
20466
  var homeMode = (path) => {
19744
20467
  try {
19745
- return statSync5(path).mode & 4095;
20468
+ return statSync6(path).mode & 4095;
19746
20469
  } catch {
19747
20470
  return null;
19748
20471
  }
@@ -19787,7 +20510,7 @@ async function driveEngine(o, flags, deps, until = {}) {
19787
20510
  runner: deps.runner.name,
19788
20511
  atOnce: flags.atOnce,
19789
20512
  interval: flags.interval,
19790
- cliVersion: "0.1.60"
20513
+ cliVersion: "0.1.62"
19791
20514
  };
19792
20515
  const beat = (checkedAt) => {
19793
20516
  const m = deps.machine;
@@ -20135,14 +20858,14 @@ init_config();
20135
20858
  import {
20136
20859
  existsSync as existsSync19,
20137
20860
  mkdirSync as mkdirSync19,
20138
- readFileSync as readFileSync16,
20861
+ readFileSync as readFileSync17,
20139
20862
  readdirSync as readdirSync4,
20140
20863
  realpathSync,
20141
20864
  rmSync as rmSync11,
20142
20865
  writeFileSync as writeFileSync17
20143
20866
  } from "node:fs";
20144
20867
  import { hostname as hostname3 } from "node:os";
20145
- import { basename as basename6, dirname as dirname16, isAbsolute as isAbsolute4, join as join25 } from "node:path";
20868
+ import { basename as basename6, dirname as dirname17, isAbsolute as isAbsolute4, join as join26 } from "node:path";
20146
20869
  init_state();
20147
20870
  var ENGINE_LABEL = "ai.sleds.engine";
20148
20871
  var ENGINE_INSTALL_USAGE = `usage: sleds engine install [engine flags] | sleds engine uninstall [--name <n>] [--org <slug>]
@@ -20163,13 +20886,13 @@ function engineLabel(name, org, seat) {
20163
20886
  return [ENGINE_LABEL, org, seat?.trim(), name].filter((p) => Boolean(p)).map((p, i) => i === 0 ? p : engineSlug(p)).join(".");
20164
20887
  }
20165
20888
  function enginePlistPath(home, name, org, seat) {
20166
- return join25(home, "Library", "LaunchAgents", `${engineLabel(name, org, seat)}.plist`);
20889
+ return join26(home, "Library", "LaunchAgents", `${engineLabel(name, org, seat)}.plist`);
20167
20890
  }
20168
20891
  function engineLogPath(stateDir, name) {
20169
- return join25(stateDir, `${engineSlug(name)}.log`);
20892
+ return join26(stateDir, `${engineSlug(name)}.log`);
20170
20893
  }
20171
20894
  function engineEventsPath(stateDir, name) {
20172
- return join25(stateDir, `${engineSlug(name)}.events.jsonl`);
20895
+ return join26(stateDir, `${engineSlug(name)}.events.jsonl`);
20173
20896
  }
20174
20897
  var xmlEscape4 = (s) => s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
20175
20898
  function buildEnginePlist(o) {
@@ -20266,10 +20989,10 @@ function readEngines(dir, suffix, manager, parse) {
20266
20989
  }
20267
20990
  const out = [];
20268
20991
  for (const name of names.filter((n) => n.startsWith(`${ENGINE_LABEL}.`) && n.endsWith(suffix))) {
20269
- const path = join25(dir, name);
20992
+ const path = join26(dir, name);
20270
20993
  let parsed;
20271
20994
  try {
20272
- parsed = parse(readFileSync16(path, "utf8"), name);
20995
+ parsed = parse(readFileSync17(path, "utf8"), name);
20273
20996
  } catch {
20274
20997
  parsed = null;
20275
20998
  }
@@ -20286,7 +21009,7 @@ function readEngines(dir, suffix, manager, parse) {
20286
21009
  }
20287
21010
  function installedEngines(home) {
20288
21011
  return [
20289
- ...readEngines(join25(home, "Library", "LaunchAgents"), ".plist", "launchd", parseEnginePlist),
21012
+ ...readEngines(join26(home, "Library", "LaunchAgents"), ".plist", "launchd", parseEnginePlist),
20290
21013
  ...readEngines(engineUnitDir(home), ".service", "systemd", (text, file) => {
20291
21014
  const parsed = parseEngineUnit(text);
20292
21015
  return parsed && { label: basename6(file, ".service"), ...parsed };
@@ -20336,7 +21059,7 @@ function engineInstall(o) {
20336
21059
  if (platform === "linux")
20337
21060
  return installUnit({ o, parsed, label, sledsArgv, env: agentEnv, logPath, exec });
20338
21061
  const plist = enginePlistPath(o.home, parsed.name, parsed.org, seat);
20339
- mkdirSync19(dirname16(plist), { recursive: true });
21062
+ mkdirSync19(dirname17(plist), { recursive: true });
20340
21063
  if (existsSync19(plist))
20341
21064
  exec("launchctl", ["unload", plist]);
20342
21065
  for (const other of installedEngines(o.home)) {
@@ -20378,7 +21101,7 @@ function installUnit(a) {
20378
21101
  const unit = engineUnitPath(o.home, label);
20379
21102
  const unitName = basename6(unit);
20380
21103
  const systemctl = (...args) => exec("systemctl", ["--user", ...args]);
20381
- mkdirSync19(dirname16(unit), { recursive: true });
21104
+ mkdirSync19(dirname17(unit), { recursive: true });
20382
21105
  if (existsSync19(unit))
20383
21106
  systemctl("stop", unitName);
20384
21107
  for (const other of installedEngines(o.home)) {
@@ -20456,7 +21179,7 @@ function engineUninstall(o) {
20456
21179
  }
20457
21180
 
20458
21181
  // src/commands/musher.ts
20459
- import { existsSync as existsSync20, mkdirSync as mkdirSync20, readFileSync as readFileSync17, rmSync as rmSync12, writeFileSync as writeFileSync18 } from "node:fs";
21182
+ import { existsSync as existsSync20, mkdirSync as mkdirSync20, readFileSync as readFileSync18, rmSync as rmSync12, writeFileSync as writeFileSync18 } from "node:fs";
20460
21183
  init_config();
20461
21184
  init_meter();
20462
21185
  var defaultWhich = (cmd) => {
@@ -20484,12 +21207,14 @@ var findClaude = (home, configured, which) => {
20484
21207
  return p;
20485
21208
  return null;
20486
21209
  };
21210
+ var MUSHER_SWITCHES = new Set(["install", "uninstall", "status"]);
21211
+ var MUSHER_SETTINGS = new Set(["claude", "mode", "timeout"]);
20487
21212
  var musherAgentState = (home, env = process.env) => {
20488
21213
  const plist = musherPlistPath(home, env);
20489
21214
  if (!existsSync20(plist))
20490
21215
  return { installed: false, thisSeat: false };
20491
21216
  try {
20492
- const text = readFileSync17(plist, "utf8");
21217
+ const text = readFileSync18(plist, "utf8");
20493
21218
  const want = env.SLEDS_HOME?.trim();
20494
21219
  const has = /<key>SLEDS_HOME<\/key>\s*<string>([^<]*)<\/string>/.exec(text)?.[1];
20495
21220
  return { installed: true, thisSeat: (want ?? undefined) === (has ?? undefined) };
@@ -20499,7 +21224,7 @@ var musherAgentState = (home, env = process.env) => {
20499
21224
  };
20500
21225
 
20501
21226
  // src/claudeAccount.ts
20502
- import { readFileSync as readFileSync18 } from "node:fs";
21227
+ import { readFileSync as readFileSync19 } from "node:fs";
20503
21228
  var PLANS = {
20504
21229
  claude_max: "Max",
20505
21230
  claude_pro: "Pro",
@@ -20528,7 +21253,7 @@ var parseClaudeAccount = (raw) => {
20528
21253
  };
20529
21254
  var readClaudeAccount = (home, env = process.env) => {
20530
21255
  try {
20531
- return parseClaudeAccount(JSON.parse(readFileSync18(claudeUserConfigPath2(home, env), "utf8")));
21256
+ return parseClaudeAccount(JSON.parse(readFileSync19(claudeUserConfigPath2(home, env), "utf8")));
20532
21257
  } catch {
20533
21258
  return null;
20534
21259
  }
@@ -20665,7 +21390,7 @@ async function runDoctor(o) {
20665
21390
  detail: `seat-event ${seat ? "on" : "missing"} · spec-event ${spec ? "on" : "missing"} · session-event ${session ? "on" : "missing"} · stop-event ${stop ? "on" : "missing"}`,
20666
21391
  fix: all ? undefined : "sleds hooks"
20667
21392
  });
20668
- const want = env.SLEDS_HOME?.trim() ? resolve9(home, env.SLEDS_HOME.trim()) : undefined;
21393
+ const want = env.SLEDS_HOME?.trim() ? resolve10(home, env.SLEDS_HOME.trim()) : undefined;
20669
21394
  const cmds = sledsCommandsIn(settings);
20670
21395
  const wrong = cmds.filter((c) => {
20671
21396
  const got = bakedHome(c);
@@ -20754,7 +21479,7 @@ async function runDoctor(o) {
20754
21479
  checks.push({ level: "ok", name: "path", detail: which[0] ?? "" });
20755
21480
  const link = sledsLink(home, o.platform);
20756
21481
  if (existsSync21(link)) {
20757
- const binDir = dirname17(link);
21482
+ const binDir = dirname18(link);
20758
21483
  const onPath = (env.PATH ?? "").split(delimiter3).includes(binDir);
20759
21484
  const first = which[0];
20760
21485
  const fix = `echo 'export PATH="${binDir}:$PATH"' >> ~/.zshrc`;
@@ -20943,12 +21668,12 @@ function sandboxChecks(o, env) {
20943
21668
  }
20944
21669
  ];
20945
21670
  const nonce = o.nonce ?? `sleds-probe-${randomBytes(16).toString("hex")}`;
20946
- const probe = join26(dir, SANDBOX_PROBE);
20947
- const work = mkdtempSync(join26(tmpdir(), "sleds-sandbox-"));
21671
+ const probe = join27(dir, SANDBOX_PROBE);
21672
+ const work = mkdtempSync(join27(tmpdir2(), "sleds-sandbox-"));
20948
21673
  try {
20949
21674
  writeFileSync19(probe, `${nonce}
20950
21675
  `, { mode: 384 });
20951
- const settingsFile = join26(work, "settings.json");
21676
+ const settingsFile = join27(work, "settings.json");
20952
21677
  const settings = engineSandboxSettings(o.home, env, work, SANDBOX_DEFAULT_ALLOW);
20953
21678
  const claudeSettings = settingsPath(o.home, env);
20954
21679
  writeFileSync19(settingsFile, JSON.stringify(settings, null, 2), { mode: 384 });
@@ -21122,7 +21847,7 @@ function boxChecks(o, env, which, need) {
21122
21847
  const file = boxEnvPath(o.home);
21123
21848
  let boxEnvText = null;
21124
21849
  try {
21125
- boxEnvText = readFileSync19(file, "utf8");
21850
+ boxEnvText = readFileSync20(file, "utf8");
21126
21851
  } catch {
21127
21852
  boxEnvText = null;
21128
21853
  }
@@ -21240,14 +21965,14 @@ import {
21240
21965
  appendFileSync as appendFileSync3,
21241
21966
  chmodSync as chmodSync6,
21242
21967
  mkdirSync as mkdirSync21,
21243
- readFileSync as readFileSync20,
21968
+ readFileSync as readFileSync21,
21244
21969
  existsSync as realExists2,
21245
21970
  rmSync as rmSync14,
21246
- statSync as statSync6,
21971
+ statSync as statSync7,
21247
21972
  writeFileSync as writeFileSync20
21248
21973
  } from "node:fs";
21249
- import { hostname as hostname4, userInfo as userInfo3 } from "node:os";
21250
- import { dirname as dirname18, isAbsolute as isAbsolute5, join as join27, resolve as resolve10 } from "node:path";
21974
+ import { hostname as hostname4, tmpdir as tmpdir3, userInfo as userInfo3 } from "node:os";
21975
+ import { dirname as dirname19, isAbsolute as isAbsolute5, join as join28, resolve as resolve11 } from "node:path";
21251
21976
  init_machine();
21252
21977
  init_state();
21253
21978
  init_meter();
@@ -21507,7 +22232,7 @@ function flagsFromProfile2(flags, given, profile, home) {
21507
22232
  }
21508
22233
  }
21509
22234
  if (profile.reposDir && !has("repos-dir") && !has("repo")) {
21510
- out.reposDir = splitReposDirs(profile.reposDir).map((d) => home && /^~(\/|$)/.test(d) ? join27(home, d.slice(1)) : d).join(":");
22235
+ out.reposDir = splitReposDirs(profile.reposDir).map((d) => home && /^~(\/|$)/.test(d) ? join28(home, d.slice(1)) : d).join(":");
21511
22236
  used.push(`repos ${profile.reposDir}`);
21512
22237
  }
21513
22238
  if (profile.mcpStrict !== undefined && !has("strict-mcp") && !has("no-strict-mcp")) {
@@ -21654,7 +22379,7 @@ function engineClient2(o, org, breaker) {
21654
22379
  ...src,
21655
22380
  ...breaker ? { fetchFn: breaker.fetch } : {},
21656
22381
  timeoutMs: ENGINE_TIMEOUT_MS2,
21657
- version: "0.1.60"
22382
+ version: "0.1.62"
21658
22383
  });
21659
22384
  const asked = org?.trim() || env.SLEDS_ORG?.trim();
21660
22385
  if (asked && !selectOrg(readConfig(o.home), org, env).seat)
@@ -21696,19 +22421,19 @@ function realPidAlive3(pid) {
21696
22421
  function memberMcpConfigs2(o, cwd) {
21697
22422
  const read = o.readJson ?? ((p) => {
21698
22423
  try {
21699
- return JSON.parse(readFileSync20(p, "utf8"));
22424
+ return JSON.parse(readFileSync21(p, "utf8"));
21700
22425
  } catch {
21701
22426
  return null;
21702
22427
  }
21703
22428
  });
21704
22429
  const claudeDir = (o.env ?? process.env).CLAUDE_CONFIG_DIR;
21705
22430
  return [
21706
- read(claudeDir ? join27(claudeDir, ".claude.json") : join27(o.home, ".claude.json")),
21707
- ...cwd ? [read(join27(cwd, ".mcp.json"))] : []
22431
+ read(claudeDir ? join28(claudeDir, ".claude.json") : join28(o.home, ".claude.json")),
22432
+ ...cwd ? [read(join28(cwd, ".mcp.json"))] : []
21708
22433
  ];
21709
22434
  }
21710
22435
  function engineStateDir2(o, org, seat) {
21711
- return seatStateDir(orgStateDir(o.stateDir ?? join27(configDir(o.home, o.env ?? process.env), "engine"), org), seat);
22436
+ return seatStateDir(orgStateDir(o.stateDir ?? join28(configDir(o.home, o.env ?? process.env), "engine"), org), seat);
21712
22437
  }
21713
22438
  function engineSeat2(o, org) {
21714
22439
  try {
@@ -21731,7 +22456,7 @@ function engineCredential(o) {
21731
22456
  const file = boxEnvPath(o.home);
21732
22457
  let boxEnvText = null;
21733
22458
  try {
21734
- boxEnvText = readFileSync20(file, "utf8");
22459
+ boxEnvText = readFileSync21(file, "utf8");
21735
22460
  } catch {}
21736
22461
  return detectCredential(o.env ?? process.env, {
21737
22462
  home: o.home,
@@ -21748,11 +22473,11 @@ function engineDeps2(o, flags, source, runner, stateName) {
21748
22473
  const stateDir = engineStateDir2(o, flags.org, flags.seat);
21749
22474
  const file = statePath(stateDir, stateName);
21750
22475
  const mcpOrg = flags.org && engineOrg2(o, flags.org).seated ? flags.org : undefined;
21751
- const repo = flags.repo ? resolve10(cwd, flags.repo) : undefined;
21752
- const reposDirs = splitReposDirs(flags.reposDir).map((d) => resolve10(cwd, d));
22476
+ const repo = flags.repo ? resolve11(cwd, flags.repo) : undefined;
22477
+ const reposDirs = splitReposDirs(flags.reposDir).map((d) => resolve11(cwd, d));
21753
22478
  const { branch, prTitle } = flags;
21754
- const hooksDir = join27(stateDir, "hooks");
21755
- const permissionMark = (name) => join27(hooksDir, `${name}.permission.json`);
22479
+ const hooksDir = join28(stateDir, "hooks");
22480
+ const permissionMark = (name) => join28(hooksDir, `${name}.permission.json`);
21756
22481
  const withHook = (name, settings) => applyPermissionHook(settings, [...o.sledsArgv ?? ["sleds"], "permission-event", "--mark", permissionMark(name)].map(shq2).join(" "));
21757
22482
  return {
21758
22483
  source,
@@ -21777,9 +22502,9 @@ function engineDeps2(o, flags, source, runner, stateName) {
21777
22502
  sandbox: true,
21778
22503
  writeSandboxSettings: o.writeSandboxSettings ?? ((name, cwd, allow) => {
21779
22504
  const gitCommonDir = gitCommonDirOf2(exec, cwd);
21780
- const dir = join27(stateDir, "sandbox");
22505
+ const dir = join28(stateDir, "sandbox");
21781
22506
  mkdirSync21(dir, { recursive: true, mode: 448 });
21782
- const p = join27(dir, `${name}.json`);
22507
+ const p = join28(dir, `${name}.json`);
21783
22508
  const walls = engineSandboxSettings2(o.home, o.env ?? process.env, gitCommonDir, [
21784
22509
  ...SANDBOX_DEFAULT_ALLOW,
21785
22510
  ...flags.sandboxAllow ?? []
@@ -21796,7 +22521,7 @@ function engineDeps2(o, flags, source, runner, stateName) {
21796
22521
  } : {},
21797
22522
  writeHookSettings: (name, allow) => {
21798
22523
  mkdirSync21(hooksDir, { recursive: true, mode: 448 });
21799
- const p = join27(hooksDir, `${name}.json`);
22524
+ const p = join28(hooksDir, `${name}.json`);
21800
22525
  const base = allow?.length ? { permissions: { allow: [...allow] } } : {};
21801
22526
  writeFileSync20(p, `${JSON.stringify(withHook(name, base), null, 2)}
21802
22527
  `, { mode: 384 });
@@ -21824,9 +22549,9 @@ function engineDeps2(o, flags, source, runner, stateName) {
21824
22549
  exists
21825
22550
  }),
21826
22551
  writeMcpConfig: o.writeMcpConfig ?? ((name, runToken, worktree) => {
21827
- const dir = join27(stateDir, "mcp");
22552
+ const dir = join28(stateDir, "mcp");
21828
22553
  mkdirSync21(dir, { recursive: true, mode: 448 });
21829
- const p = join27(dir, `${name}.json`);
22554
+ const p = join28(dir, `${name}.json`);
21830
22555
  let others = {};
21831
22556
  if (flags.strictMcp && flags.mcpAllow?.length) {
21832
22557
  const r = mcpPassThrough(flags.mcpAllow, memberMcpConfigs2(o, worktree));
@@ -21842,12 +22567,26 @@ function engineDeps2(o, flags, source, runner, stateName) {
21842
22567
  }),
21843
22568
  removeMcpConfig: o.removeMcpConfig ?? ((p) => rmSync14(p, { force: true })),
21844
22569
  writePrompt: o.writePrompt ?? ((name, text) => {
21845
- const dir = join27(stateDir, "prompts");
22570
+ const dir = join28(stateDir, "prompts");
21846
22571
  mkdirSync21(dir, { recursive: true, mode: 448 });
21847
- const p = join27(dir, `${name}.md`);
22572
+ const p = join28(dir, `${name}.md`);
21848
22573
  writeFileSync20(p, text, { mode: 384 });
21849
22574
  return p;
21850
22575
  }),
22576
+ mockOut: (name) => {
22577
+ const dir = join28(tmpdir3(), "sleds-mocks");
22578
+ mkdirSync21(dir, { recursive: true, mode: 448 });
22579
+ const p = join28(dir, `${name}.html`);
22580
+ rmSync14(p, { force: true });
22581
+ return p;
22582
+ },
22583
+ readMock: (p) => {
22584
+ try {
22585
+ return readFileSync21(p, "utf8");
22586
+ } catch {
22587
+ return null;
22588
+ }
22589
+ },
21851
22590
  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
22591
  ensureDraftWorktree: (e, repoPath, path, base) => ensureDetachedWorktree(e, repoPath, path, base, o.worktreeFs ?? { ...realWorktreeFs, exists }),
21853
22592
  defaultBranch,
@@ -21872,14 +22611,14 @@ function engineDeps2(o, flags, source, runner, stateName) {
21872
22611
  }
21873
22612
  function sandboxDenyRead2(home, env) {
21874
22613
  return {
21875
- dirs: [...new Set([configDir(home, env), join27(home, ".config", "sleds")])],
22614
+ dirs: [...new Set([configDir(home, env), join28(home, ".config", "sleds")])],
21876
22615
  files: [
21877
22616
  ...new Set([
21878
22617
  tokenPath(home, env),
21879
- join27(home, ".sleds-token"),
22618
+ join28(home, ".sleds-token"),
21880
22619
  settingsPath(home, env),
21881
22620
  backupPath(home, env),
21882
- join27(claudeDir(home, env), "settings.local.json")
22621
+ join28(claudeDir(home, env), "settings.local.json")
21883
22622
  ])
21884
22623
  ]
21885
22624
  };
@@ -21896,7 +22635,7 @@ function gitCommonDirOf2(exec, cwd) {
21896
22635
  const dir = r.status === 0 ? r.stdout.trim() : "";
21897
22636
  if (!dir)
21898
22637
  throw new Error(`git has no common dir for ${cwd}, so the sandbox has nowhere to commit`);
21899
- return resolve10(cwd, dir);
22638
+ return resolve11(cwd, dir);
21900
22639
  }
21901
22640
  function sandboxRefusal(flags) {
21902
22641
  if (flags.lentBy && !flags.sandbox)
@@ -21913,17 +22652,17 @@ function sandboxRefusal(flags) {
21913
22652
  }
21914
22653
  var LENT_NEEDS_SANDBOX2 = "a lent engine runs every session sandboxed (spec 48 rule 10) — add --sandbox to --lent-by";
21915
22654
  function lenderHome2(accountHome, lentBy) {
21916
- return isAbsolute5(lentBy) ? resolve10(lentBy) : join27(dirname18(accountHome), lentBy);
22655
+ return isAbsolute5(lentBy) ? resolve11(lentBy) : join28(dirname19(accountHome), lentBy);
21917
22656
  }
21918
22657
  function lentHomeProblems2(accountHome, lentBy, modeOf) {
21919
22658
  const lender = lenderHome2(accountHome, lentBy);
21920
- if (lender === resolve10(accountHome))
22659
+ if (lender === resolve11(accountHome))
21921
22660
  return [
21922
22661
  `--lent-by ${lentBy} is this account — a lent engine runs as the borrower's own OS user, not the lender's`
21923
22662
  ];
21924
22663
  const problems = [];
21925
22664
  for (const [who, home] of [
21926
- ["the borrower's", resolve10(accountHome)],
22665
+ ["the borrower's", resolve11(accountHome)],
21927
22666
  ["the lender's", lender]
21928
22667
  ]) {
21929
22668
  const mode = modeOf(home);
@@ -21936,7 +22675,7 @@ function lentHomeProblems2(accountHome, lentBy, modeOf) {
21936
22675
  }
21937
22676
  var homeMode2 = (path) => {
21938
22677
  try {
21939
- return statSync6(path).mode & 4095;
22678
+ return statSync7(path).mode & 4095;
21940
22679
  } catch {
21941
22680
  return null;
21942
22681
  }
@@ -21981,7 +22720,7 @@ async function driveEngine2(o, flags, deps, until = {}) {
21981
22720
  runner: deps.runner.name,
21982
22721
  atOnce: flags.atOnce,
21983
22722
  interval: flags.interval,
21984
- cliVersion: "0.1.60"
22723
+ cliVersion: "0.1.62"
21985
22724
  };
21986
22725
  const beat = (checkedAt) => {
21987
22726
  const m = deps.machine;
@@ -22187,7 +22926,7 @@ function engineRunner2(o, flags) {
22187
22926
  });
22188
22927
  }
22189
22928
  function engineStartGuard(home, env, now) {
22190
- return startGuard({ home, version: env.SLEDS_CLI_VERSION, now });
22929
+ return startGuard({ home, version: cliVersion(env), now });
22191
22930
  }
22192
22931
  var engineCmd = async (o) => {
22193
22932
  const log = o.log ?? console.log;
@@ -22338,7 +23077,7 @@ var engineCmd = async (o) => {
22338
23077
  log(terminalOf2(o).c.dim(n));
22339
23078
  const watch = firstBeatWatchdog({
22340
23079
  home: o.home,
22341
- version: (o.env ?? process.env).SLEDS_CLI_VERSION,
23080
+ version: cliVersion(o.env),
22342
23081
  say: error,
22343
23082
  ...o.updates?.stateFiles ? { stateFiles: o.updates.stateFiles() } : {},
22344
23083
  ...o.watchdog
@@ -22419,7 +23158,7 @@ function engineUpdaterFor(o, flags, deps, client) {
22419
23158
  return engineUpdater({
22420
23159
  home: o.home,
22421
23160
  engine: statePath(engineStateDir2(o, flags.org, flags.seat), deps.engine),
22422
- version: env.SLEDS_CLI_VERSION,
23161
+ version: cliVersion(env),
22423
23162
  compiled: o.updates?.compiled ?? isCompiled(),
22424
23163
  tokens: () => {
22425
23164
  try {
@@ -22611,14 +23350,14 @@ init_config();
22611
23350
  import {
22612
23351
  existsSync as existsSync22,
22613
23352
  mkdirSync as mkdirSync22,
22614
- readFileSync as readFileSync21,
23353
+ readFileSync as readFileSync22,
22615
23354
  readdirSync as readdirSync6,
22616
23355
  realpathSync as realpathSync2,
22617
23356
  rmSync as rmSync15,
22618
23357
  writeFileSync as writeFileSync21
22619
23358
  } from "node:fs";
22620
23359
  import { hostname as hostname5 } from "node:os";
22621
- import { basename as basename7, dirname as dirname19, isAbsolute as isAbsolute6, join as join28 } from "node:path";
23360
+ import { basename as basename7, dirname as dirname20, isAbsolute as isAbsolute6, join as join29 } from "node:path";
22622
23361
  init_state();
22623
23362
  var ENGINE_LABEL2 = "ai.sleds.engine";
22624
23363
  var ENGINE_INSTALL_USAGE2 = `usage: sleds engine install [engine flags] | sleds engine uninstall [--name <n>] [--org <slug>]
@@ -22639,13 +23378,13 @@ function engineLabel2(name, org, seat) {
22639
23378
  return [ENGINE_LABEL2, org, seat?.trim(), name].filter((p) => Boolean(p)).map((p, i) => i === 0 ? p : engineSlug(p)).join(".");
22640
23379
  }
22641
23380
  function enginePlistPath2(home, name, org, seat) {
22642
- return join28(home, "Library", "LaunchAgents", `${engineLabel2(name, org, seat)}.plist`);
23381
+ return join29(home, "Library", "LaunchAgents", `${engineLabel2(name, org, seat)}.plist`);
22643
23382
  }
22644
23383
  function engineLogPath2(stateDir, name) {
22645
- return join28(stateDir, `${engineSlug(name)}.log`);
23384
+ return join29(stateDir, `${engineSlug(name)}.log`);
22646
23385
  }
22647
23386
  function engineEventsPath2(stateDir, name) {
22648
- return join28(stateDir, `${engineSlug(name)}.events.jsonl`);
23387
+ return join29(stateDir, `${engineSlug(name)}.events.jsonl`);
22649
23388
  }
22650
23389
  var xmlEscape5 = (s) => s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
22651
23390
  function buildEnginePlist2(o) {
@@ -22742,10 +23481,10 @@ function readEngines2(dir, suffix, manager, parse) {
22742
23481
  }
22743
23482
  const out = [];
22744
23483
  for (const name of names.filter((n) => n.startsWith(`${ENGINE_LABEL2}.`) && n.endsWith(suffix))) {
22745
- const path = join28(dir, name);
23484
+ const path = join29(dir, name);
22746
23485
  let parsed;
22747
23486
  try {
22748
- parsed = parse(readFileSync21(path, "utf8"), name);
23487
+ parsed = parse(readFileSync22(path, "utf8"), name);
22749
23488
  } catch {
22750
23489
  parsed = null;
22751
23490
  }
@@ -22762,7 +23501,7 @@ function readEngines2(dir, suffix, manager, parse) {
22762
23501
  }
22763
23502
  function installedEngines2(home) {
22764
23503
  return [
22765
- ...readEngines2(join28(home, "Library", "LaunchAgents"), ".plist", "launchd", parseEnginePlist2),
23504
+ ...readEngines2(join29(home, "Library", "LaunchAgents"), ".plist", "launchd", parseEnginePlist2),
22766
23505
  ...readEngines2(engineUnitDir(home), ".service", "systemd", (text, file) => {
22767
23506
  const parsed = parseEngineUnit(text);
22768
23507
  return parsed && { label: basename7(file, ".service"), ...parsed };
@@ -22800,7 +23539,7 @@ async function ensureReposDir(o) {
22800
23539
  return false;
22801
23540
  }
22802
23541
  const exists = o.exists ?? existsSync22;
22803
- const missing = dirs.filter((d) => !exists(/^~(\/|$)/.test(d) ? join28(o.home, d.slice(1)) : d));
23542
+ const missing = dirs.filter((d) => !exists(/^~(\/|$)/.test(d) ? join29(o.home, d.slice(1)) : d));
22804
23543
  if (missing.length) {
22805
23544
  error(`Not a folder on this machine: ${missing.join(", ")}. ${REPOS_DIR_NEEDED}`);
22806
23545
  return false;
@@ -22856,7 +23595,7 @@ function engineInstall2(o) {
22856
23595
  if (platform === "linux")
22857
23596
  return installUnit2({ o, parsed, label, sledsArgv, env: agentEnv, logPath, exec });
22858
23597
  const plist = enginePlistPath2(o.home, parsed.name, parsed.org, seat);
22859
- mkdirSync22(dirname19(plist), { recursive: true });
23598
+ mkdirSync22(dirname20(plist), { recursive: true });
22860
23599
  if (existsSync22(plist))
22861
23600
  exec("launchctl", ["unload", plist]);
22862
23601
  for (const other of installedEngines2(o.home)) {
@@ -22898,7 +23637,7 @@ function installUnit2(a) {
22898
23637
  const unit = engineUnitPath(o.home, label);
22899
23638
  const unitName = basename7(unit);
22900
23639
  const systemctl = (...args) => exec("systemctl", ["--user", ...args]);
22901
- mkdirSync22(dirname19(unit), { recursive: true });
23640
+ mkdirSync22(dirname20(unit), { recursive: true });
22902
23641
  if (existsSync22(unit))
22903
23642
  systemctl("stop", unitName);
22904
23643
  for (const other of installedEngines2(o.home)) {
@@ -23099,7 +23838,7 @@ import { closeSync as closeSync5, fstatSync as fstatSync2, openSync as openSync5
23099
23838
  import { hostname as hostname6 } from "node:os";
23100
23839
 
23101
23840
  // src/commands/engineStatus.ts
23102
- import { existsSync as existsSync23, readFileSync as readFileSync22 } from "node:fs";
23841
+ import { existsSync as existsSync23, readFileSync as readFileSync23 } from "node:fs";
23103
23842
  init_state();
23104
23843
 
23105
23844
  // src/engine/status.ts
@@ -23181,7 +23920,10 @@ function engineStatus(i) {
23181
23920
  ...opt("review", r.pr.review || undefined),
23182
23921
  mergeable: r.pr.clear === true,
23183
23922
  queued: r.pr.queued === true,
23184
- ...opt("conflict", conflictOf(r, i.state.decisions, i.watchOf))
23923
+ ...opt("conflict", conflictOf(r, i.state.decisions, i.watchOf)),
23924
+ runner: r.handle.runner,
23925
+ ...r.pr.live ? { watch: i.watchOf(r), watchTarget: watchTarget(r.handle) } : {},
23926
+ ...opt("worktree", r.handle.cwd !== r.repoPath ? r.handle.cwd : undefined)
23185
23927
  });
23186
23928
  continue;
23187
23929
  }
@@ -23255,10 +23997,14 @@ function statusLines(s, now) {
23255
23997
  }
23256
23998
  lines.push(`running ${s.running.length}`);
23257
23999
  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}`);
24000
+ 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
24001
  lines.push(`following ${s.following.length}`);
23260
24002
  for (const f of s.following) {
23261
24003
  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" : ""}`);
24004
+ if (f.watch)
24005
+ lines.push(` watch: ${f.watch}`);
24006
+ else if (f.worktree)
24007
+ lines.push(` worktree: ${f.worktree}`);
23262
24008
  const c = f.conflict;
23263
24009
  if (c)
23264
24010
  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 +24136,7 @@ var engineLogCmd = (o) => {
23390
24136
 
23391
24137
  // src/commands/engineProfile.ts
23392
24138
  init_src();
23393
- import { existsSync as existsSync24, readFileSync as readFileSync23 } from "node:fs";
24139
+ import { existsSync as existsSync24, readFileSync as readFileSync24 } from "node:fs";
23394
24140
  import { hostname as hostname7 } from "node:os";
23395
24141
  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
24142
  var PROFILE_FIELDS = [
@@ -23661,7 +24407,7 @@ async function engineProfileCmd(o) {
23661
24407
  const rest = words.slice(1);
23662
24408
  let stdin = null;
23663
24409
  if (fromStdin) {
23664
- const taken = cmuxPasswordFromJson((o.stdin ?? (() => readFileSync23(0, "utf8")))());
24410
+ const taken = cmuxPasswordFromJson((o.stdin ?? (() => readFileSync24(0, "utf8")))());
23665
24411
  if ("error" in taken || taken.password !== undefined && password !== undefined) {
23666
24412
  error("error" in taken ? taken.error : "the cmux password is given once");
23667
24413
  error(PROFILE_USAGE);
@@ -23746,7 +24492,7 @@ var keychainLine = (k) => k === "set" ? "cmuxPassword: set (Keychain)" : k === "
23746
24492
 
23747
24493
  // src/commands/engineStatus.ts
23748
24494
  init_state();
23749
- import { existsSync as existsSync25, readFileSync as readFileSync24 } from "node:fs";
24495
+ import { existsSync as existsSync25, readFileSync as readFileSync25 } from "node:fs";
23750
24496
  import { hostname as hostname8 } from "node:os";
23751
24497
  var ENGINE_STATUS_USAGE = "usage: sleds engine status [--name <n>] [--org <slug>] [--json]";
23752
24498
  function parseStatusArgs(argv, host) {
@@ -23802,7 +24548,7 @@ function readEngineStatus(stateDir, engine, host, pidAlive, seat) {
23802
24548
  }
23803
24549
  function safeRead(path) {
23804
24550
  try {
23805
- return readFileSync24(path, "utf8");
24551
+ return readFileSync25(path, "utf8");
23806
24552
  } catch {
23807
24553
  return "";
23808
24554
  }
@@ -23923,10 +24669,10 @@ init_meter();
23923
24669
 
23924
24670
  // src/owed.ts
23925
24671
  init_config();
23926
- import { existsSync as existsSync26, mkdirSync as mkdirSync23, readFileSync as readFileSync25, renameSync as renameSync11, writeFileSync as writeFileSync22 } from "node:fs";
23927
- import { dirname as dirname20, join as join29 } from "node:path";
23928
- var owedPath = (home) => join29(configDir(home), "owed.json");
23929
- var sessionPath = (home) => join29(configDir(home), "session.json");
24672
+ import { existsSync as existsSync26, mkdirSync as mkdirSync23, readFileSync as readFileSync26, renameSync as renameSync11, writeFileSync as writeFileSync22 } from "node:fs";
24673
+ import { dirname as dirname21, join as join30 } from "node:path";
24674
+ var owedPath = (home) => join30(configDir(home), "owed.json");
24675
+ var sessionPath = (home) => join30(configDir(home), "session.json");
23930
24676
  var OWED_TTL_MS = 3 * 24 * 60 * 60000;
23931
24677
  var str14 = (v) => typeof v === "string" ? v : undefined;
23932
24678
  function sourceKey(s) {
@@ -23944,13 +24690,13 @@ var readJson = (p) => {
23944
24690
  if (!existsSync26(p))
23945
24691
  return null;
23946
24692
  try {
23947
- return JSON.parse(readFileSync25(p, "utf8"));
24693
+ return JSON.parse(readFileSync26(p, "utf8"));
23948
24694
  } catch {
23949
24695
  return null;
23950
24696
  }
23951
24697
  };
23952
24698
  var writeJson = (p, v) => {
23953
- mkdirSync23(dirname20(p), { recursive: true, mode: 448 });
24699
+ mkdirSync23(dirname21(p), { recursive: true, mode: 448 });
23954
24700
  const tmp = `${p}.${process.pid}.tmp`;
23955
24701
  writeFileSync22(tmp, `${JSON.stringify(v, null, 2)}
23956
24702
  `, { mode: 384 });
@@ -24084,7 +24830,7 @@ function contextFor(deps) {
24084
24830
  ...src,
24085
24831
  fetchFn: deps.fetchFn,
24086
24832
  timeoutMs: deps.timeoutMs,
24087
- version: "0.1.60"
24833
+ version: "0.1.62"
24088
24834
  }), deps.home, now) : null;
24089
24835
  return {
24090
24836
  channel: "code",
@@ -24788,19 +25534,27 @@ var init = async (o) => {
24788
25534
  const wrote = [];
24789
25535
  const seatInfo = await fetchSeatInfo(url, resolved.token, o.fetchFn);
24790
25536
  const org = seatInfo.org;
24791
- const wiring = o.proxyAlways ? "proxy-always" : o.seatFirst ? "seat-first" : org?.mode ?? "proxy-always";
25537
+ const current = readSettings(o.home, env);
25538
+ const prior = readConfig(o.home);
25539
+ const proxyUrls = [url, prior.proxyUrl, DEFAULT_PROXY_URL];
25540
+ const ours = isSledsProxyEnv(current, proxyUrls);
25541
+ const gateway = hasGatewayEnv(current) && !ours;
25542
+ const kept = ours ? "proxy-always" : "seat-first";
25543
+ if (!org && !o.proxyAlways && !o.seatFirst)
25544
+ 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.`);
25545
+ const wiring = o.proxyAlways ? "proxy-always" : o.seatFirst ? "seat-first" : org?.mode ?? kept;
24792
25546
  if (wiring === "proxy-always" && org && !org.capabilities.upstreamKey) {
24793
25547
  error(`${org.name} has no platform key for the proxy to forward with; --proxy-always would break every request. Using seat-first.`);
24794
25548
  }
24795
25549
  const finalWiring = wiring === "proxy-always" && org && !org.capabilities.upstreamKey ? "seat-first" : wiring;
24796
25550
  if (backupSettings(o.home, env))
24797
25551
  wrote.push(`backed up settings to ${backupPath(o.home, env)}`);
24798
- const current = readSettings(o.home, env);
24799
- const wired = finalWiring === "proxy-always" ? applyProxy(current, { url, token: resolved.token }) : removeProxy(current);
25552
+ const wired = finalWiring === "proxy-always" ? applyProxy(current, { url, token: resolved.token }) : ours ? switchOff(current, envToRestore([prior.priorEnv, prior.priorGatewayEnv], proxyUrls)) : current;
25553
+ const priorGatewayEnv = finalWiring !== "proxy-always" ? undefined : gateway ? capturePriorEnv(current) : prior.priorGatewayEnv;
24800
25554
  const slug = org?.slug ?? resolved.approved?.org.slug;
24801
25555
  const statusLine = applyStatusLine(wired, pinOrg(o.statusLineCommand ?? sledsCommand("statusline", o.home, o.env), slug));
24802
25556
  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`);
25557
+ 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
25558
  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
25559
  hooks2({
24806
25560
  home: o.home,
@@ -24819,12 +25573,12 @@ var init = async (o) => {
24819
25573
  writeTokenFile(o.home, resolved.token);
24820
25574
  wrote.push(`saved token to ${tokenPath(o.home)} (0600)`);
24821
25575
  }
24822
- const prior = readConfig(o.home);
24823
25576
  const base = {
24824
25577
  ...prior,
24825
25578
  proxyUrl: url,
24826
25579
  agentKind: "claude-code",
24827
- ...statusLine.prior !== undefined ? { priorStatusLine: statusLine.prior } : {}
25580
+ ...statusLine.prior !== undefined ? { priorStatusLine: statusLine.prior } : {},
25581
+ priorGatewayEnv
24828
25582
  };
24829
25583
  const email = seatInfo.email ?? resolved.approved?.email;
24830
25584
  const asked = orgChoice(prior, o.org, env);
@@ -24978,8 +25732,9 @@ var login = async (o) => {
24978
25732
  // src/commands/logout.ts
24979
25733
  init_config();
24980
25734
  init_state();
24981
- import { existsSync as existsSync27, readFileSync as readFileSync26, rmSync as rmSync16 } from "node:fs";
25735
+ import { existsSync as existsSync27, readFileSync as readFileSync27, rmSync as rmSync16 } from "node:fs";
24982
25736
  import { hostname as hostname10 } from "node:os";
25737
+ import { basename as basename8 } from "node:path";
24983
25738
  init_settings();
24984
25739
  var LOGOUT_USAGE = "usage: sleds logout [--org <slug>] [--json]";
24985
25740
  function parseLogoutArgs(argv) {
@@ -25101,8 +25856,15 @@ async function logout(o) {
25101
25856
  if (!name)
25102
25857
  continue;
25103
25858
  if (!e.org) {
25104
- (o.exec ?? defaultExec2)("launchctl", ["unload", e.plist]);
25105
- rmSync16(e.plist, { force: true });
25859
+ const exec = o.exec ?? defaultExec2;
25860
+ if (e.manager === "systemd") {
25861
+ exec("systemctl", ["--user", "disable", "--now", basename8(e.plist)]);
25862
+ rmSync16(e.plist, { force: true });
25863
+ exec("systemctl", ["--user", "daemon-reload"]);
25864
+ } else {
25865
+ exec("launchctl", ["unload", e.plist]);
25866
+ rmSync16(e.plist, { force: true });
25867
+ }
25106
25868
  continue;
25107
25869
  }
25108
25870
  engineUninstall({
@@ -25135,7 +25897,7 @@ async function logout(o) {
25135
25897
  };
25136
25898
  writeConfig(o.home, next);
25137
25899
  const tp = tokenPath(o.home, env);
25138
- if (existsSync27(tp) && readFileSync26(tp, "utf8").trim() === seat.token)
25900
+ if (existsSync27(tp) && readFileSync27(tp, "utf8").trim() === seat.token)
25139
25901
  rmSync16(tp, { force: true });
25140
25902
  const active = next.activeOrg ?? next.org?.slug ?? null;
25141
25903
  const result = { ok: true, email, org: slug, paused, active };
@@ -25213,9 +25975,9 @@ var meter = async (o) => {
25213
25975
  init_config();
25214
25976
  init_meter();
25215
25977
  import { spawnSync as spawnSync7 } from "node:child_process";
25216
- import { existsSync as existsSync28, mkdirSync as mkdirSync25, readFileSync as readFileSync27, rmSync as rmSync17, writeFileSync as writeFileSync24 } from "node:fs";
25978
+ import { existsSync as existsSync28, mkdirSync as mkdirSync25, readFileSync as readFileSync28, rmSync as rmSync17, writeFileSync as writeFileSync24 } from "node:fs";
25217
25979
  import { hostname as hostname11 } from "node:os";
25218
- import { dirname as dirname21 } from "node:path";
25980
+ import { dirname as dirname22 } from "node:path";
25219
25981
  var defaultWhich3 = (cmd) => {
25220
25982
  const path = process.env.PATH ?? "";
25221
25983
  for (const dir of path.split(":").filter(Boolean)) {
@@ -25253,7 +26015,7 @@ var musherInstall = (o) => {
25253
26015
  const plist = musherPlistPath(o.home, env);
25254
26016
  const label = musherLabel(env);
25255
26017
  const dir = configDir(o.home, env);
25256
- mkdirSync25(dirname21(plist), { recursive: true });
26018
+ mkdirSync25(dirname22(plist), { recursive: true });
25257
26019
  mkdirSync25(dir, { recursive: true, mode: 448 });
25258
26020
  const execPath = o.sledsArgv[0] ?? process.execPath;
25259
26021
  const agentEnv = {
@@ -25373,16 +26135,22 @@ var musherTick = async (o) => {
25373
26135
  };
25374
26136
  var musherConfigure = (o, flags) => {
25375
26137
  const log = o.log ?? console.log;
26138
+ const error = o.error ?? console.error;
25376
26139
  const cfg = readConfig(o.home);
25377
26140
  const musher = { ...cfg.musher ?? {} };
25378
26141
  if (flags.claude)
25379
26142
  musher.claudePath = flags.claude;
25380
- if (flags.mode)
26143
+ if (flags.mode) {
26144
+ if (!PERMISSION_MODES.includes(flags.mode)) {
26145
+ error(`--mode takes one of ${PERMISSION_MODES.join(", ")} (got ${flags.mode})`);
26146
+ return 1;
26147
+ }
25381
26148
  musher.permissionMode = flags.mode;
26149
+ }
25382
26150
  if (flags.timeout) {
25383
- const n = Number.parseInt(flags.timeout, 10);
25384
- if (!Number.isFinite(n) || n <= 0) {
25385
- (o.error ?? console.error)("--timeout takes minutes, a positive number");
26151
+ const n = /^\d+$/.test(flags.timeout) ? Number(flags.timeout) : Number.NaN;
26152
+ if (!Number.isSafeInteger(n) || n <= 0) {
26153
+ error("--timeout takes minutes, a positive whole number");
25386
26154
  return 1;
25387
26155
  }
25388
26156
  musher.timeoutMinutes = n;
@@ -25397,22 +26165,38 @@ var defaultExecSync = (cmd, args) => {
25397
26165
  return { ok: false, stderr: r.error.message };
25398
26166
  return { ok: r.status === 0, stderr: r.stderr ?? "" };
25399
26167
  };
25400
- var musher3 = async (o) => {
25401
- const args = [...o.argv];
26168
+ var MUSHER_SWITCHES2 = new Set(["install", "uninstall", "status"]);
26169
+ var MUSHER_SETTINGS2 = new Set(["claude", "mode", "timeout"]);
26170
+ var parseMusherArgs = (argv) => {
25402
26171
  const flags = {};
25403
26172
  const positional = [];
25404
- for (let i = 0;i < args.length; i++) {
25405
- const a = args[i] ?? "";
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
26173
+ for (let i = 0;i < argv.length; i++) {
26174
+ const a = argv[i] ?? "";
26175
+ if (!a.startsWith("--")) {
25414
26176
  positional.push(a);
26177
+ continue;
26178
+ }
26179
+ const eq = a.indexOf("=");
26180
+ const name = a.slice(2, eq < 0 ? undefined : eq);
26181
+ if (MUSHER_SWITCHES2.has(name) && eq < 0)
26182
+ flags[name] = "true";
26183
+ else if (MUSHER_SETTINGS2.has(name)) {
26184
+ const v = eq < 0 ? argv[++i] : a.slice(eq + 1);
26185
+ if (!v?.trim() || v.startsWith("--"))
26186
+ return { error: `--${name} needs a value` };
26187
+ flags[name] = v.trim();
26188
+ } else
26189
+ return { error: `unknown argument ${a}` };
26190
+ }
26191
+ return { flags, positional };
26192
+ };
26193
+ var musher3 = async (o) => {
26194
+ const parsed = parseMusherArgs(o.argv);
26195
+ if ("error" in parsed) {
26196
+ (o.error ?? console.error)(`sleds musher: ${parsed.error}`);
26197
+ return 1;
25415
26198
  }
26199
+ const { flags, positional } = parsed;
25416
26200
  if (flags.install)
25417
26201
  return musherInstall(o);
25418
26202
  if (flags.uninstall)
@@ -25430,13 +26214,24 @@ var musher3 = async (o) => {
25430
26214
  // src/commands/off.ts
25431
26215
  init_config();
25432
26216
  init_settings();
26217
+
26218
+ // src/commands/init.ts
26219
+ init_config();
26220
+ init_state();
26221
+ init_settings();
26222
+ var DEFAULT_PROXY_URL2 = "https://proxy.sleds.ai";
26223
+ var lineReaders2 = new WeakMap;
26224
+
26225
+ // src/commands/off.ts
25433
26226
  var off = (o) => {
25434
26227
  const log = o.log ?? console.log;
25435
26228
  const cfg = readConfig(o.home);
25436
26229
  const settings = readSettings(o.home);
25437
- const wasOn = cfg.mode === "on" || isPointedAtProxy(settings);
25438
- if (wasOn)
25439
- writeSettings(o.home, switchOff(settings, cfg.priorEnv ?? {}));
26230
+ const proxyUrls = [cfg.proxyUrl, DEFAULT_PROXY_URL2];
26231
+ const pointed = isSledsProxyEnv(settings, proxyUrls);
26232
+ const wasOn = cfg.mode === "on" || pointed;
26233
+ if (pointed)
26234
+ writeSettings(o.home, switchOff(settings, cfg.priorEnv ?? cfg.priorGatewayEnv ?? {}));
25440
26235
  if (o.scheduler)
25441
26236
  cancelRevert(o.scheduler, cfg.schedule);
25442
26237
  const next = {
@@ -25446,6 +26241,7 @@ var off = (o) => {
25446
26241
  untilReset: undefined,
25447
26242
  reason: undefined,
25448
26243
  priorEnv: undefined,
26244
+ priorGatewayEnv: pointed && !cfg.priorEnv ? undefined : cfg.priorGatewayEnv,
25449
26245
  schedule: undefined
25450
26246
  };
25451
26247
  writeConfig(o.home, next);
@@ -25460,15 +26256,6 @@ var off = (o) => {
25460
26256
  // src/commands/on.ts
25461
26257
  init_config();
25462
26258
  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
26259
  var on = (o) => {
25473
26260
  const log = o.log ?? console.log;
25474
26261
  const error = o.error ?? console.error;
@@ -25618,7 +26405,7 @@ var orgUse = (o) => {
25618
26405
  writeTokenFile(o.home, seat.token);
25619
26406
  if (existsSync29(settingsPath(o.home)) && cfg.mode !== "on") {
25620
26407
  const settings = readSettings(o.home);
25621
- const pointed = readProxy(settings);
26408
+ const pointed = isSledsProxyEnv(settings, [cfg.proxyUrl]) ? readProxy(settings) : null;
25622
26409
  const url = pointed?.url ?? cfg.proxyUrl;
25623
26410
  if (seat.wiring === "proxy-always" && url)
25624
26411
  writeSettings(o.home, applyProxy(settings, { url, token: seat.token }));
@@ -25653,7 +26440,7 @@ var orgCmd = (o) => {
25653
26440
  init_src();
25654
26441
  import { spawnSync as spawnSync8 } from "node:child_process";
25655
26442
  import { writeFileSync as writeFileSync25 } from "node:fs";
25656
- import { join as join30 } from "node:path";
26443
+ import { join as join31 } from "node:path";
25657
26444
  function packFileName(key) {
25658
26445
  return `pack-${slugify(key, 80) || "item"}.md`;
25659
26446
  }
@@ -25681,7 +26468,7 @@ var packCmd = async (o) => {
25681
26468
  log(r.text);
25682
26469
  return 0;
25683
26470
  }
25684
- const path = join30(o.cwd ?? process.cwd(), packFileName(key));
26471
+ const path = join31(o.cwd ?? process.cwd(), packFileName(key));
25685
26472
  (o.write ?? ((p, t) => writeFileSync25(p, t.endsWith(`
25686
26473
  `) ? t : `${t}
25687
26474
  `)))(path, r.text);
@@ -25690,8 +26477,8 @@ var packCmd = async (o) => {
25690
26477
  };
25691
26478
 
25692
26479
  // src/commands/permissionEvent.ts
25693
- import { chmodSync as chmodSync7, mkdirSync as mkdirSync26, readFileSync as readFileSync28, renameSync as renameSync12, rmSync as rmSync19, writeFileSync as writeFileSync26 } from "node:fs";
25694
- import { dirname as dirname22 } from "node:path";
26480
+ import { chmodSync as chmodSync7, mkdirSync as mkdirSync26, readFileSync as readFileSync29, renameSync as renameSync12, rmSync as rmSync19, writeFileSync as writeFileSync26 } from "node:fs";
26481
+ import { dirname as dirname23 } from "node:path";
25695
26482
  var isObject13 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
25696
26483
  var messageOf = (text) => {
25697
26484
  try {
@@ -25715,7 +26502,7 @@ var permissionEvent = async (o) => {
25715
26502
  if (!isToolPermission2(message))
25716
26503
  return 0;
25717
26504
  const mark = { message, at: (o.now ?? Date.now)() };
25718
- mkdirSync26(dirname22(o.mark), { recursive: true, mode: 448 });
26505
+ mkdirSync26(dirname23(o.mark), { recursive: true, mode: 448 });
25719
26506
  const tmp = `${o.mark}.${process.pid}.tmp`;
25720
26507
  writeFileSync26(tmp, `${JSON.stringify(mark)}
25721
26508
  `, { mode: 384 });
@@ -25762,6 +26549,7 @@ var exitsLine = (hasUpstreamKey) => hasUpstreamKey ? "`/sled handoff <what you w
25762
26549
  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
26550
  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
26551
  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.`;
26552
+ 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
26553
  var promptEvent = async (o) => {
25766
26554
  const log = o.log ?? console.log;
25767
26555
  const error = o.error ?? console.error;
@@ -25784,27 +26572,35 @@ var promptEvent = async (o) => {
25784
26572
  const mark = (bar, stage, at) => {
25785
26573
  told[bar] = { ...told[bar] ?? {}, [stage]: at };
25786
26574
  };
25787
- let code = 0;
26575
+ for (const e of pending)
26576
+ mark(e.bar, e.stage, e.resetsAt);
26577
+ let saved = true;
26578
+ try {
26579
+ writeSeatState(o.home, { ...state, told }, key);
26580
+ } catch {
26581
+ saved = false;
26582
+ }
26583
+ let block = false;
25788
26584
  const lines = [];
25789
26585
  for (const e of pending) {
25790
- mark(e.bar, e.stage, e.resetsAt);
25791
26586
  if (e.bar === "credits") {
25792
- if (code !== 2) {
25793
- error(creditsBlockText(hasKey));
25794
- code = 2;
25795
- }
26587
+ if (saved)
26588
+ block = true;
26589
+ else
26590
+ lines.push(creditsContext(hasKey));
25796
26591
  continue;
25797
26592
  }
25798
26593
  const pct = state.window?.[e.bar]?.usedPct;
25799
26594
  lines.push(e.stage === "approaching" ? approachingContext(e.bar, pct, e.resetsAt, hasKey, now) : hitContext(e.bar, e.resetsAt, hasKey, now));
25800
26595
  }
25801
- try {
25802
- writeSeatState(o.home, { ...state, told }, key);
25803
- } catch {}
25804
- if (code === 0 && lines.length > 0)
26596
+ if (block) {
26597
+ error(creditsBlockText(hasKey));
26598
+ return 2;
26599
+ }
26600
+ if (lines.length > 0)
25805
26601
  log(lines.join(`
25806
26602
  `));
25807
- return code;
26603
+ return 0;
25808
26604
  };
25809
26605
 
25810
26606
  // src/commands/reopen.ts
@@ -25878,13 +26674,14 @@ async function reopenCmd(o) {
25878
26674
 
25879
26675
  // src/commands/route.ts
25880
26676
  init_src();
25881
- import { readFileSync as readFileSync29 } from "node:fs";
26677
+ import { readFileSync as readFileSync30 } from "node:fs";
25882
26678
  import { hostname as hostname12 } from "node:os";
25883
26679
  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]";
26680
+ 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
26681
  var ANSWER_USAGE = "usage: sleds route answer <key>/<node> <answer…> | sleds route answer <key>/<node> --stdin [--json]";
26682
+ var INSTRUCT_USAGE = "usage: sleds route instruct <key>/<node> <instruction…> | sleds route instruct <key>/<node> --stdin [--voice] [--json]";
25886
26683
  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]";
26684
+ var APPROVE_USAGE = "usage: sleds route approve <key> [--file <nodes.json>] [--voice --stdin] [--json]";
25888
26685
  var REDRAFT_USAGE = "usage: sleds route redraft <key> [--json]";
25889
26686
  var SINGLE_USAGE = "usage: sleds route single <key> [--json]";
25890
26687
  var ELSEWHERE_USAGE = "usage: sleds route elsewhere <key> [--note <text>] [--json]";
@@ -25933,6 +26730,10 @@ function parseVerbArgs(argv, allowed) {
25933
26730
  out[flag] = true;
25934
26731
  continue;
25935
26732
  }
26733
+ if (flag === "voice") {
26734
+ out.voice = true;
26735
+ continue;
26736
+ }
25936
26737
  const value = argv[i + 1];
25937
26738
  if (value === undefined || value.startsWith("--"))
25938
26739
  return { error: `${a} needs a value` };
@@ -26056,7 +26857,7 @@ async function answerCmd(o, argv) {
26056
26857
  const words = argv.filter((a) => a !== "--json" && a !== "--stdin");
26057
26858
  const ref = words[0] ?? "";
26058
26859
  const slash = ref.lastIndexOf("/");
26059
- const answer = (fromStdin ? (o.stdin ?? (() => readFileSync29(0, "utf8")))() : words.slice(1).join(" ")).trim();
26860
+ const answer = (fromStdin ? (o.stdin ?? (() => readFileSync30(0, "utf8")))() : words.slice(1).join(" ")).trim();
26060
26861
  if (slash <= 0 || slash === ref.length - 1 || fromStdin && words.length > 1) {
26061
26862
  error(ANSWER_USAGE);
26062
26863
  return 2;
@@ -26088,6 +26889,59 @@ async function answerCmd(o, argv) {
26088
26889
  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
26890
  return 0;
26090
26891
  }
26892
+ async function instructCmd(o, argv) {
26893
+ const log = o.log ?? console.log;
26894
+ const error = o.error ?? console.error;
26895
+ const json = argv.includes("--json");
26896
+ const fromStdin = argv.includes("--stdin");
26897
+ const voice = argv.includes("--voice");
26898
+ const words = argv.filter((a) => a !== "--json" && a !== "--stdin" && a !== "--voice");
26899
+ const ref = words[0] ?? "";
26900
+ const slash = ref.lastIndexOf("/");
26901
+ if (slash <= 0 || slash === ref.length - 1 || fromStdin && words.length > 1 || voice && !fromStdin || words.some((w) => w.startsWith("--"))) {
26902
+ error(INSTRUCT_USAGE);
26903
+ return 2;
26904
+ }
26905
+ const text = fromStdin ? (o.stdin ?? (() => readFileSync30(0, "utf8")))() : words.slice(1).join(" ");
26906
+ const { md, heard } = voice ? splitVoiceStdin(text) : { md: text.trim(), heard: undefined };
26907
+ if (!md) {
26908
+ error("an instruction needs some text");
26909
+ return 2;
26910
+ }
26911
+ const client = engineClient(o);
26912
+ if ("error" in client) {
26913
+ error(client.error);
26914
+ return 1;
26915
+ }
26916
+ const r = await client.postFull("/proxy/route/instruct", {
26917
+ project: ref,
26918
+ md,
26919
+ ...heard !== undefined ? { voice: { heard } } : {}
26920
+ });
26921
+ if (!r) {
26922
+ error(`${ref}: SLEDS unreachable — nothing sent`);
26923
+ return 1;
26924
+ }
26925
+ const ok = r.status < 300 && r.json?.ok !== false;
26926
+ if (json)
26927
+ log(JSON.stringify(r.json ?? { ok: false, error: `HTTP ${r.status}` }));
26928
+ if (!ok) {
26929
+ const why = typeof r.json?.error === "string" ? r.json.error : `HTTP ${r.status}`;
26930
+ if (!json)
26931
+ error(`${ref}: ${why}`);
26932
+ return 1;
26933
+ }
26934
+ if (!json)
26935
+ log(`${ref} instructed — the engine following its PR hands it to the session`);
26936
+ return 0;
26937
+ }
26938
+ function splitVoiceStdin(text) {
26939
+ const nl = text.indexOf(`
26940
+ `);
26941
+ const md = (nl < 0 ? text : text.slice(0, nl)).trim();
26942
+ const rest = nl < 0 ? "" : text.slice(nl + 1).trim();
26943
+ return { md, heard: rest || md };
26944
+ }
26091
26945
  async function proposeCmd(o, argv) {
26092
26946
  const log = o.log ?? console.log;
26093
26947
  const error = o.error ?? console.error;
@@ -26135,12 +26989,13 @@ async function proposeCmd(o, argv) {
26135
26989
  async function approveCmd(o, argv) {
26136
26990
  const log = o.log ?? console.log;
26137
26991
  const error = o.error ?? console.error;
26138
- const a = parseVerbArgs(argv, ["file", "json"]);
26139
- if ("error" in a) {
26140
- error(a.error);
26992
+ const a = parseVerbArgs(argv, ["file", "json", "voice", "stdin"]);
26993
+ if ("error" in a || Boolean(a.voice) !== a.stdin) {
26994
+ error("error" in a ? a.error : "--voice and --stdin go together: stdin is what was heard");
26141
26995
  error(APPROVE_USAGE);
26142
26996
  return 2;
26143
26997
  }
26998
+ const heard = a.voice ? (o.stdin ?? (() => readFileSync30(0, "utf8")))().trim() : undefined;
26144
26999
  let nodes;
26145
27000
  let approach;
26146
27001
  if (a.file) {
@@ -26160,7 +27015,8 @@ async function approveCmd(o, argv) {
26160
27015
  const r = await client.postFull("/proxy/route/approve", {
26161
27016
  project: a.key,
26162
27017
  ...nodes ? { nodes } : {},
26163
- ...approach ? { approach } : {}
27018
+ ...approach ? { approach } : {},
27019
+ ...heard !== undefined ? { voice: { heard } } : {}
26164
27020
  });
26165
27021
  if (!r) {
26166
27022
  error(`${a.key}: SLEDS unreachable — nothing approved`);
@@ -26262,7 +27118,7 @@ async function decisionCmd(o, argv) {
26262
27118
  error(DECISION_USAGE);
26263
27119
  return 2;
26264
27120
  }
26265
- const md = seen || routeAlone ? "" : (fromStdin ? (o.stdin ?? (() => readFileSync29(0, "utf8")))() : rest.join(" ")).trim();
27121
+ const md = seen || routeAlone ? "" : (fromStdin ? (o.stdin ?? (() => readFileSync30(0, "utf8")))() : rest.join(" ")).trim();
26266
27122
  if (!seen && !routeAlone && (!md || fromStdin && rest.length > 0)) {
26267
27123
  error(md ? DECISION_USAGE : `${answer ? "an answer" : "an overrule"} needs some text`);
26268
27124
  return 2;
@@ -26441,7 +27297,7 @@ function approvedLine(key, j) {
26441
27297
  function readNodes(o, a) {
26442
27298
  let text;
26443
27299
  try {
26444
- text = a.file ? (o.readFile ?? ((p) => readFileSync29(p, "utf8")))(a.file) : (o.stdin ?? (() => readFileSync29(0, "utf8")))();
27300
+ text = a.file ? (o.readFile ?? ((p) => readFileSync30(p, "utf8")))(a.file) : (o.stdin ?? (() => readFileSync30(0, "utf8")))();
26445
27301
  } catch (e) {
26446
27302
  return { error: e instanceof Error ? e.message : String(e) };
26447
27303
  }
@@ -26470,6 +27326,8 @@ var routeCmd = async (o) => {
26470
27326
  return armCmd(o, o.argv.slice(1));
26471
27327
  if (o.argv[0] === "answer")
26472
27328
  return answerCmd(o, o.argv.slice(1));
27329
+ if (o.argv[0] === "instruct")
27330
+ return instructCmd(o, o.argv.slice(1));
26473
27331
  if (o.argv[0] === "propose")
26474
27332
  return proposeCmd(o, o.argv.slice(1));
26475
27333
  if (o.argv[0] === "approve")
@@ -26796,8 +27654,11 @@ var seatEvent = async (o) => {
26796
27654
  if (week && week.resetsAt > now)
26797
27655
  writeSeatState(o.home, { ...state, lastWallResetsAt: week.resetsAt }, key);
26798
27656
  } catch {}
26799
- const line = seatWallLine(readConfig(o.home).org?.capabilities.upstreamKey !== false);
26800
- log(line);
27657
+ let hasKey = true;
27658
+ try {
27659
+ hasKey = readConfig(o.home).org?.capabilities.upstreamKey !== false;
27660
+ } catch {}
27661
+ log(seatWallLine(hasKey));
26801
27662
  return 0;
26802
27663
  };
26803
27664
 
@@ -26873,14 +27734,14 @@ async function seatRevokeCmd(o) {
26873
27734
 
26874
27735
  // src/commands/seatSync.ts
26875
27736
  import { execFileSync as execFileSync5 } from "node:child_process";
26876
- import { existsSync as existsSync30, mkdirSync as mkdirSync27, readFileSync as readFileSync30, readdirSync as readdirSync7, rmSync as rmSync21, writeFileSync as writeFileSync27 } from "node:fs";
27737
+ import { existsSync as existsSync30, mkdirSync as mkdirSync27, readFileSync as readFileSync31, readdirSync as readdirSync7, rmSync as rmSync21, writeFileSync as writeFileSync27 } from "node:fs";
26877
27738
  import { platform as platform2 } from "node:os";
26878
- import { dirname as dirname23, join as join32 } from "node:path";
27739
+ import { dirname as dirname24, join as join33 } from "node:path";
26879
27740
 
26880
27741
  // src/cookieDb.ts
26881
27742
  import { copyFileSync as copyFileSync3, rmSync as rmSync20 } from "node:fs";
26882
- import { tmpdir as tmpdir2 } from "node:os";
26883
- import { join as join31 } from "node:path";
27743
+ import { tmpdir as tmpdir4 } from "node:os";
27744
+ import { join as join32 } from "node:path";
26884
27745
  var QUERY = "SELECT name, hex(encrypted_value) AS ev FROM cookies WHERE host_key LIKE '%claude.ai%'";
26885
27746
  var isBun = () => typeof globalThis.Bun !== "undefined";
26886
27747
  async function queryRows(dbPath) {
@@ -26909,7 +27770,7 @@ function nodeSqlite() {
26909
27770
  return mod;
26910
27771
  }
26911
27772
  async function readCookieRows(dbPath) {
26912
- const copy = join31(tmpdir2(), `sleds-cookies-${process.pid}-${Date.now()}.sqlite`);
27773
+ const copy = join32(tmpdir4(), `sleds-cookies-${process.pid}-${Date.now()}.sqlite`);
26913
27774
  copyFileSync3(dbPath, copy);
26914
27775
  try {
26915
27776
  const rows = await queryRows(copy);
@@ -27202,7 +28063,7 @@ function findFile(root, name) {
27202
28063
  continue;
27203
28064
  }
27204
28065
  for (const e of entries) {
27205
- const p = join32(dir, e.name);
28066
+ const p = join33(dir, e.name);
27206
28067
  if (e.isDirectory())
27207
28068
  stack.push(p);
27208
28069
  else if (e.name === name)
@@ -27213,7 +28074,7 @@ function findFile(root, name) {
27213
28074
  }
27214
28075
  function findCookieDb(home, source) {
27215
28076
  const b = BROWSERS[source.browser];
27216
- const root = join32(home, APP_SUPPORT, b.appDir);
28077
+ const root = join33(home, APP_SUPPORT, b.appDir);
27217
28078
  if (!existsSync30(root))
27218
28079
  throw new Error(`no ${source.browser} data at ${root} (is it installed?)`);
27219
28080
  if (!b.profiled) {
@@ -27222,15 +28083,15 @@ function findCookieDb(home, source) {
27222
28083
  throw new Error(`no cookie store under ${root}`);
27223
28084
  return db;
27224
28085
  }
27225
- const localStatePath = join32(root, "Local State");
27226
- const localState = existsSync30(localStatePath) ? readFileSync30(localStatePath, "utf8") : null;
28086
+ const localStatePath = join33(root, "Local State");
28087
+ const localState = existsSync30(localStatePath) ? readFileSync31(localStatePath, "utf8") : null;
27227
28088
  const wanted = source.profile ?? "Default";
27228
28089
  const { dir, ambiguous } = resolveProfileDir(localState, wanted);
27229
28090
  if (ambiguous)
27230
28091
  throw new Error(`"${wanted}" names ${ambiguous.length} ${source.browser} profiles (${ambiguous.join(", ")}) — pass the directory or the account email instead`);
27231
28092
  if (!dir)
27232
28093
  throw new Error(`no ${source.browser} profile named "${wanted}" (check chrome://version)`);
27233
- const db = cookieDbCandidates(join32(root, dir)).find((p) => existsSync30(p));
28094
+ const db = cookieDbCandidates(join33(root, dir)).find((p) => existsSync30(p));
27234
28095
  if (!db)
27235
28096
  throw new Error(`profile "${dir}" has no cookie store yet (open claude.ai in it once)`);
27236
28097
  return db;
@@ -27274,7 +28135,7 @@ function fetchSeatUsage(jar, org, now, run) {
27274
28135
  }
27275
28136
  function launchAgentPlist(argv, home, source) {
27276
28137
  const label = launchLabel(source);
27277
- const log = join32(home, ".config", "sleds", `${label.slice("ai.sleds.".length)}.log`);
28138
+ const log = join33(home, ".config", "sleds", `${label.slice("ai.sleds.".length)}.log`);
27278
28139
  const esc = (s) => s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
27279
28140
  const args = [...argv, "seat-sync", ...sourceArgs(source)].map((a) => ` <string>${esc(a)}</string>`).join(`
27280
28141
  `);
@@ -27296,20 +28157,20 @@ ${args}
27296
28157
  `;
27297
28158
  }
27298
28159
  function plistPath2(home, source) {
27299
- return join32(home, "Library", "LaunchAgents", `${launchLabel(source)}.plist`);
28160
+ return join33(home, "Library", "LaunchAgents", `${launchLabel(source)}.plist`);
27300
28161
  }
27301
28162
  function install(home, argv, source, log, launchctl) {
27302
28163
  if (!argv[0]) {
27303
28164
  log("cannot resolve the sleds command to install the timer");
27304
28165
  return 1;
27305
28166
  }
27306
- const dir = join32(home, "Library", "LaunchAgents");
28167
+ const dir = join33(home, "Library", "LaunchAgents");
27307
28168
  mkdirSync27(dir, { recursive: true });
27308
28169
  const path = plistPath2(home, source);
27309
28170
  const tokenPath = seatTokenPath(home, source);
27310
28171
  rmSync21(tokenPath, { force: true });
27311
28172
  if (source.token) {
27312
- mkdirSync27(dirname23(tokenPath), { recursive: true, mode: 448 });
28173
+ mkdirSync27(dirname24(tokenPath), { recursive: true, mode: 448 });
27313
28174
  writeFileSync27(tokenPath, `${source.token}
27314
28175
  `, { mode: 384 });
27315
28176
  }
@@ -27390,10 +28251,12 @@ var seatSync = async (o) => {
27390
28251
  log(`seat-sync: usage read failed — ${why}`);
27391
28252
  return 2;
27392
28253
  }
27393
- try {
27394
- const key = seatKeyFor(o.home, process.env);
27395
- writeSeatState(o.home, { ...readSeatState(o.home, key), window: usageAsWindow(usage) }, key);
27396
- } catch {}
28254
+ if (token === saved?.token) {
28255
+ try {
28256
+ const key = seatKeyFor(o.home, process.env);
28257
+ writeSeatState(o.home, { ...readSeatState(o.home, key), window: usageAsWindow(usage) }, key);
28258
+ } catch {}
28259
+ }
27397
28260
  const posted = await postSeat(proxyUrl, token, seatSyncBody(usage), o.fetchFn ?? fetch);
27398
28261
  const wk = usage.sevenDay ? `wk ${Math.round(usage.sevenDay.usedPct)}%` : "wk —";
27399
28262
  const models = Object.keys(usage.models).length;
@@ -27403,7 +28266,7 @@ var seatSync = async (o) => {
27403
28266
  };
27404
28267
  function readSeatToken(home, source) {
27405
28268
  try {
27406
- return readFileSync30(seatTokenPath(home, source), "utf8").trim() || undefined;
28269
+ return readFileSync31(seatTokenPath(home, source), "utf8").trim() || undefined;
27407
28270
  } catch {
27408
28271
  return;
27409
28272
  }
@@ -27486,8 +28349,8 @@ var sessionEvent = async (o) => {
27486
28349
 
27487
28350
  // src/commands/specEvent.ts
27488
28351
  init_meter();
27489
- import { readFileSync as readFileSync31, statSync as statSync7 } from "node:fs";
27490
- import { basename as basename8, isAbsolute as isAbsolute7, relative as relative2 } from "node:path";
28352
+ import { readFileSync as readFileSync32, statSync as statSync8 } from "node:fs";
28353
+ import { basename as basename9, isAbsolute as isAbsolute7, relative as relative2 } from "node:path";
27491
28354
  var HOOK_STDIN_TIMEOUT_MS3 = 1000;
27492
28355
  var ESTIMATE_TIMEOUT_MS = 4000;
27493
28356
  var MIN_SPEC_CHARS = 200;
@@ -27500,12 +28363,12 @@ function isSpecPath(path) {
27500
28363
  return false;
27501
28364
  if (NOT_SPEC.test(path))
27502
28365
  return false;
27503
- return SPEC_DIRS.test(path) || SPEC_NAMES.test(basename8(path));
28366
+ return SPEC_DIRS.test(path) || SPEC_NAMES.test(basename9(path));
27504
28367
  }
27505
28368
  function specTitle(body, path) {
27506
28369
  const m = /^#\s+(.+?)\s*$/m.exec(body);
27507
28370
  const t = m?.[1]?.trim();
27508
- return (t && t.length > 0 ? t : basename8(path).replace(/\.(md|mdx)$/i, "")).slice(0, 120);
28371
+ return (t && t.length > 0 ? t : basename9(path).replace(/\.(md|mdx)$/i, "")).slice(0, 120);
27509
28372
  }
27510
28373
  function looksLikeSpec(body) {
27511
28374
  if (body.trim().length < MIN_SPEC_CHARS)
@@ -27540,9 +28403,9 @@ function projectKey(filePath, cwd) {
27540
28403
  }
27541
28404
  function readFileOrNull(path) {
27542
28405
  try {
27543
- if (statSync7(path).size > 2000000)
28406
+ if (statSync8(path).size > 2000000)
27544
28407
  return null;
27545
- return readFileSync31(path, "utf8");
28408
+ return readFileSync32(path, "utf8");
27546
28409
  } catch {
27547
28410
  return null;
27548
28411
  }
@@ -27553,7 +28416,12 @@ var specEvent = async (o) => {
27553
28416
  const input = raw ? parseHookInput(raw) : null;
27554
28417
  if (!input || !isSpecPath(input.filePath))
27555
28418
  return 0;
27556
- const src = meterSource(o.home);
28419
+ let src;
28420
+ try {
28421
+ src = meterSource(o.home);
28422
+ } catch {
28423
+ return 0;
28424
+ }
27557
28425
  if (!src)
27558
28426
  return 0;
27559
28427
  const body = (o.readFile ?? readFileOrNull)(input.filePath);
@@ -27563,7 +28431,7 @@ var specEvent = async (o) => {
27563
28431
  const spec = {
27564
28432
  title: specTitle(body, input.filePath),
27565
28433
  body: body.slice(0, MAX_SPEC_CHARS),
27566
- repoHint: input.cwd ? basename8(input.cwd) : undefined
28434
+ repoHint: input.cwd ? basename9(input.cwd) : undefined
27567
28435
  };
27568
28436
  try {
27569
28437
  const res = await (o.fetchFn ?? fetch)(`${src.proxyUrl}/proxy/estimate`, {
@@ -27759,7 +28627,6 @@ var status3 = async (o) => {
27759
28627
  };
27760
28628
 
27761
28629
  // src/commands/statusline.ts
27762
- init_config();
27763
28630
  init_meter();
27764
28631
  var statusline = async (o) => {
27765
28632
  const log = o.log ?? console.log;
@@ -27798,7 +28665,8 @@ var statusline = async (o) => {
27798
28665
  home: o.home,
27799
28666
  now,
27800
28667
  fetchFn: o.fetchFn,
27801
- timeoutMs: o.timeoutMs
28668
+ timeoutMs: o.timeoutMs,
28669
+ env: o.env
27802
28670
  });
27803
28671
  line = formatMeterLine(meter, now, seatFragment);
27804
28672
  } catch {
@@ -27817,9 +28685,9 @@ var speakToCockpit = async (o, key, window, events, now) => {
27817
28685
  try {
27818
28686
  const st = readSeatState(o.home, key);
27819
28687
  let feed;
27820
- const src = meterSource(o.home);
28688
+ const src = meterSource(o.home, undefined, { env: o.env });
27821
28689
  if (src && now - (st.cockpit?.feed?.at ?? 0) >= FEED_MIN_INTERVAL_MS) {
27822
- const got = await fetchFeed({ ...src, email: readConfig(o.home).email, fetchFn: o.fetchFn, timeoutMs: o.timeoutMs }, now);
28690
+ const got = await fetchFeed({ ...src, email: key.email ?? undefined, fetchFn: o.fetchFn, timeoutMs: o.timeoutMs }, now);
27823
28691
  if (got)
27824
28692
  feed = got;
27825
28693
  }
@@ -27832,13 +28700,13 @@ var speakToCockpit = async (o, key, window, events, now) => {
27832
28700
  return;
27833
28701
  writeSeatState(o.home, { ...readSeatState(o.home, key), cockpit: r.state }, key);
27834
28702
  if (r.argv.length)
27835
- await runCockpit(cockpit, r.argv, { spawn: o.spawn });
28703
+ await runCockpit(cockpit, r.argv, { exec: o.exec });
27836
28704
  } catch {}
27837
28705
  };
27838
28706
  var rememberSeat = (o, key, window, session, now, input) => {
27839
28707
  const prior = readSeatState(o.home, key);
27840
28708
  let state = { ...prior, window, ...session ? { session } : {} };
27841
- const src = meterSource(o.home);
28709
+ const src = meterSource(o.home, undefined, { env: o.env });
27842
28710
  const posts = [];
27843
28711
  if (src && shouldPostSeat(prior, window, now)) {
27844
28712
  state = markPosted(state, window, now);
@@ -27868,7 +28736,7 @@ var creditsEvent = (o, prior, input, now) => {
27868
28736
  var announce = (o, events) => {
27869
28737
  if (events.length === 0)
27870
28738
  return [];
27871
- const src = meterSource(o.home);
28739
+ const src = meterSource(o.home, undefined, { env: o.env });
27872
28740
  if (!src)
27873
28741
  return [];
27874
28742
  return events.map((e) => postSeatWall({ ...src, fetchFn: o.fetchFn, timeoutMs: o.timeoutMs ?? SEAT_POST_TIMEOUT_MS }, {
@@ -27882,7 +28750,7 @@ var announce = (o, events) => {
27882
28750
 
27883
28751
  // src/commands/stopEvent.ts
27884
28752
  init_meter();
27885
- import { existsSync as existsSync32, readFileSync as readFileSync32 } from "node:fs";
28753
+ import { existsSync as existsSync32, readFileSync as readFileSync33 } from "node:fs";
27886
28754
 
27887
28755
  // src/transcript.ts
27888
28756
  var isObject16 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
@@ -27975,10 +28843,15 @@ async function postTranscript(o, payload, now) {
27975
28843
  const path = payload.transcript_path?.trim();
27976
28844
  if (!sessionId || !path)
27977
28845
  return false;
27978
- const src = meterSource(o.home);
28846
+ let src;
28847
+ try {
28848
+ src = meterSource(o.home);
28849
+ } catch {
28850
+ return false;
28851
+ }
27979
28852
  if (!src)
27980
28853
  return false;
27981
- const read = o.readFile ?? ((p) => existsSync32(p) ? readFileSync32(p, "utf8") : null);
28854
+ const read = o.readFile ?? ((p) => existsSync32(p) ? readFileSync33(p, "utf8") : null);
27982
28855
  let text;
27983
28856
  try {
27984
28857
  text = read(path);
@@ -28072,21 +28945,27 @@ var uninstall2 = (o) => {
28072
28945
  if (existsSync33(settingsPath(o.home))) {
28073
28946
  let settings = readSettings(o.home);
28074
28947
  let changed = false;
28075
- if (hasEitherKey(settings)) {
28076
- settings = removeProxy(settings);
28948
+ const cfg = readConfig(o.home);
28949
+ const proxyUrls = [cfg.proxyUrl, DEFAULT_PROXY_URL2];
28950
+ const restoredEnv = isSledsProxyEnv(settings, proxyUrls);
28951
+ if (restoredEnv) {
28952
+ const restore = envToRestore([cfg.priorEnv, cfg.priorGatewayEnv], proxyUrls);
28953
+ settings = switchOff(settings, restore);
28077
28954
  changed = true;
28078
- removed.push(`env.ANTHROPIC_BASE_URL and env.ANTHROPIC_AUTH_TOKEN from ${settingsPath(o.home)}`);
28955
+ 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
28956
  }
28080
- if (isSledsStatusLine(settings[STATUS_LINE_KEY])) {
28081
- const cfg = readConfig(o.home);
28957
+ const restoredLine = isSledsStatusLine(settings[STATUS_LINE_KEY]);
28958
+ if (restoredLine) {
28082
28959
  settings = removeStatusLine(settings, cfg.priorStatusLine);
28083
28960
  changed = true;
28084
28961
  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
28962
  }
28963
+ if (restoredLine && cfg.priorStatusLine !== undefined || restoredEnv && (cfg.priorEnv !== undefined || cfg.priorGatewayEnv !== undefined))
28964
+ writeConfig(o.home, {
28965
+ ...cfg,
28966
+ ...restoredLine ? { priorStatusLine: undefined } : {},
28967
+ ...restoredEnv ? { priorEnv: undefined, priorGatewayEnv: undefined } : {}
28968
+ });
28090
28969
  if (hasSeatHook(settings)) {
28091
28970
  settings = removeSeatHook(settings);
28092
28971
  changed = true;
@@ -28142,18 +29021,12 @@ var uninstall2 = (o) => {
28142
29021
  log(`Kept ${tokenPath(o.home)} (delete it yourself if you want the token gone).`);
28143
29022
  return 0;
28144
29023
  };
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
29024
 
28152
29025
  // src/commands/update.ts
28153
29026
  init_server();
28154
29027
  import { existsSync as existsSync34 } from "node:fs";
28155
29028
  import { homedir as homedir8, userInfo as userInfo4 } from "node:os";
28156
- import { basename as basename9 } from "node:path";
29029
+ import { basename as basename10 } from "node:path";
28157
29030
  init_config();
28158
29031
  init_state();
28159
29032
  init_meter();
@@ -28218,7 +29091,7 @@ var realPidAlive4 = (pid) => {
28218
29091
  };
28219
29092
  function machineClient(home, env) {
28220
29093
  const src = meterSource(home, undefined, { env });
28221
- return src ? fetchClient({ ...src, version: "0.1.60" }) : null;
29094
+ return src ? fetchClient({ ...src, version: "0.1.62" }) : null;
28222
29095
  }
28223
29096
  async function noteAnswer2(home, a, now) {
28224
29097
  try {
@@ -28230,7 +29103,7 @@ async function noteAnswer2(home, a, now) {
28230
29103
  }
28231
29104
  function busyEngines(files) {
28232
29105
  return files.map((f) => ({
28233
- name: basename9(f, ".json"),
29106
+ name: basename10(f, ".json"),
28234
29107
  running: readState(f).runs.filter(isRunning).length
28235
29108
  })).filter((e) => e.running > 0);
28236
29109
  }
@@ -28252,7 +29125,7 @@ async function updateCmd(o) {
28252
29125
  error(UPDATE_USAGE);
28253
29126
  return 2;
28254
29127
  }
28255
- const version = o.version ?? env.SLEDS_CLI_VERSION;
29128
+ const version = o.version ?? cliVersion(env);
28256
29129
  if (parsed.mode === "off" || parsed.mode === "on") {
28257
29130
  const off = parsed.mode === "off";
28258
29131
  await withUpdateLock(home, (s) => {
@@ -28543,13 +29416,13 @@ var webhooksCmd = async (o) => {
28543
29416
 
28544
29417
  // src/config.ts
28545
29418
  init_settings();
28546
- import { chmodSync as chmodSync8, existsSync as existsSync35, mkdirSync as mkdirSync28, readFileSync as readFileSync33, renameSync as renameSync13, writeFileSync as writeFileSync28 } from "node:fs";
28547
- import { join as join33, resolve as resolve11 } from "node:path";
29419
+ import { chmodSync as chmodSync8, existsSync as existsSync35, mkdirSync as mkdirSync28, readFileSync as readFileSync34, renameSync as renameSync13, writeFileSync as writeFileSync28 } from "node:fs";
29420
+ import { join as join34, resolve as resolve12 } from "node:path";
28548
29421
  var configDir2 = (home, env = process.env) => {
28549
29422
  const o = env.SLEDS_HOME?.trim();
28550
- return o ? resolve11(home, o) : join33(home, ".config", "sleds");
29423
+ return o ? resolve12(home, o) : join34(home, ".config", "sleds");
28551
29424
  };
28552
- var configPath2 = (home) => join33(configDir2(home), "config.json");
29425
+ var configPath2 = (home) => join34(configDir2(home), "config.json");
28553
29426
  var isObject17 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
28554
29427
  var optString2 = (v) => typeof v === "string" ? v : undefined;
28555
29428
  var parsePriorEnv2 = (v) => {
@@ -28599,6 +29472,9 @@ var parseConfig2 = (raw) => {
28599
29472
  cfg.schedule = schedule;
28600
29473
  if (raw.priorStatusLine !== undefined)
28601
29474
  cfg.priorStatusLine = raw.priorStatusLine;
29475
+ const priorGatewayEnv = parsePriorEnv2(raw.priorGatewayEnv);
29476
+ if (priorGatewayEnv !== undefined)
29477
+ cfg.priorGatewayEnv = priorGatewayEnv;
28602
29478
  if (raw.wiring === "seat-first" || raw.wiring === "proxy-always")
28603
29479
  cfg.wiring = raw.wiring;
28604
29480
  if (raw.agentKind === "claude-code" || raw.agentKind === "codex")
@@ -28683,7 +29559,7 @@ var readConfig2 = (home) => {
28683
29559
  const p = configPath2(home);
28684
29560
  if (!existsSync35(p))
28685
29561
  return {};
28686
- return parseConfig2(JSON.parse(readFileSync33(p, "utf8")));
29562
+ return parseConfig2(JSON.parse(readFileSync34(p, "utf8")));
28687
29563
  };
28688
29564
 
28689
29565
  // src/runners/exec.ts
@@ -28741,9 +29617,9 @@ function runnerFor3(name, exec = defaultExec, o = {}) {
28741
29617
 
28742
29618
  // src/runtime.ts
28743
29619
  import { homedir as homedir9 } from "node:os";
28744
- import { join as join34, win32 as win323 } from "node:path";
29620
+ import { join as join35, win32 as win323 } from "node:path";
28745
29621
  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") : join34(home, ".sleds", "bin", "sleds");
29622
+ 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
29623
 
28748
29624
  // src/schedule.ts
28749
29625
  import { spawnSync as spawnSync11 } from "node:child_process";
@@ -28755,15 +29631,15 @@ var defaultExec5 = (cmd, args) => {
28755
29631
  };
28756
29632
 
28757
29633
  // src/settings.ts
28758
- import { dirname as dirname24, join as join35, resolve as resolve12 } from "node:path";
29634
+ import { dirname as dirname25, join as join36, resolve as resolve13 } from "node:path";
28759
29635
  var withSeatEnv2 = (command, home, env = process.env, org) => {
28760
29636
  const parts = [];
28761
29637
  const sh = env.SLEDS_HOME?.trim();
28762
29638
  const cc = env.CLAUDE_CONFIG_DIR?.trim();
28763
29639
  if (sh)
28764
- parts.push(`SLEDS_HOME=${shellWord2(resolve12(home, sh))}`);
29640
+ parts.push(`SLEDS_HOME=${shellWord2(resolve13(home, sh))}`);
28765
29641
  if (cc)
28766
- parts.push(`CLAUDE_CONFIG_DIR=${shellWord2(resolve12(home, cc))}`);
29642
+ parts.push(`CLAUDE_CONFIG_DIR=${shellWord2(resolve13(home, cc))}`);
28767
29643
  parts.push(command);
28768
29644
  return pinOrg4(parts.join(" "), org);
28769
29645
  };
@@ -28779,12 +29655,16 @@ var pinOrg4 = (command, org) => {
28779
29655
  };
28780
29656
  var shellWord2 = (v) => /^[\w./~-]+$/.test(v) ? v : `'${v.replace(/'/g, "'\\''")}'`;
28781
29657
  var isObject18 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
29658
+ var invokesSleds2 = (command) => /\bsleds\b/.test(command);
28782
29659
  var isSledsHookFor2 = (sub) => (entry) => {
28783
29660
  if (!isObject18(entry) || !Array.isArray(entry.hooks))
28784
29661
  return false;
28785
29662
  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));
29663
+ return entry.hooks.some((h) => isObject18(h) && h.type === "command" && typeof h.command === "string" && invokesSleds2(h.command) && re.test(h.command));
28787
29664
  };
29665
+ var isSledsSeatHook2 = isSledsHookFor2("seat-event");
29666
+ var isSledsSpecHook2 = isSledsHookFor2("spec-event");
29667
+ var isSledsPromptHook2 = isSledsHookFor2("prompt-event");
28788
29668
  var isSledsSessionHook2 = isSledsHookFor2("session-event");
28789
29669
  var isSledsStopHook2 = isSledsHookFor2("stop-event");
28790
29670
  var isSledsPermissionHook2 = isSledsHookFor2("permission-event");
@@ -29081,7 +29961,7 @@ var main = async (argv) => {
29081
29961
  now: Date.now(),
29082
29962
  stderr: (line) => console.error(line),
29083
29963
  isTTY: !!process.stderr.isTTY,
29084
- version: "0.1.60"
29964
+ version: "0.1.62"
29085
29965
  });
29086
29966
  switch (cmd) {
29087
29967
  case "update":
@@ -29314,7 +30194,7 @@ ${renderHelpText(argv[1])}`);
29314
30194
  case "version":
29315
30195
  case "--version":
29316
30196
  case "-v":
29317
- console.log("sleds 0.1.60");
30197
+ console.log("sleds 0.1.62");
29318
30198
  return 0;
29319
30199
  default:
29320
30200
  console.error(`unknown command: ${cmd}