@tablation/crew 0.123.1 → 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.";
@@ -406,7 +415,7 @@ function registerCodeServer(worktree, url, server = INTEL_SERVER_NAME) {
406
415
 
407
416
  // src/intel-serena.ts
408
417
  import { spawn, spawnSync } from "child_process";
409
- import { closeSync, cpSync, existsSync as existsSync6, mkdirSync as mkdirSync4, openSync, readFileSync as readFileSync4, rmSync, writeFileSync as writeFileSync4 } from "fs";
418
+ import { closeSync, cpSync, existsSync as existsSync6, mkdirSync as mkdirSync4, openSync, readFileSync as readFileSync4, rmSync, symlinkSync, writeFileSync as writeFileSync4 } from "fs";
410
419
  import { dirname as dirname3, join as join5 } from "path";
411
420
 
412
421
  // src/ports.ts
@@ -575,11 +584,15 @@ function startSerena(d, s) {
575
584
  const p = serenaPaths(s.stateDir, s.route, String(s.ticket));
576
585
  try {
577
586
  prepareFolder(d, p.home, p.project, s.worktree);
587
+ if (s.languageServers && d.exists(s.languageServers) && d.link) {
588
+ const dest = join5(p.home, "language_servers");
589
+ if (!d.exists(dest)) d.link(s.languageServers, dest);
590
+ }
578
591
  const mainCache = join5(p.mainProject, "cache");
579
592
  const cache = join5(p.project, "cache");
580
593
  if (!d.exists(cache) && d.exists(mainCache)) d.copyDir(mainCache, cache);
581
594
  try {
582
- ensureMainIndex(d, s);
595
+ if (!s.noMainIndex) ensureMainIndex(d, s);
583
596
  } catch {
584
597
  }
585
598
  const port = intelPortFor(s.ticket);
@@ -625,6 +638,7 @@ function realSerenaDeps(onPath2, extraEnv = process.env) {
625
638
  spawnDetached: detached,
626
639
  // `&&` so the stamp exists only for an index that finished; a killed one is retried next start.
627
640
  spawnIndex: (args, env, log, stamp, stampText) => detached("sh", ["-c", 'serena "$@" && printf %s "$STAMP_TEXT" > "$STAMP_FILE"', "serena", ...args], { ...env, STAMP_FILE: stamp, STAMP_TEXT: stampText }, log),
641
+ link: (target, path) => symlinkSync(target, path),
628
642
  alive: (pid) => {
629
643
  try {
630
644
  process.kill(pid, 0);
@@ -6290,8 +6304,9 @@ function attentionProblems(items, already) {
6290
6304
  import { createHash as createHash6 } from "crypto";
6291
6305
  import { spawn as spawn5 } from "child_process";
6292
6306
  import { execFileSync as execFileSync8 } from "child_process";
6293
- 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";
6294
6308
  import { dirname as dirname14, join as join32 } from "path";
6309
+ import { fileURLToPath } from "url";
6295
6310
  import { tmpdir as tmpdir3 } from "os";
6296
6311
 
6297
6312
  // src/passenger-containers.ts
@@ -6845,6 +6860,12 @@ exit 125
6845
6860
  ...CONTAINER_ENGINES.map((e) => `printf '%b' ${shSingle(body)} > ${shSingle(`${dir}/${e}`)} && chmod 755 ${shSingle(`${dir}/${e}`)}`)
6846
6861
  ].join(" && ");
6847
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
+ }
6848
6869
  function shSingle(s) {
6849
6870
  return `'${s.replace(/'/g, `'\\''`)}'`;
6850
6871
  }
@@ -6871,6 +6892,17 @@ function sandboxCheckoutSection(workDir, heading) {
6871
6892
  "skill for setup, ports and the hand-off push."
6872
6893
  ];
6873
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
+ }
6874
6906
  function sandboxEnv(ship) {
6875
6907
  return ship.runner === "sandbox" ? { sandbox: { workDir: ship.sandbox?.workDir ?? DEFAULT_SANDBOX_WORKDIR } } : {};
6876
6908
  }
@@ -7170,7 +7202,7 @@ function renderEnvironment(i) {
7170
7202
  );
7171
7203
  }
7172
7204
  if (only && i.sandbox) {
7173
- lines2.push(...sandboxCheckoutSection(i.sandbox.workDir, "## Your checkout"));
7205
+ lines2.push(...sandboxCheckoutSection(i.sandbox.workDir, "## Your checkout"), ...sandboxCodeToolsSection(only.config.hooks.intel));
7174
7206
  } else if (only) {
7175
7207
  lines2.push(...repoSection(only, key, "## Your worktree"));
7176
7208
  } else {
@@ -7186,7 +7218,7 @@ function renderEnvironment(i) {
7186
7218
  );
7187
7219
  if (i.sandbox) lines2.push(...sandboxCheckoutSection(i.sandbox.workDir, "### Your checkout"));
7188
7220
  for (const r of repos) {
7189
- 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}\``));
7190
7222
  lines2.push("", `Hooks for ${r.name} \u2014 the only commands you should run here:`, "", hookTable(r.config));
7191
7223
  lines2.push(...mapSection(r, `#### Repository map \u2014 ${r.name}`));
7192
7224
  }
@@ -7214,6 +7246,8 @@ import {
7214
7246
  } from "@modelcontextprotocol/sdk/types.js";
7215
7247
  var STATUS_TOOL = "code_status";
7216
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.`;
7217
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.";
7218
7252
  function intelDir(stateDir, route2, ticket) {
7219
7253
  const safe = (s) => s.replace(/[^A-Za-z0-9._-]+/g, "_");
@@ -7253,6 +7287,8 @@ function createBridge(o) {
7253
7287
  let lost = false;
7254
7288
  let connecting = false;
7255
7289
  let timer;
7290
+ let starting;
7291
+ let startError;
7256
7292
  const notify2 = () => {
7257
7293
  void server.sendToolListChanged().catch(() => {
7258
7294
  });
@@ -7265,8 +7301,40 @@ function createBridge(o) {
7265
7301
  });
7266
7302
  notify2();
7267
7303
  };
7268
- 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
+ };
7269
7336
  server.setRequestHandler(ListToolsRequestSchema, async () => {
7337
+ ensureStarted();
7270
7338
  if (!upstream) return { tools: [statusTool] };
7271
7339
  try {
7272
7340
  return await upstream.listTools();
@@ -7276,6 +7344,11 @@ function createBridge(o) {
7276
7344
  }
7277
7345
  });
7278
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();
7279
7352
  if (!upstream) return status2();
7280
7353
  try {
7281
7354
  return await upstream.callTool({ name: req.params.name, arguments: req.params.arguments });
@@ -10728,6 +10801,9 @@ var SandboxRunner = class {
10728
10801
  lockfile: cfg.depsLockfile ?? DEFAULT_CACHE_SETTINGS.depsLockfile
10729
10802
  })} && ${containerEngineStubScript()}`;
10730
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
+ }
10731
10807
  const harness = cfg.agentBin ?? basename8(plan.bin);
10732
10808
  const viaUpload = Buffer.byteLength(plan.prompt) > SANDBOX_STDIN_LIMIT;
10733
10809
  if (viaUpload) {
@@ -10877,6 +10953,15 @@ function lspPluginDirsFor(route2, repo, stateDir) {
10877
10953
  return void 0;
10878
10954
  }
10879
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
+ }
10880
10965
  var AgentSpawnError = class extends AgentError {
10881
10966
  };
10882
10967
  var WorkspaceAuthRefused = class extends AgentSpawnError {
@@ -10912,7 +10997,7 @@ function streamShowsAllowanceRefusal(streamPath) {
10912
10997
  }
10913
10998
  var digestPathFor = (stateDir, routeLabel, role, runId) => join29(stateDir, `digest-${routeLabel}-${role}${runId ? `-${runId}` : ""}.md`);
10914
10999
  function codeBridgeFor(o, harness) {
10915
- if (o.ship.runner === "sandbox" || !o.ticket) return void 0;
11000
+ if (!o.ticket) return void 0;
10916
11001
  const number = /(\d+)$/.exec(o.ticket)?.[1];
10917
11002
  if (!number) return void 0;
10918
11003
  let intel;
@@ -10923,14 +11008,33 @@ function codeBridgeFor(o, harness) {
10923
11008
  } catch {
10924
11009
  }
10925
11010
  if (!wantsCodeBridge(intel)) return void 0;
10926
- const args = [...process.execArgv, process.argv[1] ?? "crew", "intel", "bridge", o.route.route, number, "--state-dir", o.stateDir];
10927
- 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 };
10928
11026
  if (harness === "gemini") {
10929
- const file = join29(intelDir(o.stateDir, o.route.route, number), "gemini-settings.json");
10930
- mkdirSync18(dirname13(file), { recursive: true });
10931
- writeFileSync19(file, `${JSON.stringify({ mcpServers: { [INTEL_SERVER_NAME]: { command: bridge.command, args } } }, null, 2)}
10932
- `);
10933
- 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
+ }
10934
11038
  }
10935
11039
  return bridge;
10936
11040
  }
@@ -10991,11 +11095,13 @@ function planAgentRun(o) {
10991
11095
  }
10992
11096
  const adapter = adapterFor(toolchain.harness);
10993
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 ?? []];
10994
11100
  const invocation = adapter.invocation(toolchain, {
10995
11101
  code,
10996
11102
  role: o.role,
10997
11103
  model,
10998
- 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),
10999
11105
  // Skipped when there's no key to send: a route `crew connect` hasn't
11000
11106
  // resolved yet has nothing to authenticate the MCP connection with either.
11001
11107
  mcp: o.apiKey ? { url: `${o.route.baseUrl}/api/mcp`, keyVar: API_KEY_VAR } : void 0
@@ -11042,7 +11148,8 @@ function planAgentRun(o) {
11042
11148
  ...o.ship.runner === "sandbox" ? {
11043
11149
  runner: "sandbox",
11044
11150
  ...o.ship.sandbox ? { sandboxConfig: o.ship.sandbox } : {},
11045
- 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 } : {}
11046
11153
  } : {},
11047
11154
  resolution: { toolchain: seat.toolchainWhy, model: seat.modelWhy },
11048
11155
  warnings: [...seat.warnings ?? [], ...expanded.warnings],
@@ -12681,6 +12788,18 @@ var SANDBOX_CLAUDE_SHA256 = {
12681
12788
  };
12682
12789
  var SANDBOX_UID = 1e3;
12683
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
+ }
12799
+ var SANDBOX_UV_VERSION = "0.13.6";
12800
+ var SANDBOX_SERENA_VERSION = "2.1.0";
12801
+ var SERENA_IMAGE_HOME = `${CREW_PREFIX}/serena`;
12802
+ var SERENA_LANGUAGE_SERVERS_ENV = "CREW_SERENA_LANGUAGE_SERVERS";
12684
12803
  var SANDBOX_RETRY_AFTER_MS = 60 * 60 * 1e3;
