@tablation/crew 0.124.0 → 0.126.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.";
@@ -579,11 +588,11 @@ function startSerena(d, s) {
579
588
  const dest = join5(p.home, "language_servers");
580
589
  if (!d.exists(dest)) d.link(s.languageServers, dest);
581
590
  }
582
- const mainCache = join5(p.mainProject, "cache");
591
+ const mainCache = s.seedCache ?? join5(p.mainProject, "cache");
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,25 +10997,49 @@ 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;
11004
+ let seed;
10924
11005
  try {
10925
11006
  const repos = reposOf(o.route);
10926
11007
  const target = repos.find((r) => r.name === o.repo) ?? (repos.length === 1 ? repos[0] : void 0);
10927
11008
  intel = target ? loadRepoConfig(target.dir)?.hooks?.intel : void 0;
11009
+ const origin = target && o.ship.runner === "sandbox" && intelFlavor(intel) === "serena" ? originUrlOf(target.dir, "origin") : null;
11010
+ const remote = origin ? parseRemote(origin) : null;
11011
+ if (remote) seed = `${o.ship.sandbox?.mirrorDir ?? "/cache"}/${intelRelDir(remote)}/cache`;
10928
11012
  } catch {
10929
11013
  }
10930
11014
  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 };
11015
+ const sandbox = o.ship.runner === "sandbox";
11016
+ const args = sandbox ? [
11017
+ SANDBOX_BRIDGE_PATH,
11018
+ "--attach-file",
11019
+ `${SANDBOX_INTEL_DIR}/url`,
11020
+ "--start",
11021
+ intel.trim(),
11022
+ "--clone",
11023
+ o.ship.sandbox?.workDir ?? DEFAULT_SANDBOX_WORKDIR,
11024
+ "--state-dir",
11025
+ dirname13(SANDBOX_INTEL_DIR),
11026
+ "--ticket",
11027
+ number,
11028
+ ...seed ? ["--seed-cache", seed] : []
11029
+ ] : [...process.execArgv, process.argv[1] ?? "crew", "intel", "bridge", o.route.route, number, "--state-dir", o.stateDir];
11030
+ const bridge = { command: sandbox ? "node" : process.execPath, args };
10933
11031
  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;
11032
+ const text2 = `${JSON.stringify({ mcpServers: { [INTEL_SERVER_NAME]: { command: bridge.command, args } } }, null, 2)}
11033
+ `;
11034
+ if (sandbox) {
11035
+ bridge.settingsFile = `${SANDBOX_INTEL_DIR}/gemini-settings.json`;
11036
+ bridge.files = [{ path: bridge.settingsFile, content: text2 }];
11037
+ } else {
11038
+ const file = join29(intelDir(o.stateDir, o.route.route, number), "gemini-settings.json");
11039
+ mkdirSync18(dirname13(file), { recursive: true });
11040
+ writeFileSync19(file, text2);
11041
+ bridge.settingsFile = file;
11042
+ }
10939
11043
  }
10940
11044
  return bridge;
10941
11045
  }
@@ -10996,11 +11100,13 @@ function planAgentRun(o) {
10996
11100
  }
10997
11101
  const adapter = adapterFor(toolchain.harness);
10998
11102
  const code = codeBridgeFor(o, adapter.harness);
