@tablation/crew 0.125.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
@@ -588,7 +588,7 @@ function startSerena(d, s) {
588
588
  const dest = join5(p.home, "language_servers");
589
589
  if (!d.exists(dest)) d.link(s.languageServers, dest);
590
590
  }
591
- const mainCache = join5(p.mainProject, "cache");
591
+ const mainCache = s.seedCache ?? join5(p.mainProject, "cache");
592
592
  const cache = join5(p.project, "cache");
593
593
  if (!d.exists(cache) && d.exists(mainCache)) d.copyDir(mainCache, cache);
594
594
  try {
@@ -11001,10 +11001,14 @@ function codeBridgeFor(o, harness) {
11001
11001
  const number = /(\d+)$/.exec(o.ticket)?.[1];
11002
11002
  if (!number) return void 0;
11003
11003
  let intel;
11004
+ let seed;
11004
11005
  try {
11005
11006
  const repos = reposOf(o.route);
11006
11007
  const target = repos.find((r) => r.name === o.repo) ?? (repos.length === 1 ? repos[0] : void 0);
11007
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`;
11008
11012
  } catch {
11009
11013
  }
11010
11014
  if (!wantsCodeBridge(intel)) return void 0;
@@ -11020,7 +11024,8 @@ function codeBridgeFor(o, harness) {
11020
11024
  "--state-dir",
11021
11025
  dirname13(SANDBOX_INTEL_DIR),
11022
11026
  "--ticket",
11023
- number
11027
+ number,
11028
+ ...seed ? ["--seed-cache", seed] : []
11024
11029
  ] : [...process.execArgv, process.argv[1] ?? "crew", "intel", "bridge", o.route.route, number, "--state-dir", o.stateDir];
11025
11030
  const bridge = { command: sandbox ? "node" : process.execPath, args };
11026
11031
  if (harness === "gemini") {
@@ -12345,6 +12350,9 @@ function mirrorRelPath(r) {
12345
12350
  function depsRelDir(r) {
12346
12351
  return `cache/deps/${r.host}/${r.path}`;
12347
12352
  }
12353
+ function intelRelDir(r) {
12354
+ return `intel/${r.host}/${r.path}`;
12355
+ }
12348
12356
  var q = (s) => `'${s.replace(/'/g, `'\\''`)}'`;
12349
12357
  function cloneScript(o) {
12350
12358
  const mirror = `${o.mirrorDir}/${mirrorRelPath(o.repo)}`;
@@ -12395,6 +12403,30 @@ find . -name node_modules -type d -prune | while read -r p; do
12395
12403
  done
12396
12404
  rm -rf "$droot/$h"; mv "$droot/$h.tmp" "$droot/$h"
12397
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
+ `;
12398
12430
  var MATERIALIZE_SCRIPT = `
12399
12431
  set -e
12400
12432
  export GIT_TERMINAL_PROMPT=0
@@ -12473,6 +12505,14 @@ var SandboxCache = class {
12473
12505
  continue;
12474
12506
  }
12475
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
+ }
12476
12516
  out.push({ repo: r, ok: true });
12477
12517
  }
12478
12518
  return out;
@@ -12498,13 +12538,13 @@ var SandboxCache = class {
12498
12538
  } catch {
12499
12539
  return [];
12500
12540
  }
12501
- 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`;
12502
12542
  const { stdout } = await this.helper(this.s.helperImage, script, []);
12503
12543
  const rows = [];
12504
12544
  for (const line of stdout.split("\n").filter(Boolean)) {
12505
- const [rel, when] = line.split(" ");
12545
+ const [rel, when, commit, indexedAt] = line.split(" ");
12506
12546
  const m = /^([^/]+)\/(.+)\.git$/.exec(rel ?? "");
12507
- 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 } } : {} });
12508
12548
  }
12509
12549
  return rows;
12510
12550
  }
@@ -12513,7 +12553,7 @@ var SandboxCache = class {
12513
12553
  const have = new Set(keep.map((r) => `${r.host}/${r.path}`));
12514
12554
  const drop = (await this.list()).map((m) => m.repo).filter((r) => !have.has(`${r.host}/${r.path}`));
12515
12555
  for (const r of drop) {
12516
- 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))}`, []);
12517
12557
  }
12518
12558
  return drop;
12519
12559
  }