12685
12804
  var KEEP_TAGS = 2;
12686
12805
  var CREDENTIAL_HELPER = `#!/bin/sh
@@ -12717,6 +12836,21 @@ function claudeInstallCommand() {
12717
12836
  `${CREW_PREFIX}/bin/claude --version | grep -F "${SANDBOX_CLAUDE_VERSION}"`
12718
12837
  ].join("; ");
12719
12838
  }
12839
+ function serenaInstallCommand() {
12840
+ return [
12841
+ `python3 -m venv ${CREW_PREFIX}/uv-venv`,
12842
+ `${CREW_PREFIX}/uv-venv/bin/pip install --no-cache-dir uv==${SANDBOX_UV_VERSION}`,
12843
+ `ln -sf ${CREW_PREFIX}/uv-venv/bin/uv ${CREW_PREFIX}/bin/uv`,
12844
+ `UV_TOOL_DIR=${CREW_PREFIX}/uv/tools UV_TOOL_BIN_DIR=${CREW_PREFIX}/bin UV_PYTHON_INSTALL_DIR=${CREW_PREFIX}/uv/python UV_CACHE_DIR=${CREW_PREFIX}/cache/uv ${CREW_PREFIX}/bin/uv tool install -p 3.13 serena-agent==${SANDBOX_SERENA_VERSION}`,
12845
+ `${CREW_PREFIX}/bin/serena --help >/dev/null`
12846
+ ].join("; ");
12847
+ }
12848
+ function serenaWarmCommand() {
12849
+ return [
12850
+ `mkdir -p ${SERENA_IMAGE_HOME}`,
12851
+ `SERENA_HOME=${SERENA_IMAGE_HOME} serena project index ${WARM_DIR}`
12852
+ ].join("; ");
12853
+ }
12720
12854
  function baseLayer() {
12721
12855
  const dockerfile = [
12722
12856
  `FROM ${SANDBOX_BASE_IMAGE}`,
@@ -12725,18 +12859,21 @@ function baseLayer() {
12725
12859
  ].join("\n");
12726
12860
  return { name: "base", tag: `crew-sandbox-base:${hash({ SANDBOX_BASE_IMAGE, SANDBOX_OPENSHELL_VERSION })}`, dockerfile, files: {} };
12727
12861
  }
12728
- function crewLayer(base) {
12729
- 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 } : {} };
12730
12864
  const env = Object.entries(CREW_ENV).map(([k, v]) => `${k}=${v}`).join(" ");
12731
12865
  const dockerfile = [
12732
12866
  `FROM ${base.tag}`,
12733
12867
  "USER root",
12734
- run2("apt-get update && apt-get install -y --no-install-recommends ca-certificates curl git && rm -rf /var/lib/apt/lists/*"),
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/*`),
12735
12869
  `COPY credential-helper ${CREW_PREFIX}/bin/credential-helper`,
12736
12870
  `COPY mcp.json ${CREW_PREFIX}/mcp/mcp.json`,
12871
+ ...bridge ? [`COPY intel-bridge ${SANDBOX_BRIDGE_PATH}`] : [],
12737
12872
  run2(claudeInstallCommand()),
12873
+ ...intel ? [run2(`mkdir -p ${CREW_PREFIX}/cache && ${serenaInstallCommand()}`)] : [],
12738
12874
  run2(`chmod 0755 ${CREW_PREFIX}/bin/credential-helper && git config --system credential.helper ${CREW_PREFIX}/bin/credential-helper`),
12739
12875
  run2(`mkdir -p ${CREW_PREFIX}/cache ${CREW_PREFIX}/pnpm-store ${CREW_PREFIX}/pnpm /sandbox && chown -R ${SANDBOX_UID}:${SANDBOX_UID} ${CREW_PREFIX} /sandbox`),
12876
+ ...intel ? [`ENV ${SERENA_LANGUAGE_SERVERS_ENV}=${SERENA_IMAGE_HOME}/language_servers UV_PYTHON_INSTALL_DIR=${CREW_PREFIX}/uv/python`] : [],
12740
12877
  `ENV ${env} PATH=${CREW_PREFIX}/pnpm:${CREW_PREFIX}/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin`,
12741
12878
  `LABEL io.crew.sandbox.layer="crew" io.crew.sandbox.claude="${SANDBOX_CLAUDE_VERSION}"`,
12742
12879
  ""
@@ -12790,14 +12927,15 @@ function repoLayer(repo, decl, crew, warm) {
12790
12927
  lines2.push(`COPY --chown=${SANDBOX_UID}:${SANDBOX_UID} warm/ ${WARM_DIR}/`, `WORKDIR ${WARM_DIR}`);
12791
12928
  }
12792
12929
  for (const step of decl.setup) lines2.push(run2(step));
12930
+ if (warming && decl.intel) lines2.push(run2(serenaWarmCommand()));
12793
12931
  if (warming) lines2.push("WORKDIR /sandbox", run2(`rm -rf ${WARM_DIR}`));
12794
12932
  lines2.push(`LABEL io.crew.sandbox.layer="repo" io.crew.sandbox.repo=${JSON.stringify(repo)}`, "");
12795
12933
  const dockerfile = lines2.join("\n");
