@tablation/crew 0.127.1 → 0.127.2

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 +217 -92
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -6356,7 +6356,7 @@ function attentionProblems(items, already) {
6356
6356
  import { createHash as createHash7 } from "crypto";
6357
6357
  import { spawn as spawn5 } from "child_process";
6358
6358
  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";
6359
+ import { existsSync as existsSync30, mkdirSync as mkdirSync19, readFileSync as readFileSync28, renameSync as renameSync3, writeFileSync as writeFileSync20, rmSync as rmSync15 } from "fs";
6360
6360
  import { dirname as dirname15, join as join32 } from "path";
6361
6361
  import { fileURLToPath } from "url";
6362
6362
  import { tmpdir as tmpdir3 } from "os";
@@ -6374,12 +6374,12 @@ import { spawnSync as spawnSync7 } from "child_process";
6374
6374
  import { existsSync as existsSync29, readdirSync as readdirSync11 } from "fs";
6375
6375
 
6376
6376
  // src/interrupted.ts
6377
- import { existsSync as existsSync27, readdirSync as readdirSync9, readFileSync as readFileSync26 } from "fs";
6377
+ import { existsSync as existsSync27, readdirSync as readdirSync9, readFileSync as readFileSync27 } from "fs";
6378
6378
  import { join as join30 } from "path";
6379
6379
 
6380
6380
  // src/agent.ts
6381
6381
  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";
6382
+ 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
6383
  import { StringDecoder } from "string_decoder";
6384
6384
  import { finished } from "stream/promises";
6385
6385
  import { join as join29, dirname as dirname14, basename as basename9 } from "path";
@@ -6887,6 +6887,97 @@ var State = class _State {
6887
6887
  }
6888
6888
  };
6889
6889
 
6890
+ // src/signed-out.ts
6891
+ import { readFileSync as readFileSync20 } from "fs";
6892
+ 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;
6893
+ function authModeOf(spec, hostEnv = process.env) {
6894
+ const env = { ...hostEnv, ...spec.env };
6895
+ const on = (v) => !!v && v !== "0" && v.toLowerCase() !== "false";
6896
+ if (spec.auth === "subscription") return "login";
6897
+ if (on(env.CLAUDE_CODE_USE_VERTEX)) return "vertex";
6898
+ if (env.ANTHROPIC_API_KEY) return "api-key";
6899
+ return "login";
6900
+ }
6901
+ var SIGNED_OUT_REMEDY = {
6902
+ vertex: "run `gcloud auth application-default login` on this Mac",
6903
+ "api-key": "check the API key the toolchain uses (ANTHROPIC_API_KEY) is valid and unrevoked",
6904
+ login: "run `claude` in a terminal on this Mac and `/login`"
6905
+ };
6906
+ function signedOutMessage(toolchain, mode) {
6907
+ return `toolchain ${toolchain} is signed out \u2014 its runs end at the harness's sign-out error. To fix: ${SIGNED_OUT_REMEDY[mode]}`;
6908
+ }
6909
+ var signedOutKey = (toolchain) => `signed_out:${toolchain}`;
6910
+ function signedOutItem(toolchain, mode, nowIso = (/* @__PURE__ */ new Date()).toISOString()) {
6911
+ return { kind: "signed_out", key: signedOutKey(toolchain), since: nowIso, message: signedOutMessage(toolchain, mode) };
6912
+ }
6913
+ var textOf = (v) => typeof v === "string" ? v : JSON.stringify(v ?? "");
6914
+ function eventsShowSignedOut(events) {
6915
+ let assistantTurns = 0;
6916
+ let toolUse = false;
6917
+ let onlyTurnSignedOut = false;
6918
+ for (const ev of events) {
6919
+ if (typeof ev !== "object" || ev === null) continue;
6920
+ const e = ev;
6921
+ if (e.type === "result") {
6922
+ if (e.is_error === true && SIGNED_OUT_PATTERN.test(textOf(e.result))) return true;
6923
+ } else if (e.type === "assistant") {
6924
+ assistantTurns++;
6925
+ const content = Array.isArray(e.message?.content) ? e.message.content : [];
6926
+ if (content.some((b) => b?.type === "tool_use")) toolUse = true;
6927
+ if (SIGNED_OUT_PATTERN.test(textOf(e.message))) {
6928
+ if (e.isApiErrorMessage === true || e.error !== void 0) return true;
6929
+ onlyTurnSignedOut = true;
6930
+ }
6931
+ }
6932
+ }
6933
+ return assistantTurns === 1 && onlyTurnSignedOut && !toolUse;
6934
+ }
6935
+ function streamShowsSignedOut(streamPath) {
6936
+ let raw;
6937
+ try {
6938
+ raw = readFileSync20(streamPath, "utf8");
6939
+ } catch {
6940
+ return false;
6941
+ }
6942
+ if (!SIGNED_OUT_PATTERN.test(raw)) return false;
6943
+ const events = [];
6944
+ for (const line of raw.split("\n")) {
6945
+ if (!line.trim()) continue;
6946
+ try {
6947
+ events.push(JSON.parse(line));
6948
+ } catch {
6949
+ }
6950
+ }
6951
+ return eventsShowSignedOut(events);
6952
+ }
6953
+ function streamShowsRealTurn(streamPath) {
6954
+ let raw;
6955
+ try {
6956
+ raw = readFileSync20(streamPath, "utf8");
6957
+ } catch {
6958
+ return false;
6959
+ }
6960
+ return raw.split("\n").some((line) => {
6961
+ if (!line.includes('"assistant"')) return false;
6962
+ try {
6963
+ const e = JSON.parse(line);
6964
+ return e.type === "assistant" && e.isApiErrorMessage !== true && e.error === void 0 && !SIGNED_OUT_PATTERN.test(textOf(e.message));
6965
+ } catch {
6966
+ return false;
6967
+ }
6968
+ });
6969
+ }
6970
+ var SignedOutError = class extends Error {
6971
+ toolchain;
6972
+ mode;
6973
+ constructor(toolchain, mode) {
6974
+ super(signedOutMessage(toolchain, mode));
6975
+ this.toolchain = toolchain;
6976
+ this.mode = mode;
6977
+ }
6978
+ };
6979
+ var SIGNED_OUT_PAUSE_MS = 15 * 60 * 1e3;
6980
+
6890
6981
  // src/environment.ts
6891
6982
  import { basename as basename6 } from "path";
6892
6983
 
@@ -7286,7 +7377,7 @@ function renderEnvironment(i) {
7286
7377
  }
7287
7378
 
7288
7379
  // src/intel-bridge.ts
7289
- import { existsSync as existsSync20, mkdirSync as mkdirSync14, readFileSync as readFileSync20, rmSync as rmSync11, writeFileSync as writeFileSync15 } from "fs";
7380
+ import { existsSync as existsSync20, mkdirSync as mkdirSync14, readFileSync as readFileSync21, rmSync as rmSync11, writeFileSync as writeFileSync15 } from "fs";
7290
7381
  import { dirname as dirname11, join as join22 } from "path";
7291
7382
  import { Client } from "@modelcontextprotocol/sdk/client/index.js";
7292
7383
  import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
@@ -7313,7 +7404,7 @@ function writeAttachFile(file, url) {
7313
7404
  }
