@tablation/crew 0.127.0 → 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 +271 -111
  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 {};
@@ -13253,7 +13346,7 @@ async function sandboxBuildTick(repos, deps, force = false) {
13253
13346
  }
13254
13347
 
13255
13348
  // src/doctor-checks.ts
13256
- import { execFileSync as execFileSync9 } from "child_process";
13349
+ import { execFileSync as execFileSync9, spawnSync as spawnSync8 } from "child_process";
13257
13350
  var PROBE_BRANCH = "crew-probe-branch";
13258
13351
  var realGit = (cwd, args, env) => {
13259
13352
  execFileSync9("git", args, { cwd, env, stdio: ["ignore", "pipe", "pipe"], timeout: 3e4 });
@@ -13386,10 +13479,25 @@ function managedRuntimeWarnings(kind, ship) {
13386
13479
  }
13387
13480
  return out;
13388
13481
  }
13482
+ var MANAGED_GIT_EMAIL = "crew@tablation.dev";
13483
+ function readGlobalGitIdentity() {
13484
+ const get2 = (key) => {
13485
+ const res = spawnSync8("git", ["config", "--global", "--get", key], { encoding: "utf8" });
13486
+ const v = (res.stdout ?? "").trim();
13487
+ return res.status === 0 && v ? v : null;
13488
+ };
13489
+ return { name: get2("user.name"), email: get2("user.email") };
13490
+ }
13491
+ function managedGitIdentityWarnings(kind, identity) {
13492
+ if (kind !== "managed_instance" && kind !== "pooled_instance" && kind !== "pooled") return [];
13493
+ const missing = [identity.name ? "" : "user.name", identity.email ? "" : "user.email"].filter(Boolean);
13494
+ if (missing.length === 0) return [];
13495
+ return [`git ${missing.join(" and ")} not set for this user on a ${kind} ship; every commit crew makes here fails with "Committer identity unknown" (re-run \`crew provision\`, or \`git config --global user.name <ship name>\` and \`user.email ${MANAGED_GIT_EMAIL}\`)`];
13496
+ }
13389
13497
 
13390
13498
  // src/selftest.ts
13391
13499
  import { spawn as spawn6 } from "child_process";
13392
- 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";
13393
13501
  import { tmpdir as tmpdir4 } from "os";
13394
13502
  import { join as join33 } from "path";
13395
13503
  var SELFTEST_TIMEOUT_MS = 12e4;
@@ -13523,6 +13631,11 @@ ${seen.texts.join("\n")}`)) {
13523
13631
  return fail2("mcp-unavailable", "the tracker MCP tool was not available to the session");
13524
13632
  }
13525
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
+ }
13526
13639
  if (!answered || LOGIN.test(run3.stderr) || LOGIN.test(seen.finalText ?? "")) {
13527
13640
  return fail2("login-missing", `\`${bin}\` exited${run3.code !== null ? ` (${run3.code})` : ""} before answering; the vendor login is probably missing`);
13528
13641
  }
