@tablation/crew 0.127.1 → 0.127.3

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/cli.js +244 -98
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -6215,7 +6215,8 @@ async function raiseShipAttention(o, item) {
6215
6215
  const previous = store.previous();
6216
6216
  const next = upsert(previous, item);
6217
6217
  store.persist(next);
6218
- if (transitions(previous, next).length === 0) return false;
6218
+ const changed2 = item.kind === "sandbox_cache" && previous.some((i) => i.key === item.key && i.message !== item.message);
6219
+ if (transitions(previous, next).length === 0 && !changed2) return false;
6219
6220
  announce(o.emit, item);
6220
6221
  if (o.route && o.ship) {
6221
6222
  await notify(o.route, o.ship, { level: "warn", headline: item.message }, o.emit);
@@ -6356,7 +6357,7 @@ function attentionProblems(items, already) {
6356
6357
  import { createHash as createHash7 } from "crypto";
6357
6358
  import { spawn as spawn5 } from "child_process";
6358
6359
  import { execFileSync as execFileSync8 } from "child_process";
6359
- import { existsSync as existsSync30, mkdirSync as mkdirSync19, readFileSync as readFileSync27, renameSync as renameSync3, writeFileSync as writeFileSync20, rmSync as rmSync15 } from "fs";
6360
+ import { existsSync as existsSync30, mkdirSync as mkdirSync19, readFileSync as readFileSync28, renameSync as renameSync3, writeFileSync as writeFileSync20, rmSync as rmSync15 } from "fs";
6360
6361
  import { dirname as dirname15, join as join32 } from "path";
6361
6362
  import { fileURLToPath } from "url";
6362
6363
  import { tmpdir as tmpdir3 } from "os";
@@ -6374,12 +6375,12 @@ import { spawnSync as spawnSync7 } from "child_process";
6374
6375
  import { existsSync as existsSync29, readdirSync as readdirSync11 } from "fs";
6375
6376
 
6376
6377
  // src/interrupted.ts
6377
- import { existsSync as existsSync27, readdirSync as readdirSync9, readFileSync as readFileSync26 } from "fs";
6378
+ import { existsSync as existsSync27, readdirSync as readdirSync9, readFileSync as readFileSync27 } from "fs";
6378
6379
  import { join as join30 } from "path";
6379
6380
 
6380
6381
  // src/agent.ts
6381
6382
  import { spawnSync as spawnSync6 } from "child_process";
6382
- import { readFileSync as readFileSync25, existsSync as existsSync26, statSync as statSync8, mkdirSync as mkdirSync18, writeFileSync as writeFileSync19, createWriteStream, openSync as openSync4, closeSync as closeSync4, readSync } from "fs";
6383
+ import { readFileSync as readFileSync26, existsSync as existsSync26, statSync as statSync8, mkdirSync as mkdirSync18, writeFileSync as writeFileSync19, createWriteStream, openSync as openSync4, closeSync as closeSync4, readSync } from "fs";
6383
6384
  import { StringDecoder } from "string_decoder";
6384
6385
  import { finished } from "stream/promises";
6385
6386
  import { join as join29, dirname as dirname14, basename as basename9 } from "path";
@@ -6887,6 +6888,97 @@ var State = class _State {
6887
6888
  }
6888
6889
  };
6889
6890
 
6891
+ // src/signed-out.ts
6892
+ import { readFileSync as readFileSync20 } from "fs";
6893
+ var SIGNED_OUT_PATTERN = /not logged in|please run \/?login|\/login|invalid api key|failed to authenticate|oauth session expired|could not be refreshed|reauthentication is needed|application default credentials/i;
6894
+ function authModeOf(spec, hostEnv = process.env) {
6895
+ const env = { ...hostEnv, ...spec.env };
6896
+ const on = (v) => !!v && v !== "0" && v.toLowerCase() !== "false";
6897
+ if (spec.auth === "subscription") return "login";
6898
+ if (on(env.CLAUDE_CODE_USE_VERTEX)) return "vertex";
6899
+ if (env.ANTHROPIC_API_KEY) return "api-key";
6900
+ return "login";
6901
+ }
6902
+ var SIGNED_OUT_REMEDY = {
6903
+ vertex: "run `gcloud auth application-default login` on this Mac",
6904
+ "api-key": "check the API key the toolchain uses (ANTHROPIC_API_KEY) is valid and unrevoked",
6905
+ login: "run `claude` in a terminal on this Mac and `/login`"
6906
+ };
6907
+ function signedOutMessage(toolchain, mode) {
6908
+ return `toolchain ${toolchain} is signed out \u2014 its runs end at the harness's sign-out error. To fix: ${SIGNED_OUT_REMEDY[mode]}`;
6909
+ }
6910
+ var signedOutKey = (toolchain) => `signed_out:${toolchain}`;
6911
+ function signedOutItem(toolchain, mode, nowIso = (/* @__PURE__ */ new Date()).toISOString()) {
6912
+ return { kind: "signed_out", key: signedOutKey(toolchain), since: nowIso, message: signedOutMessage(toolchain, mode) };
6913
+ }
6914
+ var textOf = (v) => typeof v === "string" ? v : JSON.stringify(v ?? "");
6915
+ function eventsShowSignedOut(events) {
6916
+ let assistantTurns = 0;
6917
+ let toolUse = false;
6918
+ let onlyTurnSignedOut = false;
6919
+ for (const ev of events) {
6920
+ if (typeof ev !== "object" || ev === null) continue;
6921
+ const e = ev;
6922
+ if (e.type === "result") {
6923
+ if (e.is_error === true && SIGNED_OUT_PATTERN.test(textOf(e.result))) return true;
6924
+ } else if (e.type === "assistant") {
6925
+ assistantTurns++;
6926
+ const content = Array.isArray(e.message?.content) ? e.message.content : [];
6927
+ if (content.some((b) => b?.type === "tool_use")) toolUse = true;
6928
+ if (SIGNED_OUT_PATTERN.test(textOf(e.message))) {
6929
+ if (e.isApiErrorMessage === true || e.error !== void 0) return true;
6930
+ onlyTurnSignedOut = true;
6931
+ }
6932
+ }
6933
+ }
6934
+ return assistantTurns === 1 && onlyTurnSignedOut && !toolUse;
6935
+ }
6936
+ function streamShowsSignedOut(streamPath) {
6937
+ let raw;
6938
+ try {
6939
+ raw = readFileSync20(streamPath, "utf8");
6940
+ } catch {
6941
+ return false;
6942
+ }
6943
+ if (!SIGNED_OUT_PATTERN.test(raw)) return false;
6944
+ const events = [];
6945
+ for (const line of raw.split("\n")) {
6946
+ if (!line.trim()) continue;
6947
+ try {
6948
+ events.push(JSON.parse(line));
6949
+ } catch {
6950
+ }
6951
+ }
6952
+ return eventsShowSignedOut(events);
6953
+ }
6954
+ function streamShowsRealTurn(streamPath) {
6955
+ let raw;
6956
+ try {
6957
+ raw = readFileSync20(streamPath, "utf8");
6958
+ } catch {
6959
+ return false;
6960
+ }
6961
+ return raw.split("\n").some((line) => {
6962
+ if (!line.includes('"assistant"')) return false;
6963
+ try {
6964
+ const e = JSON.parse(line);
6965
+ return e.type === "assistant" && e.isApiErrorMessage !== true && e.error === void 0 && !SIGNED_OUT_PATTERN.test(textOf(e.message));
6966
+ } catch {
6967
+ return false;
6968
+ }
6969
+ });
6970
+ }
6971
+ var SignedOutError = class extends Error {
6972
+ toolchain;
6973
+ mode;
6974
+ constructor(toolchain, mode) {
6975
+ super(signedOutMessage(toolchain, mode));
6976
+ this.toolchain = toolchain;
6977
+ this.mode = mode;
6978
+ }
6979
+ };
6980
+ var SIGNED_OUT_PAUSE_MS = 15 * 60 * 1e3;
6981
+
6890
6982
  // src/environment.ts
6891
6983
  import { basename as basename6 } from "path";
6892
6984
 
@@ -7286,7 +7378,7 @@ function renderEnvironment(i) {
7286
7378
  }
7287
7379
 
7288
7380
  // src/intel-bridge.ts
7289
- import { existsSync as existsSync20, mkdirSync as mkdirSync14, readFileSync as readFileSync20, rmSync as rmSync11, writeFileSync as writeFileSync15 } from "fs";
7381
+ import { existsSync as existsSync20, mkdirSync as mkdirSync14, readFileSync as readFileSync21, rmSync as rmSync11, writeFileSync as writeFileSync15 } from "fs";
7290
7382
  import { dirname as dirname11, join as join22 } from "path";
7291
7383
  import { Client } from "@modelcontextprotocol/sdk/client/index.js";
7292
7384
  import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
@@ -7313,7 +7405,7 @@ function writeAttachFile(file, url) {
7313
7405
  }
7314
7406
  function readAttachFile(file) {
7315
7407
  try {
7316
- const url = readFileSync20(file, "utf8").trim();
7408
+ const url = readFileSync21(file, "utf8").trim();
7317
7409
  return /^https?:\/\/\S+$/i.test(url) ? url : void 0;
7318
7410
  } catch {
7319
7411
  return void 0;
@@ -7692,7 +7784,7 @@ var BeforeFirstEditTracker = class {
7692
7784
  };
7693
7785
 
7694
7786
  // src/agent-log.ts
7695
- import { existsSync as existsSync21, mkdirSync as mkdirSync15, readdirSync as readdirSync6, readFileSync as readFileSync21, rmSync as rmSync12, writeFileSync as writeFileSync16 } from "fs";
7787
+ import { existsSync as existsSync21, mkdirSync as mkdirSync15, readdirSync as readdirSync6, readFileSync as readFileSync22, rmSync as rmSync12, writeFileSync as writeFileSync16 } from "fs";
7696
7788
  import { join as join23 } from "path";
7697
7789
 
7698
7790
  // src/pending-backoff.ts
@@ -7909,7 +8001,7 @@ async function replayQueuedAgentRuns(stateDir, route2, keyFor, log, now = /* @__
7909
8001
  const path = join23(dir, f);
7910
8002
  let q2;
7911
8003
  try {
7912
- q2 = JSON.parse(readFileSync21(path, "utf8"));
8004
+ q2 = JSON.parse(readFileSync22(path, "utf8"));
7913
8005
  } catch {
7914
8006
  continue;
7915
8007
  }
@@ -7962,7 +8054,7 @@ async function replayQueuedTicketCounters(stateDir, route2, keyFor, log, now = /
7962
8054
  const path = join23(dir, f);
7963
8055
  let q2;
7964
8056
  try {
7965
- q2 = JSON.parse(readFileSync21(path, "utf8"));
8057
+ q2 = JSON.parse(readFileSync22(path, "utf8"));
7966
8058
  } catch {
7967
8059
  continue;
7968
8060
  }
@@ -8184,7 +8276,7 @@ var HandoffTranscriptTracker = class {
8184
8276
  };
8185
8277
 
8186
8278
  // src/pending-handoff.ts
8187
- import { existsSync as existsSync23, mkdirSync as mkdirSync16, readdirSync as readdirSync7, readFileSync as readFileSync22, rmSync as rmSync13, writeFileSync as writeFileSync17 } from "fs";
8279
+ import { existsSync as existsSync23, mkdirSync as mkdirSync16, readdirSync as readdirSync7, readFileSync as readFileSync23, rmSync as rmSync13, writeFileSync as writeFileSync17 } from "fs";
8188
8280
  import { join as join25 } from "path";
8189
8281
 
8190
8282
  // src/ticket-branch.ts
@@ -9879,7 +9971,7 @@ function listPendingHandoffs(stateDir, route2) {
9879
9971
  const out = [];
9880
9972
  for (const f of readdirSync7(dir).filter((n) => n.endsWith(".json")).sort()) {
9881
9973
  try {
9882
- const rec = JSON.parse(readFileSync22(join25(dir, f), "utf8"));
9974
+ const rec = JSON.parse(readFileSync23(join25(dir, f), "utf8"));
9883
9975
  if (rec.version === 1 && rec.ticketRecordId && (!route2 || rec.route === route2)) out.push(rec);
9884
9976
  } catch {
9885
9977
  }
@@ -9997,7 +10089,7 @@ function expandSkillMarkers(text2, lookup2, opts = {}) {
9997
10089
  }
9998
10090
 
9999
10091
  // src/skills.ts
10000
- import { readFileSync as readFileSync24, readdirSync as readdirSync8, existsSync as existsSync25 } from "fs";
10092
+ import { readFileSync as readFileSync25, readdirSync as readdirSync8, existsSync as existsSync25 } from "fs";
10001
10093
  import { join as join27 } from "path";
10002
10094
  import { parse as parseYaml } from "yaml";
10003
10095
  import { TablationClient as TablationClient3, StaleWriteError as StaleWriteError3, TablationApiError as TablationApiError2 } from "@tablation/client";
@@ -10014,7 +10106,7 @@ ${additions}`;
10014
10106
  }
10015
10107
 
10016
10108
  // src/agents.ts
10017
- import { readFileSync as readFileSync23, existsSync as existsSync24 } from "fs";
10109
+ import { readFileSync as readFileSync24, existsSync as existsSync24 } from "fs";
10018
10110
  import { join as join26 } from "path";
10019
10111
  import { TablationClient as TablationClient2, StaleWriteError as StaleWriteError2, TablationApiError } from "@tablation/client";
10020
10112
  var PERSONA_NAME = {
@@ -10031,10 +10123,10 @@ var AgentsSyncError = class extends Error {
10031
10123
  function personaDefaultPrompt(promptsDir, role) {
10032
10124
  const briefPath = join26(promptsDir, "personas", `lane-${role}.md`);
10033
10125
  if (!existsSync24(briefPath)) throw new AgentsSyncError(`no brief at ${briefPath}`);
10034
- if (SELF_CONTAINED_ROLES.has(role)) return readFileSync23(briefPath, "utf8");
10126
+ if (SELF_CONTAINED_ROLES.has(role)) return readFileSync24(briefPath, "utf8");
10035
10127
  const commonPath = join26(promptsDir, "personas", "common.md");
10036
- if (!existsSync24(commonPath)) return readFileSync23(briefPath, "utf8");
10037
- return `${readFileSync23(commonPath, "utf8")}${readFileSync23(briefPath, "utf8")}`;
10128
+ if (!existsSync24(commonPath)) return readFileSync24(briefPath, "utf8");
10129
+ return `${readFileSync24(commonPath, "utf8")}${readFileSync24(briefPath, "utf8")}`;
10038
10130
  }
10039
10131
  async function resolveAgentId(route2, role, opts) {
10040
10132
  const cached = route2.resolved?.agentPersonas?.[role]?.agentId;
@@ -10481,7 +10573,7 @@ function describeCrewLink(o) {
10481
10573
  var SkillSyncError = class extends Error {
10482
10574
  };
10483
10575
  function parseSkillFile(path) {
10484
- const text2 = readFileSync24(path, "utf8");
10576
+ const text2 = readFileSync25(path, "utf8");
10485
10577
  const match = /^---\n([\s\S]*?)\n---\n([\s\S]*)$/.exec(text2);
10486
10578
  if (!match) throw new SkillSyncError(`${path}: missing YAML frontmatter (expected a leading --- block)`);
10487
10579
  const frontmatter = match[1];
@@ -11061,7 +11153,7 @@ var ALLOWANCE_REFUSAL_MARKER = "AI_ALLOWANCE_EXCEEDED";
11061
11153
  function streamShowsAllowanceRefusal(streamPath) {
11062
11154
  let raw;
11063
11155
  try {
11064
- raw = readFileSync25(streamPath, "utf8");
11156
+ raw = readFileSync26(streamPath, "utf8");
11065
11157
  } catch {
11066
11158
  return false;
11067
11159
  }
@@ -11147,10 +11239,10 @@ function planAgentRun(o) {
11147
11239
  let ageSeconds;
11148
11240
  if (existsSync26(digestPath2)) {
11149
11241
  ageSeconds = Math.floor((now() - statSync8(digestPath2).mtimeMs) / 1e3);
11150
- if (ageSeconds <= DIGEST_MAX_AGE_SECONDS) digest = readFileSync25(digestPath2, "utf8");
11242
+ if (ageSeconds <= DIGEST_MAX_AGE_SECONDS) digest = readFileSync26(digestPath2, "utf8");
11151
11243
  }
11152
- const common = usesCommon ? readFileSync25(commonPath, "utf8") : "";
11153
- const brief = readFileSync25(briefPath, "utf8");
11244
+ const common = usesCommon ? readFileSync26(commonPath, "utf8") : "";
11245
+ const brief = readFileSync26(briefPath, "utf8");
11154
11246
  const effectiveCommon = o.divergedPrompt ? "" : common;
11155
11247
  const effectiveBrief = appendAdditions(o.divergedPrompt ?? brief, o.personaAdditions);
11156
11248
  const localSkills = new Map(listLocalSkills(o.route.promptsDir).map((s) => [s.name, s.prompt]));
@@ -11239,6 +11331,7 @@ function planAgentRun(o) {
11239
11331
  toolchain: toolchain.name,
11240
11332
  harness: toolchain.harness,
11241
11333
  workspaceAuth: toolchain.auth === "workspace",
11334
+ authMode: authModeOf(toolchain),
11242
11335
  provider: toolchainProvider(toolchain),
11243
11336
  maxRunMinutes: resolveMaxRunMinutes(o.route.maxRunMinutes, toolchain.maxRunMinutes),
11244
11337
  ...o.ship.runner === "sandbox" ? {
@@ -11305,7 +11398,7 @@ function describePlan(p) {
11305
11398
  var pidPathFor = (streamPath) => `${streamPath}.pid`;
11306
11399
  function readRunSidecar(streamPath) {
11307
11400
  try {
11308
- const sc = JSON.parse(readFileSync25(pidPathFor(streamPath), "utf8"));
11401
+ const sc = JSON.parse(readFileSync26(pidPathFor(streamPath), "utf8"));
11309
11402
  return typeof sc.pid === "number" && typeof sc.spawnedAt === "string" && typeof sc.role === "string" ? sc : null;
11310
11403
  } catch {
11311
11404
  return null;
@@ -11620,10 +11713,11 @@ async function finishRun(id, obs, emit, startedAt, end, agentLogKey, hintMoved)
11620
11713
  ...result?.costUsd !== void 0 ? { totalCostUsd: result.costUsd } : {}
11621
11714
  }
11622
11715
  });
11716
+ const signedOut = !end.interrupted && streamShowsSignedOut(id.streamPath);
11623
11717
  if (id.agentLog && agentLogKey) {
11624
11718
  const report = {
11625
11719
  ticketReference: attributedTicket,
11626
- outcome: end.interrupted ? "interrupted" : end.code === 0 || end.code === null && obs.sawResult ? "success" : "error",
11720
+ outcome: end.interrupted ? "interrupted" : signedOut ? "error" : end.code === 0 || end.code === null && obs.sawResult ? "success" : "error",
11627
11721
  startedAt,
11628
11722
  finishedAt: (/* @__PURE__ */ new Date()).toISOString(),
11629
11723
  cycles: obs.cycles,
@@ -11878,7 +11972,7 @@ function runFinished(streamPath) {
11878
11972
  const path = `${streamPath}.meta.json`;
11879
11973
  if (!existsSync27(path)) return false;
11880
11974
  try {
11881
- return JSON.parse(readFileSync26(path, "utf8")).running !== true;
11975
+ return JSON.parse(readFileSync27(path, "utf8")).running !== true;
11882
11976
  } catch {
11883
11977
  return true;
11884
11978
  }
@@ -12611,7 +12705,7 @@ var SandboxCache = class {
12611
12705
  try {
12612
12706
  await this.helper(this.s.helperImage, REFRESH_SCRIPT, [mirrorRelPath(r), forgeDescriptorFor(r.host).httpsUrl(r.host, r.path)], env);
12613
12707
  } catch (e) {
12614
- out.push({ repo: r, ok: false, detail: describe(e) });
12708
+ out.push({ repo: r, ok: false, detail: r.token ? describe(e) : `no readable forge token for ${r.host} (see \`crew forge list\`); ${describe(e)}` });
12615
12709
  continue;
12616
12710
  }
12617
12711
  if (this.s.depsCommand && this.s.depsImage) {
@@ -12715,6 +12809,10 @@ function cacheSettingsOf(cfg) {
12715
12809
  };
12716
12810
  }
12717
12811
  var sandboxCacheKey = (volume, r) => `sandbox_cache:${volume}:${r.host}/${r.path}`;
12812
+ function unmirroredRepos(served, mirrors) {
12813
+ const have = new Set(mirrors.map((m) => `${m.repo.host}/${m.repo.path}`));
12814
+ return served.map((r) => `${r.host}/${r.path}`).filter((k, i, a) => !have.has(k) && a.indexOf(k) === i);
12815
+ }
12718
12816
  var CACHE_REFRESH_INTERVAL_MS = 6e4;
12719
12817
  function makeCachePass(o) {
12720
12818
  const now = o.now ?? Date.now;
@@ -12737,7 +12835,7 @@ function makeCachePass(o) {
12737
12835
  for (const out of all) {
12738
12836
  const key = sandboxCacheKey(o.volume, out.repo);
12739
12837
  if (out.ok) o.ok(key);
12740
- else await o.fail(key, `sandbox cache: ${out.repo.host}/${out.repo.path} did not refresh (${out.detail}); runs use the previous mirror. Resumes automatically when a later pass refreshes it`);
12838
+ else await o.fail(key, `sandbox cache: ${out.repo.host}/${out.repo.path} did not refresh (${out.detail}); runs use the previous mirror. Resumes automatically when a later pass refreshes it`, new Date(now()).toISOString());
12741
12839
  }
12742
12840
  return all;
12743
12841
  };
@@ -12969,7 +13067,7 @@ var SANDBOX_BRIDGE_PATH = `${CREW_PREFIX}/bin/intel-bridge`;
12969
13067
  function readBridgeBundle() {
12970
13068
  const here = dirname15(fileURLToPath(import.meta.url));
12971
13069
  for (const p of [join32(here, "sandbox-intel-bridge.cjs"), join32(here, "..", "dist", "sandbox-intel-bridge.cjs")]) {
12972
- if (existsSync30(p)) return readFileSync27(p, "utf8");
13070
+ if (existsSync30(p)) return readFileSync28(p, "utf8");
12973
13071
  }
12974
13072
  return void 0;
12975
13073
  }
@@ -13121,7 +13219,7 @@ var shipSandboxRef = () => ({ name: SHIP_SANDBOX_KEY, decl: { packages: [], setu
13121
13219
  var sandboxStatePath = (stateDir) => join32(stateDir, "sandbox-images.json");
13122
13220
  function readSandboxState(stateDir) {
13123
13221
  try {
13124
- const v = JSON.parse(readFileSync27(sandboxStatePath(stateDir), "utf8"));
13222
+ const v = JSON.parse(readFileSync28(sandboxStatePath(stateDir), "utf8"));
13125
13223
  return v && typeof v === "object" && !Array.isArray(v) ? v : {};
13126
13224
  } catch {
13127
13225
  return {};
@@ -13404,7 +13502,7 @@ function managedGitIdentityWarnings(kind, identity) {
13404
13502
 
13405
13503
  // src/selftest.ts
13406
13504
  import { spawn as spawn6 } from "child_process";
13407
- import { existsSync as existsSync31, mkdirSync as mkdirSync20, mkdtempSync as mkdtempSync2, readFileSync as readFileSync28, rmSync as rmSync16, writeFileSync as writeFileSync21 } from "fs";
13505
+ import { existsSync as existsSync31, mkdirSync as mkdirSync20, mkdtempSync as mkdtempSync2, readFileSync as readFileSync29, rmSync as rmSync16, writeFileSync as writeFileSync21 } from "fs";
13408
13506
  import { tmpdir as tmpdir4 } from "os";
13409
13507
  import { join as join33 } from "path";
13410
13508
  var SELFTEST_TIMEOUT_MS = 12e4;
@@ -13538,6 +13636,11 @@ ${seen.texts.join("\n")}`)) {
13538
13636
  return fail2("mcp-unavailable", "the tracker MCP tool was not available to the session");
13539
13637
  }
13540
13638
  const answered = seen.texts.length > 0 || seen.finalText;
13639
+ if (SIGNED_OUT_PATTERN.test(`${run3.stderr}
13640
+ ${seen.finalText ?? ""}
13641
+ ${seen.texts.join("\n")}`)) {
13642
+ return { failure: "login-missing", detail: signedOutMessage(spec.name, authModeOf(spec)) };
13643
+ }
13541
13644
  if (!answered || LOGIN.test(run3.stderr) || LOGIN.test(seen.finalText ?? "")) {
13542
13645
  return fail2("login-missing", `\`${bin}\` exited${run3.code !== null ? ` (${run3.code})` : ""} before answering; the vendor login is probably missing`);
13543
13646
  }
@@ -13604,7 +13707,7 @@ var fileFor2 = (stateDir) => join33(stateDir, "selftest.json");
13604
13707
  function readSelftest(stateDir) {
13605
13708
  try {
13606
13709
  const p = fileFor2(stateDir);
13607
- return existsSync31(p) ? JSON.parse(readFileSync28(p, "utf8")) : {};
13710
+ return existsSync31(p) ? JSON.parse(readFileSync29(p, "utf8")) : {};
13608
13711
  } catch {
13609
13712
  return {};
13610
13713
  }
@@ -13823,14 +13926,14 @@ function rosterMarkdown(roster, meId) {
13823
13926
  }
13824
13927
 
13825
13928
  // src/connect-resume.ts
13826
- import { mkdirSync as mkdirSync21, readFileSync as readFileSync29, rmSync as rmSync17, writeFileSync as writeFileSync22 } from "fs";
13929
+ import { mkdirSync as mkdirSync21, readFileSync as readFileSync30, rmSync as rmSync17, writeFileSync as writeFileSync22 } from "fs";
13827
13930
  import { dirname as dirname16, join as join34 } from "path";
13828
13931
  function lastSessionFile(stateDir) {
13829
13932
  return join34(stateDir, "connect-session.json");
13830
13933
  }
13831
13934
  function readPointers(stateDir) {
13832
13935
  try {
13833
- const parsed = JSON.parse(readFileSync29(lastSessionFile(stateDir), "utf8"));
13936
+ const parsed = JSON.parse(readFileSync30(lastSessionFile(stateDir), "utf8"));
13834
13937
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return {};
13835
13938
  const out = {};
13836
13939
  for (const [host, v] of Object.entries(parsed)) {
@@ -13930,7 +14033,7 @@ async function signOutSessions(o) {
13930
14033
  }
13931
14034
 
13932
14035
  // src/reset.ts
13933
- import { existsSync as existsSync32, mkdirSync as mkdirSync22, readFileSync as readFileSync30, readdirSync as readdirSync12, rmSync as rmSync18, statSync as statSync10, writeFileSync as writeFileSync23 } from "fs";
14036
+ import { existsSync as existsSync32, mkdirSync as mkdirSync22, readFileSync as readFileSync31, readdirSync as readdirSync12, rmSync as rmSync18, statSync as statSync10, writeFileSync as writeFileSync23 } from "fs";
13934
14037
  import { dirname as dirname17, join as join35 } from "path";
13935
14038
  import { hostFromUrl as hostFromUrl3 } from "@tablation/client";
13936
14039
  function clonedCheckoutsFile(stateDir) {
@@ -13938,7 +14041,7 @@ function clonedCheckoutsFile(stateDir) {
13938
14041
  }
13939
14042
  function readClonedCheckouts(stateDir) {
13940
14043
  try {
13941
- const parsed = JSON.parse(readFileSync30(clonedCheckoutsFile(stateDir), "utf8"));
14044
+ const parsed = JSON.parse(readFileSync31(clonedCheckoutsFile(stateDir), "utf8"));
13942
14045
  return Array.isArray(parsed) ? parsed.filter((d) => typeof d === "string") : [];
13943
14046
  } catch {
13944
14047
  return [];
@@ -14129,7 +14232,7 @@ async function runDeprovision(o, fx, deps) {
14129
14232
  }
14130
14233
 
14131
14234
  // src/pool-member.ts
14132
- import { mkdirSync as mkdirSync23, readFileSync as readFileSync31, renameSync as renameSync4, writeFileSync as writeFileSync24 } from "fs";
14235
+ import { mkdirSync as mkdirSync23, readFileSync as readFileSync32, renameSync as renameSync4, writeFileSync as writeFileSync24 } from "fs";
14133
14236
  import { homedir as homedir8 } from "os";
14134
14237
  import { dirname as dirname18, join as join36 } from "path";
14135
14238
  var POOL_EXIT = { ok: 0, failed: 1, credential: 2 };
@@ -14369,7 +14472,7 @@ function fileMemberStore(dir) {
14369
14472
  return {
14370
14473
  read() {
14371
14474
  try {
14372
- return JSON.parse(readFileSync31(file, "utf8"));
14475
+ return JSON.parse(readFileSync32(file, "utf8"));
14373
14476
  } catch {
14374
14477
  return {};
14375
14478
  }
@@ -14435,7 +14538,7 @@ import { hostname as hostname3 } from "os";
14435
14538
  // src/machine-id.ts
14436
14539
  import { execFileSync as execFileSync10 } from "child_process";
14437
14540
  import { createHash as createHash8 } from "crypto";
14438
- import { existsSync as existsSync33, mkdirSync as mkdirSync24, readFileSync as readFileSync32, writeFileSync as writeFileSync25 } from "fs";
14541
+ import { existsSync as existsSync33, mkdirSync as mkdirSync24, readFileSync as readFileSync33, writeFileSync as writeFileSync25 } from "fs";
14439
14542
  import { dirname as dirname19, join as join37 } from "path";
14440
14543
  function machineIdPathFor(stateDir) {
14441
14544
  return join37(stateDir, "machine-id");
@@ -14452,7 +14555,7 @@ function readRawMachineId(platform2 = process.platform) {
14452
14555
  }
14453
14556
  for (const p of ["/etc/machine-id", "/var/lib/dbus/machine-id"]) {
14454
14557
  if (existsSync33(p)) {
14455
- const id = readFileSync32(p, "utf8").trim();
14558
+ const id = readFileSync33(p, "utf8").trim();
14456
14559
  if (id) return id;
14457
14560
  }
14458
14561
  }
@@ -14464,7 +14567,7 @@ function ensureMachineId(stateDir, opts = {}) {
14464
14567
  if (opts.override?.trim()) return sha256(opts.override.trim());
14465
14568
  const path = machineIdPathFor(stateDir);
14466
14569
  if (existsSync33(path)) {
14467
- const cached = readFileSync32(path, "utf8").trim();
14570
+ const cached = readFileSync33(path, "utf8").trim();
14468
14571
  if (/^[0-9a-f]{64}$/.test(cached)) return cached;
14469
14572
  }
14470
14573
  const raw = (opts.read ?? readRawMachineId)();
@@ -14484,7 +14587,7 @@ function matchShipRows(ships, shipName2, machineId) {
14484
14587
 
14485
14588
  // src/provision.ts
14486
14589
  import { spawnSync as spawnSync9 } from "child_process";
14487
- import { existsSync as existsSync34, readFileSync as readFileSync33, writeFileSync as writeFileSync26 } from "fs";
14590
+ import { existsSync as existsSync34, readFileSync as readFileSync34, writeFileSync as writeFileSync26 } from "fs";
14488
14591
  import { hostname as hostname2 } from "os";
14489
14592
  import { parseDocument as parseDocument3, isMap as isMap2, isScalar } from "yaml";
14490
14593
 
@@ -15105,7 +15208,7 @@ function realProvisionDeps(crewHome) {
15105
15208
  enableRoute(route2) {
15106
15209
  const file = configFile();
15107
15210
  if (!file) return false;
15108
- const text2 = readFileSync33(file, "utf8");
15211
+ const text2 = readFileSync34(file, "utf8");
15109
15212
  const doc = parseDocument3(text2);
15110
15213
  const routes = doc.get("routes");
15111
15214
  const entry = routes?.items?.find((r) => r.get("route") === route2);
@@ -15131,7 +15234,7 @@ function realProvisionDeps(crewHome) {
15131
15234
  continue;
15132
15235
  }
15133
15236
  for (const p of plan.unitPaths) {
15134
- if (!existsSync34(p) || readFileSync33(p, "utf8") !== plan.unitContent[p]) unitsStale = true;
15237
+ if (!existsSync34(p) || readFileSync34(p, "utf8") !== plan.unitContent[p]) unitsStale = true;
15135
15238
  }
15136
15239
  }
15137
15240
  const label = labelFor(crewHome, "run");
@@ -15155,7 +15258,7 @@ function realProvisionDeps(crewHome) {
15155
15258
  setShipSetting(key, value2) {
15156
15259
  const file = configFile();
15157
15260
  if (!file) return false;
15158
- const doc = parseDocument3(readFileSync33(file, "utf8"));
15261
+ const doc = parseDocument3(readFileSync34(file, "utf8"));
15159
15262
  if (doc.getIn(["ship", key]) === value2) return false;
15160
15263
  doc.setIn(["ship", key], value2);
15161
15264
  writeFileSync26(file, doc.toString());
@@ -15164,7 +15267,7 @@ function realProvisionDeps(crewHome) {
15164
15267
  applyManagedDefaults() {
15165
15268
  const file = configFile();
15166
15269
  if (!file) return [];
15167
- const doc = parseDocument3(readFileSync33(file, "utf8"));
15270
+ const doc = parseDocument3(readFileSync34(file, "utf8"));
15168
15271
  const wrote = applyManagedDefaultsToDoc(doc);
15169
15272
  if (wrote.length) writeFileSync26(file, doc.toString());
15170
15273
  return wrote;
@@ -15194,7 +15297,7 @@ host=${host}
15194
15297
 
15195
15298
  // src/tunnel.ts
15196
15299
  import { spawn as spawn7 } from "child_process";
15197
- import { existsSync as existsSync35, mkdirSync as mkdirSync25, readFileSync as readFileSync34, writeFileSync as writeFileSync27, unlinkSync as unlinkSync3, readdirSync as readdirSync13 } from "fs";
15300
+ import { existsSync as existsSync35, mkdirSync as mkdirSync25, readFileSync as readFileSync35, writeFileSync as writeFileSync27, unlinkSync as unlinkSync3, readdirSync as readdirSync13 } from "fs";
15198
15301
  import { randomInt } from "crypto";
15199
15302
  import { join as join39 } from "path";
15200
15303
  var DEFAULT_SPAWN_OPTIONS = { stdio: "ignore", detached: true };
@@ -15280,7 +15383,7 @@ function readPersistedTunnel(stateDir, workspaceId) {
15280
15383
  const path = tunnelStatePath(stateDir, workspaceId);
15281
15384
  if (!existsSync35(path)) return void 0;
15282
15385
  try {
15283
- return JSON.parse(readFileSync34(path, "utf8"));
15386
+ return JSON.parse(readFileSync35(path, "utf8"));
15284
15387
  } catch {
15285
15388
  return void 0;
15286
15389
  }
@@ -16825,7 +16928,7 @@ async function resolveOne(tracker, t, seat, approvedStatus, buildingStatus, affi
16825
16928
  }
16826
16929
 
16827
16930
  // src/repo-refresh.ts
16828
- import { existsSync as existsSync37, mkdirSync as mkdirSync26, readFileSync as readFileSync35, writeFileSync as writeFileSync29 } from "fs";
16931
+ import { existsSync as existsSync37, mkdirSync as mkdirSync26, readFileSync as readFileSync36, writeFileSync as writeFileSync29 } from "fs";
16829
16932
  import { dirname as dirname21 } from "path";
16830
16933
  var REFRESH_COOLDOWN_MS = 2 * 6e4;
16831
16934
  var UNSERVABLE_PASSES_BEFORE_ATTENTION = 3;
@@ -16856,7 +16959,7 @@ function applyRepoRows(resolved, rows) {
16856
16959
  function persistRepoRows(stateDir, routeName, rows) {
16857
16960
  const path = resolvedPathFor(stateDir, routeName);
16858
16961
  if (!existsSync37(path)) return;
16859
- const raw = JSON.parse(readFileSync35(path, "utf8"));
16962
+ const raw = JSON.parse(readFileSync36(path, "utf8"));
16860
16963
  applyRepoRows(raw, rows);
16861
16964
  mkdirSync26(dirname21(path), { recursive: true });
16862
16965
  writeFileSync29(path, `${JSON.stringify(raw, null, 2)}
@@ -17509,7 +17612,7 @@ function flagWizardAnswers(f, env) {
17509
17612
  }
17510
17613
 
17511
17614
  // src/release-run.ts
17512
- import { readFileSync as readFileSync36, writeFileSync as writeFileSync31, existsSync as existsSync39, copyFileSync, mkdirSync as mkdirSync28, mkdtempSync as mkdtempSync3, rmSync as rmSync19 } from "fs";
17615
+ import { readFileSync as readFileSync37, writeFileSync as writeFileSync31, existsSync as existsSync39, copyFileSync, mkdirSync as mkdirSync28, mkdtempSync as mkdtempSync3, rmSync as rmSync19 } from "fs";
17513
17616
  import { dirname as dirname24, join as join43 } from "path";
17514
17617
  import { tmpdir as tmpdir5, hostname as hostname4 } from "os";
17515
17618
 
@@ -18286,7 +18389,7 @@ async function bump(o, size) {
18286
18389
  o.emit.error(`no ${o.repo.release.versionFiles[0]} to read a version from`);
18287
18390
  return null;
18288
18391
  }
18289
- const firstText = readFileSync36(first, "utf8");
18392
+ const firstText = readFileSync37(first, "utf8");
18290
18393
  const problem = versionFileProblem(o.repo.release.versionFiles[0], firstText);
18291
18394
  if (problem) {
18292
18395
  o.emit.error(problem);
@@ -18305,7 +18408,7 @@ async function bump(o, size) {
18305
18408
  return version;
18306
18409
  }
18307
18410
  for (const f of files) {
18308
- const src = readFileSync36(f, "utf8");
18411
+ const src = readFileSync37(f, "utf8");
18309
18412
  const out = src.replace(/("version"\s*:\s*")[^"]*(")/, `$1${version}$2`);
18310
18413
  if (out === src) {
18311
18414
  o.emit.error(`no version field in ${f}`);
@@ -18324,7 +18427,7 @@ function writeChangelog(o, version, entries) {
18324
18427
  o.emit.emit(`would prepend ${entries.length} entr(ies) to ${file}`);
18325
18428
  return;
18326
18429
  }
18327
- const existing = existsSync39(path) ? readFileSync36(path, "utf8") : "# Changelog\n";
18430
+ const existing = existsSync39(path) ? readFileSync37(path, "utf8") : "# Changelog\n";
18328
18431
  writeFileSync31(path, insertChangelogSection(existing, section));
18329
18432
  }
18330
18433
  async function confirm(o, expected) {
@@ -19238,6 +19341,7 @@ function runForge(a, io) {
19238
19341
  const tok = sourceFromArgs(a);
19239
19342
  if (typeof tok === "string") return fail2(tok);
19240
19343
  const doc = parseDocument4(io.readFile(io.configFile));
19344
+ doc.setIn(["ship", "forges", host, "username"], forgeDescriptorFor(host).credentialUsername);
19241
19345
  doc.setIn(["ship", "forges", host, "token"], tok);
19242
19346
  io.writeFile(io.configFile, doc.toString());
19243
19347
  io.forges = { ...io.forges ?? {}, [host]: { username: forgeDescriptorFor(host).credentialUsername, token: tok } };
@@ -19841,7 +19945,7 @@ function nextRoles(decision, ran) {
19841
19945
  }
19842
19946
 
19843
19947
  // src/ship-name.ts
19844
- import { existsSync as existsSync42, mkdirSync as mkdirSync30, readFileSync as readFileSync37, renameSync as renameSync5, writeFileSync as writeFileSync32 } from "fs";
19948
+ import { existsSync as existsSync42, mkdirSync as mkdirSync30, readFileSync as readFileSync38, renameSync as renameSync5, writeFileSync as writeFileSync32 } from "fs";
19845
19949
  import { dirname as dirname26 } from "path";
19846
19950
  import { parseDocument as parseDocument5 } from "yaml";
19847
19951
  var ShipNameError = class extends Error {
@@ -19905,7 +20009,7 @@ async function syncRouteName(i) {
19905
20009
  function readSyncedName(resolvedFile) {
19906
20010
  if (!existsSync42(resolvedFile)) return void 0;
19907
20011
  try {
19908
- const v = JSON.parse(readFileSync37(resolvedFile, "utf8")).shipName;
20012
+ const v = JSON.parse(readFileSync38(resolvedFile, "utf8")).shipName;
19909
20013
  return typeof v === "string" ? v : void 0;
19910
20014
  } catch {
19911
20015
  return void 0;
@@ -19915,7 +20019,7 @@ function writeSyncedName(resolvedFile, name) {
19915
20019
  if (!existsSync42(resolvedFile)) return;
19916
20020
  let raw;
19917
20021
  try {
19918
- raw = JSON.parse(readFileSync37(resolvedFile, "utf8"));
20022
+ raw = JSON.parse(readFileSync38(resolvedFile, "utf8"));
19919
20023
  } catch {
19920
20024
  return;
19921
20025
  }
@@ -19930,7 +20034,7 @@ function writeSyncedName(resolvedFile, name) {
19930
20034
  async function reconcileShipName(i) {
19931
20035
  let local = i.ship.name;
19932
20036
  try {
19933
- local = shipNameOfYaml(readFileSync37(i.configFile, "utf8")) ?? local;
20037
+ local = shipNameOfYaml(readFileSync38(i.configFile, "utf8")) ?? local;
19934
20038
  } catch {
19935
20039
  }
19936
20040
  const resolvedFile = resolvedPathFor(i.stateDir, i.route);
@@ -19954,7 +20058,7 @@ async function reconcileShipName(i) {
19954
20058
  }
19955
20059
  if (result.status !== "ok") return result;
19956
20060
  if (result.action === "adopt" || result.action === "row-wins") {
19957
- const text2 = readFileSync37(i.configFile, "utf8");
20061
+ const text2 = readFileSync38(i.configFile, "utf8");
19958
20062
  writeFileSync32(i.configFile, setShipNameInYaml(text2, result.name));
19959
20063
  i.ship.name = result.name;
19960
20064
  if (result.action === "row-wins") {
@@ -20614,7 +20718,7 @@ function describeCapability(i) {
20614
20718
  }
20615
20719
 
20616
20720
  // src/agents-sync-run.ts
20617
- import { existsSync as existsSync43, mkdirSync as mkdirSync31, readFileSync as readFileSync38, writeFileSync as writeFileSync33 } from "fs";
20721
+ import { existsSync as existsSync43, mkdirSync as mkdirSync31, readFileSync as readFileSync39, writeFileSync as writeFileSync33 } from "fs";
20618
20722
  import { dirname as dirname27 } from "path";
20619
20723
  async function runAgentsSync(target, o) {
20620
20724
  const dryRun = o.dryRun ?? false;
@@ -20648,7 +20752,7 @@ async function runAgentsSync(target, o) {
20648
20752
  for (const s of skillResult?.outcomes ?? []) o.say(describeSkillSyncOutcome(s));
20649
20753
  if (!dryRun) {
20650
20754
  const resolvedPath = resolvedPathFor(o.stateDir, target.route);
20651
- const raw = existsSync43(resolvedPath) ? JSON.parse(readFileSync38(resolvedPath, "utf8")) : {};
20755
+ const raw = existsSync43(resolvedPath) ? JSON.parse(readFileSync39(resolvedPath, "utf8")) : {};
20652
20756
  raw.agentPersonas = personaResult.agentPersonas;
20653
20757
  if (skillResult) raw.agentSkills = skillResult.agentSkills;
20654
20758
  if (personaResult.releaseAgentId) raw.releaseAgentId = personaResult.releaseAgentId;
@@ -20770,7 +20874,7 @@ async function showLogEntry(route2, entryId, opts = {}) {
20770
20874
 
20771
20875
  // src/repos-check.ts
20772
20876
  import { spawnSync as spawnSync11 } from "child_process";
20773
- import { readFileSync as readFileSync39 } from "fs";
20877
+ import { readFileSync as readFileSync40 } from "fs";
20774
20878
  import { basename as basename11 } from "path";
20775
20879
  var POSIX_SHELL = /^(?:ba|z|da|k|a|mk)?sh$|^fish$/;
20776
20880
  function checkHookSyntax(script, shell) {
@@ -20811,7 +20915,7 @@ function checkRepoSpec(dir, opts = {}) {
20811
20915
  }
20812
20916
  let parsed;
20813
20917
  try {
20814
- parsed = parseRepoConfig(readFileSync39(file, "utf8"), file);
20918
+ parsed = parseRepoConfig(readFileSync40(file, "utf8"), file);
20815
20919
  } catch (e) {
20816
20920
  if (!(e instanceof RepoConfigError)) throw e;
20817
20921
  return { ...empty, file, error: e.message, exitCode: 1 };
@@ -21343,7 +21447,7 @@ function planSampleTicket(i) {
21343
21447
  }
21344
21448
 
21345
21449
  // src/worktree-sweep.ts
21346
- import { existsSync as existsSync44, readFileSync as readFileSync40, readdirSync as readdirSync14 } from "fs";
21450
+ import { existsSync as existsSync44, readFileSync as readFileSync41, readdirSync as readdirSync14 } from "fs";
21347
21451
  import { basename as basename13, dirname as dirname29, join as join45, resolve as resolve5 } from "path";
21348
21452
  function planWorktreeSweep(target, tickets, contract) {
21349
21453
  const terminal = new Set(contract.statuses.resolved.filter((s) => s !== contract.statuses.verified));
@@ -21524,7 +21628,7 @@ function isHandBackMerge(path, base) {
21524
21628
  if (!msgPath) return false;
21525
21629
  let first;
21526
21630
  try {
21527
- first = readFileSync40(resolve5(path, msgPath), "utf8").split("\n")[0] ?? "";
21631
+ first = readFileSync41(resolve5(path, msgPath), "utf8").split("\n")[0] ?? "";
21528
21632
  } catch {
21529
21633
  return false;
21530
21634
  }
@@ -21674,11 +21778,11 @@ function planEventsSweep(stateDir, retentionDays, now = Date.now()) {
21674
21778
  }
21675
21779
 
21676
21780
  // src/cli.ts
21677
- import { realpathSync as realpathSync3, readFileSync as readFileSync46, existsSync as existsSync52, mkdirSync as mkdirSync38, writeFileSync as writeFileSync38, rmSync as rmSync25, unlinkSync as unlinkSync5, copyFileSync as copyFileSync2, renameSync as renameSync6, statSync as statSync16, readdirSync as readdirSync20 } from "fs";
21781
+ import { realpathSync as realpathSync3, readFileSync as readFileSync47, existsSync as existsSync52, mkdirSync as mkdirSync38, writeFileSync as writeFileSync38, rmSync as rmSync25, unlinkSync as unlinkSync5, copyFileSync as copyFileSync2, renameSync as renameSync6, statSync as statSync16, readdirSync as readdirSync20 } from "fs";
21678
21782
  import { createInterface } from "readline/promises";
21679
21783
 
21680
21784
  // src/periscope-cmd.ts
21681
- import { existsSync as existsSync46, mkdirSync as mkdirSync32, readFileSync as readFileSync41, rmSync as rmSync21, writeFileSync as writeFileSync34 } from "fs";
21785
+ import { existsSync as existsSync46, mkdirSync as mkdirSync32, readFileSync as readFileSync42, rmSync as rmSync21, writeFileSync as writeFileSync34 } from "fs";
21682
21786
  import { join as join47 } from "path";
21683
21787
  var PeriscopeError = class extends Error {
21684
21788
  };
@@ -21704,7 +21808,7 @@ function writePeriscopePrior(stateDir, ticketRecordId, prior) {
21704
21808
  function readPeriscopePrior(stateDir, ticketRecordId) {
21705
21809
  try {
21706
21810
  const p = priorPath(stateDir, ticketRecordId);
21707
- return existsSync46(p) ? JSON.parse(readFileSync41(p, "utf8")) : void 0;
21811
+ return existsSync46(p) ? JSON.parse(readFileSync42(p, "utf8")) : void 0;
21708
21812
  } catch {
21709
21813
  return void 0;
21710
21814
  }
@@ -22259,7 +22363,7 @@ import { dirname as dirOf2, resolve as resolvePath } from "path";
22259
22363
 
22260
22364
  // src/ssh-keys.ts
22261
22365
  import { execFileSync as execFileSync13 } from "child_process";
22262
- import { existsSync as existsSync47, mkdirSync as mkdirSync33, readFileSync as readFileSync42, chmodSync as chmodSync3 } from "fs";
22366
+ import { existsSync as existsSync47, mkdirSync as mkdirSync33, readFileSync as readFileSync43, chmodSync as chmodSync3 } from "fs";
22263
22367
  import { dirname as dirname30, join as join49 } from "path";
22264
22368
  var SshKeyError = class extends Error {
22265
22369
  };
@@ -22293,7 +22397,7 @@ function ensureShipSshKeypair(stateDir) {
22293
22397
  if (!existsSync47(publicKeyPath)) {
22294
22398
  throw new SshKeyError(`${privateKeyPath} exists but ${publicKeyPath} does not \u2014 regenerate or restore it`);
22295
22399
  }
22296
- const publicKey = readFileSync42(publicKeyPath, "utf8").trim();
22400
+ const publicKey = readFileSync43(publicKeyPath, "utf8").trim();
22297
22401
  return { privateKeyPath, publicKeyPath, publicKey };
22298
22402
  }
22299
22403
  function sshKeygenAvailable() {
@@ -22457,7 +22561,7 @@ async function unregisterEndpoint(workspaceId, info, d) {
22457
22561
  import { createServer as createHttpServer } from "http";
22458
22562
  import { spawn as spawn9 } from "child_process";
22459
22563
  import { randomBytes } from "crypto";
22460
- import { existsSync as existsSync48, mkdirSync as mkdirSync34, readFileSync as readFileSync43, writeFileSync as writeFileSync35, unlinkSync as unlinkSync4, readdirSync as readdirSync16 } from "fs";
22564
+ import { existsSync as existsSync48, mkdirSync as mkdirSync34, readFileSync as readFileSync44, writeFileSync as writeFileSync35, unlinkSync as unlinkSync4, readdirSync as readdirSync16 } from "fs";
22461
22565
  import { join as join50 } from "path";
22462
22566
 
22463
22567
  // src/secret-compare.ts
@@ -22557,7 +22661,7 @@ function readPersistedSyncDaemon(stateDir, workspaceId) {
22557
22661
  const path = syncDaemonStatePath(stateDir, workspaceId);
22558
22662
  if (!existsSync48(path)) return void 0;
22559
22663
  try {
22560
- return JSON.parse(readFileSync43(path, "utf8"));
22664
+ return JSON.parse(readFileSync44(path, "utf8"));
22561
22665
  } catch {
22562
22666
  return void 0;
22563
22667
  }
@@ -23106,7 +23210,7 @@ function contractFixers(dir, baseRef, tickets, locate) {
23106
23210
  }
23107
23211
 
23108
23212
  // src/daemon.ts
23109
- import { existsSync as existsSync49, mkdirSync as mkdirSync36, readFileSync as readFileSync44, readdirSync as readdirSync17, rmSync as rmSync22, statSync as statSync14, writeFileSync as writeFileSync36 } from "fs";
23213
+ import { existsSync as existsSync49, mkdirSync as mkdirSync36, readFileSync as readFileSync45, readdirSync as readdirSync17, rmSync as rmSync22, statSync as statSync14, writeFileSync as writeFileSync36 } from "fs";
23110
23214
  import { join as join52 } from "path";
23111
23215
  import { randomBytes as randomBytes2 } from "crypto";
23112
23216
  var DEFAULT_FLOOR_MS = 5e3;
@@ -23227,7 +23331,7 @@ function noteDaemonStarted(stateDir, now = Date.now()) {
23227
23331
  }
23228
23332
  function readDaemonStarted(stateDir) {
23229
23333
  try {
23230
- const v = JSON.parse(readFileSync44(daemonStampFor(stateDir), "utf8")).startedAt;
23334
+ const v = JSON.parse(readFileSync45(daemonStampFor(stateDir), "utf8")).startedAt;
23231
23335
  return typeof v === "number" ? v : null;
23232
23336
  } catch {
23233
23337
  return null;
@@ -23410,6 +23514,12 @@ async function runOnePassUnguarded(o) {
23410
23514
  clearShipAttention(o.state, workspaceAllowanceKey(role));
23411
23515
  }).catch(async (e) => {
23412
23516
  const message = e.message;
23517
+ if (e instanceof SignedOutError) {
23518
+ breaker.pause(role, SIGNED_OUT_PAUSE_MS);
23519
+ emit.warn(`${role} paused: ${message}`, { step: "agent", role });
23520
+ await raiseShipAttention({ state: o.state, emit, route: o.route, ship: o.ship }, signedOutItem(e.toolchain, e.mode));
23521
+ return;
23522
+ }
23413
23523
  if (e instanceof WorkspaceAllowanceExhausted) {
23414
23524
  breaker.pause(role, ALLOWANCE_PAUSE_MS);
23415
23525
  emit.warn(`${role} paused: ${message}`, { step: "agent", role });
@@ -23751,6 +23861,7 @@ async function runRoleAgentLocked(role, decision, ctx, hooks, held) {
23751
23861
  if (plan.workspaceAuth && streamShowsAllowanceRefusal(plan.streamPath)) {
23752
23862
  throw new WorkspaceAllowanceExhausted("the inference endpoint refused the run: the workspace is past its AI allowance");
23753
23863
  }
23864
+ await settleSignOut(plan, tracker, ctx.state, workingId, emit);
23754
23865
  }
23755
23866
  } finally {
23756
23867
  await settleRunEnd({
@@ -23787,6 +23898,26 @@ async function buildEnvironment(route2, ship, ticket) {
23787
23898
  const attachments = await tracker.attachmentsProbe().catch(() => null);
23788
23899
  return renderEnvironment({ route: route2, userAgent: ship.userAgent, repos: await withRepoMaps(repos, ship.mapTokenBudget, ship.shell), contract, sourceTicket: ticket, authorship, attachments, shipName: ship.name, ...sandboxEnv(ship) });
23789
23900
  }
23901
+ async function settleSignOut(plan, tracker, state, ticketId, emit) {
23902
+ const toolchain = plan.toolchain;
23903
+ if (!toolchain) return;
23904
+ if (!streamShowsSignedOut(plan.streamPath)) {
23905
+ if (streamShowsRealTurn(plan.streamPath)) clearShipAttention(state, signedOutKey(toolchain));
23906
+ return;
23907
+ }
23908
+ if (ticketId) {
23909
+ try {
23910
+ const affinity = await tracker.claimAffinity();
23911
+ const current = await tracker.ticket(ticketId);
23912
+ if (affinity && current[affinity.column] === affinity.shipId) {
23913
+ await tracker.updateTicket(ticketId, { [affinity.column]: null }, current.updated_at);
23914
+ }
23915
+ } catch (e) {
23916
+ emit.warn(`could not release the ticket hold after a sign-out: ${e.message}`, { step: "agent" });
23917
+ }
23918
+ }
23919
+ throw new SignedOutError(toolchain, plan.authMode ?? "login");
23920
+ }
23790
23921
 
23791
23922
  // src/control-api.ts
23792
23923
  import { createHash as createHash11 } from "crypto";
@@ -24477,7 +24608,7 @@ import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/
24477
24608
 
24478
24609
  // src/periscope-endpoints.ts
24479
24610
  import { randomBytes as randomBytes3 } from "crypto";
24480
- import { chmodSync as chmodSync5, existsSync as existsSync51, mkdirSync as mkdirSync37, readdirSync as readdirSync19, readFileSync as readFileSync45, rmSync as rmSync24, writeFileSync as writeFileSync37 } from "fs";
24611
+ import { chmodSync as chmodSync5, existsSync as existsSync51, mkdirSync as mkdirSync37, readdirSync as readdirSync19, readFileSync as readFileSync46, rmSync as rmSync24, writeFileSync as writeFileSync37 } from "fs";
24481
24612
  import { join as join54 } from "path";
24482
24613
  var secretDir = (stateDir) => join54(stateDir, "periscope-mcp");
24483
24614
  var secretFile = (stateDir, id) => join54(secretDir(stateDir), `${id}.json`);
@@ -24494,7 +24625,7 @@ function mintPeriscopeSecret(stateDir, ticketRecordId) {
24494
24625
  }
24495
24626
  function readPeriscopeSecret(stateDir, ticketRecordId) {
24496
24627
  try {
24497
- const v = JSON.parse(readFileSync45(secretFile(stateDir, ticketRecordId), "utf8"));
24628
+ const v = JSON.parse(readFileSync46(secretFile(stateDir, ticketRecordId), "utf8"));
24498
24629
  return typeof v.secret === "string" && v.secret ? v.secret : void 0;
24499
24630
  } catch {
24500
24631
  return void 0;
@@ -24505,7 +24636,7 @@ function removePeriscopeSecret(stateDir, ticketRecordId) {
24505
24636
  }
24506
24637
  function readPeriscopeEndpoint(stateDir, ticketRecordId) {
24507
24638
  try {
24508
- return JSON.parse(readFileSync45(endpointFile(stateDir, ticketRecordId), "utf8"));
24639
+ return JSON.parse(readFileSync46(endpointFile(stateDir, ticketRecordId), "utf8"));
24509
24640
  } catch {
24510
24641
  return void 0;
24511
24642
  }
@@ -25254,7 +25385,7 @@ if (command === "provision") {
25254
25385
  process.exit(outcome.code);
25255
25386
  }
25256
25387
  if (command === "pool-member") {
25257
- const pool = poolConfigFrom(argv, process.env, () => readFileSync46(0, "utf8"));
25388
+ const pool = poolConfigFrom(argv, process.env, () => readFileSync47(0, "utf8"));
25258
25389
  if (typeof pool === "string") {
25259
25390
  process.stderr.write(`crew pool-member: ${pool}
25260
25391
  `);
@@ -25415,7 +25546,7 @@ if (command === "passenger-sync-daemon") {
25415
25546
  if (intelFlavor(loadRepoConfig(t.dir)?.hooks.intel) === "serena") intelImage = sandboxImageFor(images ??= readSandboxState(ship.stateDir), t.name);
25416
25547
  } catch {
25417
25548
  }
25418
- out.push({ ...repo, dir: t.dir, token, username: forgeFor(ship.forges, repo.host)?.entry.username, intelImage });
25549
+ out.push({ ...repo, dir: t.dir, token, username: forgeFor(ship.forges, repo.host)?.entry.username || forgeDescriptorFor(repo.host).credentialUsername, intelImage });
25419
25550
  }
25420
25551
  return out;
25421
25552
  }, buildSandboxCachePass = function(c, ship, state2, emit2) {
@@ -25426,7 +25557,7 @@ if (command === "passenger-sync-daemon") {
25426
25557
  volume: settings.volume,
25427
25558
  repos: () => sandboxCacheRepos(c, ship),
25428
25559
  lock: (dir) => acquireCheckoutLock(dir),
25429
- fail: (key, message) => raiseShipAttention({ state: state2, emit: emit2, route: c, ship }, { kind: "sandbox_cache", key, message, since: (/* @__PURE__ */ new Date()).toISOString() }).then(() => void 0),
25560
+ fail: (key, message, failedAt) => raiseShipAttention({ state: state2, emit: emit2, route: c, ship }, { kind: "sandbox_cache", key, message, since: failedAt, lastFailedAt: failedAt }).then(() => void 0),
25430
25561
  ok: (key) => clearShipAttention(state2, key)
25431
25562
  });
25432
25563
  return async () => {
@@ -25518,7 +25649,7 @@ if (command === "passenger-sync-daemon") {
25518
25649
  throw e;
25519
25650
  }
25520
25651
  if (command === "credential") {
25521
- const r = runCredential(positional[1], readFileSync46(0, "utf8"), cfg.ship.forges, cfg.ship.stateDir);
25652
+ const r = runCredential(positional[1], readFileSync47(0, "utf8"), cfg.ship.forges, cfg.ship.stateDir);
25522
25653
  process.stdout.write(r.out);
25523
25654
  process.stderr.write(r.err);
25524
25655
  process.exit(r.code);
@@ -25530,9 +25661,9 @@ if (command === "passenger-sync-daemon") {
25530
25661
  forges: cfg.ship.forges,
25531
25662
  stateDir: cfg.ship.stateDir,
25532
25663
  configFile: cfg.configFile,
25533
- readFile: (p) => readFileSync46(p, "utf8"),
25664
+ readFile: (p) => readFileSync47(p, "utf8"),
25534
25665
  writeFile: (p, t) => writeFileSync38(p, t),
25535
- stdin: () => readFileSync46(0, "utf8"),
25666
+ stdin: () => readFileSync47(0, "utf8"),
25536
25667
  env: process.env
25537
25668
  }
25538
25669
  );
@@ -25929,7 +26060,7 @@ if (command === "passenger-sync-daemon") {
25929
26060
  for (const a of plan.actions) {
25930
26061
  if (a.kind === "enable") {
25931
26062
  if (prompter && !await prompter.confirm(`Route ${a.route} passes every check \u2014 set enabled: true in ${cfg.configFile}?`, false)) continue;
25932
- writeFileSync38(cfg.configFile, planEnable(readFileSync46(cfg.configFile, "utf8"), a.route));
26063
+ writeFileSync38(cfg.configFile, planEnable(readFileSync47(cfg.configFile, "utf8"), a.route));
25933
26064
  enabledSound.add(a.route);
25934
26065
  changed2.push(`enabled route ${a.route} in ${cfg.configFile}`);
25935
26066
  } else {
@@ -27430,7 +27561,7 @@ ${cu.tail.trimEnd()}
27430
27561
  `);
27431
27562
  if (!dryRun) {
27432
27563
  const resolvedPath = resolvedPathFor(cfg.ship.stateDir, target.route);
27433
- const raw = existsSync52(resolvedPath) ? JSON.parse(readFileSync46(resolvedPath, "utf8")) : {};
27564
+ const raw = existsSync52(resolvedPath) ? JSON.parse(readFileSync47(resolvedPath, "utf8")) : {};
27434
27565
  raw.agentSkills = result.agentSkills;
27435
27566
  mkdirSync38(dirOf2(resolvedPath), { recursive: true });
27436
27567
  writeFileSync38(resolvedPath, `${JSON.stringify(raw, null, 2)}
@@ -27456,7 +27587,7 @@ ${cu.tail.trimEnd()}
27456
27587
  throw e;
27457
27588
  }
27458
27589
  const oldName = cfg.ship.name;
27459
- writeFileSync38(cfg.configFile, setShipNameInYaml(readFileSync46(cfg.configFile, "utf8"), newName));
27590
+ writeFileSync38(cfg.configFile, setShipNameInYaml(readFileSync47(cfg.configFile, "utf8"), newName));
27460
27591
  cfg.ship.name = newName;
27461
27592
  let renameMachineId;
27462
27593
  try {
@@ -27503,7 +27634,7 @@ ${cu.tail.trimEnd()}
27503
27634
  process.exit(2);
27504
27635
  };
27505
27636
  if (sub !== "add" && sub !== "list" && sub !== "clone" && sub !== "hooks" && sub !== "check") usage2();
27506
- const configText = readFileSync46(cfg.configFile, "utf8");
27637
+ const configText = readFileSync47(cfg.configFile, "utf8");
27507
27638
  if (sub === "check") {
27508
27639
  try {
27509
27640
  const routeArg = value("route") ?? positional[2];
@@ -27544,7 +27675,7 @@ ${cu.tail.trimEnd()}
27544
27675
  }
27545
27676
  }, read: (r) => {
27546
27677
  try {
27547
- return readFileSync46(resolvePath(dir, r), "utf8");
27678
+ return readFileSync47(resolvePath(dir, r), "utf8");
27548
27679
  } catch {
27549
27680
  return void 0;
27550
27681
  }
@@ -27695,7 +27826,7 @@ ${cu.tail.trimEnd()}
27695
27826
  let resolvedRemotes;
27696
27827
  if (existsSync52(resolvedFile)) {
27697
27828
  try {
27698
- resolvedRemotes = JSON.parse(readFileSync46(resolvedFile, "utf8"));
27829
+ resolvedRemotes = JSON.parse(readFileSync47(resolvedFile, "utf8"));
27699
27830
  } catch {
27700
27831
  }
27701
27832
  }
@@ -27717,7 +27848,7 @@ ${cu.tail.trimEnd()}
27717
27848
  repoHasHooks: (d) => REPO_CONFIG_FILENAMES.some((f) => {
27718
27849
  try {
27719
27850
  const path = resolvePath(d, f);
27720
- return Object.keys(parseRepoConfig(readFileSync46(path, "utf8"), path).hooks).length > 0;
27851
+ return Object.keys(parseRepoConfig(readFileSync47(path, "utf8"), path).hooks).length > 0;
27721
27852
  } catch {
27722
27853
  return false;
27723
27854
  }
@@ -27826,7 +27957,7 @@ ${cu.tail.trimEnd()}
27826
27957
  process.stdout.write(`created Repos row ${String(rowPayload.name)} (${String(rowPayload.remote)}) ${created.id}
27827
27958
  `);
27828
27959
  if (existsSync52(resolvedFile)) {
27829
- const raw = JSON.parse(readFileSync46(resolvedFile, "utf8"));
27960
+ const raw = JSON.parse(readFileSync47(resolvedFile, "utf8"));
27830
27961
  raw.repoNames = { ...raw.repoNames, [created.id]: String(rowPayload.name) };
27831
27962
  raw.repoRemotes = { ...raw.repoRemotes, [created.id]: String(rowPayload.remote) };
27832
27963
  writeFileSync38(resolvedFile, `${JSON.stringify(raw, null, 2)}
@@ -28204,7 +28335,7 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
28204
28335
  process.exit(2);
28205
28336
  }
28206
28337
  if (found.shipNameAdopted && !dryRun) {
28207
- writeFileSync38(cfg.configFile, setShipNameInYaml(readFileSync46(cfg.configFile, "utf8"), found.shipNameAdopted));
28338
+ writeFileSync38(cfg.configFile, setShipNameInYaml(readFileSync47(cfg.configFile, "utf8"), found.shipNameAdopted));
28208
28339
  cfg.ship.name = found.shipNameAdopted;
28209
28340
  }
28210
28341
  if (found.shipRow) {
@@ -28250,7 +28381,7 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
28250
28381
  const hadPreviousFile = existsSync52(resolvedPath);
28251
28382
  if (hadPreviousFile) {
28252
28383
  try {
28253
- previous = JSON.parse(readFileSync46(resolvedPath, "utf8"));
28384
+ previous = JSON.parse(readFileSync47(resolvedPath, "utf8"));
28254
28385
  } catch {
28255
28386
  }
28256
28387
  }
@@ -28838,8 +28969,13 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
28838
28969
  }
28839
28970
  const dropped = flag("prune") ? await cache.prune(served) : [];
28840
28971
  const mirrors = await cache.list();
28972
+ const open = openShipAttention(new State(cfg.ship.stateDir));
28973
+ const failures = served.flatMap((r) => {
28974
+ const item = open.find((i) => i.kind === "sandbox_cache" && i.key === sandboxCacheKey(settings.volume, r));
28975
+ return item ? [{ repo: `${r.host}/${r.path}`, lastFailedAt: item.lastFailedAt ?? item.since, message: item.message }] : [];
28976
+ });
28841
28977
  if (flag("json")) {
28842
- process.stdout.write(`${JSON.stringify({ volume: settings.volume, mirrors, dropped }, null, 2)}
28978
+ process.stdout.write(`${JSON.stringify({ volume: settings.volume, mirrors, failures, dropped }, null, 2)}
28843
28979
  `);
28844
28980
  } else {
28845
28981
  for (const d of dropped) process.stdout.write(`dropped ${d.host}/${d.path}
@@ -28850,6 +28986,8 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
28850
28986
  process.stdout.write(`${m.repo.host}/${m.repo.path} last refresh ${m.lastRefresh ?? "never"}${m.intel ? ` intel ${m.intel.commit.slice(0, 12)} (${m.intel.indexedAt ?? "age unknown"})` : ""}
28851
28987
  `);
28852
28988
  }
28989
+ for (const f of failures) process.stdout.write(`${f.repo} REFRESH FAILING (last failed ${f.lastFailedAt}): ${f.message}
28990
+ `);
28853
28991
  }
28854
28992
  } catch (e) {
28855
28993
  process.stderr.write(`crew sandbox cache: ${e instanceof Error ? e.message : String(e)}
@@ -29760,7 +29898,7 @@ ${where.join("\n")}`);
29760
29898
  };
29761
29899
  const droppedRoutes = [];
29762
29900
  try {
29763
- const rawCfg = parseYaml2(readFileSync46(cfg.configFile, "utf8"));
29901
+ const rawCfg = parseYaml2(readFileSync47(cfg.configFile, "utf8"));
29764
29902
  for (const r of rawCfg?.routes ?? []) {
29765
29903
  if (typeof r?.route !== "string" || cfg.routes.some((x) => x.route === r.route)) continue;
29766
29904
  try {
@@ -29879,7 +30017,7 @@ ${where.join("\n")}`);
29879
30017
  }
29880
30018
  case "log": {
29881
30019
  try {
29882
- const text2 = readFileSync46(cfg.ship.logFile, "utf8").split("\n");
30020
+ const text2 = readFileSync47(cfg.ship.logFile, "utf8").split("\n");
29883
30021
  process.stdout.write(`${text2.slice(-(Number(value("tail")) || 40)).join("\n")}
29884
30022
  `);
29885
30023
  } catch {
@@ -29902,7 +30040,7 @@ ${where.join("\n")}`);
29902
30040
  };
29903
30041
  if (!route2.resolved) fail2(`route ${route2.route} is not connected \u2014 run \`crew connect\` first`);
29904
30042
  const tracker = new Tracker(route2, cfg.ship);
29905
- const configText = readFileSync46(cfg.configFile, "utf8");
30043
+ const configText = readFileSync47(cfg.configFile, "utf8");
29906
30044
  const cfgBase = dirname34(cfg.configFile);
29907
30045
  const configured2 = configuredRepos(configText, route2.route, cfgBase);
29908
30046
  const dir = configured2.find((r) => r.name === SAMPLE_REPO)?.dir ?? sampleDir(configured2.map((r) => r.dir), route2.reposBasePath, route2.route.split("/")[0]);
@@ -29940,7 +30078,7 @@ ${where.join("\n")}`);
29940
30078
  if (row) {
29941
30079
  await tracker.deleteRepoRow(row.id);
29942
30080
  if (existsSync52(resolvedFile)) {
29943
- const raw = JSON.parse(readFileSync46(resolvedFile, "utf8"));
30081
+ const raw = JSON.parse(readFileSync47(resolvedFile, "utf8"));
29944
30082
  if (raw.repoNames) delete raw.repoNames[row.id];
29945
30083
  if (raw.repoRemotes) delete raw.repoRemotes[row.id];
29946
30084
  writeFileSync38(resolvedFile, `${JSON.stringify(raw, null, 2)}
@@ -29980,7 +30118,7 @@ ${where.join("\n")}`);
29980
30118
  });
29981
30119
  repoId = (await tracker.createRepoRow(payload)).id;
29982
30120
  if (existsSync52(resolvedFile)) {
29983
- const raw = JSON.parse(readFileSync46(resolvedFile, "utf8"));
30121
+ const raw = JSON.parse(readFileSync47(resolvedFile, "utf8"));
29984
30122
  raw.repoNames = { ...raw.repoNames, [repoId]: SAMPLE_REPO };
29985
30123
  raw.repoRemotes = { ...raw.repoRemotes, [repoId]: SAMPLE_REMOTE };
29986
30124
  writeFileSync38(resolvedFile, `${JSON.stringify(raw, null, 2)}
@@ -30399,6 +30537,14 @@ watermark: ${state.watermark()}
30399
30537
  for (const message of managedRuntimeWarnings(shipKind, cfg.ship)) problems.push({ severity: "attention", kind: "managed_runtime_default", message });
30400
30538
  for (const message of managedGitIdentityWarnings(shipKind, readGlobalGitIdentity())) problems.push({ severity: "attention", kind: "managed_git_identity", message });
30401
30539
  }
30540
+ const cacheSettings = cfg.ship.runner === "sandbox" ? cacheSettingsOf(cfg.ship.sandbox) : null;
30541
+ if (cacheSettings && !flag("offline")) {
30542
+ try {
30543
+ const missing = unmirroredRepos(sandboxCacheRepos(route2, cfg.ship), await new SandboxCache(cacheSettings).list());
30544
+ for (const r of missing) problems.push({ severity: "attention", kind: "sandbox_cache_unmirrored", route: route2.route, message: `${r} has no mirror in the cache volume ${cacheSettings.volume}, so sandboxed runs cannot clone it`, remedy: "see `crew sandbox cache` for the last refresh failure" });
30545
+ } catch {
30546
+ }
30547
+ }
30402
30548
  for (const t of rejectedTunnels(cfg.ship.stateDir)) {
30403
30549
  problems.push({ severity: "attention", kind: TUNNEL_REJECTED_KEY, message: tunnelRejectedMessage(t.workspaceId, t.quickExits), remedy: "rotate the slug with `crew rotate-passenger-url`" });
30404
30550
  }
@@ -30454,7 +30600,7 @@ routes: ${cfg.routes.map((c) => c.route).join(", ")}
30454
30600
  const unitPlan = planInstall(cfg.ship, CREW_HOME, host, detectSystemd(), "run", installContext());
30455
30601
  const abandons = unitAbandonsAgents(unitPlan, (p) => {
30456
30602
  try {
30457
- return readFileSync46(p, "utf8");
30603
+ return readFileSync47(p, "utf8");
30458
30604
  } catch {
30459
30605
  return null;
30460
30606
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tablation/crew",
3
- "version": "0.127.1",
3
+ "version": "0.127.3",
4
4
  "description": "A standing team of headless agents that picks work off a Tablation board, does it on a machine you control, and reports back.",
5
5
  "type": "module",
6
6
  "license": "MIT",