7314
7405
  function readAttachFile(file) {
7315
7406
  try {
7316
- const url = readFileSync20(file, "utf8").trim();
7407
+ const url = readFileSync21(file, "utf8").trim();
7317
7408
  return /^https?:\/\/\S+$/i.test(url) ? url : void 0;
7318
7409
  } catch {
7319
7410
  return void 0;
@@ -7692,7 +7783,7 @@ var BeforeFirstEditTracker = class {
7692
7783
  };
7693
7784
 
7694
7785
  // 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";
7786
+ import { existsSync as existsSync21, mkdirSync as mkdirSync15, readdirSync as readdirSync6, readFileSync as readFileSync22, rmSync as rmSync12, writeFileSync as writeFileSync16 } from "fs";
7696
7787
  import { join as join23 } from "path";
7697
7788
 
7698
7789
  // src/pending-backoff.ts
@@ -7909,7 +8000,7 @@ async function replayQueuedAgentRuns(stateDir, route2, keyFor, log, now = /* @__
7909
8000
  const path = join23(dir, f);
7910
8001
  let q2;
7911
8002
  try {
7912
- q2 = JSON.parse(readFileSync21(path, "utf8"));
8003
+ q2 = JSON.parse(readFileSync22(path, "utf8"));
7913
8004
  } catch {
7914
8005
  continue;
7915
8006
  }
@@ -7962,7 +8053,7 @@ async function replayQueuedTicketCounters(stateDir, route2, keyFor, log, now = /
7962
8053
  const path = join23(dir, f);
7963
8054
  let q2;
7964
8055
  try {
7965
- q2 = JSON.parse(readFileSync21(path, "utf8"));
8056
+ q2 = JSON.parse(readFileSync22(path, "utf8"));
7966
8057
  } catch {
7967
8058
  continue;
7968
8059
  }
@@ -8184,7 +8275,7 @@ var HandoffTranscriptTracker = class {
8184
8275
  };
8185
8276
 
8186
8277
  // 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";
8278
+ import { existsSync as existsSync23, mkdirSync as mkdirSync16, readdirSync as readdirSync7, readFileSync as readFileSync23, rmSync as rmSync13, writeFileSync as writeFileSync17 } from "fs";
8188
8279
  import { join as join25 } from "path";
8189
8280
 
8190
8281
  // src/ticket-branch.ts
@@ -9879,7 +9970,7 @@ function listPendingHandoffs(stateDir, route2) {
9879
9970
  const out = [];
9880
9971
  for (const f of readdirSync7(dir).filter((n) => n.endsWith(".json")).sort()) {
9881
9972
  try {
9882
- const rec = JSON.parse(readFileSync22(join25(dir, f), "utf8"));
9973
+ const rec = JSON.parse(readFileSync23(join25(dir, f), "utf8"));
9883
9974
  if (rec.version === 1 && rec.ticketRecordId && (!route2 || rec.route === route2)) out.push(rec);
9884
9975
  } catch {
9885
9976
  }
@@ -9997,7 +10088,7 @@ function expandSkillMarkers(text2, lookup2, opts = {}) {
9997
10088
  }
9998
10089
 
9999
10090
  // src/skills.ts
10000
- import { readFileSync as readFileSync24, readdirSync as readdirSync8, existsSync as existsSync25 } from "fs";
10091
+ import { readFileSync as readFileSync25, readdirSync as readdirSync8, existsSync as existsSync25 } from "fs";
10001
10092
  import { join as join27 } from "path";
10002
10093
  import { parse as parseYaml } from "yaml";
10003
10094
  import { TablationClient as TablationClient3, StaleWriteError as StaleWriteError3, TablationApiError as TablationApiError2 } from "@tablation/client";
@@ -10014,7 +10105,7 @@ ${additions}`;
10014
10105
  }
10015
10106
 
10016
10107
  // src/agents.ts
10017
- import { readFileSync as readFileSync23, existsSync as existsSync24 } from "fs";
10108
+ import { readFileSync as readFileSync24, existsSync as existsSync24 } from "fs";
10018
10109
  import { join as join26 } from "path";
10019
10110
  import { TablationClient as TablationClient2, StaleWriteError as StaleWriteError2, TablationApiError } from "@tablation/client";
10020
10111
  var PERSONA_NAME = {
@@ -10031,10 +10122,10 @@ var AgentsSyncError = class extends Error {
10031
10122
  function personaDefaultPrompt(promptsDir, role) {
10032
10123
  const briefPath = join26(promptsDir, "personas", `lane-${role}.md`);
10033
10124
  if (!existsSync24(briefPath)) throw new AgentsSyncError(`no brief at ${briefPath}`);
10034
- if (SELF_CONTAINED_ROLES.has(role)) return readFileSync23(briefPath, "utf8");
10125
+ if (SELF_CONTAINED_ROLES.has(role)) return readFileSync24(briefPath, "utf8");
10035
10126
  const commonPath = join26(promptsDir, "personas", "common.md");
10036
- if (!existsSync24(commonPath)) return readFileSync23(briefPath, "utf8");
10037
- return `${readFileSync23(commonPath, "utf8")}${readFileSync23(briefPath, "utf8")}`;
10127
+ if (!existsSync24(commonPath)) return readFileSync24(briefPath, "utf8");
10128
+ return `${readFileSync24(commonPath, "utf8")}${readFileSync24(briefPath, "utf8")}`;
10038
10129
  }
10039
10130
  async function resolveAgentId(route2, role, opts) {
10040
10131
  const cached = route2.resolved?.agentPersonas?.[role]?.agentId;
@@ -10481,7 +10572,7 @@ function describeCrewLink(o) {
10481
10572
  var SkillSyncError = class extends Error {
10482
10573
  };
10483
10574
  function parseSkillFile(path) {
10484
- const text2 = readFileSync24(path, "utf8");
10575
+ const text2 = readFileSync25(path, "utf8");
10485
10576
  const match = /^---\n([\s\S]*?)\n---\n([\s\S]*)$/.exec(text2);
10486
10577
  if (!match) throw new SkillSyncError(`${path}: missing YAML frontmatter (expected a leading --- block)`);
10487
10578
  const frontmatter = match[1];
@@ -11061,7 +11152,7 @@ var ALLOWANCE_REFUSAL_MARKER = "AI_ALLOWANCE_EXCEEDED";
11061
11152
  function streamShowsAllowanceRefusal(streamPath) {
11062
11153
  let raw;
11063
11154
  try {
11064
- raw = readFileSync25(streamPath, "utf8");
11155
+ raw = readFileSync26(streamPath, "utf8");
11065
11156
  } catch {
11066
11157
  return false;
11067
11158
  }
@@ -11147,10 +11238,10 @@ function planAgentRun(o) {
11147
11238
  let ageSeconds;
11148
11239
  if (existsSync26(digestPath2)) {
11149
11240
  ageSeconds = Math.floor((now() - statSync8(digestPath2).mtimeMs) / 1e3);
11150
- if (ageSeconds <= DIGEST_MAX_AGE_SECONDS) digest = readFileSync25(digestPath2, "utf8");
11241
+ if (ageSeconds <= DIGEST_MAX_AGE_SECONDS) digest = readFileSync26(digestPath2, "utf8");
11151
11242
  }
11152
- const common = usesCommon ? readFileSync25(commonPath, "utf8") : "";
11153
- const brief = readFileSync25(briefPath, "utf8");
11243
+ const common = usesCommon ? readFileSync26(commonPath, "utf8") : "";
11244
+ const brief = readFileSync26(briefPath, "utf8");
11154
11245
  const effectiveCommon = o.divergedPrompt ? "" : common;
11155
11246
  const effectiveBrief = appendAdditions(o.divergedPrompt ?? brief, o.personaAdditions);
11156
11247
  const localSkills = new Map(listLocalSkills(o.route.promptsDir).map((s) => [s.name, s.prompt]));
@@ -11239,6 +11330,7 @@ function planAgentRun(o) {
11239
11330
  toolchain: toolchain.name,
11240
11331
  harness: toolchain.harness,
11241
11332
  workspaceAuth: toolchain.auth === "workspace",
11333
+ authMode: authModeOf(toolchain),
11242
11334
  provider: toolchainProvider(toolchain),
11243
11335
  maxRunMinutes: resolveMaxRunMinutes(o.route.maxRunMinutes, toolchain.maxRunMinutes),
11244
11336
  ...o.ship.runner === "sandbox" ? {
@@ -11305,7 +11397,7 @@ function describePlan(p) {
11305
11397
  var pidPathFor = (streamPath) => `${streamPath}.pid`;
11306
11398
  function readRunSidecar(streamPath) {
11307
11399
  try {
11308
- const sc = JSON.parse(readFileSync25(pidPathFor(streamPath), "utf8"));
11400
+ const sc = JSON.parse(readFileSync26(pidPathFor(streamPath), "utf8"));
11309
11401
  return typeof sc.pid === "number" && typeof sc.spawnedAt === "string" && typeof sc.role === "string" ? sc : null;
11310
11402
  } catch {
11311
11403
  return null;
@@ -11620,10 +11712,11 @@ async function finishRun(id, obs, emit, startedAt, end, agentLogKey, hintMoved)
11620
11712
  ...result?.costUsd !== void 0 ? { totalCostUsd: result.costUsd } : {}
11621
11713
  }
11622
11714
  });
11715
+ const signedOut = !end.interrupted && streamShowsSignedOut(id.streamPath);
11623
11716
  if (id.agentLog && agentLogKey) {
11624
11717
  const report = {
11625
11718
  ticketReference: attributedTicket,
11626
- outcome: end.interrupted ? "interrupted" : end.code === 0 || end.code === null && obs.sawResult ? "success" : "error",
11719
+ outcome: end.interrupted ? "interrupted" : signedOut ? "error" : end.code === 0 || end.code === null && obs.sawResult ? "success" : "error",
11627
11720
  startedAt,
11628
11721
  finishedAt: (/* @__PURE__ */ new Date()).toISOString(),
11629
11722
  cycles: obs.cycles,
@@ -11878,7 +11971,7 @@ function runFinished(streamPath) {
11878
11971
  const path = `${streamPath}.meta.json`;
11879
11972
  if (!existsSync27(path)) return false;
11880
11973
  try {
11881
- return JSON.parse(readFileSync26(path, "utf8")).running !== true;
11974
+ return JSON.parse(readFileSync27(path, "utf8")).running !== true;
11882
11975
  } catch {
11883
11976
  return true;
11884
11977
  }
@@ -12969,7 +13062,7 @@ var SANDBOX_BRIDGE_PATH = `${CREW_PREFIX}/bin/intel-bridge`;
12969
13062
  function readBridgeBundle() {
12970
13063
  const here = dirname15(fileURLToPath(import.meta.url));
12971
13064
  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");
13065
+ if (existsSync30(p)) return readFileSync28(p, "utf8");
12973
13066
  }
12974
13067
  return void 0;
12975
13068
  }
@@ -13121,7 +13214,7 @@ var shipSandboxRef = () => ({ name: SHIP_SANDBOX_KEY, decl: { packages: [], setu
13121
13214
  var sandboxStatePath = (stateDir) => join32(stateDir, "sandbox-images.json");
13122
13215
  function readSandboxState(stateDir) {
13123
13216
  try {
13124
- const v = JSON.parse(readFileSync27(sandboxStatePath(stateDir), "utf8"));
13217
+ const v = JSON.parse(readFileSync28(sandboxStatePath(stateDir), "utf8"));
13125
13218
  return v && typeof v === "object" && !Array.isArray(v) ? v : {};
13126
13219
  } catch {
13127
13220
  return {};
@@ -13404,7 +13497,7 @@ function managedGitIdentityWarnings(kind, identity) {
13404
13497
 
13405
13498
  // src/selftest.ts
13406
13499
  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";
13500
+ import { existsSync as existsSync31, mkdirSync as mkdirSync20, mkdtempSync as mkdtempSync2, readFileSync as readFileSync29, rmSync as rmSync16, writeFileSync as writeFileSync21 } from "fs";
13408
13501
  import { tmpdir as tmpdir4 } from "os";
13409
13502
  import { join as join33 } from "path";
13410
13503
  var SELFTEST_TIMEOUT_MS = 12e4;
@@ -13538,6 +13631,11 @@ ${seen.texts.join("\n")}`)) {
13538
13631
  return fail2("mcp-unavailable", "the tracker MCP tool was not available to the session");
13539
13632
  }
13540
13633
  const answered = seen.texts.length > 0 || seen.finalText;
13634
+ if (SIGNED_OUT_PATTERN.test(`${run3.stderr}
13635
+ ${seen.finalText ?? ""}
13636
+ ${seen.texts.join("\n")}`)) {
13637
+ return { failure: "login-missing", detail: signedOutMessage(spec.name, authModeOf(spec)) };
13638
+ }
13541
13639
  if (!answered || LOGIN.test(run3.stderr) || LOGIN.test(seen.finalText ?? "")) {
13542
13640
  return fail2("login-missing", `\`${bin}\` exited${run3.code !== null ? ` (${run3.code})` : ""} before answering; the vendor login is probably missing`);
13543
13641
  }
@@ -13604,7 +13702,7 @@ var fileFor2 = (stateDir) => join33(stateDir, "selftest.json");
13604
13702
  function readSelftest(stateDir) {
13605
13703
  try {
13606
13704
  const p = fileFor2(stateDir);
13607
- return existsSync31(p) ? JSON.parse(readFileSync28(p, "utf8")) : {};
13705
+ return existsSync31(p) ? JSON.parse(readFileSync29(p, "utf8")) : {};
13608
13706
  } catch {
13609
13707
  return {};
13610
13708
  }
@@ -13823,14 +13921,14 @@ function rosterMarkdown(roster, meId) {
13823
13921
  }
13824
13922
 
13825
13923
  // src/connect-resume.ts
13826
- import { mkdirSync as mkdirSync21, readFileSync as readFileSync29, rmSync as rmSync17, writeFileSync as writeFileSync22 } from "fs";
13924
+ import { mkdirSync as mkdirSync21, readFileSync as readFileSync30, rmSync as rmSync17, writeFileSync as writeFileSync22 } from "fs";
13827
13925
  import { dirname as dirname16, join as join34 } from "path";
13828
13926
  function lastSessionFile(stateDir) {
13829
13927
  return join34(stateDir, "connect-session.json");
13830
13928
  }
13831
13929
  function readPointers(stateDir) {
13832
13930
  try {
13833
- const parsed = JSON.parse(readFileSync29(lastSessionFile(stateDir), "utf8"));
13931
+ const parsed = JSON.parse(readFileSync30(lastSessionFile(stateDir), "utf8"));
13834
13932
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return {};
13835
13933
  const out = {};
13836
13934
  for (const [host, v] of Object.entries(parsed)) {
@@ -13930,7 +14028,7 @@ async function signOutSessions(o) {
13930
14028
  }
13931
14029
 
13932
14030
  // 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";
14031
+ 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
14032
  import { dirname as dirname17, join as join35 } from "path";
13935
14033
  import { hostFromUrl as hostFromUrl3 } from "@tablation/client";
13936
14034
  function clonedCheckoutsFile(stateDir) {
@@ -13938,7 +14036,7 @@ function clonedCheckoutsFile(stateDir) {
13938
14036
  }
13939
14037
  function readClonedCheckouts(stateDir) {
13940
14038
  try {
13941
- const parsed = JSON.parse(readFileSync30(clonedCheckoutsFile(stateDir), "utf8"));
14039
+ const parsed = JSON.parse(readFileSync31(clonedCheckoutsFile(stateDir), "utf8"));
13942
14040
  return Array.isArray(parsed) ? parsed.filter((d) => typeof d === "string") : [];
13943
14041
  } catch {
13944
14042
  return [];
@@ -14129,7 +14227,7 @@ async function runDeprovision(o, fx, deps) {
14129
14227
  }
14130
14228
 
14131
14229
  // src/pool-member.ts
14132
- import { mkdirSync as mkdirSync23, readFileSync as readFileSync31, renameSync as renameSync4, writeFileSync as writeFileSync24 } from "fs";
14230
+ import { mkdirSync as mkdirSync23, readFileSync as readFileSync32, renameSync as renameSync4, writeFileSync as writeFileSync24 } from "fs";
14133
14231
  import { homedir as homedir8 } from "os";
14134
14232
  import { dirname as dirname18, join as join36 } from "path";
14135
14233
  var POOL_EXIT = { ok: 0, failed: 1, credential: 2 };
@@ -14369,7 +14467,7 @@ function fileMemberStore(dir) {
14369
14467
  return {
14370
14468
  read() {
14371
14469
  try {
14372
- return JSON.parse(readFileSync31(file, "utf8"));
14470
+ return JSON.parse(readFileSync32(file, "utf8"));
14373
14471
  } catch {
14374
14472
  return {};
14375
14473
  }
@@ -14435,7 +14533,7 @@ import { hostname as hostname3 } from "os";
14435
14533
  // src/machine-id.ts
14436
14534
  import { execFileSync as execFileSync10 } from "child_process";
14437
14535
  import { createHash as createHash8 } from "crypto";
14438
- import { existsSync as existsSync33, mkdirSync as mkdirSync24, readFileSync as readFileSync32, writeFileSync as writeFileSync25 } from "fs";
14536
+ import { existsSync as existsSync33, mkdirSync as mkdirSync24, readFileSync as readFileSync33, writeFileSync as writeFileSync25 } from "fs";
14439
14537
  import { dirname as dirname19, join as join37 } from "path";
14440
14538
  function machineIdPathFor(stateDir) {
14441
14539
  return join37(stateDir, "machine-id");
@@ -14452,7 +14550,7 @@ function readRawMachineId(platform2 = process.platform) {
14452
14550
  }
14453
14551
  for (const p of ["/etc/machine-id", "/var/lib/dbus/machine-id"]) {
14454
14552
  if (existsSync33(p)) {
14455
- const id = readFileSync32(p, "utf8").trim();
14553
+ const id = readFileSync33(p, "utf8").trim();
14456
14554
  if (id) return id;
14457
14555
  }
14458
14556
  }
@@ -14464,7 +14562,7 @@ function ensureMachineId(stateDir, opts = {}) {
14464
14562
  if (opts.override?.trim()) return sha256(opts.override.trim());
14465
14563
  const path = machineIdPathFor(stateDir);
14466
14564
  if (existsSync33(path)) {
14467
- const cached = readFileSync32(path, "utf8").trim();
14565
+ const cached = readFileSync33(path, "utf8").trim();
14468
14566
  if (/^[0-9a-f]{64}$/.test(cached)) return cached;
14469
14567
  }
14470
14568
  const raw = (opts.read ?? readRawMachineId)();
@@ -14484,7 +14582,7 @@ function matchShipRows(ships, shipName2, machineId) {
14484
14582
 
14485
14583
  // src/provision.ts
14486
14584
  import { spawnSync as spawnSync9 } from "child_process";
14487
- import { existsSync as existsSync34, readFileSync as readFileSync33, writeFileSync as writeFileSync26 } from "fs";
14585
+ import { existsSync as existsSync34, readFileSync as readFileSync34, writeFileSync as writeFileSync26 } from "fs";
14488
14586
  import { hostname as hostname2 } from "os";
14489
14587
  import { parseDocument as parseDocument3, isMap as isMap2, isScalar } from "yaml";
14490
14588
 
@@ -15105,7 +15203,7 @@ function realProvisionDeps(crewHome) {
15105
15203
  enableRoute(route2) {
15106
15204
  const file = configFile();
15107
15205
  if (!file) return false;
15108
- const text2 = readFileSync33(file, "utf8");
15206
+ const text2 = readFileSync34(file, "utf8");
15109
15207
  const doc = parseDocument3(text2);
15110
15208
  const routes = doc.get("routes");
15111
15209
  const entry = routes?.items?.find((r) => r.get("route") === route2);
@@ -15131,7 +15229,7 @@ function realProvisionDeps(crewHome) {
15131
15229
  continue;
15132
15230
  }
15133
15231
  for (const p of plan.unitPaths) {
15134
- if (!existsSync34(p) || readFileSync33(p, "utf8") !== plan.unitContent[p]) unitsStale = true;
15232
+ if (!existsSync34(p) || readFileSync34(p, "utf8") !== plan.unitContent[p]) unitsStale = true;
15135
15233
  }
15136
15234
  }
15137
15235
  const label = labelFor(crewHome, "run");
@@ -15155,7 +15253,7 @@ function realProvisionDeps(crewHome) {
15155
15253
  setShipSetting(key, value2) {
15156
15254
  const file = configFile();
15157
15255
  if (!file) return false;
15158
- const doc = parseDocument3(readFileSync33(file, "utf8"));
15256
+ const doc = parseDocument3(readFileSync34(file, "utf8"));
15159
15257
  if (doc.getIn(["ship", key]) === value2) return false;
15160
15258
  doc.setIn(["ship", key], value2);
15161
15259
  writeFileSync26(file, doc.toString());
@@ -15164,7 +15262,7 @@ function realProvisionDeps(crewHome) {
15164
15262
  applyManagedDefaults() {
15165
15263
  const file = configFile();
15166
15264
  if (!file) return [];
15167
- const doc = parseDocument3(readFileSync33(file, "utf8"));
15265
+ const doc = parseDocument3(readFileSync34(file, "utf8"));
15168
15266
  const wrote = applyManagedDefaultsToDoc(doc);
15169
15267
  if (wrote.length) writeFileSync26(file, doc.toString());
15170
15268
  return wrote;
@@ -15194,7 +15292,7 @@ host=${host}
15194
15292
 
15195
15293
  // src/tunnel.ts
15196
15294
  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";
15295
+ import { existsSync as existsSync35, mkdirSync as mkdirSync25, readFileSync as readFileSync35, writeFileSync as writeFileSync27, unlinkSync as unlinkSync3, readdirSync as readdirSync13 } from "fs";
15198
15296
  import { randomInt } from "crypto";
15199
15297
  import { join as join39 } from "path";
15200
15298
  var DEFAULT_SPAWN_OPTIONS = { stdio: "ignore", detached: true };
@@ -15280,7 +15378,7 @@ function readPersistedTunnel(stateDir, workspaceId) {
15280
15378
  const path = tunnelStatePath(stateDir, workspaceId);
15281
15379
  if (!existsSync35(path)) return void 0;
15282
15380
  try {
15283
- return JSON.parse(readFileSync34(path, "utf8"));
15381
+ return JSON.parse(readFileSync35(path, "utf8"));
15284
15382
  } catch {
15285
15383
  return void 0;
15286
15384
  }
@@ -16825,7 +16923,7 @@ async function resolveOne(tracker, t, seat, approvedStatus, buildingStatus, affi
16825
16923
  }
16826
16924
 
16827
16925
  // src/repo-refresh.ts
16828
- import { existsSync as existsSync37, mkdirSync as mkdirSync26, readFileSync as readFileSync35, writeFileSync as writeFileSync29 } from "fs";
16926
+ import { existsSync as existsSync37, mkdirSync as mkdirSync26, readFileSync as readFileSync36, writeFileSync as writeFileSync29 } from "fs";
16829
16927
  import { dirname as dirname21 } from "path";
16830
16928
  var REFRESH_COOLDOWN_MS = 2 * 6e4;
16831
16929
  var UNSERVABLE_PASSES_BEFORE_ATTENTION = 3;
@@ -16856,7 +16954,7 @@ function applyRepoRows(resolved, rows) {
16856
16954
  function persistRepoRows(stateDir, routeName, rows) {
16857
16955
  const path = resolvedPathFor(stateDir, routeName);
16858
16956
  if (!existsSync37(path)) return;
16859
- const raw = JSON.parse(readFileSync35(path, "utf8"));
16957
+ const raw = JSON.parse(readFileSync36(path, "utf8"));
16860
16958
  applyRepoRows(raw, rows);
16861
16959
  mkdirSync26(dirname21(path), { recursive: true });
16862
16960
  writeFileSync29(path, `${JSON.stringify(raw, null, 2)}
@@ -17509,7 +17607,7 @@ function flagWizardAnswers(f, env) {
17509
17607
  }
17510
17608
 
17511
17609
  // 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";
17610
+ import { readFileSync as readFileSync37, writeFileSync as writeFileSync31, existsSync as existsSync39, copyFileSync, mkdirSync as mkdirSync28, mkdtempSync as mkdtempSync3, rmSync as rmSync19 } from "fs";
17513
17611
  import { dirname as dirname24, join as join43 } from "path";
17514
17612
  import { tmpdir as tmpdir5, hostname as hostname4 } from "os";
17515
17613
 
@@ -18286,7 +18384,7 @@ async function bump(o, size) {
18286
18384
  o.emit.error(`no ${o.repo.release.versionFiles[0]} to read a version from`);
18287
18385
  return null;
18288
18386
  }
18289
- const firstText = readFileSync36(first, "utf8");
18387
+ const firstText = readFileSync37(first, "utf8");
18290
18388
  const problem = versionFileProblem(o.repo.release.versionFiles[0], firstText);
18291
18389
  if (problem) {
18292
18390
  o.emit.error(problem);
@@ -18305,7 +18403,7 @@ async function bump(o, size) {
18305
18403
  return version;
18306
18404
  }
18307
18405
  for (const f of files) {
18308
- const src = readFileSync36(f, "utf8");
18406
+ const src = readFileSync37(f, "utf8");
18309
18407
  const out = src.replace(/("version"\s*:\s*")[^"]*(")/, `$1${version}$2`);
18310
18408
  if (out === src) {
18311
18409
  o.emit.error(`no version field in ${f}`);
@@ -18324,7 +18422,7 @@ function writeChangelog(o, version, entries) {
18324
18422
  o.emit.emit(`would prepend ${entries.length} entr(ies) to ${file}`);
18325
18423
  return;
18326
18424
  }
18327
- const existing = existsSync39(path) ? readFileSync36(path, "utf8") : "# Changelog\n";
18425
+ const existing = existsSync39(path) ? readFileSync37(path, "utf8") : "# Changelog\n";
18328
18426
  writeFileSync31(path, insertChangelogSection(existing, section));
18329
18427
  }
18330
18428
  async function confirm(o, expected) {
@@ -19841,7 +19939,7 @@ function nextRoles(decision, ran) {
19841
19939
  }
19842
19940
 
19843
19941
  // src/ship-name.ts
19844
- import { existsSync as existsSync42, mkdirSync as mkdirSync30, readFileSync as readFileSync37, renameSync as renameSync5, writeFileSync as writeFileSync32 } from "fs";
19942
+ import { existsSync as existsSync42, mkdirSync as mkdirSync30, readFileSync as readFileSync38, renameSync as renameSync5, writeFileSync as writeFileSync32 } from "fs";
19845
19943
  import { dirname as dirname26 } from "path";
19846
19944
  import { parseDocument as parseDocument5 } from "yaml";
19847
19945
  var ShipNameError = class extends Error {
@@ -19905,7 +20003,7 @@ async function syncRouteName(i) {
19905
20003
  function readSyncedName(resolvedFile) {
19906
20004
  if (!existsSync42(resolvedFile)) return void 0;
19907
20005
  try {
19908
- const v = JSON.parse(readFileSync37(resolvedFile, "utf8")).shipName;
20006
+ const v = JSON.parse(readFileSync38(resolvedFile, "utf8")).shipName;
19909
20007
  return typeof v === "string" ? v : void 0;
19910
20008
  } catch {
19911
20009
  return void 0;
@@ -19915,7 +20013,7 @@ function writeSyncedName(resolvedFile, name) {
19915
20013
  if (!existsSync42(resolvedFile)) return;
19916
20014
  let raw;
19917
20015
  try {
19918
- raw = JSON.parse(readFileSync37(resolvedFile, "utf8"));
20016
+ raw = JSON.parse(readFileSync38(resolvedFile, "utf8"));
19919
20017
  } catch {
19920
20018
  return;
19921
20019
  }
@@ -19930,7 +20028,7 @@ function writeSyncedName(resolvedFile, name) {
19930
20028
  async function reconcileShipName(i) {
19931
20029
  let local = i.ship.name;
19932
20030
  try {
19933
- local = shipNameOfYaml(readFileSync37(i.configFile, "utf8")) ?? local;
20031
+ local = shipNameOfYaml(readFileSync38(i.configFile, "utf8")) ?? local;
19934
20032
  } catch {
19935
20033
  }
19936
20034
  const resolvedFile = resolvedPathFor(i.stateDir, i.route);
@@ -19954,7 +20052,7 @@ async function reconcileShipName(i) {
19954
20052
  }
19955
20053
  if (result.status !== "ok") return result;
19956
20054
  if (result.action === "adopt" || result.action === "row-wins") {
19957
- const text2 = readFileSync37(i.configFile, "utf8");
20055
+ const text2 = readFileSync38(i.configFile, "utf8");
19958
20056
  writeFileSync32(i.configFile, setShipNameInYaml(text2, result.name));
19959
20057
  i.ship.name = result.name;
19960
20058
  if (result.action === "row-wins") {
@@ -20614,7 +20712,7 @@ function describeCapability(i) {
20614
20712
  }
20615
20713
 
20616
20714
  // src/agents-sync-run.ts
20617
- import { existsSync as existsSync43, mkdirSync as mkdirSync31, readFileSync as readFileSync38, writeFileSync as writeFileSync33 } from "fs";
20715
+ import { existsSync as existsSync43, mkdirSync as mkdirSync31, readFileSync as readFileSync39, writeFileSync as writeFileSync33 } from "fs";
20618
20716
  import { dirname as dirname27 } from "path";
20619
20717
  async function runAgentsSync(target, o) {
20620
20718
  const dryRun = o.dryRun ?? false;
@@ -20648,7 +20746,7 @@ async function runAgentsSync(target, o) {
20648
20746
  for (const s of skillResult?.outcomes ?? []) o.say(describeSkillSyncOutcome(s));
20649
20747
  if (!dryRun) {
20650
20748
  const resolvedPath = resolvedPathFor(o.stateDir, target.route);
20651
- const raw = existsSync43(resolvedPath) ? JSON.parse(readFileSync38(resolvedPath, "utf8")) : {};
20749
+ const raw = existsSync43(resolvedPath) ? JSON.parse(readFileSync39(resolvedPath, "utf8")) : {};
20652
20750
  raw.agentPersonas = personaResult.agentPersonas;
20653
20751
  if (skillResult) raw.agentSkills = skillResult.agentSkills;
20654
20752
  if (personaResult.releaseAgentId) raw.releaseAgentId = personaResult.releaseAgentId;
@@ -20770,7 +20868,7 @@ async function showLogEntry(route2, entryId, opts = {}) {
20770
20868
 
20771
20869
  // src/repos-check.ts
20772
20870
  import { spawnSync as spawnSync11 } from "child_process";
20773
- import { readFileSync as readFileSync39 } from "fs";
20871
+ import { readFileSync as readFileSync40 } from "fs";
20774
20872
  import { basename as basename11 } from "path";
20775
20873
  var POSIX_SHELL = /^(?:ba|z|da|k|a|mk)?sh$|^fish$/;
20776
20874
  function checkHookSyntax(script, shell) {
@@ -20811,7 +20909,7 @@ function checkRepoSpec(dir, opts = {}) {
20811
20909
  }
20812
20910
  let parsed;
20813
20911
  try {
20814
- parsed = parseRepoConfig(readFileSync39(file, "utf8"), file);
20912
+ parsed = parseRepoConfig(readFileSync40(file, "utf8"), file);
20815
20913
  } catch (e) {
20816
20914
  if (!(e instanceof RepoConfigError)) throw e;
20817
20915
  return { ...empty, file, error: e.message, exitCode: 1 };
@@ -21343,7 +21441,7 @@ function planSampleTicket(i) {
21343
21441
  }
21344
21442
 
21345
21443
  // src/worktree-sweep.ts
21346
- import { existsSync as existsSync44, readFileSync as readFileSync40, readdirSync as readdirSync14 } from "fs";
21444
+ import { existsSync as existsSync44, readFileSync as readFileSync41, readdirSync as readdirSync14 } from "fs";
21347
21445
  import { basename as basename13, dirname as dirname29, join as join45, resolve as resolve5 } from "path";
21348
21446
  function planWorktreeSweep(target, tickets, contract) {
21349
21447
  const terminal = new Set(contract.statuses.resolved.filter((s) => s !== contract.statuses.verified));
@@ -21524,7 +21622,7 @@ function isHandBackMerge(path, base) {
21524
21622
  if (!msgPath) return false;
21525
21623
  let first;
21526
21624
  try {
21527
- first = readFileSync40(resolve5(path, msgPath), "utf8").split("\n")[0] ?? "";
21625
+ first = readFileSync41(resolve5(path, msgPath), "utf8").split("\n")[0] ?? "";
21528
21626
  } catch {
21529
21627
  return false;
21530
21628
  }
@@ -21674,11 +21772,11 @@ function planEventsSweep(stateDir, retentionDays, now = Date.now()) {
21674
21772
  }
21675
21773
 
21676
21774
  // 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";
21775
+ 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
21776
  import { createInterface } from "readline/promises";
21679
21777
 
21680
21778
  // src/periscope-cmd.ts
21681
- import { existsSync as existsSync46, mkdirSync as mkdirSync32, readFileSync as readFileSync41, rmSync as rmSync21, writeFileSync as writeFileSync34 } from "fs";
21779
+ import { existsSync as existsSync46, mkdirSync as mkdirSync32, readFileSync as readFileSync42, rmSync as rmSync21, writeFileSync as writeFileSync34 } from "fs";
21682
21780
  import { join as join47 } from "path";
21683
21781
  var PeriscopeError = class extends Error {
21684
21782
  };
@@ -21704,7 +21802,7 @@ function writePeriscopePrior(stateDir, ticketRecordId, prior) {
21704
21802
  function readPeriscopePrior(stateDir, ticketRecordId) {
21705
21803
  try {
21706
21804
  const p = priorPath(stateDir, ticketRecordId);
21707
- return existsSync46(p) ? JSON.parse(readFileSync41(p, "utf8")) : void 0;
21805
+ return existsSync46(p) ? JSON.parse(readFileSync42(p, "utf8")) : void 0;
21708
21806
  } catch {
21709
21807
  return void 0;
21710
21808
  }
@@ -22259,7 +22357,7 @@ import { dirname as dirOf2, resolve as resolvePath } from "path";
22259
22357
 
22260
22358
  // src/ssh-keys.ts
22261
22359
  import { execFileSync as execFileSync13 } from "child_process";
22262
- import { existsSync as existsSync47, mkdirSync as mkdirSync33, readFileSync as readFileSync42, chmodSync as chmodSync3 } from "fs";
22360
+ import { existsSync as existsSync47, mkdirSync as mkdirSync33, readFileSync as readFileSync43, chmodSync as chmodSync3 } from "fs";
22263
22361
  import { dirname as dirname30, join as join49 } from "path";
22264
22362
  var SshKeyError = class extends Error {
22265
22363
  };
@@ -22293,7 +22391,7 @@ function ensureShipSshKeypair(stateDir) {
22293
22391
  if (!existsSync47(publicKeyPath)) {
22294
22392
  throw new SshKeyError(`${privateKeyPath} exists but ${publicKeyPath} does not \u2014 regenerate or restore it`);
22295
22393
  }
22296
- const publicKey = readFileSync42(publicKeyPath, "utf8").trim();
22394
+ const publicKey = readFileSync43(publicKeyPath, "utf8").trim();
22297
22395
  return { privateKeyPath, publicKeyPath, publicKey };
22298
22396
  }
22299
22397
  function sshKeygenAvailable() {
@@ -22457,7 +22555,7 @@ async function unregisterEndpoint(workspaceId, info, d) {
22457
22555
  import { createServer as createHttpServer } from "http";
22458
22556
  import { spawn as spawn9 } from "child_process";
22459
22557
  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";
22558
+ import { existsSync as existsSync48, mkdirSync as mkdirSync34, readFileSync as readFileSync44, writeFileSync as writeFileSync35, unlinkSync as unlinkSync4, readdirSync as readdirSync16 } from "fs";
22461
22559
  import { join as join50 } from "path";
22462
22560
 
22463
22561
  // src/secret-compare.ts
@@ -22557,7 +22655,7 @@ function readPersistedSyncDaemon(stateDir, workspaceId) {
22557
22655
  const path = syncDaemonStatePath(stateDir, workspaceId);
22558
22656
  if (!existsSync48(path)) return void 0;
22559
22657
  try {
22560
- return JSON.parse(readFileSync43(path, "utf8"));
22658
+ return JSON.parse(readFileSync44(path, "utf8"));
22561
22659
  } catch {
22562
22660
  return void 0;
22563
22661
  }
@@ -23106,7 +23204,7 @@ function contractFixers(dir, baseRef, tickets, locate) {
23106
23204
  }
23107
23205
 
23108
23206
  // 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";
23207
+ 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
23208
  import { join as join52 } from "path";
23111
23209
  import { randomBytes as randomBytes2 } from "crypto";
23112
23210
  var DEFAULT_FLOOR_MS = 5e3;
@@ -23227,7 +23325,7 @@ function noteDaemonStarted(stateDir, now = Date.now()) {
23227
23325
  }
23228
23326
  function readDaemonStarted(stateDir) {
23229
23327
  try {
23230
- const v = JSON.parse(readFileSync44(daemonStampFor(stateDir), "utf8")).startedAt;
23328
+ const v = JSON.parse(readFileSync45(daemonStampFor(stateDir), "utf8")).startedAt;
23231
23329
  return typeof v === "number" ? v : null;
23232
23330
  } catch {
23233
23331
  return null;
@@ -23410,6 +23508,12 @@ async function runOnePassUnguarded(o) {
23410
23508
  clearShipAttention(o.state, workspaceAllowanceKey(role));
23411
23509
  }).catch(async (e) => {
23412
23510
  const message = e.message;
23511
+ if (e instanceof SignedOutError) {
23512
+ breaker.pause(role, SIGNED_OUT_PAUSE_MS);
23513
+ emit.warn(`${role} paused: ${message}`, { step: "agent", role });
23514
+ await raiseShipAttention({ state: o.state, emit, route: o.route, ship: o.ship }, signedOutItem(e.toolchain, e.mode));
23515
+ return;
23516
+ }
23413
23517
  if (e instanceof WorkspaceAllowanceExhausted) {
23414
23518
  breaker.pause(role, ALLOWANCE_PAUSE_MS);
23415
23519
  emit.warn(`${role} paused: ${message}`, { step: "agent", role });
@@ -23751,6 +23855,7 @@ async function runRoleAgentLocked(role, decision, ctx, hooks, held) {
23751
23855
  if (plan.workspaceAuth && streamShowsAllowanceRefusal(plan.streamPath)) {
23752
23856
  throw new WorkspaceAllowanceExhausted("the inference endpoint refused the run: the workspace is past its AI allowance");
23753
23857
  }
23858
+ await settleSignOut(plan, tracker, ctx.state, workingId, emit);
23754
23859
  }
23755
23860
  } finally {
23756
23861
  await settleRunEnd({
@@ -23787,6 +23892,26 @@ async function buildEnvironment(route2, ship, ticket) {
23787
23892
  const attachments = await tracker.attachmentsProbe().catch(() => null);
23788
23893
  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
23894
  }
23895
+ async function settleSignOut(plan, tracker, state, ticketId, emit) {
23896
+ const toolchain = plan.toolchain;
23897
+ if (!toolchain) return;
23898
+ if (!streamShowsSignedOut(plan.streamPath)) {
23899
+ if (streamShowsRealTurn(plan.streamPath)) clearShipAttention(state, signedOutKey(toolchain));
23900
+ return;
23901
+ }
23902
+ if (ticketId) {
23903
+ try {
23904
+ const affinity = await tracker.claimAffinity();
23905
+ const current = await tracker.ticket(ticketId);
23906
+ if (affinity && current[affinity.column] === affinity.shipId) {
23907
+ await tracker.updateTicket(ticketId, { [affinity.column]: null }, current.updated_at);
23908
+ }
23909
+ } catch (e) {
23910
+ emit.warn(`could not release the ticket hold after a sign-out: ${e.message}`, { step: "agent" });
23911
+ }
23912
+ }
23913
+ throw new SignedOutError(toolchain, plan.authMode ?? "login");
23914
+ }
23790
23915
 
23791
23916
  // src/control-api.ts
23792
23917
  import { createHash as createHash11 } from "crypto";
@@ -24477,7 +24602,7 @@ import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/
24477
24602
 
24478
24603
  // src/periscope-endpoints.ts
24479
24604
  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";
24605
+ 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
24606
  import { join as join54 } from "path";
24482
24607
  var secretDir = (stateDir) => join54(stateDir, "periscope-mcp");
24483
24608
  var secretFile = (stateDir, id) => join54(secretDir(stateDir), `${id}.json`);
@@ -24494,7 +24619,7 @@ function mintPeriscopeSecret(stateDir, ticketRecordId) {
24494
24619
  }
24495
24620
  function readPeriscopeSecret(stateDir, ticketRecordId) {
24496
24621
  try {
24497
- const v = JSON.parse(readFileSync45(secretFile(stateDir, ticketRecordId), "utf8"));
24622
+ const v = JSON.parse(readFileSync46(secretFile(stateDir, ticketRecordId), "utf8"));
24498
24623
  return typeof v.secret === "string" && v.secret ? v.secret : void 0;
24499
24624
  } catch {
24500
24625
  return void 0;
@@ -24505,7 +24630,7 @@ function removePeriscopeSecret(stateDir, ticketRecordId) {
24505
24630
  }
24506
24631
  function readPeriscopeEndpoint(stateDir, ticketRecordId) {
24507
24632
  try {
24508
- return JSON.parse(readFileSync45(endpointFile(stateDir, ticketRecordId), "utf8"));
24633
+ return JSON.parse(readFileSync46(endpointFile(stateDir, ticketRecordId), "utf8"));
24509
24634
  } catch {
24510
24635
  return void 0;
24511
24636
  }
@@ -25254,7 +25379,7 @@ if (command === "provision") {
25254
25379
  process.exit(outcome.code);
25255
25380
  }
25256
25381
  if (command === "pool-member") {
25257
- const pool = poolConfigFrom(argv, process.env, () => readFileSync46(0, "utf8"));
25382
+ const pool = poolConfigFrom(argv, process.env, () => readFileSync47(0, "utf8"));
25258
25383
  if (typeof pool === "string") {
25259
25384
  process.stderr.write(`crew pool-member: ${pool}
25260
25385
  `);
@@ -25518,7 +25643,7 @@ if (command === "passenger-sync-daemon") {
25518
25643
  throw e;
25519
25644
  }
25520
25645
  if (command === "credential") {
25521
- const r = runCredential(positional[1], readFileSync46(0, "utf8"), cfg.ship.forges, cfg.ship.stateDir);
25646
+ const r = runCredential(positional[1], readFileSync47(0, "utf8"), cfg.ship.forges, cfg.ship.stateDir);
25522
25647
  process.stdout.write(r.out);
25523
25648
  process.stderr.write(r.err);
25524
25649
  process.exit(r.code);
@@ -25530,9 +25655,9 @@ if (command === "passenger-sync-daemon") {
25530
25655
  forges: cfg.ship.forges,
25531
25656
  stateDir: cfg.ship.stateDir,
25532
25657
  configFile: cfg.configFile,
25533
- readFile: (p) => readFileSync46(p, "utf8"),
25658
+ readFile: (p) => readFileSync47(p, "utf8"),
25534
25659
  writeFile: (p, t) => writeFileSync38(p, t),
25535
- stdin: () => readFileSync46(0, "utf8"),
25660
+ stdin: () => readFileSync47(0, "utf8"),
25536
25661
  env: process.env
25537
25662
  }
25538
25663
  );
@@ -25929,7 +26054,7 @@ if (command === "passenger-sync-daemon") {
25929
26054
  for (const a of plan.actions) {
25930
26055
  if (a.kind === "enable") {
25931
26056
  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));
26057
+ writeFileSync38(cfg.configFile, planEnable(readFileSync47(cfg.configFile, "utf8"), a.route));
25933
26058
  enabledSound.add(a.route);
25934
26059
  changed2.push(`enabled route ${a.route} in ${cfg.configFile}`);
25935
26060
  } else {
@@ -27430,7 +27555,7 @@ ${cu.tail.trimEnd()}
27430
27555
  `);
27431
27556
  if (!dryRun) {
27432
27557
  const resolvedPath = resolvedPathFor(cfg.ship.stateDir, target.route);
27433
- const raw = existsSync52(resolvedPath) ? JSON.parse(readFileSync46(resolvedPath, "utf8")) : {};
27558
+ const raw = existsSync52(resolvedPath) ? JSON.parse(readFileSync47(resolvedPath, "utf8")) : {};
27434
27559
  raw.agentSkills = result.agentSkills;
27435
27560
  mkdirSync38(dirOf2(resolvedPath), { recursive: true });
27436
27561
  writeFileSync38(resolvedPath, `${JSON.stringify(raw, null, 2)}
@@ -27456,7 +27581,7 @@ ${cu.tail.trimEnd()}
27456
27581
  throw e;
27457
27582
  }
27458
27583
  const oldName = cfg.ship.name;
27459
- writeFileSync38(cfg.configFile, setShipNameInYaml(readFileSync46(cfg.configFile, "utf8"), newName));
27584
+ writeFileSync38(cfg.configFile, setShipNameInYaml(readFileSync47(cfg.configFile, "utf8"), newName));
27460
27585
  cfg.ship.name = newName;
27461
27586
  let renameMachineId;
27462
27587
  try {
@@ -27503,7 +27628,7 @@ ${cu.tail.trimEnd()}
27503
27628
  process.exit(2);
27504
27629
  };
27505
27630
  if (sub !== "add" && sub !== "list" && sub !== "clone" && sub !== "hooks" && sub !== "check") usage2();
27506
- const configText = readFileSync46(cfg.configFile, "utf8");
27631
+ const configText = readFileSync47(cfg.configFile, "utf8");
27507
27632
  if (sub === "check") {
27508
27633
  try {
27509
27634
  const routeArg = value("route") ?? positional[2];
@@ -27544,7 +27669,7 @@ ${cu.tail.trimEnd()}
27544
27669
  }
27545
27670
  }, read: (r) => {
27546
27671
  try {
27547
- return readFileSync46(resolvePath(dir, r), "utf8");
27672
+ return readFileSync47(resolvePath(dir, r), "utf8");
27548
27673
  } catch {
27549
27674
  return void 0;
27550
27675
  }
@@ -27695,7 +27820,7 @@ ${cu.tail.trimEnd()}
27695
27820
  let resolvedRemotes;
27696
27821
  if (existsSync52(resolvedFile)) {
27697
27822
  try {
27698
- resolvedRemotes = JSON.parse(readFileSync46(resolvedFile, "utf8"));
27823
+ resolvedRemotes = JSON.parse(readFileSync47(resolvedFile, "utf8"));
27699
27824
  } catch {
27700
27825
  }
27701
27826
  }
@@ -27717,7 +27842,7 @@ ${cu.tail.trimEnd()}
27717
27842
  repoHasHooks: (d) => REPO_CONFIG_FILENAMES.some((f) => {
27718
27843
  try {
27719
27844
  const path = resolvePath(d, f);
27720
- return Object.keys(parseRepoConfig(readFileSync46(path, "utf8"), path).hooks).length > 0;
27845
+ return Object.keys(parseRepoConfig(readFileSync47(path, "utf8"), path).hooks).length > 0;
27721
27846
  } catch {
27722
27847
  return false;
27723
27848
  }
@@ -27826,7 +27951,7 @@ ${cu.tail.trimEnd()}
27826
27951
  process.stdout.write(`created Repos row ${String(rowPayload.name)} (${String(rowPayload.remote)}) ${created.id}
27827
27952
  `);
27828
27953
  if (existsSync52(resolvedFile)) {
27829
- const raw = JSON.parse(readFileSync46(resolvedFile, "utf8"));
27954
+ const raw = JSON.parse(readFileSync47(resolvedFile, "utf8"));
27830
27955
  raw.repoNames = { ...raw.repoNames, [created.id]: String(rowPayload.name) };
27831
27956
  raw.repoRemotes = { ...raw.repoRemotes, [created.id]: String(rowPayload.remote) };
27832
27957
  writeFileSync38(resolvedFile, `${JSON.stringify(raw, null, 2)}
@@ -28204,7 +28329,7 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
28204
28329
  process.exit(2);
28205
28330
  }
28206
28331
  if (found.shipNameAdopted && !dryRun) {
28207
- writeFileSync38(cfg.configFile, setShipNameInYaml(readFileSync46(cfg.configFile, "utf8"), found.shipNameAdopted));
28332
+ writeFileSync38(cfg.configFile, setShipNameInYaml(readFileSync47(cfg.configFile, "utf8"), found.shipNameAdopted));
28208
28333
  cfg.ship.name = found.shipNameAdopted;
28209
28334
  }
28210
28335
  if (found.shipRow) {
@@ -28250,7 +28375,7 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
28250
28375
  const hadPreviousFile = existsSync52(resolvedPath);
28251
28376
  if (hadPreviousFile) {
28252
28377
  try {
28253
- previous = JSON.parse(readFileSync46(resolvedPath, "utf8"));
28378
+ previous = JSON.parse(readFileSync47(resolvedPath, "utf8"));
28254
28379
  } catch {
28255
28380
  }
28256
28381
  }
@@ -29760,7 +29885,7 @@ ${where.join("\n")}`);
29760
29885
  };
29761
29886
  const droppedRoutes = [];
29762
29887
  try {
29763
- const rawCfg = parseYaml2(readFileSync46(cfg.configFile, "utf8"));
29888
+ const rawCfg = parseYaml2(readFileSync47(cfg.configFile, "utf8"));
29764
29889
  for (const r of rawCfg?.routes ?? []) {
29765
29890
  if (typeof r?.route !== "string" || cfg.routes.some((x) => x.route === r.route)) continue;
29766
29891
  try {
@@ -29879,7 +30004,7 @@ ${where.join("\n")}`);
29879
30004
  }
29880
30005
  case "log": {
29881
30006
  try {
29882
- const text2 = readFileSync46(cfg.ship.logFile, "utf8").split("\n");
30007
+ const text2 = readFileSync47(cfg.ship.logFile, "utf8").split("\n");
29883
30008
  process.stdout.write(`${text2.slice(-(Number(value("tail")) || 40)).join("\n")}
29884
30009
  `);
29885
30010
  } catch {
@@ -29902,7 +30027,7 @@ ${where.join("\n")}`);
29902
30027
  };
29903
30028
  if (!route2.resolved) fail2(`route ${route2.route} is not connected \u2014 run \`crew connect\` first`);
29904
30029
  const tracker = new Tracker(route2, cfg.ship);
29905
- const configText = readFileSync46(cfg.configFile, "utf8");
30030
+ const configText = readFileSync47(cfg.configFile, "utf8");
29906
30031
  const cfgBase = dirname34(cfg.configFile);
29907
30032
  const configured2 = configuredRepos(configText, route2.route, cfgBase);
29908
30033
  const dir = configured2.find((r) => r.name === SAMPLE_REPO)?.dir ?? sampleDir(configured2.map((r) => r.dir), route2.reposBasePath, route2.route.split("/")[0]);
@@ -29940,7 +30065,7 @@ ${where.join("\n")}`);
29940
30065
  if (row) {
29941
30066
  await tracker.deleteRepoRow(row.id);
29942
30067
  if (existsSync52(resolvedFile)) {
29943
- const raw = JSON.parse(readFileSync46(resolvedFile, "utf8"));
30068
+ const raw = JSON.parse(readFileSync47(resolvedFile, "utf8"));
29944
30069
  if (raw.repoNames) delete raw.repoNames[row.id];
29945
30070
  if (raw.repoRemotes) delete raw.repoRemotes[row.id];
29946
30071
  writeFileSync38(resolvedFile, `${JSON.stringify(raw, null, 2)}
@@ -29980,7 +30105,7 @@ ${where.join("\n")}`);
29980
30105
  });
29981
30106
  repoId = (await tracker.createRepoRow(payload)).id;
29982
30107
  if (existsSync52(resolvedFile)) {
29983
- const raw = JSON.parse(readFileSync46(resolvedFile, "utf8"));
30108
+ const raw = JSON.parse(readFileSync47(resolvedFile, "utf8"));
29984
30109
  raw.repoNames = { ...raw.repoNames, [repoId]: SAMPLE_REPO };
29985
30110
  raw.repoRemotes = { ...raw.repoRemotes, [repoId]: SAMPLE_REMOTE };
29986
30111
  writeFileSync38(resolvedFile, `${JSON.stringify(raw, null, 2)}
@@ -30454,7 +30579,7 @@ routes: ${cfg.routes.map((c) => c.route).join(", ")}
30454
30579
  const unitPlan = planInstall(cfg.ship, CREW_HOME, host, detectSystemd(), "run", installContext());
30455
30580
  const abandons = unitAbandonsAgents(unitPlan, (p) => {
30456
30581
  try {
30457
- return readFileSync46(p, "utf8");
30582
+ return readFileSync47(p, "utf8");
30458
30583
  } catch {
30459
30584
  return null;
30460
30585
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tablation/crew",
3
- "version": "0.127.1",
3
+ "version": "0.127.2",
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",