@@ -13589,7 +13702,7 @@ var fileFor2 = (stateDir) => join33(stateDir, "selftest.json");
13589
13702
  function readSelftest(stateDir) {
13590
13703
  try {
13591
13704
  const p = fileFor2(stateDir);
13592
- return existsSync31(p) ? JSON.parse(readFileSync28(p, "utf8")) : {};
13705
+ return existsSync31(p) ? JSON.parse(readFileSync29(p, "utf8")) : {};
13593
13706
  } catch {
13594
13707
  return {};
13595
13708
  }
@@ -13613,7 +13726,7 @@ function doctorSelftestLines(stored, toolchains) {
13613
13726
  import { resolve as resolve7, dirname as dirname34, join as join56, basename as basename16 } from "path";
13614
13727
  import { fileURLToPath as fileURLToPath2 } from "url";
13615
13728
  import { hostname as hostname5, homedir as homedir11 } from "os";
13616
- import { spawnSync as spawnSync11 } from "child_process";
13729
+ import { spawnSync as spawnSync12 } from "child_process";
13617
13730
  import { parse as parseYaml2 } from "yaml";
13618
13731
 
13619
13732
  // src/argv.ts
@@ -13808,14 +13921,14 @@ function rosterMarkdown(roster, meId) {
13808
13921
  }
13809
13922
 
13810
13923
  // src/connect-resume.ts
13811
- 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";
13812
13925
  import { dirname as dirname16, join as join34 } from "path";
13813
13926
  function lastSessionFile(stateDir) {
13814
13927
  return join34(stateDir, "connect-session.json");
13815
13928
  }
13816
13929
  function readPointers(stateDir) {
13817
13930
  try {
13818
- const parsed = JSON.parse(readFileSync29(lastSessionFile(stateDir), "utf8"));
13931
+ const parsed = JSON.parse(readFileSync30(lastSessionFile(stateDir), "utf8"));
13819
13932
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return {};
13820
13933
  const out = {};
13821
13934
  for (const [host, v] of Object.entries(parsed)) {
@@ -13915,7 +14028,7 @@ async function signOutSessions(o) {
13915
14028
  }
13916
14029
 
13917
14030
  // src/reset.ts
13918
- 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";
13919
14032
  import { dirname as dirname17, join as join35 } from "path";
13920
14033
  import { hostFromUrl as hostFromUrl3 } from "@tablation/client";
13921
14034
  function clonedCheckoutsFile(stateDir) {
@@ -13923,7 +14036,7 @@ function clonedCheckoutsFile(stateDir) {
13923
14036
  }
13924
14037
  function readClonedCheckouts(stateDir) {
13925
14038
  try {
13926
- const parsed = JSON.parse(readFileSync30(clonedCheckoutsFile(stateDir), "utf8"));
14039
+ const parsed = JSON.parse(readFileSync31(clonedCheckoutsFile(stateDir), "utf8"));
13927
14040
  return Array.isArray(parsed) ? parsed.filter((d) => typeof d === "string") : [];
13928
14041
  } catch {
13929
14042
  return [];
@@ -14114,7 +14227,7 @@ async function runDeprovision(o, fx, deps) {
14114
14227
  }
14115
14228
 
14116
14229
  // src/pool-member.ts
14117
- 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";
14118
14231
  import { homedir as homedir8 } from "os";
14119
14232
  import { dirname as dirname18, join as join36 } from "path";
14120
14233
  var POOL_EXIT = { ok: 0, failed: 1, credential: 2 };
@@ -14354,7 +14467,7 @@ function fileMemberStore(dir) {
14354
14467
  return {
14355
14468
  read() {
14356
14469
  try {
14357
- return JSON.parse(readFileSync31(file, "utf8"));
14470
+ return JSON.parse(readFileSync32(file, "utf8"));
14358
14471
  } catch {
14359
14472
  return {};
14360
14473
  }
@@ -14420,7 +14533,7 @@ import { hostname as hostname3 } from "os";
14420
14533
  // src/machine-id.ts
14421
14534
  import { execFileSync as execFileSync10 } from "child_process";
14422
14535
  import { createHash as createHash8 } from "crypto";
14423
- 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";
14424
14537
  import { dirname as dirname19, join as join37 } from "path";
14425
14538
  function machineIdPathFor(stateDir) {
14426
14539
  return join37(stateDir, "machine-id");
@@ -14437,7 +14550,7 @@ function readRawMachineId(platform2 = process.platform) {
14437
14550
  }
14438
14551
  for (const p of ["/etc/machine-id", "/var/lib/dbus/machine-id"]) {
14439
14552
  if (existsSync33(p)) {
14440
- const id = readFileSync32(p, "utf8").trim();
14553
+ const id = readFileSync33(p, "utf8").trim();
14441
14554
  if (id) return id;
14442
14555
  }
14443
14556
  }
@@ -14449,7 +14562,7 @@ function ensureMachineId(stateDir, opts = {}) {
14449
14562
  if (opts.override?.trim()) return sha256(opts.override.trim());
14450
14563
  const path = machineIdPathFor(stateDir);
14451
14564
  if (existsSync33(path)) {
14452
- const cached = readFileSync32(path, "utf8").trim();
14565
+ const cached = readFileSync33(path, "utf8").trim();
14453
14566
  if (/^[0-9a-f]{64}$/.test(cached)) return cached;
14454
14567
  }
14455
14568
  const raw = (opts.read ?? readRawMachineId)();
@@ -14468,8 +14581,8 @@ function matchShipRows(ships, shipName2, machineId) {
14468
14581
  }
14469
14582
 
14470
14583
  // src/provision.ts
14471
- import { spawnSync as spawnSync8 } from "child_process";
14472
- import { existsSync as existsSync34, readFileSync as readFileSync33, writeFileSync as writeFileSync26 } from "fs";
14584
+ import { spawnSync as spawnSync9 } from "child_process";
14585
+ import { existsSync as existsSync34, readFileSync as readFileSync34, writeFileSync as writeFileSync26 } from "fs";
14473
14586
  import { hostname as hostname2 } from "os";
14474
14587
  import { parseDocument as parseDocument3, isMap as isMap2, isScalar } from "yaml";
14475
14588
 
@@ -14916,6 +15029,11 @@ async function runProvision(o, deps, write, human) {
14916
15029
  emit("connect", `set ${key}`, { setting: key });
14917
15030
  }
14918
15031
  }
15032
+ if (isManagedShipKind(o.shipKind)) {
15033
+ summary.gitIdentity = deps.ensureGitIdentity(shipName2, MANAGED_GIT_EMAIL);
15034
+ if (summary.gitIdentity === "set") summary.changed = true;
15035
+ emit("git_identity", `git identity: ${summary.gitIdentity}`, { action: summary.gitIdentity });
15036
+ }
14919
15037
  for (const host of o.forgeHosts) {
14920
15038
  const token = o.tokens[host];
14921
15039
  const stored = deps.storedToken(host);
@@ -15072,7 +15190,7 @@ function realProvisionDeps(crewHome) {
15072
15190
  const env = { ...process.env, ...o.env };
15073
15191
  delete env.CREW_FORGE_TOKEN;
15074
15192
  for (const k of Object.keys(env)) if (k.startsWith("CREW_CONNECT_KEY_")) delete env[k];
15075
- const res = spawnSync8(bin, [...pre, ...args], { cwd: crewHome, encoding: "utf8", env, input: o.input, maxBuffer: 64 * 1024 * 1024 });
15193
+ const res = spawnSync9(bin, [...pre, ...args], { cwd: crewHome, encoding: "utf8", env, input: o.input, maxBuffer: 64 * 1024 * 1024 });
15076
15194
  return { status: res.status ?? 1, stdout: res.stdout ?? "", stderr: `${res.stderr ?? ""}${res.error ? String(res.error) : ""}` };
15077
15195
  };
15078
15196
  const configFile = () => {
@@ -15085,7 +15203,7 @@ function realProvisionDeps(crewHome) {
15085
15203
  enableRoute(route2) {
15086
15204
  const file = configFile();
15087
15205
  if (!file) return false;
15088
- const text2 = readFileSync33(file, "utf8");
15206
+ const text2 = readFileSync34(file, "utf8");
15089
15207
  const doc = parseDocument3(text2);
15090
15208
  const routes = doc.get("routes");
15091
15209
  const entry = routes?.items?.find((r) => r.get("route") === route2);
@@ -15111,7 +15229,7 @@ function realProvisionDeps(crewHome) {
15111
15229
  continue;
15112
15230
  }
15113
15231
  for (const p of plan.unitPaths) {
15114
- if (!existsSync34(p) || readFileSync33(p, "utf8") !== plan.unitContent[p]) unitsStale = true;
15232
+ if (!existsSync34(p) || readFileSync34(p, "utf8") !== plan.unitContent[p]) unitsStale = true;
15115
15233
  }
15116
15234
  }
15117
15235
  const label = labelFor(crewHome, "run");
@@ -15120,22 +15238,22 @@ function realProvisionDeps(crewHome) {
15120
15238
  if (mechanism === "systemd") {
15121
15239
  const system = ctx.root || existsSync34(systemUnitPathFor(label));
15122
15240
  const ctl = system ? ["systemctl"] : ["systemctl", "--user"];
15123
- running = spawnSync8(ctl[0], [...ctl.slice(1), "is-active", "--quiet", `${label}.service`]).status === 0;
15241
+ running = spawnSync9(ctl[0], [...ctl.slice(1), "is-active", "--quiet", `${label}.service`]).status === 0;
15124
15242
  if (!system) {
15125
- const out = spawnSync8("loginctl", ["show-user", ctx.user, "-p", "Linger"], { encoding: "utf8" });
15243
+ const out = spawnSync9("loginctl", ["show-user", ctx.user, "-p", "Linger"], { encoding: "utf8" });
15126
15244
  lingering = /Linger=yes/.test(out.stdout ?? "");
15127
15245
  }
15128
15246
  } else if (mechanism === "cron") {
15129
15247
  running = true;
15130
15248
  } else if (mechanism === "launchd") {
15131
- running = spawnSync8("launchctl", ["list", label]).status === 0;
15249
+ running = spawnSync9("launchctl", ["list", label]).status === 0;
15132
15250
  }
15133
15251
  return { mechanism, unitsStale, running, lingering };
15134
15252
  },
15135
15253
  setShipSetting(key, value2) {
15136
15254
  const file = configFile();
15137
15255
  if (!file) return false;
15138
- const doc = parseDocument3(readFileSync33(file, "utf8"));
15256
+ const doc = parseDocument3(readFileSync34(file, "utf8"));
15139
15257
  if (doc.getIn(["ship", key]) === value2) return false;
15140
15258
  doc.setIn(["ship", key], value2);
15141
15259
  writeFileSync26(file, doc.toString());
@@ -15144,11 +15262,22 @@ function realProvisionDeps(crewHome) {
15144
15262
  applyManagedDefaults() {
15145
15263
  const file = configFile();
15146
15264
  if (!file) return [];
15147
- const doc = parseDocument3(readFileSync33(file, "utf8"));
15265
+ const doc = parseDocument3(readFileSync34(file, "utf8"));
15148
15266
  const wrote = applyManagedDefaultsToDoc(doc);
15149
15267
  if (wrote.length) writeFileSync26(file, doc.toString());
15150
15268
  return wrote;
15151
15269
  },
15270
+ ensureGitIdentity(name, email) {
15271
+ const have = readGlobalGitIdentity();
15272
+ let wrote = false;
15273
+ for (const [key, value2] of [["user.name", have.name ? null : name], ["user.email", have.email ? null : email]]) {
15274
+ if (value2 === null) continue;
15275
+ const res = spawnSync9("git", ["config", "--global", key, value2], { encoding: "utf8" });
15276
+ if (res.status !== 0) throw new Error(`git config --global ${key}: ${(res.stderr ?? "").trim() || `exited ${res.status}`}`);
15277
+ wrote = true;
15278
+ }
15279
+ return wrote ? "set" : "kept";
15280
+ },
15152
15281
  storedToken(host) {
15153
15282
  const res = spawnCrew(["credential", "get"], { input: `protocol=https
15154
15283
  host=${host}
@@ -15163,7 +15292,7 @@ host=${host}
15163
15292
 
15164
15293
  // src/tunnel.ts
15165
15294
  import { spawn as spawn7 } from "child_process";
15166
- 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";
15167
15296
  import { randomInt } from "crypto";
15168
15297
  import { join as join39 } from "path";
15169
15298
  var DEFAULT_SPAWN_OPTIONS = { stdio: "ignore", detached: true };
@@ -15249,7 +15378,7 @@ function readPersistedTunnel(stateDir, workspaceId) {
15249
15378
  const path = tunnelStatePath(stateDir, workspaceId);
15250
15379
  if (!existsSync35(path)) return void 0;
15251
15380
  try {
15252
- return JSON.parse(readFileSync34(path, "utf8"));
15381
+ return JSON.parse(readFileSync35(path, "utf8"));
15253
15382
  } catch {
15254
15383
  return void 0;
15255
15384
  }
@@ -16794,7 +16923,7 @@ async function resolveOne(tracker, t, seat, approvedStatus, buildingStatus, affi
16794
16923
  }
16795
16924
 
16796
16925
  // src/repo-refresh.ts
16797
- 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";
16798
16927
  import { dirname as dirname21 } from "path";
16799
16928
  var REFRESH_COOLDOWN_MS = 2 * 6e4;
16800
16929
  var UNSERVABLE_PASSES_BEFORE_ATTENTION = 3;
@@ -16825,7 +16954,7 @@ function applyRepoRows(resolved, rows) {
16825
16954
  function persistRepoRows(stateDir, routeName, rows) {
16826
16955
  const path = resolvedPathFor(stateDir, routeName);
16827
16956
  if (!existsSync37(path)) return;
16828
- const raw = JSON.parse(readFileSync35(path, "utf8"));
16957
+ const raw = JSON.parse(readFileSync36(path, "utf8"));
16829
16958
  applyRepoRows(raw, rows);
16830
16959
  mkdirSync26(dirname21(path), { recursive: true });
16831
16960
  writeFileSync29(path, `${JSON.stringify(raw, null, 2)}
@@ -17478,7 +17607,7 @@ function flagWizardAnswers(f, env) {
17478
17607
  }
17479
17608
 
17480
17609
  // src/release-run.ts
17481
- 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";
17482
17611
  import { dirname as dirname24, join as join43 } from "path";
17483
17612
  import { tmpdir as tmpdir5, hostname as hostname4 } from "os";
17484
17613
 
@@ -18255,7 +18384,7 @@ async function bump(o, size) {
18255
18384
  o.emit.error(`no ${o.repo.release.versionFiles[0]} to read a version from`);
18256
18385
  return null;
18257
18386
  }
18258
- const firstText = readFileSync36(first, "utf8");
18387
+ const firstText = readFileSync37(first, "utf8");
18259
18388
  const problem = versionFileProblem(o.repo.release.versionFiles[0], firstText);
18260
18389
  if (problem) {
18261
18390
  o.emit.error(problem);
@@ -18274,7 +18403,7 @@ async function bump(o, size) {
18274
18403
  return version;
18275
18404
  }
18276
18405
  for (const f of files) {
18277
- const src = readFileSync36(f, "utf8");
18406
+ const src = readFileSync37(f, "utf8");
18278
18407
  const out = src.replace(/("version"\s*:\s*")[^"]*(")/, `$1${version}$2`);
18279
18408
  if (out === src) {
18280
18409
  o.emit.error(`no version field in ${f}`);
@@ -18293,7 +18422,7 @@ function writeChangelog(o, version, entries) {
18293
18422
  o.emit.emit(`would prepend ${entries.length} entr(ies) to ${file}`);
18294
18423
  return;
18295
18424
  }
18296
- const existing = existsSync39(path) ? readFileSync36(path, "utf8") : "# Changelog\n";
18425
+ const existing = existsSync39(path) ? readFileSync37(path, "utf8") : "# Changelog\n";
18297
18426
  writeFileSync31(path, insertChangelogSection(existing, section));
18298
18427
  }
18299
18428
  async function confirm(o, expected) {
@@ -19810,7 +19939,7 @@ function nextRoles(decision, ran) {
19810
19939
  }
19811
19940
 
19812
19941
  // src/ship-name.ts
19813
- 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";
19814
19943
  import { dirname as dirname26 } from "path";
19815
19944
  import { parseDocument as parseDocument5 } from "yaml";
19816
19945
  var ShipNameError = class extends Error {
@@ -19874,7 +20003,7 @@ async function syncRouteName(i) {
19874
20003
  function readSyncedName(resolvedFile) {
19875
20004
  if (!existsSync42(resolvedFile)) return void 0;
19876
20005
  try {
19877
- const v = JSON.parse(readFileSync37(resolvedFile, "utf8")).shipName;
20006
+ const v = JSON.parse(readFileSync38(resolvedFile, "utf8")).shipName;
19878
20007
  return typeof v === "string" ? v : void 0;
19879
20008
  } catch {
19880
20009
  return void 0;
@@ -19884,7 +20013,7 @@ function writeSyncedName(resolvedFile, name) {
19884
20013
  if (!existsSync42(resolvedFile)) return;
19885
20014
  let raw;
19886
20015
  try {
19887
- raw = JSON.parse(readFileSync37(resolvedFile, "utf8"));
20016
+ raw = JSON.parse(readFileSync38(resolvedFile, "utf8"));
19888
20017
  } catch {
19889
20018
  return;
19890
20019
  }
@@ -19899,7 +20028,7 @@ function writeSyncedName(resolvedFile, name) {
19899
20028
  async function reconcileShipName(i) {
19900
20029
  let local = i.ship.name;
19901
20030
  try {
19902
- local = shipNameOfYaml(readFileSync37(i.configFile, "utf8")) ?? local;
20031
+ local = shipNameOfYaml(readFileSync38(i.configFile, "utf8")) ?? local;
19903
20032
  } catch {
19904
20033
  }
19905
20034
  const resolvedFile = resolvedPathFor(i.stateDir, i.route);
@@ -19923,7 +20052,7 @@ async function reconcileShipName(i) {
19923
20052
  }
19924
20053
  if (result.status !== "ok") return result;
19925
20054
  if (result.action === "adopt" || result.action === "row-wins") {
19926
- const text2 = readFileSync37(i.configFile, "utf8");
20055
+ const text2 = readFileSync38(i.configFile, "utf8");
19927
20056
  writeFileSync32(i.configFile, setShipNameInYaml(text2, result.name));
19928
20057
  i.ship.name = result.name;
19929
20058
  if (result.action === "row-wins") {
@@ -20583,7 +20712,7 @@ function describeCapability(i) {
20583
20712
  }
20584
20713
 
20585
20714
  // src/agents-sync-run.ts
20586
- 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";
20587
20716
  import { dirname as dirname27 } from "path";
20588
20717
  async function runAgentsSync(target, o) {
20589
20718
  const dryRun = o.dryRun ?? false;
@@ -20617,7 +20746,7 @@ async function runAgentsSync(target, o) {
20617
20746
  for (const s of skillResult?.outcomes ?? []) o.say(describeSkillSyncOutcome(s));
20618
20747
  if (!dryRun) {
20619
20748
  const resolvedPath = resolvedPathFor(o.stateDir, target.route);
20620
- const raw = existsSync43(resolvedPath) ? JSON.parse(readFileSync38(resolvedPath, "utf8")) : {};
20749
+ const raw = existsSync43(resolvedPath) ? JSON.parse(readFileSync39(resolvedPath, "utf8")) : {};
20621
20750
  raw.agentPersonas = personaResult.agentPersonas;
20622
20751
  if (skillResult) raw.agentSkills = skillResult.agentSkills;
20623
20752
  if (personaResult.releaseAgentId) raw.releaseAgentId = personaResult.releaseAgentId;
@@ -20738,8 +20867,8 @@ async function showLogEntry(route2, entryId, opts = {}) {
20738
20867
  }
20739
20868
 
20740
20869
  // src/repos-check.ts
20741
- import { spawnSync as spawnSync10 } from "child_process";
20742
- import { readFileSync as readFileSync39 } from "fs";
20870
+ import { spawnSync as spawnSync11 } from "child_process";
20871
+ import { readFileSync as readFileSync40 } from "fs";
20743
20872
  import { basename as basename11 } from "path";
20744
20873
  var POSIX_SHELL = /^(?:ba|z|da|k|a|mk)?sh$|^fish$/;
20745
20874
  function checkHookSyntax(script, shell) {
@@ -20747,10 +20876,10 @@ function checkHookSyntax(script, shell) {
20747
20876
  const name = basename11(spec.bin).replace(/\.exe$/i, "").toLowerCase();
20748
20877
  let run3;
20749
20878
  if (POSIX_SHELL.test(name)) {
20750
- run3 = spawnSync10(spec.bin, ["-n"], { input: script, encoding: "utf8" });
20879
+ run3 = spawnSync11(spec.bin, ["-n"], { input: script, encoding: "utf8" });
20751
20880
  } else if (/^(pwsh|powershell)$/.test(name)) {
20752
20881
  const parse3 = "$e=$null;[void][System.Management.Automation.Language.Parser]::ParseInput($env:CREW_CHECK_SCRIPT,[ref]$null,[ref]$e);if($e){$e|ForEach-Object{[Console]::Error.WriteLine($_.Message)};exit 1}";
20753
- run3 = spawnSync10(spec.bin, ["-NoProfile", "-NonInteractive", "-Command", parse3], {
20882
+ run3 = spawnSync11(spec.bin, ["-NoProfile", "-NonInteractive", "-Command", parse3], {
20754
20883
  encoding: "utf8",
20755
20884
  env: { ...process.env, CREW_CHECK_SCRIPT: script }
20756
20885
  });
@@ -20780,7 +20909,7 @@ function checkRepoSpec(dir, opts = {}) {
20780
20909
  }
20781
20910
  let parsed;
20782
20911
  try {
20783
- parsed = parseRepoConfig(readFileSync39(file, "utf8"), file);
20912
+ parsed = parseRepoConfig(readFileSync40(file, "utf8"), file);
20784
20913
  } catch (e) {
20785
20914
  if (!(e instanceof RepoConfigError)) throw e;
20786
20915
  return { ...empty, file, error: e.message, exitCode: 1 };
@@ -21312,7 +21441,7 @@ function planSampleTicket(i) {
21312
21441
  }
21313
21442
 
21314
21443
  // src/worktree-sweep.ts
21315
- 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";
21316
21445
  import { basename as basename13, dirname as dirname29, join as join45, resolve as resolve5 } from "path";
21317
21446
  function planWorktreeSweep(target, tickets, contract) {
21318
21447
  const terminal = new Set(contract.statuses.resolved.filter((s) => s !== contract.statuses.verified));
@@ -21493,7 +21622,7 @@ function isHandBackMerge(path, base) {
21493
21622
  if (!msgPath) return false;
21494
21623
  let first;
21495
21624
  try {
21496
- first = readFileSync40(resolve5(path, msgPath), "utf8").split("\n")[0] ?? "";
21625
+ first = readFileSync41(resolve5(path, msgPath), "utf8").split("\n")[0] ?? "";
21497
21626
  } catch {
21498
21627
  return false;
21499
21628
  }
@@ -21643,11 +21772,11 @@ function planEventsSweep(stateDir, retentionDays, now = Date.now()) {
21643
21772
  }
21644
21773
 
21645
21774
  // src/cli.ts
21646
- 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";
21647
21776
  import { createInterface } from "readline/promises";
21648
21777
 
21649
21778
  // src/periscope-cmd.ts
21650
- 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";
21651
21780
  import { join as join47 } from "path";
21652
21781
  var PeriscopeError = class extends Error {
21653
21782
  };
@@ -21673,7 +21802,7 @@ function writePeriscopePrior(stateDir, ticketRecordId, prior) {
21673
21802
  function readPeriscopePrior(stateDir, ticketRecordId) {
21674
21803
  try {
21675
21804
  const p = priorPath(stateDir, ticketRecordId);
21676
- return existsSync46(p) ? JSON.parse(readFileSync41(p, "utf8")) : void 0;
21805
+ return existsSync46(p) ? JSON.parse(readFileSync42(p, "utf8")) : void 0;
21677
21806
  } catch {
21678
21807
  return void 0;
21679
21808
  }
@@ -22228,7 +22357,7 @@ import { dirname as dirOf2, resolve as resolvePath } from "path";
22228
22357
 
22229
22358
  // src/ssh-keys.ts
22230
22359
  import { execFileSync as execFileSync13 } from "child_process";
22231
- 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";
22232
22361
  import { dirname as dirname30, join as join49 } from "path";
22233
22362
  var SshKeyError = class extends Error {
22234
22363
  };
@@ -22262,7 +22391,7 @@ function ensureShipSshKeypair(stateDir) {
22262
22391
  if (!existsSync47(publicKeyPath)) {
22263
22392
  throw new SshKeyError(`${privateKeyPath} exists but ${publicKeyPath} does not \u2014 regenerate or restore it`);
22264
22393
  }
22265
- const publicKey = readFileSync42(publicKeyPath, "utf8").trim();
22394
+ const publicKey = readFileSync43(publicKeyPath, "utf8").trim();
22266
22395
  return { privateKeyPath, publicKeyPath, publicKey };
22267
22396
  }
22268
22397
  function sshKeygenAvailable() {
@@ -22426,7 +22555,7 @@ async function unregisterEndpoint(workspaceId, info, d) {
22426
22555
  import { createServer as createHttpServer } from "http";
22427
22556
  import { spawn as spawn9 } from "child_process";
22428
22557
  import { randomBytes } from "crypto";
22429
- 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";
22430
22559
  import { join as join50 } from "path";
22431
22560
 
22432
22561
  // src/secret-compare.ts
@@ -22526,7 +22655,7 @@ function readPersistedSyncDaemon(stateDir, workspaceId) {
22526
22655
  const path = syncDaemonStatePath(stateDir, workspaceId);
22527
22656
  if (!existsSync48(path)) return void 0;
22528
22657
  try {
22529
- return JSON.parse(readFileSync43(path, "utf8"));
22658
+ return JSON.parse(readFileSync44(path, "utf8"));
22530
22659
  } catch {
22531
22660
  return void 0;
22532
22661
  }
@@ -23075,7 +23204,7 @@ function contractFixers(dir, baseRef, tickets, locate) {
23075
23204
  }
23076
23205
 
23077
23206
  // src/daemon.ts
23078
- 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";
23079
23208
  import { join as join52 } from "path";
23080
23209
  import { randomBytes as randomBytes2 } from "crypto";
23081
23210
  var DEFAULT_FLOOR_MS = 5e3;
@@ -23196,7 +23325,7 @@ function noteDaemonStarted(stateDir, now = Date.now()) {
23196
23325
  }
23197
23326
  function readDaemonStarted(stateDir) {
23198
23327
  try {
23199
- const v = JSON.parse(readFileSync44(daemonStampFor(stateDir), "utf8")).startedAt;
23328
+ const v = JSON.parse(readFileSync45(daemonStampFor(stateDir), "utf8")).startedAt;
23200
23329
  return typeof v === "number" ? v : null;
23201
23330
  } catch {
23202
23331
  return null;
@@ -23379,6 +23508,12 @@ async function runOnePassUnguarded(o) {
23379
23508
  clearShipAttention(o.state, workspaceAllowanceKey(role));
23380
23509
  }).catch(async (e) => {
23381
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
+ }
23382
23517
  if (e instanceof WorkspaceAllowanceExhausted) {
23383
23518
  breaker.pause(role, ALLOWANCE_PAUSE_MS);
23384
23519
  emit.warn(`${role} paused: ${message}`, { step: "agent", role });
@@ -23720,6 +23855,7 @@ async function runRoleAgentLocked(role, decision, ctx, hooks, held) {
23720
23855
  if (plan.workspaceAuth && streamShowsAllowanceRefusal(plan.streamPath)) {
23721
23856
  throw new WorkspaceAllowanceExhausted("the inference endpoint refused the run: the workspace is past its AI allowance");
23722
23857
  }
23858
+ await settleSignOut(plan, tracker, ctx.state, workingId, emit);
23723
23859
  }
23724
23860
  } finally {
23725
23861
  await settleRunEnd({
@@ -23756,6 +23892,26 @@ async function buildEnvironment(route2, ship, ticket) {
23756
23892
  const attachments = await tracker.attachmentsProbe().catch(() => null);
23757
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) });
23758
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
+ }
23759
23915
 
23760
23916
  // src/control-api.ts
23761
23917
  import { createHash as createHash11 } from "crypto";
@@ -24446,7 +24602,7 @@ import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/
24446
24602
 
24447
24603
  // src/periscope-endpoints.ts
24448
24604
  import { randomBytes as randomBytes3 } from "crypto";
24449
- 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";
24450
24606
  import { join as join54 } from "path";
24451
24607
  var secretDir = (stateDir) => join54(stateDir, "periscope-mcp");
24452
24608
  var secretFile = (stateDir, id) => join54(secretDir(stateDir), `${id}.json`);
@@ -24463,7 +24619,7 @@ function mintPeriscopeSecret(stateDir, ticketRecordId) {
24463
24619
  }
24464
24620
  function readPeriscopeSecret(stateDir, ticketRecordId) {
24465
24621
  try {
24466
- const v = JSON.parse(readFileSync45(secretFile(stateDir, ticketRecordId), "utf8"));
24622
+ const v = JSON.parse(readFileSync46(secretFile(stateDir, ticketRecordId), "utf8"));
24467
24623
  return typeof v.secret === "string" && v.secret ? v.secret : void 0;
24468
24624
  } catch {
24469
24625
  return void 0;
@@ -24474,7 +24630,7 @@ function removePeriscopeSecret(stateDir, ticketRecordId) {
24474
24630
  }
24475
24631
  function readPeriscopeEndpoint(stateDir, ticketRecordId) {
24476
24632
  try {
24477
- return JSON.parse(readFileSync45(endpointFile(stateDir, ticketRecordId), "utf8"));
24633
+ return JSON.parse(readFileSync46(endpointFile(stateDir, ticketRecordId), "utf8"));
24478
24634
  } catch {
24479
24635
  return void 0;
24480
24636
  }
@@ -25223,7 +25379,7 @@ if (command === "provision") {
25223
25379
  process.exit(outcome.code);
25224
25380
  }
25225
25381
  if (command === "pool-member") {
25226
- const pool = poolConfigFrom(argv, process.env, () => readFileSync46(0, "utf8"));
25382
+ const pool = poolConfigFrom(argv, process.env, () => readFileSync47(0, "utf8"));
25227
25383
  if (typeof pool === "string") {
25228
25384
  process.stderr.write(`crew pool-member: ${pool}
25229
25385
  `);
@@ -25487,7 +25643,7 @@ if (command === "passenger-sync-daemon") {
25487
25643
  throw e;
25488
25644
  }
25489
25645
  if (command === "credential") {
25490
- 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);
25491
25647
  process.stdout.write(r.out);
25492
25648
  process.stderr.write(r.err);
25493
25649
  process.exit(r.code);
@@ -25499,9 +25655,9 @@ if (command === "passenger-sync-daemon") {
25499
25655
  forges: cfg.ship.forges,
25500
25656
  stateDir: cfg.ship.stateDir,
25501
25657
  configFile: cfg.configFile,
25502
- readFile: (p) => readFileSync46(p, "utf8"),
25658
+ readFile: (p) => readFileSync47(p, "utf8"),
25503
25659
  writeFile: (p, t) => writeFileSync38(p, t),
25504
- stdin: () => readFileSync46(0, "utf8"),
25660
+ stdin: () => readFileSync47(0, "utf8"),
25505
25661
  env: process.env
25506
25662
  }
25507
25663
  );
@@ -25831,7 +25987,7 @@ if (command === "passenger-sync-daemon") {
25831
25987
  `
25832
25988
  );
25833
25989
  if (!dryRun) {
25834
- spawnSync11("systemctl", ["--user", "disable", "--now", `${label}.timer`], { cwd: CREW_HOME });
25990
+ spawnSync12("systemctl", ["--user", "disable", "--now", `${label}.timer`], { cwd: CREW_HOME });
25835
25991
  try {
25836
25992
  unlinkSync5(staleTimer);
25837
25993
  } catch {
@@ -25882,7 +26038,7 @@ if (command === "passenger-sync-daemon") {
25882
26038
  async function doctorFix(o) {
25883
26039
  const routes = await Promise.all(routesInScope(cfg.routes, named).map((c) => gatherHealth({ route: c.route, enabled: c.enabled, hasRepos: reposOf(c).length > 0 }, () => routeProblems(c))));
25884
26040
  const unitExists = (p) => existsSync52(p);
25885
- const crontab = o.host === "linux" ? spawnSync11("crontab", ["-l"], { encoding: "utf8" }).stdout ?? "" : "";
26041
+ const crontab = o.host === "linux" ? spawnSync12("crontab", ["-l"], { encoding: "utf8" }).stdout ?? "" : "";
25886
26042
  const schedulerAbsent = o.host !== "windows" ? !schedulerInstalled(planInstall(cfg.ship, CREW_HOME, o.host, detectSystemd(), "run", installContext()), unitExists, crontab) : true;
25887
26043
  const plan = planFix({ routes, schedulerAbsent, host: o.host });
25888
26044
  for (const n of plan.notes) process.stdout.write(`fix: ${n}
@@ -25898,7 +26054,7 @@ if (command === "passenger-sync-daemon") {
25898
26054
  for (const a of plan.actions) {
25899
26055
  if (a.kind === "enable") {
25900
26056
  if (prompter && !await prompter.confirm(`Route ${a.route} passes every check \u2014 set enabled: true in ${cfg.configFile}?`, false)) continue;
25901
- writeFileSync38(cfg.configFile, planEnable(readFileSync46(cfg.configFile, "utf8"), a.route));
26057
+ writeFileSync38(cfg.configFile, planEnable(readFileSync47(cfg.configFile, "utf8"), a.route));
25902
26058
  enabledSound.add(a.route);
25903
26059
  changed2.push(`enabled route ${a.route} in ${cfg.configFile}`);
25904
26060
  } else {
@@ -27053,7 +27209,7 @@ ${cu.tail.trimEnd()}
27053
27209
  }
27054
27210
  let failed = false;
27055
27211
  for (const cmd2 of commands) {
27056
- const res2 = spawnSync11(cmd2[0], cmd2.slice(1), { cwd: CREW_HOME, encoding: "utf8" });
27212
+ const res2 = spawnSync12(cmd2[0], cmd2.slice(1), { cwd: CREW_HOME, encoding: "utf8" });
27057
27213
  if (res2.stdout) process.stdout.write(res2.stdout);
27058
27214
  if (res2.stderr) process.stderr.write(res2.stderr);
27059
27215
  if (cmd2 === control.startCommand && (res2.error || res2.status !== 0)) {
@@ -27071,7 +27227,7 @@ ${cu.tail.trimEnd()}
27071
27227
  `);
27072
27228
  break;
27073
27229
  }
27074
- const res = spawnSync11(cmd[0], cmd.slice(1), { cwd: CREW_HOME, encoding: "utf8" });
27230
+ const res = spawnSync12(cmd[0], cmd.slice(1), { cwd: CREW_HOME, encoding: "utf8" });
27075
27231
  if (res.stdout) process.stdout.write(res.stdout);
27076
27232
  if (res.stderr) process.stderr.write(res.stderr);
27077
27233
  if (daemonSub !== "status" && (res.error || res.status !== 0)) {
@@ -27399,7 +27555,7 @@ ${cu.tail.trimEnd()}
27399
27555
  `);
27400
27556
  if (!dryRun) {
27401
27557
  const resolvedPath = resolvedPathFor(cfg.ship.stateDir, target.route);
27402
- const raw = existsSync52(resolvedPath) ? JSON.parse(readFileSync46(resolvedPath, "utf8")) : {};
27558
+ const raw = existsSync52(resolvedPath) ? JSON.parse(readFileSync47(resolvedPath, "utf8")) : {};
27403
27559
  raw.agentSkills = result.agentSkills;
27404
27560
  mkdirSync38(dirOf2(resolvedPath), { recursive: true });
27405
27561
  writeFileSync38(resolvedPath, `${JSON.stringify(raw, null, 2)}
@@ -27425,7 +27581,7 @@ ${cu.tail.trimEnd()}
27425
27581
  throw e;
27426
27582
  }
27427
27583
  const oldName = cfg.ship.name;
27428
- writeFileSync38(cfg.configFile, setShipNameInYaml(readFileSync46(cfg.configFile, "utf8"), newName));
27584
+ writeFileSync38(cfg.configFile, setShipNameInYaml(readFileSync47(cfg.configFile, "utf8"), newName));
27429
27585
  cfg.ship.name = newName;
27430
27586
  let renameMachineId;
27431
27587
  try {
@@ -27472,7 +27628,7 @@ ${cu.tail.trimEnd()}
27472
27628
  process.exit(2);
27473
27629
  };
27474
27630
  if (sub !== "add" && sub !== "list" && sub !== "clone" && sub !== "hooks" && sub !== "check") usage2();
27475
- const configText = readFileSync46(cfg.configFile, "utf8");
27631
+ const configText = readFileSync47(cfg.configFile, "utf8");
27476
27632
  if (sub === "check") {
27477
27633
  try {
27478
27634
  const routeArg = value("route") ?? positional[2];
@@ -27513,7 +27669,7 @@ ${cu.tail.trimEnd()}
27513
27669
  }
27514
27670
  }, read: (r) => {
27515
27671
  try {
27516
- return readFileSync46(resolvePath(dir, r), "utf8");
27672
+ return readFileSync47(resolvePath(dir, r), "utf8");
27517
27673
  } catch {
27518
27674
  return void 0;
27519
27675
  }
@@ -27664,7 +27820,7 @@ ${cu.tail.trimEnd()}
27664
27820
  let resolvedRemotes;
27665
27821
  if (existsSync52(resolvedFile)) {
27666
27822
  try {
27667
- resolvedRemotes = JSON.parse(readFileSync46(resolvedFile, "utf8"));
27823
+ resolvedRemotes = JSON.parse(readFileSync47(resolvedFile, "utf8"));
27668
27824
  } catch {
27669
27825
  }
27670
27826
  }
@@ -27686,7 +27842,7 @@ ${cu.tail.trimEnd()}
27686
27842
  repoHasHooks: (d) => REPO_CONFIG_FILENAMES.some((f) => {
27687
27843
  try {
27688
27844
  const path = resolvePath(d, f);
27689
- return Object.keys(parseRepoConfig(readFileSync46(path, "utf8"), path).hooks).length > 0;
27845
+ return Object.keys(parseRepoConfig(readFileSync47(path, "utf8"), path).hooks).length > 0;
27690
27846
  } catch {
27691
27847
  return false;
27692
27848
  }
@@ -27795,7 +27951,7 @@ ${cu.tail.trimEnd()}
27795
27951
  process.stdout.write(`created Repos row ${String(rowPayload.name)} (${String(rowPayload.remote)}) ${created.id}
27796
27952
  `);
27797
27953
  if (existsSync52(resolvedFile)) {
27798
- const raw = JSON.parse(readFileSync46(resolvedFile, "utf8"));
27954
+ const raw = JSON.parse(readFileSync47(resolvedFile, "utf8"));
27799
27955
  raw.repoNames = { ...raw.repoNames, [created.id]: String(rowPayload.name) };
27800
27956
  raw.repoRemotes = { ...raw.repoRemotes, [created.id]: String(rowPayload.remote) };
27801
27957
  writeFileSync38(resolvedFile, `${JSON.stringify(raw, null, 2)}
@@ -28173,7 +28329,7 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
28173
28329
  process.exit(2);
28174
28330
  }
28175
28331
  if (found.shipNameAdopted && !dryRun) {
28176
- writeFileSync38(cfg.configFile, setShipNameInYaml(readFileSync46(cfg.configFile, "utf8"), found.shipNameAdopted));
28332
+ writeFileSync38(cfg.configFile, setShipNameInYaml(readFileSync47(cfg.configFile, "utf8"), found.shipNameAdopted));
28177
28333
  cfg.ship.name = found.shipNameAdopted;
28178
28334
  }
28179
28335
  if (found.shipRow) {
@@ -28219,7 +28375,7 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
28219
28375
  const hadPreviousFile = existsSync52(resolvedPath);
28220
28376
  if (hadPreviousFile) {
28221
28377
  try {
28222
- previous = JSON.parse(readFileSync46(resolvedPath, "utf8"));
28378
+ previous = JSON.parse(readFileSync47(resolvedPath, "utf8"));
28223
28379
  } catch {
28224
28380
  }
28225
28381
  }
@@ -29528,7 +29684,7 @@ ${where.join("\n")}`);
29528
29684
  process.stdout.write(upOutput(res, terminal));
29529
29685
  const opener = openerFor(process.platform);
29530
29686
  if (opener && shouldOpen({ open: flag("open"), noOpen: flag("no-open"), json: jsonP, terminal })) {
29531
- const r = spawnSync11(opener, [res.url], { stdio: "ignore" });
29687
+ const r = spawnSync12(opener, [res.url], { stdio: "ignore" });
29532
29688
  if (r.error || r.status !== 0) process.stderr.write(`crew periscope: could not open ${res.url} with ${opener}
29533
29689
  `);
29534
29690
  }
@@ -29729,7 +29885,7 @@ ${where.join("\n")}`);
29729
29885
  };
29730
29886
  const droppedRoutes = [];
29731
29887
  try {
29732
- const rawCfg = parseYaml2(readFileSync46(cfg.configFile, "utf8"));
29888
+ const rawCfg = parseYaml2(readFileSync47(cfg.configFile, "utf8"));
29733
29889
  for (const r of rawCfg?.routes ?? []) {
29734
29890
  if (typeof r?.route !== "string" || cfg.routes.some((x) => x.route === r.route)) continue;
29735
29891
  try {
@@ -29776,7 +29932,7 @@ ${where.join("\n")}`);
29776
29932
  }
29777
29933
  const host = hostPlatform();
29778
29934
  const control = host === "windows" ? null : planDaemonControl(CREW_HOME, host, detectSystemd());
29779
- const unitStatus = control?.statusCommand ? spawnSync11(control.statusCommand[0], control.statusCommand.slice(1), { cwd: CREW_HOME, encoding: "utf8" }) : null;
29935
+ const unitStatus = control?.statusCommand ? spawnSync12(control.statusCommand[0], control.statusCommand.slice(1), { cwd: CREW_HOME, encoding: "utf8" }) : null;
29780
29936
  const unitRunning = unitStatus !== null && !unitStatus.error && unitStatus.status === 0;
29781
29937
  if (!unitRunning) {
29782
29938
  process.stdout.write("crew reload: no daemon unit is running \u2014 nothing to reload (the next run reads crew.yaml fresh)\n");
@@ -29790,7 +29946,7 @@ ${where.join("\n")}`);
29790
29946
  );
29791
29947
  if (flag("now") && control?.stopCommand && control.startCommand) {
29792
29948
  for (const cmd of [control.stopCommand, control.startCommand]) {
29793
- const res = spawnSync11(cmd[0], cmd.slice(1), { cwd: CREW_HOME, encoding: "utf8" });
29949
+ const res = spawnSync12(cmd[0], cmd.slice(1), { cwd: CREW_HOME, encoding: "utf8" });
29794
29950
  if (cmd === control.startCommand && (res.error || res.status !== 0)) {
29795
29951
  process.stderr.write(`crew reload: ${cmd.join(" ")} exited ${res.status ?? "error"}
29796
29952
  `);
@@ -29848,7 +30004,7 @@ ${where.join("\n")}`);
29848
30004
  }
29849
30005
  case "log": {
29850
30006
  try {
29851
- const text2 = readFileSync46(cfg.ship.logFile, "utf8").split("\n");
30007
+ const text2 = readFileSync47(cfg.ship.logFile, "utf8").split("\n");
29852
30008
  process.stdout.write(`${text2.slice(-(Number(value("tail")) || 40)).join("\n")}
29853
30009
  `);
29854
30010
  } catch {
@@ -29871,7 +30027,7 @@ ${where.join("\n")}`);
29871
30027
  };
29872
30028
  if (!route2.resolved) fail2(`route ${route2.route} is not connected \u2014 run \`crew connect\` first`);
29873
30029
  const tracker = new Tracker(route2, cfg.ship);
29874
- const configText = readFileSync46(cfg.configFile, "utf8");
30030
+ const configText = readFileSync47(cfg.configFile, "utf8");
29875
30031
  const cfgBase = dirname34(cfg.configFile);
29876
30032
  const configured2 = configuredRepos(configText, route2.route, cfgBase);
29877
30033
  const dir = configured2.find((r) => r.name === SAMPLE_REPO)?.dir ?? sampleDir(configured2.map((r) => r.dir), route2.reposBasePath, route2.route.split("/")[0]);
@@ -29909,7 +30065,7 @@ ${where.join("\n")}`);
29909
30065
  if (row) {
29910
30066
  await tracker.deleteRepoRow(row.id);
29911
30067
  if (existsSync52(resolvedFile)) {
29912
- const raw = JSON.parse(readFileSync46(resolvedFile, "utf8"));
30068
+ const raw = JSON.parse(readFileSync47(resolvedFile, "utf8"));
29913
30069
  if (raw.repoNames) delete raw.repoNames[row.id];
29914
30070
  if (raw.repoRemotes) delete raw.repoRemotes[row.id];
29915
30071
  writeFileSync38(resolvedFile, `${JSON.stringify(raw, null, 2)}
@@ -29932,7 +30088,7 @@ ${where.join("\n")}`);
29932
30088
  writeFileSync38(join56(dir, "README.md"), SAMPLE_README);
29933
30089
  writeFileSync38(join56(dir, ".crew.yaml"), SAMPLE_CREW_YAML);
29934
30090
  const g = (...a) => {
29935
- const r = spawnSync11("git", a, { cwd: dir, encoding: "utf8" });
30091
+ const r = spawnSync12("git", a, { cwd: dir, encoding: "utf8" });
29936
30092
  if (r.status !== 0) fail2(`git ${a[0]} failed in ${dir}: ${(r.stderr || r.stdout).trim()}`);
29937
30093
  };
29938
30094
  g("init", "-b", "main");
@@ -29949,7 +30105,7 @@ ${where.join("\n")}`);
29949
30105
  });
29950
30106
  repoId = (await tracker.createRepoRow(payload)).id;
29951
30107
  if (existsSync52(resolvedFile)) {
29952
- const raw = JSON.parse(readFileSync46(resolvedFile, "utf8"));
30108
+ const raw = JSON.parse(readFileSync47(resolvedFile, "utf8"));
29953
30109
  raw.repoNames = { ...raw.repoNames, [repoId]: SAMPLE_REPO };
29954
30110
  raw.repoRemotes = { ...raw.repoRemotes, [repoId]: SAMPLE_REMOTE };
29955
30111
  writeFileSync38(resolvedFile, `${JSON.stringify(raw, null, 2)}
@@ -30364,7 +30520,9 @@ watermark: ${state.watermark()}
30364
30520
  }
30365
30521
  for (const message of roleCapWarnings(cfg.ship)) problems.push({ severity: "note", kind: "role_cap_above_ship_cap", message });
30366
30522
  if (!flag("offline")) {
30367
- for (const message of managedRuntimeWarnings(await doctorShipKind(route2, cfg.ship), cfg.ship)) problems.push({ severity: "attention", kind: "managed_runtime_default", message });
30523
+ const shipKind = await doctorShipKind(route2, cfg.ship);
30524
+ for (const message of managedRuntimeWarnings(shipKind, cfg.ship)) problems.push({ severity: "attention", kind: "managed_runtime_default", message });
30525
+ for (const message of managedGitIdentityWarnings(shipKind, readGlobalGitIdentity())) problems.push({ severity: "attention", kind: "managed_git_identity", message });
30368
30526
  }
30369
30527
  for (const t of rejectedTunnels(cfg.ship.stateDir)) {
30370
30528
  problems.push({ severity: "attention", kind: TUNNEL_REJECTED_KEY, message: tunnelRejectedMessage(t.workspaceId, t.quickExits), remedy: "rotate the slug with `crew rotate-passenger-url`" });
@@ -30421,7 +30579,7 @@ routes: ${cfg.routes.map((c) => c.route).join(", ")}
30421
30579
  const unitPlan = planInstall(cfg.ship, CREW_HOME, host, detectSystemd(), "run", installContext());
30422
30580
  const abandons = unitAbandonsAgents(unitPlan, (p) => {
30423
30581
  try {
30424
- return readFileSync46(p, "utf8");
30582
+ return readFileSync47(p, "utf8");
30425
30583
  } catch {
30426
30584
  return null;
30427
30585
  }
@@ -30615,6 +30773,8 @@ crew manifest: ${seats2.length ? seats2.map((c) => c.name).join(", ") : "non
30615
30773
  `
30616
30774
  );
30617
30775
  for (const w of managedRuntimeWarnings(row.kind, cfg.ship)) process.stdout.write(`managed runtime: WARN ${w}
30776
+ `);
30777
+ for (const w of managedGitIdentityWarnings(row.kind, readGlobalGitIdentity())) process.stdout.write(`managed git: WARN ${w}
30618
30778
  `);
30619
30779
  }
30620
30780
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tablation/crew",
3
- "version": "0.127.0",
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",