@tablation/crew 0.124.0 → 0.125.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -234,14 +234,8 @@ function ensureLspPlugin(stateDir, flavor, mainCheckout) {
234
234
  const def = LSP_SERVERS[flavor];
235
235
  const dir = join2(stateDir, "intel", "plugins", flavor);
236
236
  const command2 = existsSync3(lspLocalBin(mainCheckout, flavor)) ? `\${CLAUDE_PROJECT_DIR}/node_modules/.bin/${def.bin}` : def.bin;
237
- const files = [
238
- [join2(".claude-plugin", "plugin.json"), { name: `crew-${flavor}`, version: "1.0.0", description: `Language server for the ${flavor} intel flavor, generated by crew` }],
239
- [".lsp.json", { [def.bin]: { command: command2, args: [...def.args], extensionToLanguage: def.extensionToLanguage } }]
240
- ];
241
- for (const [rel, doc] of files) {
237
+ for (const [rel, text2] of lspPluginFiles(flavor, command2)) {
242
238
  const path = join2(dir, rel);
243
- const text2 = `${JSON.stringify(doc, null, 2)}
244
- `;
245
239
  let old;
246
240
  try {
247
241
  old = readFileSync2(path, "utf8");
@@ -253,6 +247,21 @@ function ensureLspPlugin(stateDir, flavor, mainCheckout) {
253
247
  }
254
248
  return dir;
255
249
  }
250
+ function lspPluginFiles(flavor, command2) {
251
+ const def = LSP_SERVERS[flavor];
252
+ const files = [
253
+ [join2(".claude-plugin", "plugin.json"), { name: `crew-${flavor}`, version: "1.0.0", description: `Language server for the ${flavor} intel flavor, generated by crew` }],
254
+ [".lsp.json", { [def.bin]: { command: command2, args: [...def.args], extensionToLanguage: def.extensionToLanguage } }]
255
+ ];
256
+ return files.map(([rel, doc]) => [rel, `${JSON.stringify(doc, null, 2)}
257
+ `]);
258
+ }
259
+ var SANDBOX_INTEL_DIR = "/sandbox/.crew/intel";
260
+ function sandboxLspPlugin(flavor) {
261
+ const dir = `${SANDBOX_INTEL_DIR}/plugins/${flavor}`;
262
+ const command2 = `\${CLAUDE_PROJECT_DIR}/node_modules/.bin/${LSP_SERVERS[flavor].bin}`;
263
+ return { dir, files: lspPluginFiles(flavor, command2).map(([rel, content]) => ({ path: `${dir}/${rel}`, content })) };
264
+ }
256
265
  function lspToolsParagraph(harness, omlx = false) {
257
266
  if (harness !== "claude") return "No symbol service is available for this harness: this repository's `intel` flavor serves only the Claude Code harness's own LSP tool.";
258
267
  if (omlx) return "The harness's LSP tool is off for this launcher, so no symbol service is available for this run.";
@@ -583,7 +592,7 @@ function startSerena(d, s) {
583
592
  const cache = join5(p.project, "cache");
584
593
  if (!d.exists(cache) && d.exists(mainCache)) d.copyDir(mainCache, cache);
585
594
  try {
586
- ensureMainIndex(d, s);
595
+ if (!s.noMainIndex) ensureMainIndex(d, s);
587
596
  } catch {
588
597
  }
589
598
  const port = intelPortFor(s.ticket);
@@ -6295,8 +6304,9 @@ function attentionProblems(items, already) {
6295
6304
  import { createHash as createHash6 } from "crypto";
6296
6305
  import { spawn as spawn5 } from "child_process";
6297
6306
  import { execFileSync as execFileSync8 } from "child_process";
6298
- import { mkdirSync as mkdirSync19, readFileSync as readFileSync27, renameSync as renameSync3, writeFileSync as writeFileSync20, rmSync as rmSync15 } from "fs";
6307
+ import { existsSync as existsSync30, mkdirSync as mkdirSync19, readFileSync as readFileSync27, renameSync as renameSync3, writeFileSync as writeFileSync20, rmSync as rmSync15 } from "fs";
6299
6308
  import { dirname as dirname14, join as join32 } from "path";
6309
+ import { fileURLToPath } from "url";
6300
6310
  import { tmpdir as tmpdir3 } from "os";
6301
6311
 
6302
6312
  // src/passenger-containers.ts
@@ -6850,6 +6860,12 @@ exit 125
6850
6860
  ...CONTAINER_ENGINES.map((e) => `printf '%b' ${shSingle(body)} > ${shSingle(`${dir}/${e}`)} && chmod 755 ${shSingle(`${dir}/${e}`)}`)
6851
6861
  ].join(" && ");
6852
6862
  }
6863
+ function writeFilesScript(files) {
6864
+ return files.map((f) => {
6865
+ const dir = f.path.slice(0, f.path.lastIndexOf("/")) || "/";
6866
+ return `mkdir -p ${shSingle(dir)} && printf %s ${shSingle(Buffer.from(f.content, "utf8").toString("base64"))} | base64 -d > ${shSingle(f.path)}`;
6867
+ }).join(" && ");
6868
+ }
6853
6869
  function shSingle(s) {
6854
6870
  return `'${s.replace(/'/g, `'\\''`)}'`;
6855
6871
  }
@@ -6876,6 +6892,17 @@ function sandboxCheckoutSection(workDir, heading) {
6876
6892
  "skill for setup, ports and the hand-off push."
6877
6893
  ];
6878
6894
  }
6895
+ function sandboxCodeToolsSection(intel) {
6896
+ if (lspFlavor(intel)) return ["", `**Code tools.** On the Claude Code harness: ${lspToolsParagraph("claude").replace(/^The harness's/, "the harness's")}`];
6897
+ if (!wantsCodeBridge(intel)) return [];
6898
+ return [
6899
+ "",
6900
+ `**Code tools.** A \`${INTEL_SERVER_NAME}\` MCP server serves this repository's symbols from its language server. It offers at least: ${INTEL_MINIMUM_TOOLS.join("; ")}.`,
6901
+ "It lists only `code_status` until the symbol service is up. The service starts inside this sandbox",
6902
+ "the first time the session asks the server for tools, and it expects `setup` to have run, so run `setup`",
6903
+ "before your first lookup. `code_status` says whether it is starting, attached, or failed to start (and why)."
6904
+ ];
6905
+ }
6879
6906
  function sandboxEnv(ship) {
6880
6907
  return ship.runner === "sandbox" ? { sandbox: { workDir: ship.sandbox?.workDir ?? DEFAULT_SANDBOX_WORKDIR } } : {};
6881
6908
  }
@@ -7175,7 +7202,7 @@ function renderEnvironment(i) {
7175
7202
  );
7176
7203
  }
7177
7204
  if (only && i.sandbox) {
7178
- lines2.push(...sandboxCheckoutSection(i.sandbox.workDir, "## Your checkout"));
7205
+ lines2.push(...sandboxCheckoutSection(i.sandbox.workDir, "## Your checkout"), ...sandboxCodeToolsSection(only.config.hooks.intel));
7179
7206
  } else if (only) {
7180
7207
  lines2.push(...repoSection(only, key, "## Your worktree"));
7181
7208
  } else {
@@ -7191,7 +7218,7 @@ function renderEnvironment(i) {
7191
7218
  );
7192
7219
  if (i.sandbox) lines2.push(...sandboxCheckoutSection(i.sandbox.workDir, "### Your checkout"));
7193
7220
  for (const r of repos) {
7194
- lines2.push(...i.sandbox ? ["", `### ${r.name}`] : repoSection(r, key, `### ${r.name} \u2014 \`${r.dir}\``));
7221
+ lines2.push(...i.sandbox ? ["", `### ${r.name}`, ...sandboxCodeToolsSection(r.config.hooks.intel)] : repoSection(r, key, `### ${r.name} \u2014 \`${r.dir}\``));
7195
7222
  lines2.push("", `Hooks for ${r.name} \u2014 the only commands you should run here:`, "", hookTable(r.config));
7196
7223
  lines2.push(...mapSection(r, `#### Repository map \u2014 ${r.name}`));
7197
7224
  }
@@ -7219,6 +7246,8 @@ import {
7219
7246
  } from "@modelcontextprotocol/sdk/types.js";
7220
7247
  var STATUS_TOOL = "code_status";
7221
7248
  var STATUS_WAITING = "The symbol service is not available yet: the worktree has not been cut. Run `crew worktree` for the ticket; the symbol tools appear in this server afterwards.";
7249
+ var STATUS_STARTING = "The symbol service is starting inside this sandbox. Call `code_status` again in a few seconds; the symbol tools replace it once the service answers.";
7250
+ var statusFailed = (reason) => `The symbol service did not start (${reason}). Run the repository's \`setup\` hook first if you have not; this call has started a retry, so call \`code_status\` again in a few seconds; until it starts use text search.`;
7222
7251
  var STATUS_LOST = "The symbol service stopped answering, so its tools are unavailable. They return if the service comes back; until then use text search.";
7223
7252
  function intelDir(stateDir, route2, ticket) {
7224
7253
  const safe = (s) => s.replace(/[^A-Za-z0-9._-]+/g, "_");
@@ -7258,6 +7287,8 @@ function createBridge(o) {
7258
7287
  let lost = false;
7259
7288
  let connecting = false;
7260
7289
  let timer;
7290
+ let starting;
7291
+ let startError;
7261
7292
  const notify2 = () => {
7262
7293
  void server.sendToolListChanged().catch(() => {
7263
7294
  });
@@ -7270,8 +7301,40 @@ function createBridge(o) {
7270
7301
  });
7271
7302
  notify2();
7272
7303
  };
7273
- const status2 = () => ({ content: [{ type: "text", text: lost ? STATUS_LOST : STATUS_WAITING }] });
7304
+ const statusText = () => lost ? STATUS_LOST : starting ? STATUS_STARTING : startError !== void 0 ? statusFailed(startError) : STATUS_WAITING;
7305
+ const status2 = () => ({ content: [{ type: "text", text: statusText() }] });
7306
+ const ensureStarted = () => {
7307
+ if (!o.start || upstream || starting) return;
7308
+ startError = void 0;
7309
+ starting = (async () => {
7310
+ try {
7311
+ const r = await o.start();
7312
+ if ("error" in r) {
7313
+ startError = r.error;
7314
+ return;
7315
+ }
7316
+ writeAttachFile(o.attachFile, r.url);
7317
+ await tryAttach();
7318
+ if (!upstream) startError = `${r.url} did not accept a connection`;
7319
+ } catch (e) {
7320
+ startError = e.message.replace(/\s+/g, " ").slice(0, 200);
7321
+ } finally {
7322
+ starting = void 0;
7323
+ notify2();
7324
+ }
7325
+ })();
7326
+ };
7327
+ const waitForStart = async () => {
7328
+ const running = starting;
7329
+ if (!running) return;
7330
+ let t;
7331
+ await Promise.race([running, new Promise((r) => {
7332
+ t = setTimeout(r, o.startWaitMs ?? 12e4);
7333
+ })]);
7334
+ if (t) clearTimeout(t);
7335
+ };
7274
7336
  server.setRequestHandler(ListToolsRequestSchema, async () => {
7337
+ ensureStarted();
7275
7338
  if (!upstream) return { tools: [statusTool] };
7276
7339
  try {
7277
7340
  return await upstream.listTools();
@@ -7281,6 +7344,11 @@ function createBridge(o) {
7281
7344
  }
7282
7345
  });
7283
7346
  server.setRequestHandler(CallToolRequestSchema, async (req) => {
7347
+ const failedNow = req.params.name === STATUS_TOOL && !upstream && !starting && startError !== void 0;
7348
+ const failedText = failedNow ? statusText() : void 0;
7349
+ ensureStarted();
7350
+ if (failedText !== void 0) return { content: [{ type: "text", text: failedText }] };
7351
+ if (req.params.name !== STATUS_TOOL) await waitForStart();
7284
7352
  if (!upstream) return status2();
7285
7353
  try {
7286
7354
  return await upstream.callTool({ name: req.params.name, arguments: req.params.arguments });
@@ -10733,6 +10801,9 @@ var SandboxRunner = class {
10733
10801
  lockfile: cfg.depsLockfile ?? DEFAULT_CACHE_SETTINGS.depsLockfile
10734
10802
  })} && ${containerEngineStubScript()}`;
10735
10803
  await this.os(["sandbox", "exec", "-n", name, "--no-login-shell", "--", "sh", "-c", clone]);
10804
+ if (plan.sandboxFiles?.length) {
10805
+ await this.os(["sandbox", "exec", "-n", name, "--no-login-shell", "--", "sh", "-c", writeFilesScript(plan.sandboxFiles)]);
10806
+ }
10736
10807
  const harness = cfg.agentBin ?? basename8(plan.bin);
10737
10808
  const viaUpload = Buffer.byteLength(plan.prompt) > SANDBOX_STDIN_LIMIT;
10738
10809
  if (viaUpload) {
@@ -10882,6 +10953,15 @@ function lspPluginDirsFor(route2, repo, stateDir) {
10882
10953
  return void 0;
10883
10954
  }
10884
10955
  }
10956
+ function sandboxLspPluginFor(route2, repo) {
10957
+ try {
10958
+ const dir = reposOf(route2).find((r) => r.name === repo)?.dir ?? route2.dir;
10959
+ const flavor = lspFlavor(loadRepoConfig(dir)?.hooks.intel);
10960
+ return flavor ? sandboxLspPlugin(flavor) : void 0;
10961
+ } catch {
10962
+ return void 0;
10963
+ }
10964
+ }
10885
10965
  var AgentSpawnError = class extends AgentError {
10886
10966
  };
10887
10967
  var WorkspaceAuthRefused = class extends AgentSpawnError {
@@ -10917,7 +10997,7 @@ function streamShowsAllowanceRefusal(streamPath) {
10917
10997
  }
10918
10998
  var digestPathFor = (stateDir, routeLabel, role, runId) => join29(stateDir, `digest-${routeLabel}-${role}${runId ? `-${runId}` : ""}.md`);
10919
10999
  function codeBridgeFor(o, harness) {
10920
- if (o.ship.runner === "sandbox" || !o.ticket) return void 0;
11000
+ if (!o.ticket) return void 0;
10921
11001
  const number = /(\d+)$/.exec(o.ticket)?.[1];
10922
11002
  if (!number) return void 0;
10923
11003
  let intel;
@@ -10928,14 +11008,33 @@ function codeBridgeFor(o, harness) {
10928
11008
  } catch {
10929
11009
  }
10930
11010
  if (!wantsCodeBridge(intel)) return void 0;
10931
- const args = [...process.execArgv, process.argv[1] ?? "crew", "intel", "bridge", o.route.route, number, "--state-dir", o.stateDir];
10932
- const bridge = { command: process.execPath, args };
11011
+ const sandbox = o.ship.runner === "sandbox";
11012
+ const args = sandbox ? [
11013
+ SANDBOX_BRIDGE_PATH,
11014
+ "--attach-file",
11015
+ `${SANDBOX_INTEL_DIR}/url`,
11016
+ "--start",
11017
+ intel.trim(),
11018
+ "--clone",
11019
+ o.ship.sandbox?.workDir ?? DEFAULT_SANDBOX_WORKDIR,
11020
+ "--state-dir",
11021
+ dirname13(SANDBOX_INTEL_DIR),
11022
+ "--ticket",
11023
+ number
11024
+ ] : [...process.execArgv, process.argv[1] ?? "crew", "intel", "bridge", o.route.route, number, "--state-dir", o.stateDir];
11025
+ const bridge = { command: sandbox ? "node" : process.execPath, args };
10933
11026
  if (harness === "gemini") {
10934
- const file = join29(intelDir(o.stateDir, o.route.route, number), "gemini-settings.json");
10935
- mkdirSync18(dirname13(file), { recursive: true });
10936
- writeFileSync19(file, `${JSON.stringify({ mcpServers: { [INTEL_SERVER_NAME]: { command: bridge.command, args } } }, null, 2)}
10937
- `);
10938
- bridge.settingsFile = file;
11027
+ const text2 = `${JSON.stringify({ mcpServers: { [INTEL_SERVER_NAME]: { command: bridge.command, args } } }, null, 2)}
11028
+ `;
11029
+ if (sandbox) {
11030
+ bridge.settingsFile = `${SANDBOX_INTEL_DIR}/gemini-settings.json`;
11031
+ bridge.files = [{ path: bridge.settingsFile, content: text2 }];
11032
+ } else {
11033
+ const file = join29(intelDir(o.stateDir, o.route.route, number), "gemini-settings.json");
11034
+ mkdirSync18(dirname13(file), { recursive: true });
11035
+ writeFileSync19(file, text2);
11036
+ bridge.settingsFile = file;
11037
+ }
10939
11038
  }
10940
11039
  return bridge;
10941
11040
  }
@@ -10996,11 +11095,13 @@ function planAgentRun(o) {
10996
11095
  }
10997
11096
  const adapter = adapterFor(toolchain.harness);
10998
11097
  const code = codeBridgeFor(o, adapter.harness);
11098
+ const sandboxLsp = o.ship.runner === "sandbox" && adapter.harness === "claude" ? sandboxLspPluginFor(o.route, o.repo) : void 0;
11099
+ const sandboxFiles = [...code?.files ?? [], ...sandboxLsp?.files ?? []];
10999
11100
  const invocation = adapter.invocation(toolchain, {
11000
11101
  code,
11001
11102
  role: o.role,
11002
11103
  model,
11003
- pluginDirs: adapter.harness === "claude" ? lspPluginDirsFor(o.route, o.repo, o.stateDir) : void 0,
11104
+ pluginDirs: adapter.harness !== "claude" ? void 0 : sandboxLsp ? [sandboxLsp.dir] : lspPluginDirsFor(o.route, o.repo, o.stateDir),
11004
11105
  // Skipped when there's no key to send: a route `crew connect` hasn't
11005
11106
  // resolved yet has nothing to authenticate the MCP connection with either.
11006
11107
  mcp: o.apiKey ? { url: `${o.route.baseUrl}/api/mcp`, keyVar: API_KEY_VAR } : void 0
@@ -11047,7 +11148,8 @@ function planAgentRun(o) {
11047
11148
  ...o.ship.runner === "sandbox" ? {
11048
11149
  runner: "sandbox",
11049
11150
  ...o.ship.sandbox ? { sandboxConfig: o.ship.sandbox } : {},
11050
- sandboxPolicy: sandboxPolicyInputFor(o.route, toolchain, { harnessBin: invocation.bin, repo: o.repo, sandbox: o.ship.sandbox })
11151
+ sandboxPolicy: sandboxPolicyInputFor(o.route, toolchain, { harnessBin: invocation.bin, repo: o.repo, sandbox: o.ship.sandbox }),
11152
+ ...sandboxFiles.length ? { sandboxFiles } : {}
11051
11153
  } : {},
11052
11154
  resolution: { toolchain: seat.toolchainWhy, model: seat.modelWhy },
11053
11155
  warnings: [...seat.warnings ?? [], ...expanded.warnings],
@@ -12686,6 +12788,14 @@ var SANDBOX_CLAUDE_SHA256 = {
12686
12788
  };
12687
12789
  var SANDBOX_UID = 1e3;
12688
12790
  var CREW_PREFIX = "/opt/crew";
12791
+ var SANDBOX_BRIDGE_PATH = `${CREW_PREFIX}/bin/intel-bridge`;
12792
+ function readBridgeBundle() {
12793
+ const here = dirname14(fileURLToPath(import.meta.url));
12794
+ for (const p of [join32(here, "sandbox-intel-bridge.cjs"), join32(here, "..", "dist", "sandbox-intel-bridge.cjs")]) {
12795
+ if (existsSync30(p)) return readFileSync27(p, "utf8");
12796
+ }
12797
+ return void 0;
12798
+ }
12689
12799
  var SANDBOX_UV_VERSION = "0.13.6";
12690
12800
  var SANDBOX_SERENA_VERSION = "2.1.0";
12691
12801
  var SERENA_IMAGE_HOME = `${CREW_PREFIX}/serena`;
@@ -12749,8 +12859,8 @@ function baseLayer() {
12749
12859
  ].join("\n");
12750
12860
  return { name: "base", tag: `crew-sandbox-base:${hash({ SANDBOX_BASE_IMAGE, SANDBOX_OPENSHELL_VERSION })}`, dockerfile, files: {} };
12751
12861
  }
12752
- function crewLayer(base, intel) {
12753
- const files = { "credential-helper": CREDENTIAL_HELPER, "mcp.json": MCP_CONFIG };
12862
+ function crewLayer(base, intel, bridge) {
12863
+ const files = { "credential-helper": CREDENTIAL_HELPER, "mcp.json": MCP_CONFIG, ...bridge ? { "intel-bridge": bridge } : {} };
12754
12864
  const env = Object.entries(CREW_ENV).map(([k, v]) => `${k}=${v}`).join(" ");
12755
12865
  const dockerfile = [
12756
12866
  `FROM ${base.tag}`,
@@ -12758,6 +12868,7 @@ function crewLayer(base, intel) {
12758
12868
  run2(`apt-get update && apt-get install -y --no-install-recommends ca-certificates curl git${intel ? " python3 python3-venv nodejs" : ""} && rm -rf /var/lib/apt/lists/*`),
12759
12869
  `COPY credential-helper ${CREW_PREFIX}/bin/credential-helper`,
12760
12870
  `COPY mcp.json ${CREW_PREFIX}/mcp/mcp.json`,
12871
+ ...bridge ? [`COPY intel-bridge ${SANDBOX_BRIDGE_PATH}`] : [],
12761
12872
  run2(claudeInstallCommand()),
12762
12873
  ...intel ? [run2(`mkdir -p ${CREW_PREFIX}/cache && ${serenaInstallCommand()}`)] : [],
12763
12874
  run2(`chmod 0755 ${CREW_PREFIX}/bin/credential-helper && git config --system credential.helper ${CREW_PREFIX}/bin/credential-helper`),
@@ -12822,9 +12933,9 @@ function repoLayer(repo, decl, crew, warm) {
12822
12933
  const dockerfile = lines2.join("\n");
12823
12934
  return { name: "repo", tag: `crew-sandbox/${slug(repo)}:${hash({ crew: crew.tag, dockerfile, files })}`, dockerfile, files };
12824
12935
  }
12825
- function planSandbox(repo, decl, warm = {}) {
12936
+ function planSandbox(repo, decl, warm = {}, bridge) {
12826
12937
  const base = baseLayer();
12827
- const crew = crewLayer(base, decl.intel);
12938
+ const crew = crewLayer(base, decl.intel, bridge);
12828
12939
  const layer = repoLayer(repo, decl, crew, warm);
12829
12940
  return { repo, tag: layer.tag, claude: SANDBOX_CLAUDE_VERSION, layers: [base, crew, layer] };
12830
12941
  }
@@ -12934,7 +13045,7 @@ function sandboxFailedItem(repo, tag, layer, logPath, nowIso = (/* @__PURE__ */
12934
13045
  async function sandboxBuildTick(repos, deps, force = false) {
12935
13046
  const nowMs = (deps.now ?? (() => /* @__PURE__ */ new Date()))().getTime();
12936
13047
  for (const r of repos) {
12937
- const plan = planSandbox(r.name, r.decl, r.warm);
13048
+ const plan = planSandbox(r.name, r.decl, r.warm, r.bridge);
12938
13049
  const state = readSandboxState(deps.stateDir)[r.name];
12939
13050
  const upToDate = state?.tags[0]?.tag === plan.tag && deps.hasImage(plan.tag);
12940
13051
  if (upToDate) {
@@ -13093,7 +13204,7 @@ function managedRuntimeWarnings(kind, ship) {
13093
13204
 
13094
13205
  // src/selftest.ts
13095
13206
  import { spawn as spawn6 } from "child_process";
13096
- import { existsSync as existsSync30, mkdirSync as mkdirSync20, mkdtempSync as mkdtempSync2, readFileSync as readFileSync28, rmSync as rmSync16, writeFileSync as writeFileSync21 } from "fs";
13207
+ import { existsSync as existsSync31, mkdirSync as mkdirSync20, mkdtempSync as mkdtempSync2, readFileSync as readFileSync28, rmSync as rmSync16, writeFileSync as writeFileSync21 } from "fs";
13097
13208
  import { tmpdir as tmpdir4 } from "os";
13098
13209
  import { join as join33 } from "path";
13099
13210
  var SELFTEST_TIMEOUT_MS = 12e4;
@@ -13293,7 +13404,7 @@ var fileFor2 = (stateDir) => join33(stateDir, "selftest.json");
13293
13404
  function readSelftest(stateDir) {
13294
13405
  try {
13295
13406
  const p = fileFor2(stateDir);
13296
- return existsSync30(p) ? JSON.parse(readFileSync28(p, "utf8")) : {};
13407
+ return existsSync31(p) ? JSON.parse(readFileSync28(p, "utf8")) : {};
13297
13408
  } catch {
13298
13409
  return {};
13299
13410
  }
@@ -13315,7 +13426,7 @@ function doctorSelftestLines(stored, toolchains) {
13315
13426
 
13316
13427
  // src/cli.ts
13317
13428
  import { resolve as resolve7, dirname as dirname33, join as join55, basename as basename16 } from "path";
13318
- import { fileURLToPath } from "url";
13429
+ import { fileURLToPath as fileURLToPath2 } from "url";
13319
13430
  import { hostname as hostname4, homedir as homedir11 } from "os";
13320
13431
  import { spawnSync as spawnSync11 } from "child_process";
13321
13432
  import { parse as parseYaml2 } from "yaml";
@@ -13619,7 +13730,7 @@ async function signOutSessions(o) {
13619
13730
  }
13620
13731
 
13621
13732
  // src/reset.ts
13622
- import { existsSync as existsSync31, mkdirSync as mkdirSync22, readFileSync as readFileSync30, readdirSync as readdirSync12, rmSync as rmSync18, statSync as statSync10, writeFileSync as writeFileSync23 } from "fs";
13733
+ import { existsSync as existsSync32, mkdirSync as mkdirSync22, readFileSync as readFileSync30, readdirSync as readdirSync12, rmSync as rmSync18, statSync as statSync10, writeFileSync as writeFileSync23 } from "fs";
13623
13734
  import { dirname as dirname16, join as join35 } from "path";
13624
13735
  import { hostFromUrl as hostFromUrl3 } from "@tablation/client";
13625
13736
  function clonedCheckoutsFile(stateDir) {
@@ -13692,7 +13803,7 @@ async function runReset(o, fx) {
13692
13803
  }
13693
13804
  if (o.beforeState) steps.push(...await o.beforeState());
13694
13805
  steps.push(...o.wipeState ? wipeStateDir(o.stateDir, o.dryRun) : removeState(o.stateDir, o.dryRun));
13695
- if (existsSync31(o.configFile)) {
13806
+ if (existsSync32(o.configFile)) {
13696
13807
  if (!o.dryRun) rmSync18(o.configFile, { force: true });
13697
13808
  steps.push({ step: "config", target: o.configFile, action: mark(o.dryRun) });
13698
13809
  } else {
@@ -13701,7 +13812,7 @@ async function runReset(o, fx) {
13701
13812
  return steps;
13702
13813
  }
13703
13814
  function removeCheckout(dir, dryRun, fx) {
13704
- if (!existsSync31(dir)) return { step: "checkout", target: dir, action: "absent", detail: "already gone" };
13815
+ if (!existsSync32(dir)) return { step: "checkout", target: dir, action: "absent", detail: "already gone" };
13705
13816
  const { dirty, linkedWorktrees } = fx.inspectCheckout(dir);
13706
13817
  if (dirty.length) {
13707
13818
  return { step: "checkout", target: dir, action: "kept", detail: `has uncommitted changes (${dirty.length} path${dirty.length === 1 ? "" : "s"}: ${dirty.slice(0, 5).join(", ")}${dirty.length > 5 ? ", \u2026" : ""})` };
@@ -13722,7 +13833,7 @@ function removeState(stateDir, dryRun) {
13722
13833
  } catch {
13723
13834
  }
13724
13835
  for (const t of targets) {
13725
- if (!existsSync31(t)) continue;
13836
+ if (!existsSync32(t)) continue;
13726
13837
  if (!dryRun) rmSync18(t, { recursive: true, force: true });
13727
13838
  out.push({ step: "state", target: t, action: mark(dryRun) });
13728
13839
  }
@@ -14118,13 +14229,13 @@ function hostArchitecture(arch = process.arch) {
14118
14229
 
14119
14230
  // src/pool-member-host.ts
14120
14231
  import "child_process";
14121
- import { existsSync as existsSync35 } from "fs";
14232
+ import { existsSync as existsSync36 } from "fs";
14122
14233
  import { hostname as hostname3 } from "os";
14123
14234
 
14124
14235
  // src/machine-id.ts
14125
14236
  import { execFileSync as execFileSync10 } from "child_process";
14126
14237
  import { createHash as createHash7 } from "crypto";
14127
- import { existsSync as existsSync32, mkdirSync as mkdirSync24, readFileSync as readFileSync32, writeFileSync as writeFileSync25 } from "fs";
14238
+ import { existsSync as existsSync33, mkdirSync as mkdirSync24, readFileSync as readFileSync32, writeFileSync as writeFileSync25 } from "fs";
14128
14239
  import { dirname as dirname18, join as join37 } from "path";
14129
14240
  function machineIdPathFor(stateDir) {
14130
14241
  return join37(stateDir, "machine-id");
@@ -14140,7 +14251,7 @@ function readRawMachineId(platform2 = process.platform) {
14140
14251
  return /"IOPlatformUUID"\s*=\s*"([^"]+)"/.exec(out)?.[1];
14141
14252
  }
14142
14253
  for (const p of ["/etc/machine-id", "/var/lib/dbus/machine-id"]) {
14143
- if (existsSync32(p)) {
14254
+ if (existsSync33(p)) {
14144
14255
  const id = readFileSync32(p, "utf8").trim();
14145
14256
  if (id) return id;
14146
14257
  }
@@ -14152,7 +14263,7 @@ function readRawMachineId(platform2 = process.platform) {
14152
14263
  function ensureMachineId(stateDir, opts = {}) {
14153
14264
  if (opts.override?.trim()) return sha256(opts.override.trim());
14154
14265
  const path = machineIdPathFor(stateDir);
14155
- if (existsSync32(path)) {
14266
+ if (existsSync33(path)) {
14156
14267
  const cached = readFileSync32(path, "utf8").trim();
14157
14268
  if (/^[0-9a-f]{64}$/.test(cached)) return cached;
14158
14269
  }
@@ -14173,7 +14284,7 @@ function matchShipRows(ships, shipName2, machineId) {
14173
14284
 
14174
14285
  // src/provision.ts
14175
14286
  import { spawnSync as spawnSync8 } from "child_process";
14176
- import { existsSync as existsSync33, readFileSync as readFileSync33, writeFileSync as writeFileSync26 } from "fs";
14287
+ import { existsSync as existsSync34, readFileSync as readFileSync33, writeFileSync as writeFileSync26 } from "fs";
14177
14288
  import { hostname as hostname2 } from "os";
14178
14289
  import { parseDocument as parseDocument3, isMap as isMap2, isScalar } from "yaml";
14179
14290
 
@@ -14767,7 +14878,7 @@ function realProvisionDeps(crewHome) {
14767
14878
  };
14768
14879
  const configFile = () => {
14769
14880
  const f = findConfigFile(crewHome);
14770
- return existsSync33(f) ? f : null;
14881
+ return existsSync34(f) ? f : null;
14771
14882
  };
14772
14883
  return {
14773
14884
  crew: spawnCrew,
@@ -14801,14 +14912,14 @@ function realProvisionDeps(crewHome) {
14801
14912
  continue;
14802
14913
  }
14803
14914
  for (const p of plan.unitPaths) {
14804
- if (!existsSync33(p) || readFileSync33(p, "utf8") !== plan.unitContent[p]) unitsStale = true;
14915
+ if (!existsSync34(p) || readFileSync33(p, "utf8") !== plan.unitContent[p]) unitsStale = true;
14805
14916
  }
14806
14917
  }
14807
14918
  const label = labelFor(crewHome, "run");
14808
14919
  let running = false;
14809
14920
  let lingering = true;
14810
14921
  if (mechanism === "systemd") {
14811
- const system = ctx.root || existsSync33(systemUnitPathFor(label));
14922
+ const system = ctx.root || existsSync34(systemUnitPathFor(label));
14812
14923
  const ctl = system ? ["systemctl"] : ["systemctl", "--user"];
14813
14924
  running = spawnSync8(ctl[0], [...ctl.slice(1), "is-active", "--quiet", `${label}.service`]).status === 0;
14814
14925
  if (!system) {
@@ -14853,7 +14964,7 @@ host=${host}
14853
14964
 
14854
14965
  // src/tunnel.ts
14855
14966
  import { spawn as spawn7 } from "child_process";
14856
- import { existsSync as existsSync34, mkdirSync as mkdirSync25, readFileSync as readFileSync34, writeFileSync as writeFileSync27, unlinkSync as unlinkSync3, readdirSync as readdirSync13 } from "fs";
14967
+ import { existsSync as existsSync35, mkdirSync as mkdirSync25, readFileSync as readFileSync34, writeFileSync as writeFileSync27, unlinkSync as unlinkSync3, readdirSync as readdirSync13 } from "fs";
14857
14968
  import { randomInt } from "crypto";
14858
14969
  import { join as join39 } from "path";
14859
14970
  var DEFAULT_SPAWN_OPTIONS = { stdio: "ignore", detached: true };
@@ -14931,7 +15042,7 @@ function tunnelStatePath(stateDir, workspaceId) {
14931
15042
  }
14932
15043
  function readPersistedTunnel(stateDir, workspaceId) {
14933
15044
  const path = tunnelStatePath(stateDir, workspaceId);
14934
- if (!existsSync34(path)) return void 0;
15045
+ if (!existsSync35(path)) return void 0;
14935
15046
  try {
14936
15047
  return JSON.parse(readFileSync34(path, "utf8"));
14937
15048
  } catch {
@@ -15044,7 +15155,7 @@ function syncPassengerTunnels(plans, relayHost, relayPort, privateKeyPath, state
15044
15155
  }
15045
15156
  function readAllPersistedWorkspaceIds(stateDir) {
15046
15157
  const dir = join39(stateDir, "passengers");
15047
- if (!existsSync34(dir)) return [];
15158
+ if (!existsSync35(dir)) return [];
15048
15159
  return readdirSync13(dir).filter((f) => f.endsWith(".json") && !f.endsWith("-sync.json")).map((f) => f.slice(0, -".json".length));
15049
15160
  }
15050
15161
 
@@ -15069,7 +15180,7 @@ function realPoolMemberDeps(o) {
15069
15180
  crewVersion: crewVersion(o.crewHome),
15070
15181
  hostname: hostname3()
15071
15182
  },
15072
- hasShipConfig: () => existsSync35(findConfigFile(o.crewHome)),
15183
+ hasShipConfig: () => existsSync36(findConfigFile(o.crewHome)),
15073
15184
  async provision(lease, instanceClass, shipKind) {
15074
15185
  const hosts = lease.forges.map((f) => f.host.toLowerCase());
15075
15186
  const lines2 = [];
@@ -16439,7 +16550,7 @@ async function resolveOne(tracker, t, seat, approvedStatus, buildingStatus, affi
16439
16550
  }
16440
16551
 
16441
16552
  // src/repo-refresh.ts
16442
- import { existsSync as existsSync36, mkdirSync as mkdirSync26, readFileSync as readFileSync35, writeFileSync as writeFileSync29 } from "fs";
16553
+ import { existsSync as existsSync37, mkdirSync as mkdirSync26, readFileSync as readFileSync35, writeFileSync as writeFileSync29 } from "fs";
16443
16554
  import { dirname as dirname20 } from "path";
16444
16555
  var REFRESH_COOLDOWN_MS = 2 * 6e4;
16445
16556
  var UNSERVABLE_PASSES_BEFORE_ATTENTION = 3;
@@ -16469,7 +16580,7 @@ function applyRepoRows(resolved, rows) {
16469
16580
  }
16470
16581
  function persistRepoRows(stateDir, routeName, rows) {
16471
16582
  const path = resolvedPathFor(stateDir, routeName);
16472
- if (!existsSync36(path)) return;
16583
+ if (!existsSync37(path)) return;
16473
16584
  const raw = JSON.parse(readFileSync35(path, "utf8"));
16474
16585
  applyRepoRows(raw, rows);
16475
16586
  mkdirSync26(dirname20(path), { recursive: true });
@@ -16924,12 +17035,12 @@ var localRefSkipReported = (ticket, problem, events) => events.some((e) => e.rec
16924
17035
 
16925
17036
  // src/connect-wizard.ts
16926
17037
  import { delimiter, dirname as dirname21, join as join41 } from "path";
16927
- import { accessSync as accessSync3, constants as constants3, existsSync as existsSync37, mkdirSync as mkdirSync27, writeFileSync as writeFileSync30 } from "fs";
17038
+ import { accessSync as accessSync3, constants as constants3, existsSync as existsSync38, mkdirSync as mkdirSync27, writeFileSync as writeFileSync30 } from "fs";
16928
17039
  import { stringify as stringify3 } from "yaml";
16929
17040
  var HOST_PASSENGERS_DOCS_URL = "https://crew.tablation.dev/host-passengers";
16930
17041
  function shouldRunWizard(opts) {
16931
17042
  if (!opts.isTTY) return false;
16932
- const exists = opts.configExists ?? existsSync37;
17043
+ const exists = opts.configExists ?? existsSync38;
16933
17044
  return !configSearchPath(opts.crewHome).some((p) => exists(p));
16934
17045
  }
16935
17046
  function firstRunConfigPath() {
@@ -17123,7 +17234,7 @@ function flagWizardAnswers(f, env) {
17123
17234
  }
17124
17235
 
17125
17236
  // src/release-run.ts
17126
- import { readFileSync as readFileSync36, writeFileSync as writeFileSync31, existsSync as existsSync38, copyFileSync, mkdirSync as mkdirSync28, mkdtempSync as mkdtempSync3, rmSync as rmSync19 } from "fs";
17237
+ import { readFileSync as readFileSync36, writeFileSync as writeFileSync31, existsSync as existsSync39, copyFileSync, mkdirSync as mkdirSync28, mkdtempSync as mkdtempSync3, rmSync as rmSync19 } from "fs";
17127
17238
  import { dirname as dirname23, join as join43 } from "path";
17128
17239
  import { tmpdir as tmpdir5 } from "os";
17129
17240
 
@@ -17867,7 +17978,7 @@ async function bump(o, size) {
17867
17978
  }
17868
17979
  const files = o.repo.release.versionFiles.map((f) => join43(o.cwd, f));
17869
17980
  const first = files[0];
17870
- if (!existsSync38(first)) {
17981
+ if (!existsSync39(first)) {
17871
17982
  o.emit.error(`no ${o.repo.release.versionFiles[0]} to read a version from`);
17872
17983
  return null;
17873
17984
  }
@@ -17903,7 +18014,7 @@ function writeChangelog(o, version, entries) {
17903
18014
  o.emit.emit(`would prepend ${entries.length} entr(ies) to ${file}`);
17904
18015
  return;
17905
18016
  }
17906
- const existing = existsSync38(path) ? readFileSync36(path, "utf8") : "# Changelog\n";
18017
+ const existing = existsSync39(path) ? readFileSync36(path, "utf8") : "# Changelog\n";
17907
18018
  writeFileSync31(path, insertChangelogSection(existing, section));
17908
18019
  }
17909
18020
  async function confirm(o, expected) {
@@ -18009,7 +18120,7 @@ function openReleaseCheckout(o) {
18009
18120
  }
18010
18121
  for (const f of o.repo.worktrees.copy) {
18011
18122
  const src = join43(o.cwd, f);
18012
- if (!existsSync38(src)) continue;
18123
+ if (!existsSync39(src)) continue;
18013
18124
  mkdirSync28(dirname23(join43(dir, f)), { recursive: true });
18014
18125
  copyFileSync(src, join43(dir, f));
18015
18126
  }
@@ -18990,7 +19101,7 @@ function missingReleaserWarning(repo, release, ships, now = Date.now()) {
18990
19101
  }
18991
19102
 
18992
19103
  // src/watch.ts
18993
- import { watch as watchFile, existsSync as existsSync39, statSync as statSync11, openSync as openSync5, readSync as readSync2, closeSync as closeSync5 } from "fs";
19104
+ import { watch as watchFile, existsSync as existsSync40, statSync as statSync11, openSync as openSync5, readSync as readSync2, closeSync as closeSync5 } from "fs";
18994
19105
  var E = "\x1B[";
18995
19106
  var dim = (s) => `${E}2m${s}${E}0m`;
18996
19107
  var bold = (s) => `${E}1m${s}${E}0m`;
@@ -19091,7 +19202,7 @@ function renderHeaders(states, now = Date.now()) {
19091
19202
  return states.map((s) => renderHeader(s, now)).join("");
19092
19203
  }
19093
19204
  function readFrom(file, from) {
19094
- if (!existsSync39(file)) return { events: [], offset: from };
19205
+ if (!existsSync40(file)) return { events: [], offset: from };
19095
19206
  const size = statSync11(file).size;
19096
19207
  if (size < from) return readFrom(file, 0);
19097
19208
  if (size === from) return { events: [], offset: from };
@@ -19334,7 +19445,7 @@ function nextRoles(decision, ran) {
19334
19445
  }
19335
19446
 
19336
19447
  // src/ship-name.ts
19337
- import { existsSync as existsSync40, mkdirSync as mkdirSync29, readFileSync as readFileSync37, renameSync as renameSync5, writeFileSync as writeFileSync32 } from "fs";
19448
+ import { existsSync as existsSync41, mkdirSync as mkdirSync29, readFileSync as readFileSync37, renameSync as renameSync5, writeFileSync as writeFileSync32 } from "fs";
19338
19449
  import { dirname as dirname24 } from "path";
19339
19450
  import { parseDocument as parseDocument5 } from "yaml";
19340
19451
  var ShipNameError = class extends Error {
@@ -19396,7 +19507,7 @@ async function syncRouteName(i) {
19396
19507
  return { status: "ok", ...base, action, name: i.local };
19397
19508
  }
19398
19509
  function readSyncedName(resolvedFile) {
19399
- if (!existsSync40(resolvedFile)) return void 0;
19510
+ if (!existsSync41(resolvedFile)) return void 0;
19400
19511
  try {
19401
19512
  const v = JSON.parse(readFileSync37(resolvedFile, "utf8")).shipName;
19402
19513
  return typeof v === "string" ? v : void 0;
@@ -19405,7 +19516,7 @@ function readSyncedName(resolvedFile) {
19405
19516
  }
19406
19517
  }
19407
19518
  function writeSyncedName(resolvedFile, name) {
19408
- if (!existsSync40(resolvedFile)) return;
19519
+ if (!existsSync41(resolvedFile)) return;
19409
19520
  let raw;
19410
19521
  try {
19411
19522
  raw = JSON.parse(readFileSync37(resolvedFile, "utf8"));
@@ -20107,7 +20218,7 @@ function describeCapability(i) {
20107
20218
  }
20108
20219
 
20109
20220
  // src/agents-sync-run.ts
20110
- import { existsSync as existsSync41, mkdirSync as mkdirSync30, readFileSync as readFileSync38, writeFileSync as writeFileSync33 } from "fs";
20221
+ import { existsSync as existsSync42, mkdirSync as mkdirSync30, readFileSync as readFileSync38, writeFileSync as writeFileSync33 } from "fs";
20111
20222
  import { dirname as dirname25 } from "path";
20112
20223
  async function runAgentsSync(target, o) {
20113
20224
  const dryRun = o.dryRun ?? false;
@@ -20141,7 +20252,7 @@ async function runAgentsSync(target, o) {
20141
20252
  for (const s of skillResult?.outcomes ?? []) o.say(describeSkillSyncOutcome(s));
20142
20253
  if (!dryRun) {
20143
20254
  const resolvedPath = resolvedPathFor(o.stateDir, target.route);
20144
- const raw = existsSync41(resolvedPath) ? JSON.parse(readFileSync38(resolvedPath, "utf8")) : {};
20255
+ const raw = existsSync42(resolvedPath) ? JSON.parse(readFileSync38(resolvedPath, "utf8")) : {};
20145
20256
  raw.agentPersonas = personaResult.agentPersonas;
20146
20257
  if (skillResult) raw.agentSkills = skillResult.agentSkills;
20147
20258
  if (personaResult.releaseAgentId) raw.releaseAgentId = personaResult.releaseAgentId;
@@ -20836,7 +20947,7 @@ function planSampleTicket(i) {
20836
20947
  }
20837
20948
 
20838
20949
  // src/worktree-sweep.ts
20839
- import { existsSync as existsSync42, readFileSync as readFileSync40, readdirSync as readdirSync14 } from "fs";
20950
+ import { existsSync as existsSync43, readFileSync as readFileSync40, readdirSync as readdirSync14 } from "fs";
20840
20951
  import { basename as basename13, dirname as dirname27, join as join45, resolve as resolve5 } from "path";
20841
20952
  function planWorktreeSweep(target, tickets, contract) {
20842
20953
  const terminal = new Set(contract.statuses.resolved.filter((s) => s !== contract.statuses.verified));
@@ -20949,7 +21060,7 @@ async function applyWorktreeSweep(cwd, actions, dryRun, log, remote = "origin",
20949
21060
  let removed = 0;
20950
21061
  let keptBranches = 0;
20951
21062
  for (const a of actions) {
20952
- if (!dryRun && base && !a.keepBranch && existsSync42(a.path) && isHandBackMerge(a.path, base)) {
21063
+ if (!dryRun && base && !a.keepBranch && existsSync43(a.path) && isHandBackMerge(a.path, base)) {
20953
21064
  if (gitOk(a.path, ["merge", "--abort"]) !== null) {
20954
21065
  log.emit(
20955
21066
  `abandoned the hand-back merge of ${base} in ${a.path}: ${a.ticket.issue_id} deployed without it`,
@@ -20957,7 +21068,7 @@ async function applyWorktreeSweep(cwd, actions, dryRun, log, remote = "origin",
20957
21068
  );
20958
21069
  }
20959
21070
  }
20960
- if (!dryRun && existsSync42(a.path)) {
21071
+ if (!dryRun && existsSync43(a.path)) {
20961
21072
  let dirty = false;
20962
21073
  try {
20963
21074
  dirty = status(a.path).length > 0;
@@ -20968,7 +21079,7 @@ async function applyWorktreeSweep(cwd, actions, dryRun, log, remote = "origin",
20968
21079
  continue;
20969
21080
  }
20970
21081
  }
20971
- if (!dryRun && a.requirePushed && a.branch && existsSync42(a.path) && remoteConfigured(cwd, remote) && worktreeHoldsUnpushed(a.path, remote)) {
21082
+ if (!dryRun && a.requirePushed && a.branch && existsSync43(a.path) && remoteConfigured(cwd, remote) && worktreeHoldsUnpushed(a.path, remote)) {
20972
21083
  refuse2(a, "unpushed commits", `${a.branch} has commits that are not on ${remote}`);
20973
21084
  continue;
20974
21085
  }
@@ -21122,12 +21233,12 @@ function applyRemoteBranchCleanup(cwd, remote, branches2, dryRun, log) {
21122
21233
  }
21123
21234
 
21124
21235
  // src/stream-sweep.ts
21125
- import { existsSync as existsSync43, readdirSync as readdirSync15, rmSync as rmSync20, statSync as statSync12 } from "fs";
21236
+ import { existsSync as existsSync44, readdirSync as readdirSync15, rmSync as rmSync20, statSync as statSync12 } from "fs";
21126
21237
  import { join as join46 } from "path";
21127
21238
  var DAY_MS2 = 24 * 60 * 60 * 1e3;
21128
21239
  function planStreamSweep(stateDir, retentionDays, now = Date.now()) {
21129
21240
  const dir = join46(stateDir, "streams");
21130
- if (!existsSync43(dir)) return [];
21241
+ if (!existsSync44(dir)) return [];
21131
21242
  const cutoff = now - retentionDays * DAY_MS2;
21132
21243
  return readdirSync15(dir).map((name) => join46(dir, name)).filter((path) => {
21133
21244
  try {
@@ -21155,7 +21266,7 @@ function applyStreamSweep(paths, dryRun, log) {
21155
21266
  return removed;
21156
21267
  }
21157
21268
  function planEventsSweep(stateDir, retentionDays, now = Date.now()) {
21158
- if (!existsSync43(stateDir)) return [];
21269
+ if (!existsSync44(stateDir)) return [];
21159
21270
  const cutoff = now - retentionDays * DAY_MS2;
21160
21271
  return readdirSync15(stateDir).filter((name) => ROTATED_EVENTS_RE.test(name)).map((name) => join46(stateDir, name)).filter((path) => {
21161
21272
  try {
@@ -21167,11 +21278,11 @@ function planEventsSweep(stateDir, retentionDays, now = Date.now()) {
21167
21278
  }
21168
21279
 
21169
21280
  // src/cli.ts
21170
- import { realpathSync as realpathSync3, readFileSync as readFileSync46, existsSync as existsSync50, 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";
21281
+ import { realpathSync as realpathSync3, readFileSync as readFileSync46, existsSync as existsSync51, 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";
21171
21282
  import { createInterface } from "readline/promises";
21172
21283
 
21173
21284
  // src/periscope-cmd.ts
21174
- import { existsSync as existsSync44, mkdirSync as mkdirSync31, readFileSync as readFileSync41, rmSync as rmSync21, writeFileSync as writeFileSync34 } from "fs";
21285
+ import { existsSync as existsSync45, mkdirSync as mkdirSync31, readFileSync as readFileSync41, rmSync as rmSync21, writeFileSync as writeFileSync34 } from "fs";
21175
21286
  import { join as join47 } from "path";
21176
21287
  var PeriscopeError = class extends Error {
21177
21288
  };
@@ -21197,7 +21308,7 @@ function writePeriscopePrior(stateDir, ticketRecordId, prior) {
21197
21308
  function readPeriscopePrior(stateDir, ticketRecordId) {
21198
21309
  try {
21199
21310
  const p = priorPath(stateDir, ticketRecordId);
21200
- return existsSync44(p) ? JSON.parse(readFileSync41(p, "utf8")) : void 0;
21311
+ return existsSync45(p) ? JSON.parse(readFileSync41(p, "utf8")) : void 0;
21201
21312
  } catch {
21202
21313
  return void 0;
21203
21314
  }
@@ -21752,7 +21863,7 @@ import { dirname as dirOf2, resolve as resolvePath } from "path";
21752
21863
 
21753
21864
  // src/ssh-keys.ts
21754
21865
  import { execFileSync as execFileSync13 } from "child_process";
21755
- import { existsSync as existsSync45, mkdirSync as mkdirSync32, readFileSync as readFileSync42, chmodSync as chmodSync3 } from "fs";
21866
+ import { existsSync as existsSync46, mkdirSync as mkdirSync32, readFileSync as readFileSync42, chmodSync as chmodSync3 } from "fs";
21756
21867
  import { dirname as dirname28, join as join49 } from "path";
21757
21868
  var SshKeyError = class extends Error {
21758
21869
  };
@@ -21762,7 +21873,7 @@ function shipSshKeyPathFor(stateDir) {
21762
21873
  function ensureShipSshKeypair(stateDir) {
21763
21874
  const privateKeyPath = shipSshKeyPathFor(stateDir);
21764
21875
  const publicKeyPath = `${privateKeyPath}.pub`;
21765
- if (!existsSync45(privateKeyPath)) {
21876
+ if (!existsSync46(privateKeyPath)) {
21766
21877
  mkdirSync32(dirname28(privateKeyPath), { recursive: true });
21767
21878
  try {
21768
21879
  execFileSync13("ssh-keygen", [
@@ -21783,7 +21894,7 @@ function ensureShipSshKeypair(stateDir) {
21783
21894
  }
21784
21895
  chmodSync3(privateKeyPath, 384);
21785
21896
  }
21786
- if (!existsSync45(publicKeyPath)) {
21897
+ if (!existsSync46(publicKeyPath)) {
21787
21898
  throw new SshKeyError(`${privateKeyPath} exists but ${publicKeyPath} does not \u2014 regenerate or restore it`);
21788
21899
  }
21789
21900
  const publicKey = readFileSync42(publicKeyPath, "utf8").trim();
@@ -21950,7 +22061,7 @@ async function unregisterEndpoint(workspaceId, info, d) {
21950
22061
  import { createServer as createHttpServer } from "http";
21951
22062
  import { spawn as spawn9 } from "child_process";
21952
22063
  import { randomBytes } from "crypto";
21953
- import { existsSync as existsSync46, mkdirSync as mkdirSync34, readFileSync as readFileSync43, writeFileSync as writeFileSync35, unlinkSync as unlinkSync4, readdirSync as readdirSync16 } from "fs";
22064
+ import { existsSync as existsSync47, mkdirSync as mkdirSync34, readFileSync as readFileSync43, writeFileSync as writeFileSync35, unlinkSync as unlinkSync4, readdirSync as readdirSync16 } from "fs";
21954
22065
  import { join as join50 } from "path";
21955
22066
 
21956
22067
  // src/passenger-mirror.ts
@@ -22085,7 +22196,7 @@ function syncDaemonStatePath(stateDir, workspaceId) {
22085
22196
  }
22086
22197
  function readPersistedSyncDaemon(stateDir, workspaceId) {
22087
22198
  const path = syncDaemonStatePath(stateDir, workspaceId);
22088
- if (!existsSync46(path)) return void 0;
22199
+ if (!existsSync47(path)) return void 0;
22089
22200
  try {
22090
22201
  return JSON.parse(readFileSync43(path, "utf8"));
22091
22202
  } catch {
@@ -22116,7 +22227,7 @@ function waitForExit(pid, alive, timeoutMs = 5e3) {
22116
22227
  }
22117
22228
  function readAllPersistedSyncWorkspaceIds(stateDir) {
22118
22229
  const dir = join50(stateDir, "passengers");
22119
- if (!existsSync46(dir)) return [];
22230
+ if (!existsSync47(dir)) return [];
22120
22231
  const suffix = "-sync.json";
22121
22232
  return readdirSync16(dir).filter((f) => f.endsWith(suffix)).map((f) => f.slice(0, -suffix.length));
22122
22233
  }
@@ -22613,7 +22724,7 @@ function describeCause(err3) {
22613
22724
  }
22614
22725
 
22615
22726
  // src/daemon.ts
22616
- import { existsSync as existsSync47, mkdirSync as mkdirSync36, readFileSync as readFileSync44, readdirSync as readdirSync17, rmSync as rmSync22, statSync as statSync14, writeFileSync as writeFileSync36 } from "fs";
22727
+ import { existsSync as existsSync48, mkdirSync as mkdirSync36, readFileSync as readFileSync44, readdirSync as readdirSync17, rmSync as rmSync22, statSync as statSync14, writeFileSync as writeFileSync36 } from "fs";
22617
22728
  import { join as join51 } from "path";
22618
22729
  import { randomBytes as randomBytes2 } from "crypto";
22619
22730
  var DEFAULT_FLOOR_MS = 5e3;
@@ -22711,7 +22822,7 @@ function computeTreeSignature(paths) {
22711
22822
  }
22712
22823
  function makeStaleChecker(paths, opts = {}) {
22713
22824
  const initial = computeTreeSignature(paths);
22714
- return () => computeTreeSignature(paths) !== initial || opts.reloadSentinel !== void 0 && existsSync47(opts.reloadSentinel);
22825
+ return () => computeTreeSignature(paths) !== initial || opts.reloadSentinel !== void 0 && existsSync48(opts.reloadSentinel);
22715
22826
  }
22716
22827
  function reloadSentinelFor(stateDir) {
22717
22828
  return join51(stateDir, "reload-requested");
@@ -23300,7 +23411,7 @@ import { createHash as createHash10 } from "crypto";
23300
23411
  import {
23301
23412
  chmodSync as chmodSync4,
23302
23413
  closeSync as closeSync6,
23303
- existsSync as existsSync48,
23414
+ existsSync as existsSync49,
23304
23415
  fstatSync,
23305
23416
  openSync as openSync6,
23306
23417
  readSync as readSync3,
@@ -23671,7 +23782,7 @@ function lastLines2(path, n) {
23671
23782
  }
23672
23783
  function readTail(live, opts) {
23673
23784
  const max = opts.maxBytes ?? MAX_TAIL_BYTES;
23674
- if (!existsSync48(live)) {
23785
+ if (!existsSync49(live)) {
23675
23786
  return { lines: [], cursor: opts.after ?? encodeCursor({ dev: 0, ino: 0, off: 0 }), reset: false };
23676
23787
  }
23677
23788
  const cur = opts.after ? decodeCursor(opts.after) : null;
@@ -23828,7 +23939,7 @@ async function route(deps, method, url, req, abort) {
23828
23939
  const run3 = decodeURIComponent(p.slice("/streams/".length));
23829
23940
  if (!RUN_NAME_RE.test(run3) || run3.includes("..") || /\.(pid|meta\.json|events\.jsonl)$/.test(run3)) return err2(400, "not a run name");
23830
23941
  const file = join52(deps.stateDir, "streams", run3.endsWith(".jsonl") ? run3 : `${run3}.jsonl`);
23831
- if (!existsSync48(file)) return err2(404, "no such run");
23942
+ if (!existsSync49(file)) return err2(404, "no such run");
23832
23943
  return tailReply(file, url.searchParams, false, abort);
23833
23944
  }
23834
23945
  if ((p === "/pause" || p === "/resume") && post3) {
@@ -23872,7 +23983,7 @@ function send(res, r) {
23872
23983
  res.end(body);
23873
23984
  }
23874
23985
  async function socketIsStale(path) {
23875
- if (!existsSync48(path)) return false;
23986
+ if (!existsSync49(path)) return false;
23876
23987
  return new Promise((resolve8) => {
23877
23988
  const c = createConnection(path);
23878
23989
  c.once("connect", () => {
@@ -23891,7 +24002,7 @@ async function startControlServer(deps) {
23891
24002
  return null;
23892
24003
  }
23893
24004
  if (await socketIsStale(path)) rmSync23(path, { force: true });
23894
- else if (existsSync48(path)) {
24005
+ else if (existsSync49(path)) {
23895
24006
  log(`control API not started: ${path} is in use by another process`);
23896
24007
  return null;
23897
24008
  }
@@ -23984,7 +24095,7 @@ import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/
23984
24095
 
23985
24096
  // src/periscope-endpoints.ts
23986
24097
  import { randomBytes as randomBytes3 } from "crypto";
23987
- import { chmodSync as chmodSync5, existsSync as existsSync49, mkdirSync as mkdirSync37, readdirSync as readdirSync19, readFileSync as readFileSync45, rmSync as rmSync24, writeFileSync as writeFileSync37 } from "fs";
24098
+ import { chmodSync as chmodSync5, existsSync as existsSync50, mkdirSync as mkdirSync37, readdirSync as readdirSync19, readFileSync as readFileSync45, rmSync as rmSync24, writeFileSync as writeFileSync37 } from "fs";
23988
24099
  import { join as join53 } from "path";
23989
24100
  var secretDir = (stateDir) => join53(stateDir, "periscope-mcp");
23990
24101
  var secretFile = (stateDir, id) => join53(secretDir(stateDir), `${id}.json`);
@@ -24027,7 +24138,7 @@ function removePeriscopeEndpointRecord(stateDir, ticketRecordId) {
24027
24138
  }
24028
24139
  function listPeriscopeEndpoints(stateDir) {
24029
24140
  const dir = endpointDir(stateDir);
24030
- if (!existsSync49(dir)) return [];
24141
+ if (!existsSync50(dir)) return [];
24031
24142
  const out = [];
24032
24143
  for (const name of readdirSync19(dir)) {
24033
24144
  if (!name.endsWith(".json")) continue;
@@ -24391,7 +24502,7 @@ function findPeriscope(sidecars, segment) {
24391
24502
  }
24392
24503
 
24393
24504
  // src/cli.ts
24394
- var CREW_HOME = isCompiledBinary(import.meta.url) ? dirname33(process.execPath) : resolve7(dirname33(fileURLToPath(import.meta.url)), "..");
24505
+ var CREW_HOME = isCompiledBinary(import.meta.url) ? dirname33(process.execPath) : resolve7(dirname33(fileURLToPath2(import.meta.url)), "..");
24395
24506
  var RELEASE_LOCK_TTL_MS = 20 * 60 * 1e3;
24396
24507
  var CYCLE_SECONDS2 = 120;
24397
24508
  var STALE_CYCLES = 3;
@@ -25004,7 +25115,7 @@ if (command === "passenger-sync-daemon") {
25004
25115
  } else {
25005
25116
  const toleratesNoRoutes = command === "credential" || command === "forge" || command === "repos" || command === "ship" || command === "disconnect" && flag("reset") || command === "deprovision" || command === "connect" && flag("sign-out");
25006
25117
  const configless = command === "connect" && flag("sign-out") || command === "disconnect" && flag("reset") || command === "deprovision";
25007
- if (configless && !existsSync50(findConfigFile(CREW_HOME))) {
25118
+ if (configless && !existsSync51(findConfigFile(CREW_HOME))) {
25008
25119
  cfg = loadConfig(CREW_HOME, firstRunConfigPath(), { text: "ship: {}\n", allowNoRoutes: true });
25009
25120
  } else {
25010
25121
  cfg = loadConfig(CREW_HOME, void 0, toleratesNoRoutes ? { allowNoRoutes: true } : {});
@@ -25327,7 +25438,7 @@ if (command === "passenger-sync-daemon") {
25327
25438
  if (host === "linux" && hasSystemd) {
25328
25439
  const label = labelFor(CREW_HOME, "run");
25329
25440
  const staleTimer = join55(homedir11(), ".config", "systemd", "user", `${label}.timer`);
25330
- if (existsSync50(staleTimer)) {
25441
+ if (existsSync51(staleTimer)) {
25331
25442
  process.stdout.write(
25332
25443
  `removing stale run timer from before the persistent-service cutover: ${staleTimer}${dryRun ? " (dry run)" : ""}
25333
25444
  `
@@ -25354,7 +25465,7 @@ if (command === "passenger-sync-daemon") {
25354
25465
  const tracker = new Tracker(c, cfg.ship);
25355
25466
  problems.push(...validateContract(tracker.contract).map((p) => `contract: ${p}`));
25356
25467
  for (const r of await resolvedRepos(c)) {
25357
- if (!existsSync50(r.dir)) problems.push(`repo ${r.name}: ${r.dir} is missing on disk`);
25468
+ if (!existsSync51(r.dir)) problems.push(`repo ${r.name}: ${r.dir} is missing on disk`);
25358
25469
  if (!satisfies(host, r.config.platform)) problems.push(`repo ${r.name}: requires ${r.config.platform}, this host is ${host}`);
25359
25470
  readiness.push(...validateEffective(r.config).map((p) => `repo ${r.name}: ${p}`));
25360
25471
  }
@@ -25381,7 +25492,7 @@ if (command === "passenger-sync-daemon") {
25381
25492
  }
25382
25493
  async function doctorFix(o) {
25383
25494
  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))));
25384
- const unitExists = (p) => existsSync50(p);
25495
+ const unitExists = (p) => existsSync51(p);
25385
25496
  const crontab = o.host === "linux" ? spawnSync11("crontab", ["-l"], { encoding: "utf8" }).stdout ?? "" : "";
25386
25497
  const schedulerAbsent = o.host !== "windows" ? !schedulerInstalled(planInstall(cfg.ship, CREW_HOME, o.host, detectSystemd(), "run", installContext()), unitExists, crontab) : true;
25387
25498
  const plan = planFix({ routes, schedulerAbsent, host: o.host });
@@ -25427,7 +25538,7 @@ ${changed2.map((c) => ` - ${c}
25427
25538
  warm = readWarmFiles(r.dir, `${r.config.branch.remote}/${r.config.branch.base}`, r.config.sandbox.warm ?? []);
25428
25539
  } catch {
25429
25540
  }
25430
- out.set(r.name, { name: r.name, decl: intelFlavor(r.config.hooks.intel) === "serena" ? { ...r.config.sandbox, intel: "serena" } : r.config.sandbox, warm });
25541
+ out.set(r.name, { name: r.name, decl: intelFlavor(r.config.hooks.intel) === "serena" ? { ...r.config.sandbox, intel: "serena" } : r.config.sandbox, warm, ...wantsCodeBridge(r.config.hooks.intel) ? { bridge: readBridgeBundle() } : {} });
25431
25542
  }
25432
25543
  }
25433
25544
  return [...out.values()];
@@ -26842,7 +26953,7 @@ ${cu.tail.trimEnd()}
26842
26953
  `);
26843
26954
  if (!dryRun) {
26844
26955
  const resolvedPath = resolvedPathFor(cfg.ship.stateDir, target.route);
26845
- const raw = existsSync50(resolvedPath) ? JSON.parse(readFileSync46(resolvedPath, "utf8")) : {};
26956
+ const raw = existsSync51(resolvedPath) ? JSON.parse(readFileSync46(resolvedPath, "utf8")) : {};
26846
26957
  raw.agentSkills = result.agentSkills;
26847
26958
  mkdirSync38(dirOf2(resolvedPath), { recursive: true });
26848
26959
  writeFileSync38(resolvedPath, `${JSON.stringify(raw, null, 2)}
@@ -26947,8 +27058,8 @@ ${cu.tail.trimEnd()}
26947
27058
  try {
26948
27059
  const a = parseHooksArgs(positional.slice(2), routeNamesOf(configText));
26949
27060
  const dir = repoDirOf(configText, dirname33(cfg.configFile), a.route, a.name);
26950
- if (!existsSync50(dir)) throw new ReposError(`${dir} does not exist on disk`);
26951
- const files = { exists: (r) => existsSync50(resolvePath(dir, r)), list: (r) => {
27061
+ if (!existsSync51(dir)) throw new ReposError(`${dir} does not exist on disk`);
27062
+ const files = { exists: (r) => existsSync51(resolvePath(dir, r)), list: (r) => {
26952
27063
  try {
26953
27064
  return readdirSync20(resolvePath(dir, r));
26954
27065
  } catch {
@@ -27066,7 +27177,7 @@ ${cu.tail.trimEnd()}
27066
27177
  if (sub === "list" && !routeName) usage2();
27067
27178
  if (sub === "add") {
27068
27179
  try {
27069
- const a = parseAddArgs(positional.slice(2), routeNamesOf(configText), (p) => existsSync50(resolvePath(p.replace(/^~(?=\/|$)/, homedir11()))));
27180
+ const a = parseAddArgs(positional.slice(2), routeNamesOf(configText), (p) => existsSync51(resolvePath(p.replace(/^~(?=\/|$)/, homedir11()))));
27070
27181
  routeName = a.route;
27071
27182
  addPath = a.path;
27072
27183
  addName = a.name;
@@ -27096,7 +27207,7 @@ ${cu.tail.trimEnd()}
27096
27207
  const resolvedFile = resolvedPathFor(cfg.ship.stateDir, routeName);
27097
27208
  if (sub === "list" || sub === "add") {
27098
27209
  const liveRoute = cfg.routes.find((r) => r.route === routeName);
27099
- if (liveRoute?.resolved?.reposModelId && existsSync50(resolvedFile) && !dryRun) {
27210
+ if (liveRoute?.resolved?.reposModelId && existsSync51(resolvedFile) && !dryRun) {
27100
27211
  try {
27101
27212
  await hydrateApiKeys([liveRoute]);
27102
27213
  persistRepoRows(cfg.ship.stateDir, liveRoute.route, await new Tracker(liveRoute, cfg.ship).repoRowsForMap());
@@ -27105,7 +27216,7 @@ ${cu.tail.trimEnd()}
27105
27216
  }
27106
27217
  }
27107
27218
  let resolvedRemotes;
27108
- if (existsSync50(resolvedFile)) {
27219
+ if (existsSync51(resolvedFile)) {
27109
27220
  try {
27110
27221
  resolvedRemotes = JSON.parse(readFileSync46(resolvedFile, "utf8"));
27111
27222
  } catch {
@@ -27114,7 +27225,7 @@ ${cu.tail.trimEnd()}
27114
27225
  const originOf = (dir) => gitOk(dir, ["remote", "get-url", "origin"]) ?? void 0;
27115
27226
  const text2 = configText;
27116
27227
  const base = dirname33(cfg.configFile);
27117
- const isCheckout = (d) => existsSync50(`${d}/.git`);
27228
+ const isCheckout = (d) => existsSync51(`${d}/.git`);
27118
27229
  try {
27119
27230
  if (sub === "list") {
27120
27231
  if (flag("json")) {
@@ -27124,7 +27235,7 @@ ${cu.tail.trimEnd()}
27124
27235
  base,
27125
27236
  resolved: resolvedRemotes,
27126
27237
  originOf,
27127
- exists: (d) => existsSync50(d),
27238
+ exists: (d) => existsSync51(d),
27128
27239
  isCheckout,
27129
27240
  repoHasHooks: (d) => REPO_CONFIG_FILENAMES.some((f) => {
27130
27241
  try {
@@ -27145,7 +27256,7 @@ ${cu.tail.trimEnd()}
27145
27256
  for (const r of repos) {
27146
27257
  const origin2 = originOf(r.dir);
27147
27258
  const row = matchRepoName(origin2, resolvedRemotes);
27148
- const note = !existsSync50(r.dir) ? "missing on disk" : row ? `Repos row: ${row}` : `no Repos row matches ${normalizeRemote(origin2) ?? "this checkout (no origin remote)"}`;
27259
+ const note = !existsSync51(r.dir) ? "missing on disk" : row ? `Repos row: ${row}` : `no Repos row matches ${normalizeRemote(origin2) ?? "this checkout (no origin remote)"}`;
27149
27260
  process.stdout.write(`${r.name} ${r.dir} ${note}
27150
27261
  `);
27151
27262
  }
@@ -27157,9 +27268,9 @@ ${cu.tail.trimEnd()}
27157
27268
  ` : `${human}
27158
27269
  `);
27159
27270
  const plan2 = planRepoClone({ text: text2, base, route: routeName, name: addName, resolved: resolvedRemotes, dir: value("dir"), isCheckout });
27160
- if (existsSync50(plan2.target) && (existsSync50(`${plan2.target}/.git`) || readdirSync20(plan2.target).length > 0)) {
27271
+ if (existsSync51(plan2.target) && (existsSync51(`${plan2.target}/.git`) || readdirSync20(plan2.target).length > 0)) {
27161
27272
  throw new ReposError(
27162
- existsSync50(`${plan2.target}/.git`) ? `${plan2.target} is already a checkout \u2014 attach it with \`crew repos add ${routeName} ${plan2.name} ${plan2.target}\`` : `${plan2.target} exists and is not empty \u2014 pass --dir to clone somewhere else`
27273
+ existsSync51(`${plan2.target}/.git`) ? `${plan2.target} is already a checkout \u2014 attach it with \`crew repos add ${routeName} ${plan2.name} ${plan2.target}\`` : `${plan2.target} exists and is not empty \u2014 pass --dir to clone somewhere else`
27163
27274
  );
27164
27275
  }
27165
27276
  if (dryRun) {
@@ -27184,7 +27295,7 @@ ${cu.tail.trimEnd()}
27184
27295
  break;
27185
27296
  }
27186
27297
  const target = resolvePath(addPath.replace(/^~(?=\/|$)/, homedir11()));
27187
- const top = existsSync50(target) ? gitOk(target, ["rev-parse", "--show-toplevel"]) : null;
27298
+ const top = existsSync51(target) ? gitOk(target, ["rev-parse", "--show-toplevel"]) : null;
27188
27299
  if (!top) throw new ReposError(`${target} is not a git checkout`);
27189
27300
  const origin = originOf(top);
27190
27301
  const plan = planRepoAdd({
@@ -27236,7 +27347,7 @@ ${cu.tail.trimEnd()}
27236
27347
  const created = await rowTracker.createRepoRow(rowPayload);
27237
27348
  process.stdout.write(`created Repos row ${String(rowPayload.name)} (${String(rowPayload.remote)}) ${created.id}
27238
27349
  `);
27239
- if (existsSync50(resolvedFile)) {
27350
+ if (existsSync51(resolvedFile)) {
27240
27351
  const raw = JSON.parse(readFileSync46(resolvedFile, "utf8"));
27241
27352
  raw.repoNames = { ...raw.repoNames, [created.id]: String(rowPayload.name) };
27242
27353
  raw.repoRemotes = { ...raw.repoRemotes, [created.id]: String(rowPayload.remote) };
@@ -27658,7 +27769,7 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
27658
27769
  }
27659
27770
  }
27660
27771
  let previous;
27661
- const hadPreviousFile = existsSync50(resolvedPath);
27772
+ const hadPreviousFile = existsSync51(resolvedPath);
27662
27773
  if (hadPreviousFile) {
27663
27774
  try {
27664
27775
  previous = JSON.parse(readFileSync46(resolvedPath, "utf8"));
@@ -28190,7 +28301,7 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
28190
28301
  if (subS === "images") {
28191
28302
  const stateS = readSandboxState(cfg.ship.stateDir);
28192
28303
  const rows = declared.map((r) => {
28193
- const plan = planSandbox(r.name, r.decl, r.warm);
28304
+ const plan = planSandbox(r.name, r.decl, r.warm, r.bridge);
28194
28305
  return { repo: r.name, wants: plan.tag, tags: stateS[r.name]?.tags ?? [], ...sandboxImageStatus(stateS, plan) };
28195
28306
  });
28196
28307
  if (jsonS) {
@@ -28224,7 +28335,7 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
28224
28335
  for (const t of targets) {
28225
28336
  await sandboxBuildTick([t], depsS, true);
28226
28337
  const after = readSandboxState(cfg.ship.stateDir);
28227
- const st = sandboxImageStatus(after, planSandbox(t.name, t.decl, t.warm));
28338
+ const st = sandboxImageStatus(after, planSandbox(t.name, t.decl, t.warm, t.bridge));
28228
28339
  if (st.status === "failed") failed = true;
28229
28340
  process.stdout.write(`${t.name}: ${st.status} \u2014 ${st.detail}
28230
28341
  `);
@@ -28536,7 +28647,7 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
28536
28647
  prepare: async (path) => {
28537
28648
  for (const f of repoWt.config.worktrees.copy) {
28538
28649
  const src = join55(repoWt.dir, f);
28539
- if (!existsSync50(src) || existsSync50(join55(path, f))) continue;
28650
+ if (!existsSync51(src) || existsSync51(join55(path, f))) continue;
28540
28651
  mkdirSync38(dirOf2(join55(path, f)), { recursive: true });
28541
28652
  copyFileSync2(src, join55(path, f));
28542
28653
  }
@@ -28758,7 +28869,7 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
28758
28869
  const shellR = hit && resolveShell(hit.repo.config.shell ?? cfg.ship.shell);
28759
28870
  const resumed = await runPeriscopeResume({
28760
28871
  find: async () => hit && { row: hit.row, tag: hit.tag },
28761
- worktreeExists: (p) => existsSync50(p),
28872
+ worktreeExists: (p) => existsSync51(p),
28762
28873
  prepareDropFile,
28763
28874
  startProxy: (wt, url, n) => launchProxy(crewInvocation(CREW_HOME), wt, url, Number(n), { host: hit.row.host || "localhost", inject: hit.repo.config.periscope?.inspector !== "repo" }),
28764
28875
  caps: periscopeCaps(cfg.ship),
@@ -28855,7 +28966,7 @@ ${where.join("\n")}`);
28855
28966
  prepare: async (path) => {
28856
28967
  for (const f of repoP.config.worktrees.copy) {
28857
28968
  const src = join55(repoP.dir, f);
28858
- if (!existsSync50(src)) continue;
28969
+ if (!existsSync51(src)) continue;
28859
28970
  mkdirSync38(dirOf2(join55(path, f)), { recursive: true });
28860
28971
  copyFileSync2(src, join55(path, f));
28861
28972
  }
@@ -29347,7 +29458,7 @@ ${where.join("\n")}`);
29347
29458
  }
29348
29459
  if (row) {
29349
29460
  await tracker.deleteRepoRow(row.id);
29350
- if (existsSync50(resolvedFile)) {
29461
+ if (existsSync51(resolvedFile)) {
29351
29462
  const raw = JSON.parse(readFileSync46(resolvedFile, "utf8"));
29352
29463
  if (raw.repoNames) delete raw.repoNames[row.id];
29353
29464
  if (raw.repoRemotes) delete raw.repoRemotes[row.id];
@@ -29355,13 +29466,13 @@ ${where.join("\n")}`);
29355
29466
  `);
29356
29467
  }
29357
29468
  }
29358
- if (existsSync50(join55(dir, ".git")) && basename16(dir) === SAMPLE_REPO) rmSync25(dir, { recursive: true, force: true });
29469
+ if (existsSync51(join55(dir, ".git")) && basename16(dir) === SAMPLE_REPO) rmSync25(dir, { recursive: true, force: true });
29359
29470
  say("removed", { dir, closed }, `removed the sample repo${closed.length ? `, closed ${closed.join(", ")}` : ""}`);
29360
29471
  break;
29361
29472
  }
29362
29473
  const projectId = route2.resolved.areaId ?? resolveRepoProject(await tracker.projectRows(), value("project"));
29363
- const fresh = !existsSync50(join55(dir, ".git"));
29364
- if (existsSync50(dir) && fresh && readdirSync20(dir).length > 0) fail2(`${dir} exists and is not a git checkout \u2014 move it aside`);
29474
+ const fresh = !existsSync51(join55(dir, ".git"));
29475
+ if (existsSync51(dir) && fresh && readdirSync20(dir).length > 0) fail2(`${dir} exists and is not a git checkout \u2014 move it aside`);
29365
29476
  if (dryRun) {
29366
29477
  say("dry-run", { dir, newCheckout: fresh, repoRow: row?.id ?? null }, `would ${fresh ? "create" : "reuse"} ${dir}${row ? "" : ", create the Repos row"}, and file one ${tracker.contract.statuses.approved} ticket`);
29367
29478
  break;
@@ -29387,7 +29498,7 @@ ${where.join("\n")}`);
29387
29498
  spec: { platform: "unix", releaseMode: "integrate", ciProvider: "none" }
29388
29499
  });
29389
29500
  repoId = (await tracker.createRepoRow(payload)).id;
29390
- if (existsSync50(resolvedFile)) {
29501
+ if (existsSync51(resolvedFile)) {
29391
29502
  const raw = JSON.parse(readFileSync46(resolvedFile, "utf8"));
29392
29503
  raw.repoNames = { ...raw.repoNames, [repoId]: SAMPLE_REPO };
29393
29504
  raw.repoRemotes = { ...raw.repoRemotes, [repoId]: SAMPLE_REMOTE };
@@ -29743,7 +29854,7 @@ watermark: ${state.watermark()}
29743
29854
  }
29744
29855
  for (const r of repos) {
29745
29856
  const remote = r.config.branch.remote;
29746
- if (existsSync50(r.dir)) {
29857
+ if (existsSync51(r.dir)) {
29747
29858
  pushAuth.push({
29748
29859
  route: c.route,
29749
29860
  repo: r.name,
@@ -29839,7 +29950,7 @@ routes: ${cfg.routes.map((c) => c.route).join(", ")}
29839
29950
  }
29840
29951
  try {
29841
29952
  const evFile = eventFileFor(cfg.ship.stateDir);
29842
- const evSize = existsSync50(evFile) ? statSync16(evFile).size : 0;
29953
+ const evSize = existsSync51(evFile) ? statSync16(evFile).size : 0;
29843
29954
  process.stdout.write(
29844
29955
  evSize > cfg.ship.eventsRotateBytes * 4 ? `events log: WARN ${evFile} is ${Math.round(evSize / 1048576)} MB (> 4x eventsRotateBytes); run \`crew reap\` to rotate it
29845
29956
  ` : "events log: ok\n"
@@ -30303,7 +30414,7 @@ ${foreign.join("\n")}
30303
30414
  const declaredS = await sandboxReposOf(cfg.routes.filter((r) => r.enabled));
30304
30415
  const stateS = readSandboxState(cfg.ship.stateDir);
30305
30416
  for (const r of declaredS) {
30306
- const st = sandboxImageStatus(stateS, planSandbox(r.name, r.decl, r.warm));
30417
+ const st = sandboxImageStatus(stateS, planSandbox(r.name, r.decl, r.warm, r.bridge));
30307
30418
  process.stdout.write(`sandbox image: ${r.name}: ${st.status.toUpperCase()} \u2014 ${st.detail}
30308
30419
  `);
30309
30420
  }