@@ -12955,6 +12995,9 @@ function writeSandboxState(stateDir, state) {
12955
12995
  `);
12956
12996
  renameSync3(`${file}.tmp`, file);
12957
12997
  }
12998
+ function sandboxImageFor(state, repo) {
12999
+ return state[repo]?.tags[0]?.tag ?? null;
13000
+ }
12958
13001
  function sandboxImageStatus(state, plan) {
12959
13002
  const s = state[plan.repo];
12960
13003
  const current = s?.tags[0];
@@ -25018,6 +25061,7 @@ if (command === "passenger-sync-daemon") {
25018
25061
  return { action: out.action, unsafe, detail: unsafe ? describeUnsafeBase(out, remote, base) : out.detail };
25019
25062
  }, sandboxCacheRepos = function(c, ship) {
25020
25063
  const out = [];
25064
+ let images;
25021
25065
  for (const t of reposOf(c)) {
25022
25066
  const origin = originUrlOf(t.dir, "origin");
25023
25067
  const repo = origin ? parseRemote(origin) : null;
@@ -25027,7 +25071,12 @@ if (command === "passenger-sync-daemon") {
25027
25071
  token = readForgeToken(ship.forges, repo.host, ship.stateDir);
25028
25072
  } catch {
25029
25073
  }
25030
- 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 });
25031
25080
  }
25032
25081
  return out;
25033
25082
  }, buildSandboxCachePass = function(c, ship, state2, emit2) {
@@ -28368,8 +28417,10 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
28368
28417
  `);
28369
28418
  if (mirrors.length === 0) process.stdout.write(`volume ${settings.volume}: no mirrors yet
28370
28419
  `);
28371
- 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"})` : ""}
28372
28422
  `);
28423
+ }
28373
28424
  }
28374
28425
  } catch (e) {
28375
28426
  process.stderr.write(`crew sandbox cache: ${e instanceof Error ? e.message : String(e)}
@@ -24529,7 +24529,7 @@ function startSerena(d, s) {
24529
24529
  const dest = (0, import_node_path4.join)(p.home, "language_servers");
24530
24530
  if (!d.exists(dest)) d.link(s.languageServers, dest);
24531
24531
  }
24532
- const mainCache = (0, import_node_path4.join)(p.mainProject, "cache");
24532
+ const mainCache = s.seedCache ?? (0, import_node_path4.join)(p.mainProject, "cache");
24533
24533
  const cache = (0, import_node_path4.join)(p.project, "cache");
24534
24534
  if (!d.exists(cache) && d.exists(mainCache)) d.copyDir(mainCache, cache);
24535
24535
  try {
@@ -25113,12 +25113,13 @@ function parseSandboxBridgeArgs(argv) {
25113
25113
  const attachFile = get("--attach-file");
25114
25114
  const start = get("--start");
25115
25115
  const clone2 = get("--clone");
25116
- if (!attachFile || !start || !clone2) throw new Error("usage: intel-bridge --attach-file FILE --start FLAVOR|COMMAND --clone DIR [--state-dir DIR] [--ticket N] [--startup-seconds S]");
25116
+ if (!attachFile || !start || !clone2) throw new Error("usage: intel-bridge --attach-file FILE --start FLAVOR|COMMAND --clone DIR [--state-dir DIR] [--seed-cache DIR] [--ticket N] [--startup-seconds S]");
25117
25117
  return {
25118
25118
  attachFile,
25119
25119
  start,
25120
25120
  clone: clone2,
25121
25121
  stateDir: get("--state-dir") ?? "/sandbox/.crew",
25122
+ ...get("--seed-cache") ? { seedCache: get("--seed-cache") } : {},
25122
25123
  ticket: Number(get("--ticket")) || 0,
25123
25124
  startupSeconds: Number(get("--startup-seconds")) || 90
25124
25125
  };
@@ -25151,6 +25152,7 @@ function sandboxStarter(a, run = runScript) {
25151
25152
  worktree: a.clone,
25152
25153
  mainCheckout: a.clone,
25153
25154
  noMainIndex: true,
25155
+ seedCache: a.seedCache,
25154
25156
  languageServers: process.env.CREW_SERENA_LANGUAGE_SERVERS
25155
25157
  });
25156
25158
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tablation/crew",
3
- "version": "0.125.0",
3
+ "version": "0.126.0",
4
4
  "description": "A standing team of headless agents that picks work off a Tablation board, does it on a machine you control, and reports back.",
5
5
  "type": "module",
6
6
  "license": "MIT",