@tablation/crew 0.127.2 → 0.127.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/cli.js +30 -6
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -6215,7 +6215,8 @@ async function raiseShipAttention(o, item) {
6215
6215
  const previous = store.previous();
6216
6216
  const next = upsert(previous, item);
6217
6217
  store.persist(next);
6218
- if (transitions(previous, next).length === 0) return false;
6218
+ const changed2 = item.kind === "sandbox_cache" && previous.some((i) => i.key === item.key && i.message !== item.message);
6219
+ if (transitions(previous, next).length === 0 && !changed2) return false;
6219
6220
  announce(o.emit, item);
6220
6221
  if (o.route && o.ship) {
6221
6222
  await notify(o.route, o.ship, { level: "warn", headline: item.message }, o.emit);
@@ -12704,7 +12705,7 @@ var SandboxCache = class {
12704
12705
  try {
12705
12706
  await this.helper(this.s.helperImage, REFRESH_SCRIPT, [mirrorRelPath(r), forgeDescriptorFor(r.host).httpsUrl(r.host, r.path)], env);
12706
12707
  } catch (e) {
12707
- out.push({ repo: r, ok: false, detail: describe(e) });
12708
+ out.push({ repo: r, ok: false, detail: r.token ? describe(e) : `no readable forge token for ${r.host} (see \`crew forge list\`); ${describe(e)}` });
12708
12709
  continue;
12709
12710
  }
12710
12711
  if (this.s.depsCommand && this.s.depsImage) {
@@ -12808,6 +12809,10 @@ function cacheSettingsOf(cfg) {
12808
12809
  };
12809
12810
  }
12810
12811
  var sandboxCacheKey = (volume, r) => `sandbox_cache:${volume}:${r.host}/${r.path}`;
12812
+ function unmirroredRepos(served, mirrors) {
12813
+ const have = new Set(mirrors.map((m) => `${m.repo.host}/${m.repo.path}`));
12814
+ return served.map((r) => `${r.host}/${r.path}`).filter((k, i, a) => !have.has(k) && a.indexOf(k) === i);
12815
+ }
12811
12816
  var CACHE_REFRESH_INTERVAL_MS = 6e4;
12812
12817
  function makeCachePass(o) {
12813
12818
  const now = o.now ?? Date.now;
@@ -12830,7 +12835,7 @@ function makeCachePass(o) {
12830
12835
  for (const out of all) {
12831
12836
  const key = sandboxCacheKey(o.volume, out.repo);
12832
12837
  if (out.ok) o.ok(key);
12833
- else await o.fail(key, `sandbox cache: ${out.repo.host}/${out.repo.path} did not refresh (${out.detail}); runs use the previous mirror. Resumes automatically when a later pass refreshes it`);
12838
+ else await o.fail(key, `sandbox cache: ${out.repo.host}/${out.repo.path} did not refresh (${out.detail}); runs use the previous mirror. Resumes automatically when a later pass refreshes it`, new Date(now()).toISOString());
12834
12839
  }
12835
12840
  return all;
12836
12841
  };
@@ -13129,12 +13134,14 @@ function baseLayer() {
13129
13134
  ].join("\n");
13130
13135
  return { name: "base", tag: `crew-sandbox-base:${hash({ SANDBOX_BASE_IMAGE, SANDBOX_OPENSHELL_VERSION })}`, dockerfile, files: {} };
13131
13136
  }
13137
+ var APT_HTTPS_COMMAND = "sed -i -E 's#http://([a-z0-9.-]*\\.)?ubuntu\\.com#https://\\1ubuntu.com#g' /etc/apt/sources.list.d/ubuntu.sources 2>/dev/null || true";
13132
13138
  function crewLayer(base, intel, bridge) {
13133
13139
  const files = { "credential-helper": CREDENTIAL_HELPER, "mcp.json": MCP_CONFIG, ...bridge ? { "intel-bridge": bridge } : {} };
13134
13140
  const env = Object.entries(CREW_ENV).map(([k, v]) => `${k}=${v}`).join(" ");
13135
13141
  const dockerfile = [
13136
13142
  `FROM ${base.tag}`,
13137
13143
  "USER root",
13144
+ run2(APT_HTTPS_COMMAND),
13138
13145
  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/*`),
13139
13146
  `COPY credential-helper ${CREW_PREFIX}/bin/credential-helper`,
13140
13147
  `COPY mcp.json ${CREW_PREFIX}/mcp/mcp.json`,
@@ -13183,6 +13190,7 @@ function repoLayer(repo, decl, crew, warm) {
13183
13190
  lines2.push(`FROM ${crew.tag}`, "USER root");
13184
13191
  }
13185
13192
  if (decl.packages.length > 0) {
13193
+ lines2.push(run2(APT_HTTPS_COMMAND));
13186
13194
  lines2.push(run2(`apt-get update && apt-get install -y --no-install-recommends ${decl.packages.join(" ")} && rm -rf /var/lib/apt/lists/*`));
13187
13195
  }
13188
13196
  lines2.push(
@@ -19336,6 +19344,7 @@ function runForge(a, io) {
19336
19344
  const tok = sourceFromArgs(a);
19337
19345
  if (typeof tok === "string") return fail2(tok);
19338
19346
  const doc = parseDocument4(io.readFile(io.configFile));
19347
+ doc.setIn(["ship", "forges", host, "username"], forgeDescriptorFor(host).credentialUsername);
19339
19348
  doc.setIn(["ship", "forges", host, "token"], tok);
19340
19349
  io.writeFile(io.configFile, doc.toString());
19341
19350
  io.forges = { ...io.forges ?? {}, [host]: { username: forgeDescriptorFor(host).credentialUsername, token: tok } };
@@ -25540,7 +25549,7 @@ if (command === "passenger-sync-daemon") {
25540
25549
  if (intelFlavor(loadRepoConfig(t.dir)?.hooks.intel) === "serena") intelImage = sandboxImageFor(images ??= readSandboxState(ship.stateDir), t.name);
25541
25550
  } catch {
25542
25551
  }
25543
- out.push({ ...repo, dir: t.dir, token, username: forgeFor(ship.forges, repo.host)?.entry.username, intelImage });
25552
+ out.push({ ...repo, dir: t.dir, token, username: forgeFor(ship.forges, repo.host)?.entry.username || forgeDescriptorFor(repo.host).credentialUsername, intelImage });
25544
25553
  }
25545
25554
  return out;
25546
25555
  }, buildSandboxCachePass = function(c, ship, state2, emit2) {
@@ -25551,7 +25560,7 @@ if (command === "passenger-sync-daemon") {
25551
25560
  volume: settings.volume,
25552
25561
  repos: () => sandboxCacheRepos(c, ship),
25553
25562
  lock: (dir) => acquireCheckoutLock(dir),
25554
- fail: (key, message) => raiseShipAttention({ state: state2, emit: emit2, route: c, ship }, { kind: "sandbox_cache", key, message, since: (/* @__PURE__ */ new Date()).toISOString() }).then(() => void 0),
25563
+ fail: (key, message, failedAt) => raiseShipAttention({ state: state2, emit: emit2, route: c, ship }, { kind: "sandbox_cache", key, message, since: failedAt, lastFailedAt: failedAt }).then(() => void 0),
25555
25564
  ok: (key) => clearShipAttention(state2, key)
25556
25565
  });