12796
12934
  return { name: "repo", tag: `crew-sandbox/${slug(repo)}:${hash({ crew: crew.tag, dockerfile, files })}`, dockerfile, files };
12797
12935
  }
12798
- function planSandbox(repo, decl, warm = {}) {
12936
+ function planSandbox(repo, decl, warm = {}, bridge) {
12799
12937
  const base = baseLayer();
12800
- const crew = crewLayer(base);
12938
+ const crew = crewLayer(base, decl.intel, bridge);
12801
12939
  const layer = repoLayer(repo, decl, crew, warm);
12802
12940
  return { repo, tag: layer.tag, claude: SANDBOX_CLAUDE_VERSION, layers: [base, crew, layer] };
12803
12941
  }
@@ -12907,7 +13045,7 @@ function sandboxFailedItem(repo, tag, layer, logPath, nowIso = (/* @__PURE__ */
12907
13045
  async function sandboxBuildTick(repos, deps, force = false) {
12908
13046
  const nowMs = (deps.now ?? (() => /* @__PURE__ */ new Date()))().getTime();
12909
13047
  for (const r of repos) {
12910
- const plan = planSandbox(r.name, r.decl, r.warm);
13048
+ const plan = planSandbox(r.name, r.decl, r.warm, r.bridge);
12911
13049
  const state = readSandboxState(deps.stateDir)[r.name];
12912
13050
  const upToDate = state?.tags[0]?.tag === plan.tag && deps.hasImage(plan.tag);
12913
13051
  if (upToDate) {
@@ -13066,7 +13204,7 @@ function managedRuntimeWarnings(kind, ship) {
13066
13204
 
13067
13205
  // src/selftest.ts
13068
13206
  import { spawn as spawn6 } from "child_process";
13069
- 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";
13070
13208
  import { tmpdir as tmpdir4 } from "os";
13071
13209
  import { join as join33 } from "path";
13072
13210
  var SELFTEST_TIMEOUT_MS = 12e4;
@@ -13266,7 +13404,7 @@ var fileFor2 = (stateDir) => join33(stateDir, "selftest.json");
13266
13404
  function readSelftest(stateDir) {
13267
13405
  try {
13268
13406
  const p = fileFor2(stateDir);
13269
- return existsSync30(p) ? JSON.parse(readFileSync28(p, "utf8")) : {};
13407
+ return existsSync31(p) ? JSON.parse(readFileSync28(p, "utf8")) : {};
13270
13408
  } catch {
13271
13409
  return {};
13272
13410
  }
@@ -13288,7 +13426,7 @@ function doctorSelftestLines(stored, toolchains) {
13288
13426
 
13289
13427
  // src/cli.ts
13290
13428
  import { resolve as resolve7, dirname as dirname33, join as join55, basename as basename16 } from "path";
13291
- import { fileURLToPath } from "url";
13429
+ import { fileURLToPath as fileURLToPath2 } from "url";
13292
13430
  import { hostname as hostname4, homedir as homedir11 } from "os";
13293
13431
  import { spawnSync as spawnSync11 } from "child_process";
13294
13432
  import { parse as parseYaml2 } from "yaml";
@@ -13592,7 +13730,7 @@ async function signOutSessions(o) {
13592
13730
  }
13593
13731
 
13594
13732
  // src/reset.ts
13595
- 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";
13596
13734
  import { dirname as dirname16, join as join35 } from "path";
13597
13735
  import { hostFromUrl as hostFromUrl3 } from "@tablation/client";
13598
13736
  function clonedCheckoutsFile(stateDir) {
@@ -13665,7 +13803,7 @@ async function runReset(o, fx) {
13665
13803
  }
13666
13804
  if (o.beforeState) steps.push(...await o.beforeState());
13667
13805
  steps.push(...o.wipeState ? wipeStateDir(o.stateDir, o.dryRun) : removeState(o.stateDir, o.dryRun));
13668
- if (existsSync31(o.configFile)) {
13806
+ if (existsSync32(o.configFile)) {
13669
13807
  if (!o.dryRun) rmSync18(o.configFile, { force: true });
13670
13808
  steps.push({ step: "config", target: o.configFile, action: mark(o.dryRun) });
13671
13809
  } else {
@@ -13674,7 +13812,7 @@ async function runReset(o, fx) {
13674
13812
  return steps;
13675
13813
  }
13676
13814
  function removeCheckout(dir, dryRun, fx) {
13677
- 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" };
13678
13816
  const { dirty, linkedWorktrees } = fx.inspectCheckout(dir);
13679
13817
  if (dirty.length) {
13680
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" : ""})` };
@@ -13695,7 +13833,7 @@ function removeState(stateDir, dryRun) {
13695
13833
  } catch {
13696
13834
  }
13697
13835
  for (const t of targets) {
13698
- if (!existsSync31(t)) continue;
13836
+ if (!existsSync32(t)) continue;
13699
13837
  if (!dryRun) rmSync18(t, { recursive: true, force: true });
13700
13838
  out.push({ step: "state", target: t, action: mark(dryRun) });
13701
13839
  }
@@ -14091,13 +14229,13 @@ function hostArchitecture(arch = process.arch) {
14091
14229
 
14092
14230
  // src/pool-member-host.ts
14093
14231
  import "child_process";
14094
- import { existsSync as existsSync35 } from "fs";
14232
+ import { existsSync as existsSync36 } from "fs";
14095
14233
  import { hostname as hostname3 } from "os";
14096
14234
 
14097
14235
  // src/machine-id.ts
14098
14236
  import { execFileSync as execFileSync10 } from "child_process";
14099
14237
  import { createHash as createHash7 } from "crypto";
14100
- 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";
14101
14239
  import { dirname as dirname18, join as join37 } from "path";
14102
14240
  function machineIdPathFor(stateDir) {
14103
14241
  return join37(stateDir, "machine-id");
@@ -14113,7 +14251,7 @@ function readRawMachineId(platform2 = process.platform) {
14113
14251
  return /"IOPlatformUUID"\s*=\s*"([^"]+)"/.exec(out)?.[1];
14114
14252
  }
14115
14253
  for (const p of ["/etc/machine-id", "/var/lib/dbus/machine-id"]) {
14116
- if (existsSync32(p)) {
14254
+ if (existsSync33(p)) {
14117
14255
  const id = readFileSync32(p, "utf8").trim();
14118
14256
  if (id) return id;
14119
14257
  }
@@ -14125,7 +14263,7 @@ function readRawMachineId(platform2 = process.platform) {
14125
14263
  function ensureMachineId(stateDir, opts = {}) {
14126
14264
  if (opts.override?.trim()) return sha256(opts.override.trim());
14127
14265
  const path = machineIdPathFor(stateDir);
14128
- if (existsSync32(path)) {
14266
+ if (existsSync33(path)) {
14129
14267
  const cached = readFileSync32(path, "utf8").trim();
14130
14268
  if (/^[0-9a-f]{64}$/.test(cached)) return cached;
14131
14269
  }
@@ -14146,7 +14284,7 @@ function matchShipRows(ships, shipName2, machineId) {
14146
14284
 
14147
14285
  // src/provision.ts
14148
14286
  import { spawnSync as spawnSync8 } from "child_process";
14149
- 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";
14150
14288
  import { hostname as hostname2 } from "os";
14151
14289
  import { parseDocument as parseDocument3, isMap as isMap2, isScalar } from "yaml";
14152
14290
 
@@ -14740,7 +14878,7 @@ function realProvisionDeps(crewHome) {
14740
14878
  };
14741
14879
  const configFile = () => {
14742
14880
  const f = findConfigFile(crewHome);
14743
- return existsSync33(f) ? f : null;
14881
+ return existsSync34(f) ? f : null;
14744
14882
  };
14745
14883
  return {
14746
14884
  crew: spawnCrew,
@@ -14774,14 +14912,14 @@ function realProvisionDeps(crewHome) {
14774
14912
  continue;
14775
14913
  }
14776
14914
  for (const p of plan.unitPaths) {
14777
- if (!existsSync33(p) || readFileSync33(p, "utf8") !== plan.unitContent[p]) unitsStale = true;
14915
+ if (!existsSync34(p) || readFileSync33(p, "utf8") !== plan.unitContent[p]) unitsStale = true;
14778
14916
  }
14779
14917
  }
14780
14918
  const label = labelFor(crewHome, "run");
14781
14919
  let running = false;
14782
14920
  let lingering = true;
14783
14921
  if (mechanism === "systemd") {
14784
- const system = ctx.root || existsSync33(systemUnitPathFor(label));
14922
+ const system = ctx.root || existsSync34(systemUnitPathFor(label));
14785
14923
  const ctl = system ? ["systemctl"] : ["systemctl", "--user"];
14786
14924
  running = spawnSync8(ctl[0], [...ctl.slice(1), "is-active", "--quiet", `${label}.service`]).status === 0;
14787
14925
  if (!system) {
@@ -14826,7 +14964,7 @@ host=${host}
14826
14964
 
14827
14965
  // src/tunnel.ts
14828
14966
  import { spawn as spawn7 } from "child_process";
14829
- 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";
14830
14968
  import { randomInt } from "crypto";
14831
14969
  import { join as join39 } from "path";
14832
14970
  var DEFAULT_SPAWN_OPTIONS = { stdio: "ignore", detached: true };
@@ -14904,7 +15042,7 @@ function tunnelStatePath(stateDir, workspaceId) {
14904
15042
  }
14905
15043
  function readPersistedTunnel(stateDir, workspaceId) {
14906
15044
  const path = tunnelStatePath(stateDir, workspaceId);
14907
- if (!existsSync34(path)) return void 0;
15045
+ if (!existsSync35(path)) return void 0;
14908
15046
  try {
14909
15047
  return JSON.parse(readFileSync34(path, "utf8"));
14910
15048
  } catch {
@@ -15017,7 +15155,7 @@ function syncPassengerTunnels(plans, relayHost, relayPort, privateKeyPath, state
15017
15155
  }
15018
15156
  function readAllPersistedWorkspaceIds(stateDir) {
15019
15157
  const dir = join39(stateDir, "passengers");
15020
- if (!existsSync34(dir)) return [];
15158
+ if (!existsSync35(dir)) return [];
15021
15159
  return readdirSync13(dir).filter((f) => f.endsWith(".json") && !f.endsWith("-sync.json")).map((f) => f.slice(0, -".json".length));
15022
15160
  }
15023
15161
 
@@ -15042,7 +15180,7 @@ function realPoolMemberDeps(o) {
15042
15180
  crewVersion: crewVersion(o.crewHome),
15043
15181
  hostname: hostname3()
15044
15182
  },
15045
- hasShipConfig: () => existsSync35(findConfigFile(o.crewHome)),
15183
+ hasShipConfig: () => existsSync36(findConfigFile(o.crewHome)),
15046
15184
  async provision(lease, instanceClass, shipKind) {
15047
15185
  const hosts = lease.forges.map((f) => f.host.toLowerCase());
15048
15186
  const lines2 = [];
@@ -16412,7 +16550,7 @@ async function resolveOne(tracker, t, seat, approvedStatus, buildingStatus, affi
16412
16550
  }
16413
16551
 
16414
16552
  // src/repo-refresh.ts
16415
- 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";
16416
16554
  import { dirname as dirname20 } from "path";
16417
16555
  var REFRESH_COOLDOWN_MS = 2 * 6e4;
16418
16556
  var UNSERVABLE_PASSES_BEFORE_ATTENTION = 3;
@@ -16442,7 +16580,7 @@ function applyRepoRows(resolved, rows) {
16442
16580
  }
16443
16581
  function persistRepoRows(stateDir, routeName, rows) {
16444
16582
  const path = resolvedPathFor(stateDir, routeName);
16445
- if (!existsSync36(path)) return;
16583
+ if (!existsSync37(path)) return;
16446
16584
  const raw = JSON.parse(readFileSync35(path, "utf8"));
16447
16585
  applyRepoRows(raw, rows);
16448
16586
  mkdirSync26(dirname20(path), { recursive: true });
@@ -16897,12 +17035,12 @@ var localRefSkipReported = (ticket, problem, events) => events.some((e) => e.rec
16897
17035
 
16898
17036
  // src/connect-wizard.ts
16899
17037
  import { delimiter, dirname as dirname21, join as join41 } from "path";
16900
- 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";
16901
17039
  import { stringify as stringify3 } from "yaml";
16902
17040
  var HOST_PASSENGERS_DOCS_URL = "https://crew.tablation.dev/host-passengers";
16903
17041
  function shouldRunWizard(opts) {
16904
17042
  if (!opts.isTTY) return false;
16905
- const exists = opts.configExists ?? existsSync37;
17043
+ const exists = opts.configExists ?? existsSync38;
16906
17044
  return !configSearchPath(opts.crewHome).some((p) => exists(p));
16907
17045
  }
16908
17046
  function firstRunConfigPath() {
@@ -17096,7 +17234,7 @@ function flagWizardAnswers(f, env) {
17096
17234
  }
17097
17235
 
17098
17236
  // src/release-run.ts
17099
- 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";
17100
17238
  import { dirname as dirname23, join as join43 } from "path";
17101
17239
  import { tmpdir as tmpdir5 } from "os";
17102
17240
 
@@ -17840,7 +17978,7 @@ async function bump(o, size) {
17840
17978
  }
17841
17979
  const files = o.repo.release.versionFiles.map((f) => join43(o.cwd, f));
17842
17980
  const first = files[0];
17843
- if (!existsSync38(first)) {
17981
+ if (!existsSync39(first)) {
17844
17982
  o.emit.error(`no ${o.repo.release.versionFiles[0]} to read a version from`);
17845
17983
  return null;
17846
17984
  }
@@ -17876,7 +18014,7 @@ function writeChangelog(o, version, entries) {
17876
18014
  o.emit.emit(`would prepend ${entries.length} entr(ies) to ${file}`);
17877
18015
  return;
17878
18016
  }
17879
- const existing = existsSync38(path) ? readFileSync36(path, "utf8") : "# Changelog\n";
18017
+ const existing = existsSync39(path) ? readFileSync36(path, "utf8") : "# Changelog\n";
17880
18018
  writeFileSync31(path, insertChangelogSection(existing, section));
17881
18019
  }
17882
18020
  async function confirm(o, expected) {
@@ -17982,7 +18120,7 @@ function openReleaseCheckout(o) {
17982
18120
  }
17983
18121
  for (const f of o.repo.worktrees.copy) {
17984
18122
  const src = join43(o.cwd, f);
17985
- if (!existsSync38(src)) continue;
18123
+ if (!existsSync39(src)) continue;
17986
18124
  mkdirSync28(dirname23(join43(dir, f)), { recursive: true });
17987
18125
  copyFileSync(src, join43(dir, f));
17988
18126
  }
@@ -18963,7 +19101,7 @@ function missingReleaserWarning(repo, release, ships, now = Date.now()) {
18963
19101
  }
18964
19102
 
18965
19103
  // src/watch.ts
18966
- 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";
18967
19105
  var E = "\x1B[";
18968
19106
  var dim = (s) => `${E}2m${s}${E}0m`;
18969
19107
  var bold = (s) => `${E}1m${s}${E}0m`;
@@ -19064,7 +19202,7 @@ function renderHeaders(states, now = Date.now()) {
19064
19202
  return states.map((s) => renderHeader(s, now)).join("");
19065
19203
  }
19066
19204
  function readFrom(file, from) {
19067
- if (!existsSync39(file)) return { events: [], offset: from };
19205
+ if (!existsSync40(file)) return { events: [], offset: from };
19068
19206
  const size = statSync11(file).size;
19069
19207
  if (size < from) return readFrom(file, 0);
19070
19208
  if (size === from) return { events: [], offset: from };
@@ -19307,7 +19445,7 @@ function nextRoles(decision, ran) {
19307
19445
  }
19308
19446
 
19309
19447
  // src/ship-name.ts
19310
- 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";
19311
19449
  import { dirname as dirname24 } from "path";
19312
19450
  import { parseDocument as parseDocument5 } from "yaml";
19313
19451
  var ShipNameError = class extends Error {
@@ -19369,7 +19507,7 @@ async function syncRouteName(i) {
19369
19507
  return { status: "ok", ...base, action, name: i.local };
19370
19508
  }
19371
19509
  function readSyncedName(resolvedFile) {
19372
- if (!existsSync40(resolvedFile)) return void 0;
19510
+ if (!existsSync41(resolvedFile)) return void 0;
19373
19511
  try {
19374
19512
  const v = JSON.parse(readFileSync37(resolvedFile, "utf8")).shipName;
19375
19513
  return typeof v === "string" ? v : void 0;
@@ -19378,7 +19516,7 @@ function readSyncedName(resolvedFile) {
19378
19516
  }
19379
19517
  }
19380
19518
  function writeSyncedName(resolvedFile, name) {
19381
- if (!existsSync40(resolvedFile)) return;
19519
+ if (!existsSync41(resolvedFile)) return;
19382
19520
  let raw;
19383
19521
  try {
19384
19522
  raw = JSON.parse(readFileSync37(resolvedFile, "utf8"));
@@ -20080,7 +20218,7 @@ function describeCapability(i) {
20080
20218
  }
20081
20219
 
20082
20220
  // src/agents-sync-run.ts
20083
- 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";
20084
20222
  import { dirname as dirname25 } from "path";
20085
20223
  async function runAgentsSync(target, o) {
20086
20224
  const dryRun = o.dryRun ?? false;
@@ -20114,7 +20252,7 @@ async function runAgentsSync(target, o) {
20114
20252
  for (const s of skillResult?.outcomes ?? []) o.say(describeSkillSyncOutcome(s));
20115
20253
  if (!dryRun) {
20116
20254
  const resolvedPath = resolvedPathFor(o.stateDir, target.route);
20117
- const raw = existsSync41(resolvedPath) ? JSON.parse(readFileSync38(resolvedPath, "utf8")) : {};
20255
+ const raw = existsSync42(resolvedPath) ? JSON.parse(readFileSync38(resolvedPath, "utf8")) : {};
20118
20256
  raw.agentPersonas = personaResult.agentPersonas;
20119
20257
  if (skillResult) raw.agentSkills = skillResult.agentSkills;
20120
20258
  if (personaResult.releaseAgentId) raw.releaseAgentId = personaResult.releaseAgentId;
@@ -20809,7 +20947,7 @@ function planSampleTicket(i) {
20809
20947
  }
20810
20948
 
20811
20949
  // src/worktree-sweep.ts
20812
- 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";
20813
20951
  import { basename as basename13, dirname as dirname27, join as join45, resolve as resolve5 } from "path";
20814
20952
  function planWorktreeSweep(target, tickets, contract) {
20815
20953
  const terminal = new Set(contract.statuses.resolved.filter((s) => s !== contract.statuses.verified));
@@ -20922,7 +21060,7 @@ async function applyWorktreeSweep(cwd, actions, dryRun, log, remote = "origin",
20922
21060
  let removed = 0;
20923
21061
  let keptBranches = 0;
20924
21062
  for (const a of actions) {
20925
- 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)) {
20926
21064
  if (gitOk(a.path, ["merge", "--abort"]) !== null) {
20927
21065
  log.emit(
20928
21066
  `abandoned the hand-back merge of ${base} in ${a.path}: ${a.ticket.issue_id} deployed without it`,
@@ -20930,7 +21068,7 @@ async function applyWorktreeSweep(cwd, actions, dryRun, log, remote = "origin",
20930
21068
  );
20931
21069
  }
20932
21070
  }
20933
- if (!dryRun && existsSync42(a.path)) {
21071
+ if (!dryRun && existsSync43(a.path)) {
20934
21072
  let dirty = false;
20935
21073
  try {
20936
21074
  dirty = status(a.path).length > 0;
@@ -20941,7 +21079,7 @@ async function applyWorktreeSweep(cwd, actions, dryRun, log, remote = "origin",
20941
21079
  continue;
20942
21080
  }
20943
21081
  }
20944
- 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)) {
20945
21083
  refuse2(a, "unpushed commits", `${a.branch} has commits that are not on ${remote}`);
20946
21084
  continue;
20947
21085
  }
@@ -21095,12 +21233,12 @@ function applyRemoteBranchCleanup(cwd, remote, branches2, dryRun, log) {
21095
21233
  }
21096
21234
 
21097
21235
  // src/stream-sweep.ts
21098
- 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";
21099
21237
  import { join as join46 } from "path";
21100
21238
  var DAY_MS2 = 24 * 60 * 60 * 1e3;
21101
21239
  function planStreamSweep(stateDir, retentionDays, now = Date.now()) {
21102
21240
  const dir = join46(stateDir, "streams");
21103
- if (!existsSync43(dir)) return [];
21241
+ if (!existsSync44(dir)) return [];
21104
21242
  const cutoff = now - retentionDays * DAY_MS2;
21105
21243
  return readdirSync15(dir).map((name) => join46(dir, name)).filter((path) => {
21106
21244
  try {
@@ -21128,7 +21266,7 @@ function applyStreamSweep(paths, dryRun, log) {
21128
21266
  return removed;
21129
21267
  }
21130
21268
  function planEventsSweep(stateDir, retentionDays, now = Date.now()) {
21131
- if (!existsSync43(stateDir)) return [];
21269
+ if (!existsSync44(stateDir)) return [];
21132
21270
  const cutoff = now - retentionDays * DAY_MS2;
21133
21271
  return readdirSync15(stateDir).filter((name) => ROTATED_EVENTS_RE.test(name)).map((name) => join46(stateDir, name)).filter((path) => {
21134
21272
  try {
@@ -21140,11 +21278,11 @@ function planEventsSweep(stateDir, retentionDays, now = Date.now()) {
21140
21278
  }
21141
21279
 
21142
21280
  // src/cli.ts
21143
- 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";
21144
21282
  import { createInterface } from "readline/promises";
21145
21283
 
21146
21284
  // src/periscope-cmd.ts
21147
- 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";
21148
21286
  import { join as join47 } from "path";
21149
21287
  var PeriscopeError = class extends Error {
21150
21288
  };
@@ -21170,7 +21308,7 @@ function writePeriscopePrior(stateDir, ticketRecordId, prior) {
21170
21308
  function readPeriscopePrior(stateDir, ticketRecordId) {
21171
21309
  try {
21172
21310
  const p = priorPath(stateDir, ticketRecordId);
21173
- return existsSync44(p) ? JSON.parse(readFileSync41(p, "utf8")) : void 0;
21311
+ return existsSync45(p) ? JSON.parse(readFileSync41(p, "utf8")) : void 0;
21174
21312
  } catch {
21175
21313
  return void 0;
21176
21314
  }
@@ -21725,7 +21863,7 @@ import { dirname as dirOf2, resolve as resolvePath } from "path";
21725
21863
 
21726
21864
  // src/ssh-keys.ts
21727
21865
  import { execFileSync as execFileSync13 } from "child_process";
21728
- 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";
21729
21867
  import { dirname as dirname28, join as join49 } from "path";
21730
21868
  var SshKeyError = class extends Error {
21731
21869
  };
@@ -21735,7 +21873,7 @@ function shipSshKeyPathFor(stateDir) {
21735
21873
  function ensureShipSshKeypair(stateDir) {
21736
21874
  const privateKeyPath = shipSshKeyPathFor(stateDir);
21737
21875
  const publicKeyPath = `${privateKeyPath}.pub`;
21738
- if (!existsSync45(privateKeyPath)) {
21876
+ if (!existsSync46(privateKeyPath)) {
21739
21877
  mkdirSync32(dirname28(privateKeyPath), { recursive: true });
21740
21878
  try {
21741
21879
  execFileSync13("ssh-keygen", [
@@ -21756,7 +21894,7 @@ function ensureShipSshKeypair(stateDir) {
21756
21894
  }
21757
21895
  chmodSync3(privateKeyPath, 384);
21758
21896
  }
21759
- if (!existsSync45(publicKeyPath)) {
21897
+ if (!existsSync46(publicKeyPath)) {
21760
21898
  throw new SshKeyError(`${privateKeyPath} exists but ${publicKeyPath} does not \u2014 regenerate or restore it`);
21761
21899
  }
21762
21900
  const publicKey = readFileSync42(publicKeyPath, "utf8").trim();
@@ -21923,7 +22061,7 @@ async function unregisterEndpoint(workspaceId, info, d) {
21923
22061
  import { createServer as createHttpServer } from "http";
21924
22062
  import { spawn as spawn9 } from "child_process";
21925
22063
  import { randomBytes } from "crypto";
21926
- 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";
21927
22065
  import { join as join50 } from "path";
21928
22066
 
21929
22067
  // src/passenger-mirror.ts
@@ -22058,7 +22196,7 @@ function syncDaemonStatePath(stateDir, workspaceId) {
22058
22196
  }
22059
22197
  function readPersistedSyncDaemon(stateDir, workspaceId) {
22060
22198
  const path = syncDaemonStatePath(stateDir, workspaceId);
22061
- if (!existsSync46(path)) return void 0;
22199
+ if (!existsSync47(path)) return void 0;
22062
22200
  try {
22063
22201
  return JSON.parse(readFileSync43(path, "utf8"));
22064
22202
  } catch {
@@ -22089,7 +22227,7 @@ function waitForExit(pid, alive, timeoutMs = 5e3) {
22089
22227
  }
22090
22228
  function readAllPersistedSyncWorkspaceIds(stateDir) {
22091
22229
  const dir = join50(stateDir, "passengers");
22092
- if (!existsSync46(dir)) return [];
22230
+ if (!existsSync47(dir)) return [];
22093
22231
  const suffix = "-sync.json";
22094
22232
  return readdirSync16(dir).filter((f) => f.endsWith(suffix)).map((f) => f.slice(0, -suffix.length));
22095
22233
  }
@@ -22586,7 +22724,7 @@ function describeCause(err3) {
22586
22724
  }
22587
22725
 
22588
22726
  // src/daemon.ts
22589
- 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";
22590
22728
  import { join as join51 } from "path";
22591
22729
  import { randomBytes as randomBytes2 } from "crypto";
22592
22730
  var DEFAULT_FLOOR_MS = 5e3;
@@ -22684,7 +22822,7 @@ function computeTreeSignature(paths) {
22684
22822
  }
22685
22823
  function makeStaleChecker(paths, opts = {}) {
22686
22824
  const initial = computeTreeSignature(paths);
22687
- return () => computeTreeSignature(paths) !== initial || opts.reloadSentinel !== void 0 && existsSync47(opts.reloadSentinel);
22825
+ return () => computeTreeSignature(paths) !== initial || opts.reloadSentinel !== void 0 && existsSync48(opts.reloadSentinel);
22688
22826
  }
22689
22827
  function reloadSentinelFor(stateDir) {
22690
22828
  return join51(stateDir, "reload-requested");
@@ -23273,7 +23411,7 @@ import { createHash as createHash10 } from "crypto";
23273
23411
  import {
23274
23412
  chmodSync as chmodSync4,
23275
23413
  closeSync as closeSync6,
23276
- existsSync as existsSync48,
23414
+ existsSync as existsSync49,
23277
23415
  fstatSync,
23278
23416
  openSync as openSync6,
23279
23417
  readSync as readSync3,
@@ -23644,7 +23782,7 @@ function lastLines2(path, n) {
23644
23782
  }
23645
23783
  function readTail(live, opts) {
23646
23784
  const max = opts.maxBytes ?? MAX_TAIL_BYTES;
23647
- if (!existsSync48(live)) {
23785
+ if (!existsSync49(live)) {
23648
23786
  return { lines: [], cursor: opts.after ?? encodeCursor({ dev: 0, ino: 0, off: 0 }), reset: false };
23649
23787
  }
23650
23788
  const cur = opts.after ? decodeCursor(opts.after) : null;
@@ -23801,7 +23939,7 @@ async function route(deps, method, url, req, abort) {
23801
23939
  const run3 = decodeURIComponent(p.slice("/streams/".length));
23802
23940
  if (!RUN_NAME_RE.test(run3) || run3.includes("..") || /\.(pid|meta\.json|events\.jsonl)$/.test(run3)) return err2(400, "not a run name");
23803
23941
  const file = join52(deps.stateDir, "streams", run3.endsWith(".jsonl") ? run3 : `${run3}.jsonl`);
23804
- if (!existsSync48(file)) return err2(404, "no such run");
23942
+ if (!existsSync49(file)) return err2(404, "no such run");
23805
23943
  return tailReply(file, url.searchParams, false, abort);
23806
23944
  }
23807
23945
  if ((p === "/pause" || p === "/resume") && post3) {
@@ -23845,7 +23983,7 @@ function send(res, r) {
23845
23983
  res.end(body);
23846
23984
  }
23847
23985
  async function socketIsStale(path) {
23848
- if (!existsSync48(path)) return false;
23986
+ if (!existsSync49(path)) return false;
23849
23987
  return new Promise((resolve8) => {
23850
23988
  const c = createConnection(path);
23851
23989
  c.once("connect", () => {
@@ -23864,7 +24002,7 @@ async function startControlServer(deps) {
23864
24002
  return null;
23865
24003
  }
23866
24004
  if (await socketIsStale(path)) rmSync23(path, { force: true });
23867
- else if (existsSync48(path)) {
24005
+ else if (existsSync49(path)) {
23868
24006
  log(`control API not started: ${path} is in use by another process`);
23869
24007
  return null;
23870
24008
  }
@@ -23957,7 +24095,7 @@ import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/
23957
24095
 
23958
24096
  // src/periscope-endpoints.ts
23959
24097
  import { randomBytes as randomBytes3 } from "crypto";
23960
- 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";
23961
24099
  import { join as join53 } from "path";
23962
24100
  var secretDir = (stateDir) => join53(stateDir, "periscope-mcp");
23963
24101
  var secretFile = (stateDir, id) => join53(secretDir(stateDir), `${id}.json`);
@@ -24000,7 +24138,7 @@ function removePeriscopeEndpointRecord(stateDir, ticketRecordId) {
24000
24138
  }
24001
24139
  function listPeriscopeEndpoints(stateDir) {
24002
24140
  const dir = endpointDir(stateDir);
24003
- if (!existsSync49(dir)) return [];
24141
+ if (!existsSync50(dir)) return [];
24004
24142
  const out = [];
24005
24143
  for (const name of readdirSync19(dir)) {
24006
24144
  if (!name.endsWith(".json")) continue;
@@ -24364,7 +24502,7 @@ function findPeriscope(sidecars, segment) {
24364
24502
  }
24365
24503
 
24366
24504
  // src/cli.ts
24367
- 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)), "..");
24368
24506
  var RELEASE_LOCK_TTL_MS = 20 * 60 * 1e3;
24369
24507
  var CYCLE_SECONDS2 = 120;
24370
24508
  var STALE_CYCLES = 3;
@@ -24977,7 +25115,7 @@ if (command === "passenger-sync-daemon") {
24977
25115
  } else {
24978
25116
  const toleratesNoRoutes = command === "credential" || command === "forge" || command === "repos" || command === "ship" || command === "disconnect" && flag("reset") || command === "deprovision" || command === "connect" && flag("sign-out");
24979
25117
  const configless = command === "connect" && flag("sign-out") || command === "disconnect" && flag("reset") || command === "deprovision";
24980
- if (configless && !existsSync50(findConfigFile(CREW_HOME))) {
25118
+ if (configless && !existsSync51(findConfigFile(CREW_HOME))) {
24981
25119
  cfg = loadConfig(CREW_HOME, firstRunConfigPath(), { text: "ship: {}\n", allowNoRoutes: true });
24982
25120
  } else {
24983
25121
  cfg = loadConfig(CREW_HOME, void 0, toleratesNoRoutes ? { allowNoRoutes: true } : {});
@@ -25300,7 +25438,7 @@ if (command === "passenger-sync-daemon") {
25300
25438
  if (host === "linux" && hasSystemd) {
25301
25439
  const label = labelFor(CREW_HOME, "run");
25302
25440
  const staleTimer = join55(homedir11(), ".config", "systemd", "user", `${label}.timer`);
25303
- if (existsSync50(staleTimer)) {
25441
+ if (existsSync51(staleTimer)) {
25304
25442
  process.stdout.write(
25305
25443
  `removing stale run timer from before the persistent-service cutover: ${staleTimer}${dryRun ? " (dry run)" : ""}
25306
25444
  `
@@ -25327,7 +25465,7 @@ if (command === "passenger-sync-daemon") {
25327
25465
  const tracker = new Tracker(c, cfg.ship);
25328
25466
  problems.push(...validateContract(tracker.contract).map((p) => `contract: ${p}`));
25329
25467
  for (const r of await resolvedRepos(c)) {
25330
- 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`);
25331
25469
  if (!satisfies(host, r.config.platform)) problems.push(`repo ${r.name}: requires ${r.config.platform}, this host is ${host}`);
25332
25470
  readiness.push(...validateEffective(r.config).map((p) => `repo ${r.name}: ${p}`));
25333
25471
  }
@@ -25354,7 +25492,7 @@ if (command === "passenger-sync-daemon") {
25354
25492
  }
25355
25493
  async function doctorFix(o) {
25356
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))));
25357
- const unitExists = (p) => existsSync50(p);
25495
+ const unitExists = (p) => existsSync51(p);
25358
25496
  const crontab = o.host === "linux" ? spawnSync11("crontab", ["-l"], { encoding: "utf8" }).stdout ?? "" : "";
25359
25497
  const schedulerAbsent = o.host !== "windows" ? !schedulerInstalled(planInstall(cfg.ship, CREW_HOME, o.host, detectSystemd(), "run", installContext()), unitExists, crontab) : true;
25360
25498
  const plan = planFix({ routes, schedulerAbsent, host: o.host });
@@ -25400,7 +25538,7 @@ ${changed2.map((c) => ` - ${c}
25400
25538
  warm = readWarmFiles(r.dir, `${r.config.branch.remote}/${r.config.branch.base}`, r.config.sandbox.warm ?? []);
25401
25539
  } catch {
25402
25540
  }
25403
- out.set(r.name, { name: r.name, decl: 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() } : {} });
25404
25542
  }
25405
25543
  }
25406
25544
  return [...out.values()];
@@ -26815,7 +26953,7 @@ ${cu.tail.trimEnd()}
26815
26953
  `);
26816
26954
  if (!dryRun) {
26817
26955
  const resolvedPath = resolvedPathFor(cfg.ship.stateDir, target.route);
26818
- const raw = existsSync50(resolvedPath) ? JSON.parse(readFileSync46(resolvedPath, "utf8")) : {};
26956
+ const raw = existsSync51(resolvedPath) ? JSON.parse(readFileSync46(resolvedPath, "utf8")) : {};
26819
26957
  raw.agentSkills = result.agentSkills;
26820
26958
  mkdirSync38(dirOf2(resolvedPath), { recursive: true });
26821
26959
  writeFileSync38(resolvedPath, `${JSON.stringify(raw, null, 2)}
@@ -26920,8 +27058,8 @@ ${cu.tail.trimEnd()}
26920
27058
  try {
26921
27059
  const a = parseHooksArgs(positional.slice(2), routeNamesOf(configText));
26922
27060
  const dir = repoDirOf(configText, dirname33(cfg.configFile), a.route, a.name);
26923
- if (!existsSync50(dir)) throw new ReposError(`${dir} does not exist on disk`);
26924
- 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) => {
26925
27063
  try {
26926
27064
  return readdirSync20(resolvePath(dir, r));
26927
27065
  } catch {
@@ -27039,7 +27177,7 @@ ${cu.tail.trimEnd()}
27039
27177
  if (sub === "list" && !routeName) usage2();
27040
27178
  if (sub === "add") {
27041
27179
  try {
27042
- 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()))));
27043
27181
  routeName = a.route;
27044
27182
  addPath = a.path;
27045
27183
  addName = a.name;
@@ -27069,7 +27207,7 @@ ${cu.tail.trimEnd()}
27069
27207
  const resolvedFile = resolvedPathFor(cfg.ship.stateDir, routeName);
27070
27208
  if (sub === "list" || sub === "add") {
27071
27209
  const liveRoute = cfg.routes.find((r) => r.route === routeName);
27072
- if (liveRoute?.resolved?.reposModelId && existsSync50(resolvedFile) && !dryRun) {
27210
+ if (liveRoute?.resolved?.reposModelId && existsSync51(resolvedFile) && !dryRun) {
27073
27211
  try {
27074
27212
  await hydrateApiKeys([liveRoute]);
27075
27213
  persistRepoRows(cfg.ship.stateDir, liveRoute.route, await new Tracker(liveRoute, cfg.ship).repoRowsForMap());
@@ -27078,7 +27216,7 @@ ${cu.tail.trimEnd()}
27078
27216
  }
27079
27217
  }
27080
27218
  let resolvedRemotes;
27081
- if (existsSync50(resolvedFile)) {
27219
+ if (existsSync51(resolvedFile)) {
27082
27220
  try {
27083
27221
  resolvedRemotes = JSON.parse(readFileSync46(resolvedFile, "utf8"));
27084
27222
  } catch {
@@ -27087,7 +27225,7 @@ ${cu.tail.trimEnd()}
27087
27225
  const originOf = (dir) => gitOk(dir, ["remote", "get-url", "origin"]) ?? void 0;
27088
27226
  const text2 = configText;
27089
27227
  const base = dirname33(cfg.configFile);
27090
- const isCheckout = (d) => existsSync50(`${d}/.git`);
27228
+ const isCheckout = (d) => existsSync51(`${d}/.git`);
27091
27229
  try {
27092
27230
  if (sub === "list") {
27093
27231
  if (flag("json")) {
@@ -27097,7 +27235,7 @@ ${cu.tail.trimEnd()}
27097
27235
  base,
27098
27236
  resolved: resolvedRemotes,
27099
27237
  originOf,
27100
- exists: (d) => existsSync50(d),
27238
+ exists: (d) => existsSync51(d),
27101
27239
  isCheckout,
27102
27240
  repoHasHooks: (d) => REPO_CONFIG_FILENAMES.some((f) => {
27103
27241
  try {
@@ -27118,7 +27256,7 @@ ${cu.tail.trimEnd()}
27118
27256
  for (const r of repos) {
27119
27257
  const origin2 = originOf(r.dir);
27120
27258
  const row = matchRepoName(origin2, resolvedRemotes);
27121
- 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)"}`;
27122
27260
  process.stdout.write(`${r.name} ${r.dir} ${note}
27123
27261
  `);
27124
27262
  }
@@ -27130,9 +27268,9 @@ ${cu.tail.trimEnd()}
27130
27268
  ` : `${human}
27131
27269
  `);
27132
27270
  const plan2 = planRepoClone({ text: text2, base, route: routeName, name: addName, resolved: resolvedRemotes, dir: value("dir"), isCheckout });
27133
- 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)) {
27134
27272
  throw new ReposError(
27135
- 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`
27136
27274
  );
27137
27275
  }
27138
27276
  if (dryRun) {
@@ -27157,7 +27295,7 @@ ${cu.tail.trimEnd()}
27157
27295
  break;
27158
27296
  }
27159
27297
  const target = resolvePath(addPath.replace(/^~(?=\/|$)/, homedir11()));
27160
- const top = existsSync50(target) ? gitOk(target, ["rev-parse", "--show-toplevel"]) : null;
27298
+ const top = existsSync51(target) ? gitOk(target, ["rev-parse", "--show-toplevel"]) : null;
27161
27299
  if (!top) throw new ReposError(`${target} is not a git checkout`);
27162
27300
  const origin = originOf(top);
27163
27301
  const plan = planRepoAdd({
@@ -27209,7 +27347,7 @@ ${cu.tail.trimEnd()}
27209
27347
  const created = await rowTracker.createRepoRow(rowPayload);
27210
27348
  process.stdout.write(`created Repos row ${String(rowPayload.name)} (${String(rowPayload.remote)}) ${created.id}
27211
27349
  `);
27212
- if (existsSync50(resolvedFile)) {
27350
+ if (existsSync51(resolvedFile)) {
27213
27351
  const raw = JSON.parse(readFileSync46(resolvedFile, "utf8"));
27214
27352
  raw.repoNames = { ...raw.repoNames, [created.id]: String(rowPayload.name) };
27215
27353
  raw.repoRemotes = { ...raw.repoRemotes, [created.id]: String(rowPayload.remote) };
@@ -27631,7 +27769,7 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
27631
27769
  }
27632
27770
  }
27633
27771
  let previous;
27634
- const hadPreviousFile = existsSync50(resolvedPath);
27772
+ const hadPreviousFile = existsSync51(resolvedPath);
27635
27773
  if (hadPreviousFile) {
27636
27774
  try {
27637
27775
  previous = JSON.parse(readFileSync46(resolvedPath, "utf8"));
@@ -28163,7 +28301,7 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
28163
28301
  if (subS === "images") {
28164
28302
  const stateS = readSandboxState(cfg.ship.stateDir);
28165
28303
  const rows = declared.map((r) => {
28166
- const plan = planSandbox(r.name, r.decl, r.warm);
28304
+ const plan = planSandbox(r.name, r.decl, r.warm, r.bridge);
28167
28305
  return { repo: r.name, wants: plan.tag, tags: stateS[r.name]?.tags ?? [], ...sandboxImageStatus(stateS, plan) };
28168
28306
  });
28169
28307
  if (jsonS) {
@@ -28197,7 +28335,7 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
28197
28335
  for (const t of targets) {
28198
28336
  await sandboxBuildTick([t], depsS, true);
28199
28337
  const after = readSandboxState(cfg.ship.stateDir);
28200
- 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));
28201
28339
  if (st.status === "failed") failed = true;
28202
28340
  process.stdout.write(`${t.name}: ${st.status} \u2014 ${st.detail}
28203
28341
  `);
@@ -28509,7 +28647,7 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
28509
28647
  prepare: async (path) => {
28510
28648
  for (const f of repoWt.config.worktrees.copy) {
28511
28649
  const src = join55(repoWt.dir, f);
28512
- if (!existsSync50(src) || existsSync50(join55(path, f))) continue;
28650
+ if (!existsSync51(src) || existsSync51(join55(path, f))) continue;
28513
28651
  mkdirSync38(dirOf2(join55(path, f)), { recursive: true });
28514
28652
  copyFileSync2(src, join55(path, f));
28515
28653
  }
@@ -28731,7 +28869,7 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
28731
28869
  const shellR = hit && resolveShell(hit.repo.config.shell ?? cfg.ship.shell);
28732
28870
  const resumed = await runPeriscopeResume({
28733
28871
  find: async () => hit && { row: hit.row, tag: hit.tag },
28734
- worktreeExists: (p) => existsSync50(p),
28872
+ worktreeExists: (p) => existsSync51(p),
28735
28873
  prepareDropFile,
28736
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" }),
28737
28875
  caps: periscopeCaps(cfg.ship),
@@ -28828,7 +28966,7 @@ ${where.join("\n")}`);
28828
28966
  prepare: async (path) => {
28829
28967
  for (const f of repoP.config.worktrees.copy) {
28830
28968
  const src = join55(repoP.dir, f);
28831
- if (!existsSync50(src)) continue;
28969
+ if (!existsSync51(src)) continue;
28832
28970
  mkdirSync38(dirOf2(join55(path, f)), { recursive: true });
28833
28971
  copyFileSync2(src, join55(path, f));
28834
28972
  }
@@ -28845,7 +28983,8 @@ ${where.join("\n")}`);
28845
28983
  route: routeP.route,
28846
28984
  ticket: Number(/(\d+)$/.exec(env.CREW_TICKET ?? "")?.[1] ?? 0),
28847
28985
  worktree: cwd,
28848
- mainCheckout: repoP.dir
28986
+ mainCheckout: repoP.dir,
28987
+ languageServers: process.env.CREW_SERENA_LANGUAGE_SERVERS
28849
28988
  });
28850
28989
  }
28851
28990
  if (name === "intel" && lspFlavor(script)) return null;
@@ -29319,7 +29458,7 @@ ${where.join("\n")}`);
29319
29458
  }
29320
29459
  if (row) {
29321
29460
  await tracker.deleteRepoRow(row.id);
29322
- if (existsSync50(resolvedFile)) {
29461
+ if (existsSync51(resolvedFile)) {
29323
29462
  const raw = JSON.parse(readFileSync46(resolvedFile, "utf8"));
29324
29463
  if (raw.repoNames) delete raw.repoNames[row.id];
29325
29464
  if (raw.repoRemotes) delete raw.repoRemotes[row.id];
@@ -29327,13 +29466,13 @@ ${where.join("\n")}`);
29327
29466
  `);
29328
29467
  }
29329
29468
  }
29330
- 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 });
29331
29470
  say("removed", { dir, closed }, `removed the sample repo${closed.length ? `, closed ${closed.join(", ")}` : ""}`);
29332
29471
  break;
29333
29472
  }
29334
29473
  const projectId = route2.resolved.areaId ?? resolveRepoProject(await tracker.projectRows(), value("project"));
29335
- const fresh = !existsSync50(join55(dir, ".git"));
29336
- 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`);
29337
29476
  if (dryRun) {
29338
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`);
29339
29478
  break;
@@ -29359,7 +29498,7 @@ ${where.join("\n")}`);
29359
29498
  spec: { platform: "unix", releaseMode: "integrate", ciProvider: "none" }
29360
29499
  });
29361
29500
  repoId = (await tracker.createRepoRow(payload)).id;
29362
- if (existsSync50(resolvedFile)) {
29501
+ if (existsSync51(resolvedFile)) {
29363
29502
  const raw = JSON.parse(readFileSync46(resolvedFile, "utf8"));
29364
29503
  raw.repoNames = { ...raw.repoNames, [repoId]: SAMPLE_REPO };
29365
29504
  raw.repoRemotes = { ...raw.repoRemotes, [repoId]: SAMPLE_REMOTE };
@@ -29715,7 +29854,7 @@ watermark: ${state.watermark()}
29715
29854
  }
29716
29855
  for (const r of repos) {
29717
29856
  const remote = r.config.branch.remote;
29718
- if (existsSync50(r.dir)) {
29857
+ if (existsSync51(r.dir)) {
29719
29858
  pushAuth.push({
29720
29859
  route: c.route,
29721
29860
  repo: r.name,
@@ -29811,7 +29950,7 @@ routes: ${cfg.routes.map((c) => c.route).join(", ")}
29811
29950
  }
29812
29951
  try {
29813
29952
  const evFile = eventFileFor(cfg.ship.stateDir);
29814
- const evSize = existsSync50(evFile) ? statSync16(evFile).size : 0;
29953
+ const evSize = existsSync51(evFile) ? statSync16(evFile).size : 0;
29815
29954
  process.stdout.write(
29816
29955
  evSize > cfg.ship.eventsRotateBytes * 4 ? `events log: WARN ${evFile} is ${Math.round(evSize / 1048576)} MB (> 4x eventsRotateBytes); run \`crew reap\` to rotate it
29817
29956
  ` : "events log: ok\n"
@@ -30275,7 +30414,7 @@ ${foreign.join("\n")}
30275
30414
  const declaredS = await sandboxReposOf(cfg.routes.filter((r) => r.enabled));
30276
30415
  const stateS = readSandboxState(cfg.ship.stateDir);
30277
30416
  for (const r of declaredS) {
30278
- 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));
30279
30418
  process.stdout.write(`sandbox image: ${r.name}: ${st.status.toUpperCase()} \u2014 ${st.detail}
30280
30419
  `);
30281
30420
  }