11103
+ const sandboxLsp = o.ship.runner === "sandbox" && adapter.harness === "claude" ? sandboxLspPluginFor(o.route, o.repo) : void 0;
11104
+ const sandboxFiles = [...code?.files ?? [], ...sandboxLsp?.files ?? []];
10999
11105
  const invocation = adapter.invocation(toolchain, {
11000
11106
  code,
11001
11107
  role: o.role,
11002
11108
  model,
11003
- pluginDirs: adapter.harness === "claude" ? lspPluginDirsFor(o.route, o.repo, o.stateDir) : void 0,
11109
+ pluginDirs: adapter.harness !== "claude" ? void 0 : sandboxLsp ? [sandboxLsp.dir] : lspPluginDirsFor(o.route, o.repo, o.stateDir),
11004
11110
  // Skipped when there's no key to send: a route `crew connect` hasn't
11005
11111
  // resolved yet has nothing to authenticate the MCP connection with either.
11006
11112
  mcp: o.apiKey ? { url: `${o.route.baseUrl}/api/mcp`, keyVar: API_KEY_VAR } : void 0
@@ -11047,7 +11153,8 @@ function planAgentRun(o) {
11047
11153
  ...o.ship.runner === "sandbox" ? {
11048
11154
  runner: "sandbox",
11049
11155
  ...o.ship.sandbox ? { sandboxConfig: o.ship.sandbox } : {},
11050
- sandboxPolicy: sandboxPolicyInputFor(o.route, toolchain, { harnessBin: invocation.bin, repo: o.repo, sandbox: o.ship.sandbox })
11156
+ sandboxPolicy: sandboxPolicyInputFor(o.route, toolchain, { harnessBin: invocation.bin, repo: o.repo, sandbox: o.ship.sandbox }),
11157
+ ...sandboxFiles.length ? { sandboxFiles } : {}
11051
11158
  } : {},
11052
11159
  resolution: { toolchain: seat.toolchainWhy, model: seat.modelWhy },
11053
11160
  warnings: [...seat.warnings ?? [], ...expanded.warnings],
@@ -12243,6 +12350,9 @@ function mirrorRelPath(r) {
12243
12350
  function depsRelDir(r) {
12244
12351
  return `cache/deps/${r.host}/${r.path}`;
12245
12352
  }
12353
+ function intelRelDir(r) {
12354
+ return `intel/${r.host}/${r.path}`;
12355
+ }
12246
12356
  var q = (s) => `'${s.replace(/'/g, `'\\''`)}'`;
12247
12357
  function cloneScript(o) {
12248
12358
  const mirror = `${o.mirrorDir}/${mirrorRelPath(o.repo)}`;
@@ -12293,6 +12403,30 @@ find . -name node_modules -type d -prune | while read -r p; do
12293
12403
  done
12294
12404
  rm -rf "$droot/$h"; mv "$droot/$h.tmp" "$droot/$h"
12295
12405
  `;
12406
+ var INTEL_SCRIPT = `
12407
+ set -e
12408
+ m=${VOLUME_ROOT}/"$1"; iroot=${VOLUME_ROOT}/"$2"; config="$3"; ls="\${CREW_SERENA_LANGUAGE_SERVERS:-}"
12409
+ export GIT_TERMINAL_PROMPT=0
12410
+ git config --global --add safe.directory '*'
12411
+ tip=$(git -C "$m" rev-parse --verify --quiet "HEAD^{commit}") || { echo "the mirror has no default branch" >&2; exit 3; }
12412
+ [ "$(cat "$iroot/indexed-commit" 2>/dev/null)" = "$tip" ] && { echo "unchanged $tip"; exit 0; }
12413
+ w=$(mktemp -d); trap 'rm -rf "$w"' EXIT
12414
+ git clone --quiet --shared "$m" "$w/src"
12415
+ git -C "$w/src" checkout --quiet "$tip"
12416
+ mkdir -p "$w/home" "$w/project"
12417
+ printf '%s' "$config" | sed "s#@PROJECT@#$w/project#" > "$w/home/serena_config.yml"
12418
+ git -C "$w/src" show "$tip:.serena/project.yml" > "$w/project/project.yml" 2>/dev/null || : > "$w/project/project.yml"
12419
+ if grep -q '^read_only' "$w/project/project.yml"; then sed -i 's/^read_only.*/read_only: true/' "$w/project/project.yml"; else echo 'read_only: true' >> "$w/project/project.yml"; fi
12420
+ [ -n "$ls" ] && [ -d "$ls" ] && ln -s "$ls" "$w/home/language_servers"
12421
+ SERENA_HOME="$w/home" serena project index "$w/src"
12422
+ cd "$w/project"; mkdir -p cache
12423
+ mkdir -p "$(dirname "$iroot")"; rm -rf "$iroot.tmp"; mkdir -p "$iroot.tmp"
12424
+ cp -R -P cache "$iroot.tmp/"
12425
+ printf '%s
12426
+ ' "$tip" > "$iroot.tmp/indexed-commit"
12427
+ rm -rf "$iroot"; mv "$iroot.tmp" "$iroot"
12428
+ echo "updated $tip"
12429
+ `;
12296
12430
  var MATERIALIZE_SCRIPT = `
12297
12431
  set -e
12298
12432
  export GIT_TERMINAL_PROMPT=0
@@ -12371,6 +12505,14 @@ var SandboxCache = class {
12371
12505
  continue;
12372
12506
  }
12373
12507
  }
12508
+ if (r.intelImage) {
12509
+ try {
12510
+ await this.helper(r.intelImage, INTEL_SCRIPT, [mirrorRelPath(r), intelRelDir(r), serenaConfigYaml("@PROJECT@")]);
12511
+ } catch (e) {
12512
+ out.push({ repo: r, ok: false, detail: `intel index: ${describe(e)}` });
12513
+ continue;
12514
+ }
12515
+ }
12374
12516
  out.push({ repo: r, ok: true });
12375
12517
  }
12376
12518
  return out;
@@ -12396,13 +12538,13 @@ var SandboxCache = class {
12396
12538
  } catch {
12397
12539
  return [];
12398
12540
  }
12399
- const script = `cd ${VOLUME_ROOT}/mirrors 2>/dev/null || exit 0; find . -name '*.git' -type d -prune | while read -r d; do printf '%s\\t%s\\n' "\${d#./}" "$(cat "$d/crew-last-refresh" 2>/dev/null)"; done`;
12541
+ const script = `cd ${VOLUME_ROOT}/mirrors 2>/dev/null || exit 0; find . -name '*.git' -type d -prune | while read -r d; do r="\${d#./}"; i=${VOLUME_ROOT}/intel/"\${r%.git}"; printf '%s\\t%s\\t%s\\t%s\\n' "$r" "$(cat "$d/crew-last-refresh" 2>/dev/null)" "$(cat "$i/indexed-commit" 2>/dev/null)" "$(stat -c %y "$i/indexed-commit" 2>/dev/null | cut -c1-19)"; done`;
12400
12542
  const { stdout } = await this.helper(this.s.helperImage, script, []);
12401
12543
  const rows = [];
12402
12544
  for (const line of stdout.split("\n").filter(Boolean)) {
12403
- const [rel, when] = line.split(" ");
12545
+ const [rel, when, commit, indexedAt] = line.split(" ");
12404
12546
  const m = /^([^/]+)\/(.+)\.git$/.exec(rel ?? "");
12405
- if (m) rows.push({ repo: { host: m[1], path: m[2] }, lastRefresh: when?.trim() || null });
12547
+ if (m) rows.push({ repo: { host: m[1], path: m[2] }, lastRefresh: when?.trim() || null, ...commit?.trim() ? { intel: { commit: commit.trim(), indexedAt: indexedAt?.trim() || null } } : {} });
12406
12548
  }
12407
12549
  return rows;
12408
12550
  }
@@ -12411,7 +12553,7 @@ var SandboxCache = class {
12411
12553
  const have = new Set(keep.map((r) => `${r.host}/${r.path}`));
12412
12554
  const drop = (await this.list()).map((m) => m.repo).filter((r) => !have.has(`${r.host}/${r.path}`));
12413
12555
  for (const r of drop) {
12414
- await this.helper(this.s.helperImage, `rm -rf ${VOLUME_ROOT}/${q(mirrorRelPath(r))} ${VOLUME_ROOT}/${q(depsRelDir(r))}`, []);
12556
+ await this.helper(this.s.helperImage, `rm -rf ${VOLUME_ROOT}/${q(mirrorRelPath(r))} ${VOLUME_ROOT}/${q(depsRelDir(r))} ${VOLUME_ROOT}/${q(intelRelDir(r))}`, []);
12415
12557
  }
12416
12558
  return drop;
12417
12559
  }
@@ -12686,6 +12828,14 @@ var SANDBOX_CLAUDE_SHA256 = {
12686
12828
  };
12687
12829
  var SANDBOX_UID = 1e3;
12688
12830
  var CREW_PREFIX = "/opt/crew";
12831
+ var SANDBOX_BRIDGE_PATH = `${CREW_PREFIX}/bin/intel-bridge`;
12832
+ function readBridgeBundle() {
12833
+ const here = dirname14(fileURLToPath(import.meta.url));
12834
+ for (const p of [join32(here, "sandbox-intel-bridge.cjs"), join32(here, "..", "dist", "sandbox-intel-bridge.cjs")]) {
12835
+ if (existsSync30(p)) return readFileSync27(p, "utf8");
12836
+ }
12837
+ return void 0;
12838
+ }
12689
12839
  var SANDBOX_UV_VERSION = "0.13.6";
12690
12840
  var SANDBOX_SERENA_VERSION = "2.1.0";
12691
12841
  var SERENA_IMAGE_HOME = `${CREW_PREFIX}/serena`;
@@ -12749,8 +12899,8 @@ function baseLayer() {
12749
12899
  ].join("\n");
12750
12900
  return { name: "base", tag: `crew-sandbox-base:${hash({ SANDBOX_BASE_IMAGE, SANDBOX_OPENSHELL_VERSION })}`, dockerfile, files: {} };
12751
12901
  }
12752
- function crewLayer(base, intel) {
12753
- const files = { "credential-helper": CREDENTIAL_HELPER, "mcp.json": MCP_CONFIG };
12902
+ function crewLayer(base, intel, bridge) {
12903
+ const files = { "credential-helper": CREDENTIAL_HELPER, "mcp.json": MCP_CONFIG, ...bridge ? { "intel-bridge": bridge } : {} };
12754
12904
  const env = Object.entries(CREW_ENV).map(([k, v]) => `${k}=${v}`).join(" ");
12755
12905
  const dockerfile = [
12756
12906
  `FROM ${base.tag}`,
@@ -12758,6 +12908,7 @@ function crewLayer(base, intel) {
12758
12908
  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
12909
  `COPY credential-helper ${CREW_PREFIX}/bin/credential-helper`,
12760
12910
  `COPY mcp.json ${CREW_PREFIX}/mcp/mcp.json`,
12911
+ ...bridge ? [`COPY intel-bridge ${SANDBOX_BRIDGE_PATH}`] : [],
12761
12912
  run2(claudeInstallCommand()),
12762
12913
  ...intel ? [run2(`mkdir -p ${CREW_PREFIX}/cache && ${serenaInstallCommand()}`)] : [],
12763
12914
  run2(`chmod 0755 ${CREW_PREFIX}/bin/credential-helper && git config --system credential.helper ${CREW_PREFIX}/bin/credential-helper`),
@@ -12822,9 +12973,9 @@ function repoLayer(repo, decl, crew, warm) {
12822
12973
  const dockerfile = lines2.join("\n");
12823
12974
  return { name: "repo", tag: `crew-sandbox/${slug(repo)}:${hash({ crew: crew.tag, dockerfile, files })}`, dockerfile, files };
12824
12975
  }
12825
- function planSandbox(repo, decl, warm = {}) {
12976
+ function planSandbox(repo, decl, warm = {}, bridge) {
12826
12977
  const base = baseLayer();
12827
- const crew = crewLayer(base, decl.intel);
12978
+ const crew = crewLayer(base, decl.intel, bridge);
12828
12979
  const layer = repoLayer(repo, decl, crew, warm);
12829
12980
  return { repo, tag: layer.tag, claude: SANDBOX_CLAUDE_VERSION, layers: [base, crew, layer] };
12830
12981
  }
@@ -12844,6 +12995,9 @@ function writeSandboxState(stateDir, state) {
12844
12995
  `);
12845
12996
  renameSync3(`${file}.tmp`, file);
12846
12997
  }
12998
+ function sandboxImageFor(state, repo) {
12999
+ return state[repo]?.tags[0]?.tag ?? null;
13000
+ }
12847
13001
  function sandboxImageStatus(state, plan) {
12848
13002
  const s = state[plan.repo];
12849
13003
  const current = s?.tags[0];
@@ -12934,7 +13088,7 @@ function sandboxFailedItem(repo, tag, layer, logPath, nowIso = (/* @__PURE__ */
12934
13088
  async function sandboxBuildTick(repos, deps, force = false) {
12935
13089
  const nowMs = (deps.now ?? (() => /* @__PURE__ */ new Date()))().getTime();
12936
13090
  for (const r of repos) {
12937
- const plan = planSandbox(r.name, r.decl, r.warm);
13091
+ const plan = planSandbox(r.name, r.decl, r.warm, r.bridge);
12938
13092
  const state = readSandboxState(deps.stateDir)[r.name];
12939
13093
  const upToDate = state?.tags[0]?.tag === plan.tag && deps.hasImage(plan.tag);
12940
13094
  if (upToDate) {
@@ -13093,7 +13247,7 @@ function managedRuntimeWarnings(kind, ship) {
13093
13247
 
13094
13248
  // src/selftest.ts
13095
13249
  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";
13250
+ import { existsSync as existsSync31, mkdirSync as mkdirSync20, mkdtempSync as mkdtempSync2, readFileSync as readFileSync28, rmSync as rmSync16, writeFileSync as writeFileSync21 } from "fs";
13097
13251
  import { tmpdir as tmpdir4 } from "os";
13098
13252
  import { join as join33 } from "path";
13099
13253
  var SELFTEST_TIMEOUT_MS = 12e4;
@@ -13293,7 +13447,7 @@ var fileFor2 = (stateDir) => join33(stateDir, "selftest.json");
13293
13447
  function readSelftest(stateDir) {
13294
13448
  try {
13295
13449
  const p = fileFor2(stateDir);
13296
- return existsSync30(p) ? JSON.parse(readFileSync28(p, "utf8")) : {};
13450
+ return existsSync31(p) ? JSON.parse(readFileSync28(p, "utf8")) : {};
13297
13451
  } catch {
13298
13452
  return {};
13299
13453
  }
@@ -13315,7 +13469,7 @@ function doctorSelftestLines(stored, toolchains) {
13315
13469
 
13316
13470
  // src/cli.ts
13317
13471
  import { resolve as resolve7, dirname as dirname33, join as join55, basename as basename16 } from "path";
13318
- import { fileURLToPath } from "url";
13472
+ import { fileURLToPath as fileURLToPath2 } from "url";
13319
13473
  import { hostname as hostname4, homedir as homedir11 } from "os";
13320
13474
  import { spawnSync as spawnSync11 } from "child_process";
13321
13475
  import { parse as parseYaml2 } from "yaml";
@@ -13619,7 +13773,7 @@ async function signOutSessions(o) {
13619
13773
  }
13620
13774
 
13621
13775
  // 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";
13776
+ 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
13777
  import { dirname as dirname16, join as join35 } from "path";
13624
13778
  import { hostFromUrl as hostFromUrl3 } from "@tablation/client";
13625
13779
  function clonedCheckoutsFile(stateDir) {
@@ -13692,7 +13846,7 @@ async function runReset(o, fx) {
13692
13846
  }
13693
13847
  if (o.beforeState) steps.push(...await o.beforeState());
13694
13848
  steps.push(...o.wipeState ? wipeStateDir(o.stateDir, o.dryRun) : removeState(o.stateDir, o.dryRun));
13695
- if (existsSync31(o.configFile)) {
13849
+ if (existsSync32(o.configFile)) {
13696
13850
  if (!o.dryRun) rmSync18(o.configFile, { force: true });
13697
13851
  steps.push({ step: "config", target: o.configFile, action: mark(o.dryRun) });
13698
13852
  } else {
@@ -13701,7 +13855,7 @@ async function runReset(o, fx) {
13701
13855
  return steps;
13702
13856
  }
13703
13857
  function removeCheckout(dir, dryRun, fx) {
13704
- if (!existsSync31(dir)) return { step: "checkout", target: dir, action: "absent", detail: "already gone" };
13858
+ if (!existsSync32(dir)) return { step: "checkout", target: dir, action: "absent", detail: "already gone" };
13705
13859
  const { dirty, linkedWorktrees } = fx.inspectCheckout(dir);
13706
13860
  if (dirty.length) {
13707
13861
  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 +13876,7 @@ function removeState(stateDir, dryRun) {
13722
13876
  } catch {
13723
13877
  }
13724
13878
  for (const t of targets) {
13725
- if (!existsSync31(t)) continue;
13879
+ if (!existsSync32(t)) continue;
13726
13880
  if (!dryRun) rmSync18(t, { recursive: true, force: true });
13727
13881
  out.push({ step: "state", target: t, action: mark(dryRun) });
13728
13882
  }
@@ -14118,13 +14272,13 @@ function hostArchitecture(arch = process.arch) {
14118
14272
 
14119
14273
  // src/pool-member-host.ts
14120
14274
  import "child_process";
14121
- import { existsSync as existsSync35 } from "fs";
14275
+ import { existsSync as existsSync36 } from "fs";
14122
14276
  import { hostname as hostname3 } from "os";
14123
14277
 
14124
14278
  // src/machine-id.ts
14125
14279
  import { execFileSync as execFileSync10 } from "child_process";
14126
14280
  import { createHash as createHash7 } from "crypto";
14127
- import { existsSync as existsSync32, mkdirSync as mkdirSync24, readFileSync as readFileSync32, writeFileSync as writeFileSync25 } from "fs";
14281
+ import { existsSync as existsSync33, mkdirSync as mkdirSync24, readFileSync as readFileSync32, writeFileSync as writeFileSync25 } from "fs";
14128
14282
  import { dirname as dirname18, join as join37 } from "path";
14129
14283
  function machineIdPathFor(stateDir) {
14130
14284
  return join37(stateDir, "machine-id");
@@ -14140,7 +14294,7 @@ function readRawMachineId(platform2 = process.platform) {
14140
14294
  return /"IOPlatformUUID"\s*=\s*"([^"]+)"/.exec(out)?.[1];
14141
14295
  }
14142
14296
  for (const p of ["/etc/machine-id", "/var/lib/dbus/machine-id"]) {
14143
- if (existsSync32(p)) {
14297
+ if (existsSync33(p)) {
14144
14298
  const id = readFileSync32(p, "utf8").trim();
14145
14299
  if (id) return id;
14146
14300
  }
@@ -14152,7 +14306,7 @@ function readRawMachineId(platform2 = process.platform) {
14152
14306
  function ensureMachineId(stateDir, opts = {}) {
14153
14307
  if (opts.override?.trim()) return sha256(opts.override.trim());
14154
14308
  const path = machineIdPathFor(stateDir);
14155
- if (existsSync32(path)) {
14309
+ if (existsSync33(path)) {
14156
14310
  const cached = readFileSync32(path, "utf8").trim();
14157
14311
  if (/^[0-9a-f]{64}$/.test(cached)) return cached;
14158
14312
  }
@@ -14173,7 +14327,7 @@ function matchShipRows(ships, shipName2, machineId) {
14173
14327
 
14174
14328
  // src/provision.ts
14175
14329
  import { spawnSync as spawnSync8 } from "child_process";
14176
- import { existsSync as existsSync33, readFileSync as readFileSync33, writeFileSync as writeFileSync26 } from "fs";
14330
+ import { existsSync as existsSync34, readFileSync as readFileSync33, writeFileSync as writeFileSync26 } from "fs";
14177
14331
  import { hostname as hostname2 } from "os";
14178
14332
  import { parseDocument as parseDocument3, isMap as isMap2, isScalar } from "yaml";
14179
14333
 
@@ -14767,7 +14921,7 @@ function realProvisionDeps(crewHome) {
14767
14921
  };
14768
14922
  const configFile = () => {
14769
14923
  const f = findConfigFile(crewHome);
14770
- return existsSync33(f) ? f : null;
14924
+ return existsSync34(f) ? f : null;
14771
14925
  };
14772
14926
  return {
14773
14927
  crew: spawnCrew,
@@ -14801,14 +14955,14 @@ function realProvisionDeps(crewHome) {
14801
14955
  continue;
14802
14956
  }
14803
14957
  for (const p of plan.unitPaths) {
14804
- if (!existsSync33(p) || readFileSync33(p, "utf8") !== plan.unitContent[p]) unitsStale = true;
14958
+ if (!existsSync34(p) || readFileSync33(p, "utf8") !== plan.unitContent[p]) unitsStale = true;
14805
14959
  }
14806
14960
  }
14807
14961
  const label = labelFor(crewHome, "run");
14808
14962
  let running = false;
14809
14963
  let lingering = true;
14810
14964
  if (mechanism === "systemd") {
14811
- const system = ctx.root || existsSync33(systemUnitPathFor(label));
14965
+ const system = ctx.root || existsSync34(systemUnitPathFor(label));
14812
14966
  const ctl = system ? ["systemctl"] : ["systemctl", "--user"];
14813
14967
  running = spawnSync8(ctl[0], [...ctl.slice(1), "is-active", "--quiet", `${label}.service`]).status === 0;
14814
14968
  if (!system) {
@@ -14853,7 +15007,7 @@ host=${host}
14853
15007
 
14854
15008
  // src/tunnel.ts
14855
15009
  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";
15010
+ import { existsSync as existsSync35, mkdirSync as mkdirSync25, readFileSync as readFileSync34, writeFileSync as writeFileSync27, unlinkSync as unlinkSync3, readdirSync as readdirSync13 } from "fs";
14857
15011
  import { randomInt } from "crypto";
14858
15012
  import { join as join39 } from "path";
14859
15013
  var DEFAULT_SPAWN_OPTIONS = { stdio: "ignore", detached: true };
@@ -14931,7 +15085,7 @@ function tunnelStatePath(stateDir, workspaceId) {
14931
15085
  }
14932
15086
  function readPersistedTunnel(stateDir, workspaceId) {
14933
15087
  const path = tunnelStatePath(stateDir, workspaceId);
14934
- if (!existsSync34(path)) return void 0;
15088
+ if (!existsSync35(path)) return void 0;
14935
15089
  try {
14936
15090
  return JSON.parse(readFileSync34(path, "utf8"));
14937
15091
  } catch {
@@ -15044,7 +15198,7 @@ function syncPassengerTunnels(plans, relayHost, relayPort, privateKeyPath, state
15044
15198
  }
15045
15199
  function readAllPersistedWorkspaceIds(stateDir) {
15046
15200
  const dir = join39(stateDir, "passengers");
15047
- if (!existsSync34(dir)) return [];
15201
+ if (!existsSync35(dir)) return [];
15048
15202
  return readdirSync13(dir).filter((f) => f.endsWith(".json") && !f.endsWith("-sync.json")).map((f) => f.slice(0, -".json".length));
15049
15203
  }
15050
15204
 
@@ -15069,7 +15223,7 @@ function realPoolMemberDeps(o) {
15069
15223
  crewVersion: crewVersion(o.crewHome),
15070
15224
  hostname: hostname3()
15071
15225
  },
15072
- hasShipConfig: () => existsSync35(findConfigFile(o.crewHome)),
15226
+ hasShipConfig: () => existsSync36(findConfigFile(o.crewHome)),
15073
15227
  async provision(lease, instanceClass, shipKind) {
15074
15228
  const hosts = lease.forges.map((f) => f.host.toLowerCase());
15075
15229
  const lines2 = [];
@@ -16439,7 +16593,7 @@ async function resolveOne(tracker, t, seat, approvedStatus, buildingStatus, affi
16439
16593
  }
16440
16594
 
16441
16595
  // src/repo-refresh.ts
16442
- import { existsSync as existsSync36, mkdirSync as mkdirSync26, readFileSync as readFileSync35, writeFileSync as writeFileSync29 } from "fs";
16596
+ import { existsSync as existsSync37, mkdirSync as mkdirSync26, readFileSync as readFileSync35, writeFileSync as writeFileSync29 } from "fs";
16443
16597
  import { dirname as dirname20 } from "path";
16444
16598
  var REFRESH_COOLDOWN_MS = 2 * 6e4;
16445
16599
  var UNSERVABLE_PASSES_BEFORE_ATTENTION = 3;
@@ -16469,7 +16623,7 @@ function applyRepoRows(resolved, rows) {
16469
16623
  }
16470
16624
  function persistRepoRows(stateDir, routeName, rows) {
16471
16625
  const path = resolvedPathFor(stateDir, routeName);
16472
- if (!existsSync36(path)) return;
16626
+ if (!existsSync37(path)) return;
16473
16627
  const raw = JSON.parse(readFileSync35(path, "utf8"));
16474
16628
  applyRepoRows(raw, rows);
16475
16629
  mkdirSync26(dirname20(path), { recursive: true });
@@ -16924,12 +17078,12 @@ var localRefSkipReported = (ticket, problem, events) => events.some((e) => e.rec
16924
17078
 
16925
17079
  // src/connect-wizard.ts
16926
17080
  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";
17081
+ import { accessSync as accessSync3, constants as constants3, existsSync as existsSync38, mkdirSync as mkdirSync27, writeFileSync as writeFileSync30 } from "fs";
16928
17082
  import { stringify as stringify3 } from "yaml";
16929
17083
  var HOST_PASSENGERS_DOCS_URL = "https://crew.tablation.dev/host-passengers";
16930
17084
  function shouldRunWizard(opts) {
16931
17085
  if (!opts.isTTY) return false;
16932
- const exists = opts.configExists ?? existsSync37;
17086
+ const exists = opts.configExists ?? existsSync38;
16933
17087
  return !configSearchPath(opts.crewHome).some((p) => exists(p));
16934
17088
  }
16935
17089
  function firstRunConfigPath() {
@@ -17123,7 +17277,7 @@ function flagWizardAnswers(f, env) {
17123
17277
  }
17124
17278
 
17125
17279
  // 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";
17280
+ import { readFileSync as readFileSync36, writeFileSync as writeFileSync31, existsSync as existsSync39, copyFileSync, mkdirSync as mkdirSync28, mkdtempSync as mkdtempSync3, rmSync as rmSync19 } from "fs";
17127
17281
  import { dirname as dirname23, join as join43 } from "path";
17128
17282
  import { tmpdir as tmpdir5 } from "os";
17129
17283
 
@@ -17867,7 +18021,7 @@ async function bump(o, size) {
17867
18021
  }
17868
18022
  const files = o.repo.release.versionFiles.map((f) => join43(o.cwd, f));
17869
18023
  const first = files[0];
17870
- if (!existsSync38(first)) {
18024
+ if (!existsSync39(first)) {
17871
18025
  o.emit.error(`no ${o.repo.release.versionFiles[0]} to read a version from`);
17872
18026
  return null;
17873
18027
  }
@@ -17903,7 +18057,7 @@ function writeChangelog(o, version, entries) {
17903
18057
  o.emit.emit(`would prepend ${entries.length} entr(ies) to ${file}`);
17904
18058
  return;
17905
18059
  }
17906
- const existing = existsSync38(path) ? readFileSync36(path, "utf8") : "# Changelog\n";
18060
+ const existing = existsSync39(path) ? readFileSync36(path, "utf8") : "# Changelog\n";
17907
18061
  writeFileSync31(path, insertChangelogSection(existing, section));
17908
18062
  }
17909
18063
  async function confirm(o, expected) {
@@ -18009,7 +18163,7 @@ function openReleaseCheckout(o) {
18009
18163
  }
18010
18164
  for (const f of o.repo.worktrees.copy) {
18011
18165
  const src = join43(o.cwd, f);
18012
- if (!existsSync38(src)) continue;
18166
+ if (!existsSync39(src)) continue;
18013
18167
  mkdirSync28(dirname23(join43(dir, f)), { recursive: true });
18014
18168
  copyFileSync(src, join43(dir, f));
18015
18169
  }
@@ -18990,7 +19144,7 @@ function missingReleaserWarning(repo, release, ships, now = Date.now()) {
18990
19144
  }
18991
19145
 
18992
19146
  // 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";
19147
+ import { watch as watchFile, existsSync as existsSync40, statSync as statSync11, openSync as openSync5, readSync as readSync2, closeSync as closeSync5 } from "fs";
18994
19148
  var E = "\x1B[";
18995
19149
  var dim = (s) => `${E}2m${s}${E}0m`;
18996
19150
  var bold = (s) => `${E}1m${s}${E}0m`;
@@ -19091,7 +19245,7 @@ function renderHeaders(states, now = Date.now()) {
19091
19245
  return states.map((s) => renderHeader(s, now)).join("");
19092
19246
  }
19093
19247
  function readFrom(file, from) {
19094
- if (!existsSync39(file)) return { events: [], offset: from };
19248
+ if (!existsSync40(file)) return { events: [], offset: from };
19095
19249
  const size = statSync11(file).size;
19096
19250
  if (size < from) return readFrom(file, 0);
19097
19251
  if (size === from) return { events: [], offset: from };
@@ -19334,7 +19488,7 @@ function nextRoles(decision, ran) {
19334
19488
  }
19335
19489
 
19336
19490
  // src/ship-name.ts
19337
- import { existsSync as existsSync40, mkdirSync as mkdirSync29, readFileSync as readFileSync37, renameSync as renameSync5, writeFileSync as writeFileSync32 } from "fs";
19491
+ import { existsSync as existsSync41, mkdirSync as mkdirSync29, readFileSync as readFileSync37, renameSync as renameSync5, writeFileSync as writeFileSync32 } from "fs";
19338
19492
  import { dirname as dirname24 } from "path";
19339
19493
  import { parseDocument as parseDocument5 } from "yaml";
19340
19494
  var ShipNameError = class extends Error {
@@ -19396,7 +19550,7 @@ async function syncRouteName(i) {
19396
19550
  return { status: "ok", ...base, action, name: i.local };
19397
19551
  }
19398
19552
  function readSyncedName(resolvedFile) {
19399
- if (!existsSync40(resolvedFile)) return void 0;
19553
+ if (!existsSync41(resolvedFile)) return void 0;
19400
19554
  try {
19401
19555
  const v = JSON.parse(readFileSync37(resolvedFile, "utf8")).shipName;
19402
19556
  return typeof v === "string" ? v : void 0;
@@ -19405,7 +19559,7 @@ function readSyncedName(resolvedFile) {
19405
19559
  }
19406
19560
  }
19407
19561
  function writeSyncedName(resolvedFile, name) {
19408
- if (!existsSync40(resolvedFile)) return;
19562
+ if (!existsSync41(resolvedFile)) return;
19409
19563
  let raw;
19410
19564
  try {
19411
19565
  raw = JSON.parse(readFileSync37(resolvedFile, "utf8"));
@@ -20107,7 +20261,7 @@ function describeCapability(i) {
20107
20261
  }
20108
20262
 
20109
20263
  // src/agents-sync-run.ts
20110
- import { existsSync as existsSync41, mkdirSync as mkdirSync30, readFileSync as readFileSync38, writeFileSync as writeFileSync33 } from "fs";
20264
+ import { existsSync as existsSync42, mkdirSync as mkdirSync30, readFileSync as readFileSync38, writeFileSync as writeFileSync33 } from "fs";
20111
20265
  import { dirname as dirname25 } from "path";
20112
20266
  async function runAgentsSync(target, o) {
20113
20267
  const dryRun = o.dryRun ?? false;
@@ -20141,7 +20295,7 @@ async function runAgentsSync(target, o) {
20141
20295
  for (const s of skillResult?.outcomes ?? []) o.say(describeSkillSyncOutcome(s));
20142
20296
  if (!dryRun) {
20143
20297
  const resolvedPath = resolvedPathFor(o.stateDir, target.route);
20144
- const raw = existsSync41(resolvedPath) ? JSON.parse(readFileSync38(resolvedPath, "utf8")) : {};
20298
+ const raw = existsSync42(resolvedPath) ? JSON.parse(readFileSync38(resolvedPath, "utf8")) : {};
20145
20299
  raw.agentPersonas = personaResult.agentPersonas;
20146
20300
  if (skillResult) raw.agentSkills = skillResult.agentSkills;
20147
20301
  if (personaResult.releaseAgentId) raw.releaseAgentId = personaResult.releaseAgentId;
@@ -20836,7 +20990,7 @@ function planSampleTicket(i) {
20836
20990
  }
20837
20991
 
20838
20992
  // src/worktree-sweep.ts
20839
- import { existsSync as existsSync42, readFileSync as readFileSync40, readdirSync as readdirSync14 } from "fs";
20993
+ import { existsSync as existsSync43, readFileSync as readFileSync40, readdirSync as readdirSync14 } from "fs";
20840
20994
  import { basename as basename13, dirname as dirname27, join as join45, resolve as resolve5 } from "path";
20841
20995
  function planWorktreeSweep(target, tickets, contract) {
20842
20996
  const terminal = new Set(contract.statuses.resolved.filter((s) => s !== contract.statuses.verified));
@@ -20949,7 +21103,7 @@ async function applyWorktreeSweep(cwd, actions, dryRun, log, remote = "origin",
20949
21103
  let removed = 0;
20950
21104
  let keptBranches = 0;
20951
21105
  for (const a of actions) {
20952
- if (!dryRun && base && !a.keepBranch && existsSync42(a.path) && isHandBackMerge(a.path, base)) {
21106
+ if (!dryRun && base && !a.keepBranch && existsSync43(a.path) && isHandBackMerge(a.path, base)) {
20953
21107
  if (gitOk(a.path, ["merge", "--abort"]) !== null) {
20954
21108
  log.emit(
20955
21109
  `abandoned the hand-back merge of ${base} in ${a.path}: ${a.ticket.issue_id} deployed without it`,
@@ -20957,7 +21111,7 @@ async function applyWorktreeSweep(cwd, actions, dryRun, log, remote = "origin",
20957
21111
  );
20958
21112
  }
20959
21113
  }
20960
- if (!dryRun && existsSync42(a.path)) {
21114
+ if (!dryRun && existsSync43(a.path)) {
20961
21115
  let dirty = false;
20962
21116
  try {
20963
21117
  dirty = status(a.path).length > 0;
@@ -20968,7 +21122,7 @@ async function applyWorktreeSweep(cwd, actions, dryRun, log, remote = "origin",
20968
21122
  continue;
20969
21123
  }
20970
21124
  }
20971
- if (!dryRun && a.requirePushed && a.branch && existsSync42(a.path) && remoteConfigured(cwd, remote) && worktreeHoldsUnpushed(a.path, remote)) {
21125
+ if (!dryRun && a.requirePushed && a.branch && existsSync43(a.path) && remoteConfigured(cwd, remote) && worktreeHoldsUnpushed(a.path, remote)) {
20972
21126
  refuse2(a, "unpushed commits", `${a.branch} has commits that are not on ${remote}`);
20973
21127
  continue;
20974
21128
  }
@@ -21122,12 +21276,12 @@ function applyRemoteBranchCleanup(cwd, remote, branches2, dryRun, log) {
21122
21276
  }
21123
21277
 
21124
21278
  // src/stream-sweep.ts
21125
- import { existsSync as existsSync43, readdirSync as readdirSync15, rmSync as rmSync20, statSync as statSync12 } from "fs";
21279
+ import { existsSync as existsSync44, readdirSync as readdirSync15, rmSync as rmSync20, statSync as statSync12 } from "fs";
21126
21280
  import { join as join46 } from "path";
21127
21281
  var DAY_MS2 = 24 * 60 * 60 * 1e3;
21128
21282
  function planStreamSweep(stateDir, retentionDays, now = Date.now()) {
21129
21283
  const dir = join46(stateDir, "streams");
21130
- if (!existsSync43(dir)) return [];
21284
+ if (!existsSync44(dir)) return [];
21131
21285
  const cutoff = now - retentionDays * DAY_MS2;
21132
21286
  return readdirSync15(dir).map((name) => join46(dir, name)).filter((path) => {
21133
21287
  try {
@@ -21155,7 +21309,7 @@ function applyStreamSweep(paths, dryRun, log) {
21155
21309
  return removed;
21156
21310
  }
21157
21311
  function planEventsSweep(stateDir, retentionDays, now = Date.now()) {
21158
- if (!existsSync43(stateDir)) return [];
21312
+ if (!existsSync44(stateDir)) return [];
21159
21313
  const cutoff = now - retentionDays * DAY_MS2;
21160
21314
  return readdirSync15(stateDir).filter((name) => ROTATED_EVENTS_RE.test(name)).map((name) => join46(stateDir, name)).filter((path) => {
21161
21315
  try {
@@ -21167,11 +21321,11 @@ function planEventsSweep(stateDir, retentionDays, now = Date.now()) {
21167
21321
  }
21168
21322
 
21169
21323
  // 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";
21324
+ 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
21325
  import { createInterface } from "readline/promises";
21172
21326
 
21173
21327
  // src/periscope-cmd.ts
21174
- import { existsSync as existsSync44, mkdirSync as mkdirSync31, readFileSync as readFileSync41, rmSync as rmSync21, writeFileSync as writeFileSync34 } from "fs";
21328
+ import { existsSync as existsSync45, mkdirSync as mkdirSync31, readFileSync as readFileSync41, rmSync as rmSync21, writeFileSync as writeFileSync34 } from "fs";
21175
21329
  import { join as join47 } from "path";
21176
21330
  var PeriscopeError = class extends Error {
21177
21331
  };
@@ -21197,7 +21351,7 @@ function writePeriscopePrior(stateDir, ticketRecordId, prior) {
21197
21351
  function readPeriscopePrior(stateDir, ticketRecordId) {
21198
21352
  try {
21199
21353
  const p = priorPath(stateDir, ticketRecordId);
21200
- return existsSync44(p) ? JSON.parse(readFileSync41(p, "utf8")) : void 0;
21354
+ return existsSync45(p) ? JSON.parse(readFileSync41(p, "utf8")) : void 0;
21201
21355
  } catch {
21202
21356
  return void 0;
21203
21357
  }
@@ -21752,7 +21906,7 @@ import { dirname as dirOf2, resolve as resolvePath } from "path";
21752
21906
 
21753
21907
  // src/ssh-keys.ts
21754
21908
  import { execFileSync as execFileSync13 } from "child_process";
21755
- import { existsSync as existsSync45, mkdirSync as mkdirSync32, readFileSync as readFileSync42, chmodSync as chmodSync3 } from "fs";
21909
+ import { existsSync as existsSync46, mkdirSync as mkdirSync32, readFileSync as readFileSync42, chmodSync as chmodSync3 } from "fs";
21756
21910
  import { dirname as dirname28, join as join49 } from "path";
21757
21911
  var SshKeyError = class extends Error {
21758
21912
  };
@@ -21762,7 +21916,7 @@ function shipSshKeyPathFor(stateDir) {
21762
21916
  function ensureShipSshKeypair(stateDir) {
21763
21917
  const privateKeyPath = shipSshKeyPathFor(stateDir);
21764
21918
  const publicKeyPath = `${privateKeyPath}.pub`;
21765
- if (!existsSync45(privateKeyPath)) {
21919
+ if (!existsSync46(privateKeyPath)) {
21766
21920
  mkdirSync32(dirname28(privateKeyPath), { recursive: true });
21767
21921
  try {
21768
21922
  execFileSync13("ssh-keygen", [
@@ -21783,7 +21937,7 @@ function ensureShipSshKeypair(stateDir) {
21783
21937
  }
21784
21938
  chmodSync3(privateKeyPath, 384);
21785
21939
  }
21786
- if (!existsSync45(publicKeyPath)) {
21940
+ if (!existsSync46(publicKeyPath)) {
21787
21941
  throw new SshKeyError(`${privateKeyPath} exists but ${publicKeyPath} does not \u2014 regenerate or restore it`);
21788
21942
  }
21789
21943
  const publicKey = readFileSync42(publicKeyPath, "utf8").trim();
@@ -21950,7 +22104,7 @@ async function unregisterEndpoint(workspaceId, info, d) {
21950
22104
  import { createServer as createHttpServer } from "http";
21951
22105
  import { spawn as spawn9 } from "child_process";
21952
22106
  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";
22107
+ import { existsSync as existsSync47, mkdirSync as mkdirSync34, readFileSync as readFileSync43, writeFileSync as writeFileSync35, unlinkSync as unlinkSync4, readdirSync as readdirSync16 } from "fs";
21954
22108
  import { join as join50 } from "path";
21955
22109
 
21956
22110
  // src/passenger-mirror.ts
@@ -22085,7 +22239,7 @@ function syncDaemonStatePath(stateDir, workspaceId) {
22085
22239
  }
22086
22240
  function readPersistedSyncDaemon(stateDir, workspaceId) {
22087
22241
  const path = syncDaemonStatePath(stateDir, workspaceId);
22088
- if (!existsSync46(path)) return void 0;
22242
+ if (!existsSync47(path)) return void 0;
22089
22243
  try {
22090
22244
  return JSON.parse(readFileSync43(path, "utf8"));
22091
22245
  } catch {
@@ -22116,7 +22270,7 @@ function waitForExit(pid, alive, timeoutMs = 5e3) {
22116
22270
  }
22117
22271
  function readAllPersistedSyncWorkspaceIds(stateDir) {
22118
22272
  const dir = join50(stateDir, "passengers");
22119
- if (!existsSync46(dir)) return [];
22273
+ if (!existsSync47(dir)) return [];
22120
22274
  const suffix = "-sync.json";
22121
22275
  return readdirSync16(dir).filter((f) => f.endsWith(suffix)).map((f) => f.slice(0, -suffix.length));
22122
22276
  }
@@ -22613,7 +22767,7 @@ function describeCause(err3) {
22613
22767
  }
22614
22768
 
22615
22769
  // 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";
22770
+ 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
22771
  import { join as join51 } from "path";
22618
22772
  import { randomBytes as randomBytes2 } from "crypto";
22619
22773
  var DEFAULT_FLOOR_MS = 5e3;
@@ -22711,7 +22865,7 @@ function computeTreeSignature(paths) {
22711
22865
  }
22712
22866
  function makeStaleChecker(paths, opts = {}) {
22713
22867
  const initial = computeTreeSignature(paths);
22714
- return () => computeTreeSignature(paths) !== initial || opts.reloadSentinel !== void 0 && existsSync47(opts.reloadSentinel);
22868
+ return () => computeTreeSignature(paths) !== initial || opts.reloadSentinel !== void 0 && existsSync48(opts.reloadSentinel);
22715
22869
  }
22716
22870
  function reloadSentinelFor(stateDir) {
22717
22871
  return join51(stateDir, "reload-requested");
@@ -23300,7 +23454,7 @@ import { createHash as createHash10 } from "crypto";
23300
23454
  import {
23301
23455
  chmodSync as chmodSync4,
23302
23456
  closeSync as closeSync6,
23303
- existsSync as existsSync48,
23457
+ existsSync as existsSync49,
23304
23458
  fstatSync,
23305
23459
  openSync as openSync6,
23306
23460
  readSync as readSync3,
@@ -23671,7 +23825,7 @@ function lastLines2(path, n) {
23671
23825
  }
23672
23826
  function readTail(live, opts) {
23673
23827
  const max = opts.maxBytes ?? MAX_TAIL_BYTES;
23674
- if (!existsSync48(live)) {
23828
+ if (!existsSync49(live)) {
23675
23829
  return { lines: [], cursor: opts.after ?? encodeCursor({ dev: 0, ino: 0, off: 0 }), reset: false };
23676
23830
  }
23677
23831
  const cur = opts.after ? decodeCursor(opts.after) : null;
@@ -23828,7 +23982,7 @@ async function route(deps, method, url, req, abort) {
23828
23982
  const run3 = decodeURIComponent(p.slice("/streams/".length));
23829
23983
  if (!RUN_NAME_RE.test(run3) || run3.includes("..") || /\.(pid|meta\.json|events\.jsonl)$/.test(run3)) return err2(400, "not a run name");
23830
23984
  const file = join52(deps.stateDir, "streams", run3.endsWith(".jsonl") ? run3 : `${run3}.jsonl`);
23831
- if (!existsSync48(file)) return err2(404, "no such run");
23985
+ if (!existsSync49(file)) return err2(404, "no such run");
23832
23986
  return tailReply(file, url.searchParams, false, abort);
23833
23987
  }
23834
23988
  if ((p === "/pause" || p === "/resume") && post3) {
@@ -23872,7 +24026,7 @@ function send(res, r) {
23872
24026
  res.end(body);
23873
24027
  }
23874
24028
  async function socketIsStale(path) {
23875
- if (!existsSync48(path)) return false;
24029
+ if (!existsSync49(path)) return false;
23876
24030
  return new Promise((resolve8) => {
23877
24031
  const c = createConnection(path);
23878
24032
  c.once("connect", () => {
@@ -23891,7 +24045,7 @@ async function startControlServer(deps) {
23891
24045
  return null;
23892
24046
  }
23893
24047
  if (await socketIsStale(path)) rmSync23(path, { force: true });
23894
- else if (existsSync48(path)) {
24048
+ else if (existsSync49(path)) {
23895
24049
  log(`control API not started: ${path} is in use by another process`);
23896
24050
  return null;
23897
24051
  }
@@ -23984,7 +24138,7 @@ import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/
23984
24138
 
23985
24139
  // src/periscope-endpoints.ts
23986
24140
  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";
24141
+ 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
24142
  import { join as join53 } from "path";
23989
24143
  var secretDir = (stateDir) => join53(stateDir, "periscope-mcp");
23990
24144
  var secretFile = (stateDir, id) => join53(secretDir(stateDir), `${id}.json`);
@@ -24027,7 +24181,7 @@ function removePeriscopeEndpointRecord(stateDir, ticketRecordId) {
24027
24181
  }
24028
24182
  function listPeriscopeEndpoints(stateDir) {
24029
24183
  const dir = endpointDir(stateDir);
24030
- if (!existsSync49(dir)) return [];
24184
+ if (!existsSync50(dir)) return [];
24031
24185
  const out = [];
24032
24186
  for (const name of readdirSync19(dir)) {
24033
24187
  if (!name.endsWith(".json")) continue;
@@ -24391,7 +24545,7 @@ function findPeriscope(sidecars, segment) {
24391
24545
  }
24392
24546
 
24393
24547
  // src/cli.ts
24394
- var CREW_HOME = isCompiledBinary(import.meta.url) ? dirname33(process.execPath) : resolve7(dirname33(fileURLToPath(import.meta.url)), "..");
24548
+ var CREW_HOME = isCompiledBinary(import.meta.url) ? dirname33(process.execPath) : resolve7(dirname33(fileURLToPath2(import.meta.url)), "..");
24395
24549
  var RELEASE_LOCK_TTL_MS = 20 * 60 * 1e3;
24396
24550
  var CYCLE_SECONDS2 = 120;
24397
24551
  var STALE_CYCLES = 3;
@@ -24907,6 +25061,7 @@ if (command === "passenger-sync-daemon") {
24907
25061
  return { action: out.action, unsafe, detail: unsafe ? describeUnsafeBase(out, remote, base) : out.detail };
24908
25062
  }, sandboxCacheRepos = function(c, ship) {
24909
25063
  const out = [];
25064
+ let images;
24910
25065
  for (const t of reposOf(c)) {
24911
25066
  const origin = originUrlOf(t.dir, "origin");
24912
25067
  const repo = origin ? parseRemote(origin) : null;
@@ -24916,7 +25071,12 @@ if (command === "passenger-sync-daemon") {
24916
25071
  token = readForgeToken(ship.forges, repo.host, ship.stateDir);
24917
25072
  } catch {
24918
25073
  }
24919
- out.push({ ...repo, dir: t.dir, token, username: forgeFor(ship.forges, repo.host)?.entry.username });
25074
+ let intelImage = null;
25075
+ try {
25076
+ if (intelFlavor(loadRepoConfig(t.dir)?.hooks.intel) === "serena") intelImage = sandboxImageFor(images ??= readSandboxState(ship.stateDir), t.name);
25077
+ } catch {
25078
+ }
25079
+ out.push({ ...repo, dir: t.dir, token, username: forgeFor(ship.forges, repo.host)?.entry.username, intelImage });
24920
25080
  }
24921
25081
  return out;
24922
25082
  }, buildSandboxCachePass = function(c, ship, state2, emit2) {
@@ -25004,7 +25164,7 @@ if (command === "passenger-sync-daemon") {
25004
25164
  } else {
25005
25165
  const toleratesNoRoutes = command === "credential" || command === "forge" || command === "repos" || command === "ship" || command === "disconnect" && flag("reset") || command === "deprovision" || command === "connect" && flag("sign-out");
25006
25166
  const configless = command === "connect" && flag("sign-out") || command === "disconnect" && flag("reset") || command === "deprovision";
25007
- if (configless && !existsSync50(findConfigFile(CREW_HOME))) {
25167
+ if (configless && !existsSync51(findConfigFile(CREW_HOME))) {
25008
25168
  cfg = loadConfig(CREW_HOME, firstRunConfigPath(), { text: "ship: {}\n", allowNoRoutes: true });
25009
25169
  } else {
25010
25170
  cfg = loadConfig(CREW_HOME, void 0, toleratesNoRoutes ? { allowNoRoutes: true } : {});
@@ -25327,7 +25487,7 @@ if (command === "passenger-sync-daemon") {
25327
25487
  if (host === "linux" && hasSystemd) {
25328
25488
  const label = labelFor(CREW_HOME, "run");
25329
25489
  const staleTimer = join55(homedir11(), ".config", "systemd", "user", `${label}.timer`);
25330
- if (existsSync50(staleTimer)) {
25490
+ if (existsSync51(staleTimer)) {
25331
25491
  process.stdout.write(
25332
25492
  `removing stale run timer from before the persistent-service cutover: ${staleTimer}${dryRun ? " (dry run)" : ""}
25333
25493
  `
@@ -25354,7 +25514,7 @@ if (command === "passenger-sync-daemon") {
25354
25514
  const tracker = new Tracker(c, cfg.ship);
25355
25515
  problems.push(...validateContract(tracker.contract).map((p) => `contract: ${p}`));
25356
25516
  for (const r of await resolvedRepos(c)) {
25357
- if (!existsSync50(r.dir)) problems.push(`repo ${r.name}: ${r.dir} is missing on disk`);
25517
+ if (!existsSync51(r.dir)) problems.push(`repo ${r.name}: ${r.dir} is missing on disk`);
25358
25518
  if (!satisfies(host, r.config.platform)) problems.push(`repo ${r.name}: requires ${r.config.platform}, this host is ${host}`);
25359
25519
  readiness.push(...validateEffective(r.config).map((p) => `repo ${r.name}: ${p}`));
25360
25520
  }
@@ -25381,7 +25541,7 @@ if (command === "passenger-sync-daemon") {
25381
25541
  }
25382
25542
  async function doctorFix(o) {
25383
25543
  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);
25544
+ const unitExists = (p) => existsSync51(p);
25385
25545
  const crontab = o.host === "linux" ? spawnSync11("crontab", ["-l"], { encoding: "utf8" }).stdout ?? "" : "";
25386
25546
  const schedulerAbsent = o.host !== "windows" ? !schedulerInstalled(planInstall(cfg.ship, CREW_HOME, o.host, detectSystemd(), "run", installContext()), unitExists, crontab) : true;
25387
25547
  const plan = planFix({ routes, schedulerAbsent, host: o.host });
@@ -25427,7 +25587,7 @@ ${changed2.map((c) => ` - ${c}
25427
25587
  warm = readWarmFiles(r.dir, `${r.config.branch.remote}/${r.config.branch.base}`, r.config.sandbox.warm ?? []);
25428
25588
  } catch {
25429
25589
  }
25430
- out.set(r.name, { name: r.name, decl: intelFlavor(r.config.hooks.intel) === "serena" ? { ...r.config.sandbox, intel: "serena" } : r.config.sandbox, warm });
25590
+ 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
25591
  }
25432
25592
  }
25433
25593
  return [...out.values()];
@@ -26842,7 +27002,7 @@ ${cu.tail.trimEnd()}
26842
27002
  `);
26843
27003
  if (!dryRun) {
26844
27004
  const resolvedPath = resolvedPathFor(cfg.ship.stateDir, target.route);
26845
- const raw = existsSync50(resolvedPath) ? JSON.parse(readFileSync46(resolvedPath, "utf8")) : {};
27005
+ const raw = existsSync51(resolvedPath) ? JSON.parse(readFileSync46(resolvedPath, "utf8")) : {};
26846
27006
  raw.agentSkills = result.agentSkills;
26847
27007
  mkdirSync38(dirOf2(resolvedPath), { recursive: true });
26848
27008
  writeFileSync38(resolvedPath, `${JSON.stringify(raw, null, 2)}
@@ -26947,8 +27107,8 @@ ${cu.tail.trimEnd()}
26947
27107
  try {
26948
27108
  const a = parseHooksArgs(positional.slice(2), routeNamesOf(configText));
26949
27109
  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) => {
27110
+ if (!existsSync51(dir)) throw new ReposError(`${dir} does not exist on disk`);
27111
+ const files = { exists: (r) => existsSync51(resolvePath(dir, r)), list: (r) => {
26952
27112
  try {
26953
27113
  return readdirSync20(resolvePath(dir, r));
26954
27114
  } catch {
@@ -27066,7 +27226,7 @@ ${cu.tail.trimEnd()}
27066
27226
  if (sub === "list" && !routeName) usage2();
27067
27227
  if (sub === "add") {
27068
27228
  try {
27069
- const a = parseAddArgs(positional.slice(2), routeNamesOf(configText), (p) => existsSync50(resolvePath(p.replace(/^~(?=\/|$)/, homedir11()))));
27229
+ const a = parseAddArgs(positional.slice(2), routeNamesOf(configText), (p) => existsSync51(resolvePath(p.replace(/^~(?=\/|$)/, homedir11()))));
27070
27230
  routeName = a.route;
27071
27231
  addPath = a.path;
27072
27232
  addName = a.name;
@@ -27096,7 +27256,7 @@ ${cu.tail.trimEnd()}
27096
27256
  const resolvedFile = resolvedPathFor(cfg.ship.stateDir, routeName);
27097
27257
  if (sub === "list" || sub === "add") {
27098
27258
  const liveRoute = cfg.routes.find((r) => r.route === routeName);
27099
- if (liveRoute?.resolved?.reposModelId && existsSync50(resolvedFile) && !dryRun) {
27259
+ if (liveRoute?.resolved?.reposModelId && existsSync51(resolvedFile) && !dryRun) {
27100
27260
  try {
27101
27261
  await hydrateApiKeys([liveRoute]);
27102
27262
  persistRepoRows(cfg.ship.stateDir, liveRoute.route, await new Tracker(liveRoute, cfg.ship).repoRowsForMap());
@@ -27105,7 +27265,7 @@ ${cu.tail.trimEnd()}
27105
27265
  }
27106
27266
  }
27107
27267
  let resolvedRemotes;
27108
- if (existsSync50(resolvedFile)) {
27268
+ if (existsSync51(resolvedFile)) {
27109
27269
  try {
27110
27270
  resolvedRemotes = JSON.parse(readFileSync46(resolvedFile, "utf8"));
27111
27271
  } catch {
@@ -27114,7 +27274,7 @@ ${cu.tail.trimEnd()}
27114
27274
  const originOf = (dir) => gitOk(dir, ["remote", "get-url", "origin"]) ?? void 0;
27115
27275
  const text2 = configText;
27116
27276
  const base = dirname33(cfg.configFile);
27117
- const isCheckout = (d) => existsSync50(`${d}/.git`);
27277
+ const isCheckout = (d) => existsSync51(`${d}/.git`);
27118
27278
  try {
27119
27279
  if (sub === "list") {
27120
27280
  if (flag("json")) {
@@ -27124,7 +27284,7 @@ ${cu.tail.trimEnd()}
27124
27284
  base,
27125
27285
  resolved: resolvedRemotes,
27126
27286
  originOf,
27127
- exists: (d) => existsSync50(d),
27287
+ exists: (d) => existsSync51(d),
27128
27288
  isCheckout,
27129
27289
  repoHasHooks: (d) => REPO_CONFIG_FILENAMES.some((f) => {
27130
27290
  try {
@@ -27145,7 +27305,7 @@ ${cu.tail.trimEnd()}
27145
27305
  for (const r of repos) {
27146
27306
  const origin2 = originOf(r.dir);
27147
27307
  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)"}`;
27308
+ const note = !existsSync51(r.dir) ? "missing on disk" : row ? `Repos row: ${row}` : `no Repos row matches ${normalizeRemote(origin2) ?? "this checkout (no origin remote)"}`;
27149
27309
  process.stdout.write(`${r.name} ${r.dir} ${note}
27150
27310
  `);
27151
27311
  }
@@ -27157,9 +27317,9 @@ ${cu.tail.trimEnd()}
27157
27317
  ` : `${human}
27158
27318
  `);
27159
27319
  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)) {
27320
+ if (existsSync51(plan2.target) && (existsSync51(`${plan2.target}/.git`) || readdirSync20(plan2.target).length > 0)) {
27161
27321
  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`
27322
+ 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
27323
  );
27164
27324
  }
27165
27325
  if (dryRun) {
@@ -27184,7 +27344,7 @@ ${cu.tail.trimEnd()}
27184
27344
  break;
27185
27345
  }
27186
27346
  const target = resolvePath(addPath.replace(/^~(?=\/|$)/, homedir11()));
27187
- const top = existsSync50(target) ? gitOk(target, ["rev-parse", "--show-toplevel"]) : null;
27347
+ const top = existsSync51(target) ? gitOk(target, ["rev-parse", "--show-toplevel"]) : null;
27188
27348
  if (!top) throw new ReposError(`${target} is not a git checkout`);
27189
27349
  const origin = originOf(top);
27190
27350
  const plan = planRepoAdd({
@@ -27236,7 +27396,7 @@ ${cu.tail.trimEnd()}
27236
27396
  const created = await rowTracker.createRepoRow(rowPayload);
27237
27397
  process.stdout.write(`created Repos row ${String(rowPayload.name)} (${String(rowPayload.remote)}) ${created.id}
27238
27398
  `);
27239
- if (existsSync50(resolvedFile)) {
27399
+ if (existsSync51(resolvedFile)) {
27240
27400
  const raw = JSON.parse(readFileSync46(resolvedFile, "utf8"));
27241
27401
  raw.repoNames = { ...raw.repoNames, [created.id]: String(rowPayload.name) };
27242
27402
  raw.repoRemotes = { ...raw.repoRemotes, [created.id]: String(rowPayload.remote) };
@@ -27658,7 +27818,7 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
27658
27818
  }
27659
27819
  }
27660
27820
  let previous;
27661
- const hadPreviousFile = existsSync50(resolvedPath);
27821
+ const hadPreviousFile = existsSync51(resolvedPath);
27662
27822
  if (hadPreviousFile) {
27663
27823
  try {
27664
27824
  previous = JSON.parse(readFileSync46(resolvedPath, "utf8"));
@@ -28190,7 +28350,7 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
28190
28350
  if (subS === "images") {
28191
28351
  const stateS = readSandboxState(cfg.ship.stateDir);
28192
28352
  const rows = declared.map((r) => {
28193
- const plan = planSandbox(r.name, r.decl, r.warm);
28353
+ const plan = planSandbox(r.name, r.decl, r.warm, r.bridge);
28194
28354
  return { repo: r.name, wants: plan.tag, tags: stateS[r.name]?.tags ?? [], ...sandboxImageStatus(stateS, plan) };
28195
28355
  });
28196
28356
  if (jsonS) {
@@ -28224,7 +28384,7 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
28224
28384
  for (const t of targets) {
28225
28385
  await sandboxBuildTick([t], depsS, true);
28226
28386
  const after = readSandboxState(cfg.ship.stateDir);
28227
- const st = sandboxImageStatus(after, planSandbox(t.name, t.decl, t.warm));
28387
+ const st = sandboxImageStatus(after, planSandbox(t.name, t.decl, t.warm, t.bridge));
28228
28388
  if (st.status === "failed") failed = true;
28229
28389
  process.stdout.write(`${t.name}: ${st.status} \u2014 ${st.detail}
28230
28390
  `);
@@ -28257,8 +28417,10 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
28257
28417
  `);
28258
28418
  if (mirrors.length === 0) process.stdout.write(`volume ${settings.volume}: no mirrors yet
28259
28419
  `);
28260
- for (const m of mirrors) process.stdout.write(`${m.repo.host}/${m.repo.path} last refresh ${m.lastRefresh ?? "never"}
28420
+ for (const m of mirrors) {
28421
+ process.stdout.write(`${m.repo.host}/${m.repo.path} last refresh ${m.lastRefresh ?? "never"}${m.intel ? ` intel ${m.intel.commit.slice(0, 12)} (${m.intel.indexedAt ?? "age unknown"})` : ""}
28261
28422
  `);
28423
+ }
28262
28424
  }
28263
28425
  } catch (e) {
28264
28426
  process.stderr.write(`crew sandbox cache: ${e instanceof Error ? e.message : String(e)}
@@ -28536,7 +28698,7 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
28536
28698
  prepare: async (path) => {
28537
28699
  for (const f of repoWt.config.worktrees.copy) {
28538
28700
  const src = join55(repoWt.dir, f);
28539
- if (!existsSync50(src) || existsSync50(join55(path, f))) continue;
28701
+ if (!existsSync51(src) || existsSync51(join55(path, f))) continue;
28540
28702
  mkdirSync38(dirOf2(join55(path, f)), { recursive: true });
28541
28703
  copyFileSync2(src, join55(path, f));
28542
28704
  }
@@ -28758,7 +28920,7 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
28758
28920
  const shellR = hit && resolveShell(hit.repo.config.shell ?? cfg.ship.shell);
28759
28921
  const resumed = await runPeriscopeResume({
28760
28922
  find: async () => hit && { row: hit.row, tag: hit.tag },
28761
- worktreeExists: (p) => existsSync50(p),
28923
+ worktreeExists: (p) => existsSync51(p),
28762
28924
  prepareDropFile,
28763
28925
  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
28926
  caps: periscopeCaps(cfg.ship),
@@ -28855,7 +29017,7 @@ ${where.join("\n")}`);
28855
29017
  prepare: async (path) => {
28856
29018
  for (const f of repoP.config.worktrees.copy) {
28857
29019
  const src = join55(repoP.dir, f);
28858
- if (!existsSync50(src)) continue;
29020
+ if (!existsSync51(src)) continue;
28859
29021
  mkdirSync38(dirOf2(join55(path, f)), { recursive: true });
28860
29022
  copyFileSync2(src, join55(path, f));
28861
29023
  }
@@ -29347,7 +29509,7 @@ ${where.join("\n")}`);
29347
29509
  }
29348
29510
  if (row) {
29349
29511
  await tracker.deleteRepoRow(row.id);
29350
- if (existsSync50(resolvedFile)) {
29512
+ if (existsSync51(resolvedFile)) {
29351
29513
  const raw = JSON.parse(readFileSync46(resolvedFile, "utf8"));
29352
29514
  if (raw.repoNames) delete raw.repoNames[row.id];
29353
29515
  if (raw.repoRemotes) delete raw.repoRemotes[row.id];
@@ -29355,13 +29517,13 @@ ${where.join("\n")}`);
29355
29517
  `);
29356
29518
  }
29357
29519
  }
29358
- if (existsSync50(join55(dir, ".git")) && basename16(dir) === SAMPLE_REPO) rmSync25(dir, { recursive: true, force: true });
29520
+ if (existsSync51(join55(dir, ".git")) && basename16(dir) === SAMPLE_REPO) rmSync25(dir, { recursive: true, force: true });
29359
29521
  say("removed", { dir, closed }, `removed the sample repo${closed.length ? `, closed ${closed.join(", ")}` : ""}`);
29360
29522
  break;
29361
29523
  }
29362
29524
  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`);
29525
+ const fresh = !existsSync51(join55(dir, ".git"));
29526
+ if (existsSync51(dir) && fresh && readdirSync20(dir).length > 0) fail2(`${dir} exists and is not a git checkout \u2014 move it aside`);
29365
29527
  if (dryRun) {
29366
29528
  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
29529
  break;
@@ -29387,7 +29549,7 @@ ${where.join("\n")}`);
29387
29549
  spec: { platform: "unix", releaseMode: "integrate", ciProvider: "none" }
29388
29550
  });
29389
29551
  repoId = (await tracker.createRepoRow(payload)).id;
29390
- if (existsSync50(resolvedFile)) {
29552
+ if (existsSync51(resolvedFile)) {
29391
29553
  const raw = JSON.parse(readFileSync46(resolvedFile, "utf8"));
29392
29554
  raw.repoNames = { ...raw.repoNames, [repoId]: SAMPLE_REPO };
29393
29555
  raw.repoRemotes = { ...raw.repoRemotes, [repoId]: SAMPLE_REMOTE };
@@ -29743,7 +29905,7 @@ watermark: ${state.watermark()}
29743
29905
  }
29744
29906
  for (const r of repos) {
29745
29907
  const remote = r.config.branch.remote;
29746
- if (existsSync50(r.dir)) {
29908
+ if (existsSync51(r.dir)) {
29747
29909
  pushAuth.push({
29748
29910
  route: c.route,
29749
29911
  repo: r.name,
@@ -29839,7 +30001,7 @@ routes: ${cfg.routes.map((c) => c.route).join(", ")}
29839
30001
  }
29840
30002
  try {
29841
30003
  const evFile = eventFileFor(cfg.ship.stateDir);
29842
- const evSize = existsSync50(evFile) ? statSync16(evFile).size : 0;
30004
+ const evSize = existsSync51(evFile) ? statSync16(evFile).size : 0;
29843
30005
  process.stdout.write(
29844
30006
  evSize > cfg.ship.eventsRotateBytes * 4 ? `events log: WARN ${evFile} is ${Math.round(evSize / 1048576)} MB (> 4x eventsRotateBytes); run \`crew reap\` to rotate it
29845
30007
  ` : "events log: ok\n"
@@ -30303,7 +30465,7 @@ ${foreign.join("\n")}
30303
30465
  const declaredS = await sandboxReposOf(cfg.routes.filter((r) => r.enabled));
30304
30466
  const stateS = readSandboxState(cfg.ship.stateDir);
30305
30467
  for (const r of declaredS) {
30306
- const st = sandboxImageStatus(stateS, planSandbox(r.name, r.decl, r.warm));
30468
+ const st = sandboxImageStatus(stateS, planSandbox(r.name, r.decl, r.warm, r.bridge));
30307
30469
  process.stdout.write(`sandbox image: ${r.name}: ${st.status.toUpperCase()} \u2014 ${st.detail}
30308
30470
  `);
30309
30471
  }