25557
25566
  return async () => {
@@ -28963,8 +28972,13 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
28963
28972
  }
28964
28973
  const dropped = flag("prune") ? await cache.prune(served) : [];
28965
28974
  const mirrors = await cache.list();
28975
+ const open = openShipAttention(new State(cfg.ship.stateDir));
28976
+ const failures = served.flatMap((r) => {
28977
+ const item = open.find((i) => i.kind === "sandbox_cache" && i.key === sandboxCacheKey(settings.volume, r));
28978
+ return item ? [{ repo: `${r.host}/${r.path}`, lastFailedAt: item.lastFailedAt ?? item.since, message: item.message }] : [];
28979
+ });
28966
28980
  if (flag("json")) {
28967
- process.stdout.write(`${JSON.stringify({ volume: settings.volume, mirrors, dropped }, null, 2)}
28981
+ process.stdout.write(`${JSON.stringify({ volume: settings.volume, mirrors, failures, dropped }, null, 2)}
28968
28982
  `);
28969
28983
  } else {
28970
28984
  for (const d of dropped) process.stdout.write(`dropped ${d.host}/${d.path}
@@ -28975,6 +28989,8 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
28975
28989
  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"})` : ""}
28976
28990
  `);
28977
28991
  }
28992
+ for (const f of failures) process.stdout.write(`${f.repo} REFRESH FAILING (last failed ${f.lastFailedAt}): ${f.message}
28993
+ `);
28978
28994
  }
28979
28995
  } catch (e) {
28980
28996
  process.stderr.write(`crew sandbox cache: ${e instanceof Error ? e.message : String(e)}
@@ -30524,6 +30540,14 @@ watermark: ${state.watermark()}
30524
30540
  for (const message of managedRuntimeWarnings(shipKind, cfg.ship)) problems.push({ severity: "attention", kind: "managed_runtime_default", message });
30525
30541
  for (const message of managedGitIdentityWarnings(shipKind, readGlobalGitIdentity())) problems.push({ severity: "attention", kind: "managed_git_identity", message });
30526
30542
  }
30543
+ const cacheSettings = cfg.ship.runner === "sandbox" ? cacheSettingsOf(cfg.ship.sandbox) : null;
30544
+ if (cacheSettings && !flag("offline")) {
30545
+ try {
30546
+ const missing = unmirroredRepos(sandboxCacheRepos(route2, cfg.ship), await new SandboxCache(cacheSettings).list());
30547
+ for (const r of missing) problems.push({ severity: "attention", kind: "sandbox_cache_unmirrored", route: route2.route, message: `${r} has no mirror in the cache volume ${cacheSettings.volume}, so sandboxed runs cannot clone it`, remedy: "see `crew sandbox cache` for the last refresh failure" });
30548
+ } catch {
30549
+ }
30550
+ }
30527
30551
  for (const t of rejectedTunnels(cfg.ship.stateDir)) {
30528
30552
  problems.push({ severity: "attention", kind: TUNNEL_REJECTED_KEY, message: tunnelRejectedMessage(t.workspaceId, t.quickExits), remedy: "rotate the slug with `crew rotate-passenger-url`" });
30529
30553
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tablation/crew",
3
- "version": "0.127.2",
3
+ "version": "0.127.4",
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",