@tablation/crew 0.126.12 → 0.127.1

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 +78 -25
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -10821,7 +10821,8 @@ var SandboxRunner = class {
10821
10821
  }
10822
10822
  const create2 = ["sandbox", "create", "--name", name, "--detach"];
10823
10823
  if (policyFile) create2.push("--policy", policyFile);
10824
- if (cfg.image) create2.push("--from", cfg.image);
10824
+ const image = cfg.image ?? plan.sandboxImage;
10825
+ if (image) create2.push("--from", image);
10825
10826
  if (cfg.cacheVolume) {
10826
10827
  create2.push("--driver-config-json", JSON.stringify({
10827
10828
  docker: { mounts: [{ type: "volume", source: cfg.cacheVolume, target: cfg.mirrorDir, read_only: true }] }
@@ -11041,6 +11042,15 @@ function sandboxLspPluginFor(route2, repo) {
11041
11042
  return void 0;
11042
11043
  }
11043
11044
  }
11045
+ function sandboxImageOf(route2, repo, stateDir) {
11046
+ let declares = false;
11047
+ try {
11048
+ const dir = reposOf(route2).find((r) => r.name === repo)?.dir ?? route2.dir;
11049
+ declares = Boolean(loadRepoConfig(dir)?.sandbox);
11050
+ } catch {
11051
+ }
11052
+ return sandboxImageForRun(readSandboxState(stateDir), repo, declares) ?? void 0;
11053
+ }
11044
11054
  var AgentSpawnError = class extends AgentError {
11045
11055
  };
11046
11056
  var WorkspaceAuthRefused = class extends AgentSpawnError {
@@ -11181,6 +11191,7 @@ function planAgentRun(o) {
11181
11191
  const code = codeBridgeFor(o, adapter.harness);
11182
11192
  const sandboxLsp = o.ship.runner === "sandbox" && adapter.harness === "claude" ? sandboxLspPluginFor(o.route, o.repo) : void 0;
11183
11193
  const sandboxOrigin = o.ship.runner === "sandbox" ? sandboxOriginFor(o.route, o.repo) : void 0;
11194
+ const sandboxImage = o.ship.runner === "sandbox" ? sandboxImageOf(o.route, o.repo, o.stateDir) : void 0;
11184
11195
  const sandboxFiles = [...code?.files ?? [], ...sandboxLsp?.files ?? []];
11185
11196
  const invocation = adapter.invocation(toolchain, {
11186
11197
  code,
@@ -11234,6 +11245,7 @@ function planAgentRun(o) {
11234
11245
  runner: "sandbox",
11235
11246
  ...o.ship.sandbox ? { sandboxConfig: o.ship.sandbox } : {},
11236
11247
  ...sandboxOrigin ? { sandboxOrigin } : {},
11248
+ ...sandboxImage ? { sandboxImage } : {},
11237
11249
  sandboxPolicy: sandboxPolicyInputFor(o.route, toolchain, { harnessBin: invocation.bin, repo: o.repo, sandbox: o.ship.sandbox }),
11238
11250
  ...sandboxFiles.length ? { sandboxFiles } : {}
11239
11251
  } : {},
@@ -13104,6 +13116,8 @@ function planSandbox(repo, decl, warm = {}, bridge) {
13104
13116
  const layer = repoLayer(repo, decl, crew, warm);
13105
13117
  return { repo, tag: layer.tag, claude: SANDBOX_CLAUDE_VERSION, layers: [base, crew, layer] };
13106
13118
  }
13119
+ var SHIP_SANDBOX_KEY = "@ship";
13120
+ var shipSandboxRef = () => ({ name: SHIP_SANDBOX_KEY, decl: { packages: [], setup: [] } });
13107
13121
  var sandboxStatePath = (stateDir) => join32(stateDir, "sandbox-images.json");
13108
13122
  function readSandboxState(stateDir) {
13109
13123
  try {
@@ -13123,6 +13137,9 @@ function writeSandboxState(stateDir, state) {
13123
13137
  function sandboxImageFor(state, repo) {
13124
13138
  return state[repo]?.tags[0]?.tag ?? null;
13125
13139
  }
13140
+ function sandboxImageForRun(state, repo, declares) {
13141
+ return (declares && repo ? sandboxImageFor(state, repo) : null) ?? sandboxImageFor(state, SHIP_SANDBOX_KEY);
13142
+ }
13126
13143
  function sandboxImageStatus(state, plan) {
13127
13144
  const s = state[plan.repo];
13128
13145
  const current = s?.tags[0];
@@ -13236,7 +13253,7 @@ async function sandboxBuildTick(repos, deps, force = false) {
13236
13253
  }
13237
13254
 
13238
13255
  // src/doctor-checks.ts
13239
- import { execFileSync as execFileSync9 } from "child_process";
13256
+ import { execFileSync as execFileSync9, spawnSync as spawnSync8 } from "child_process";
13240
13257
  var PROBE_BRANCH = "crew-probe-branch";
13241
13258
  var realGit = (cwd, args, env) => {
13242
13259
  execFileSync9("git", args, { cwd, env, stdio: ["ignore", "pipe", "pipe"], timeout: 3e4 });
@@ -13369,6 +13386,21 @@ function managedRuntimeWarnings(kind, ship) {
13369
13386
  }
13370
13387
  return out;
13371
13388
  }
13389
+ var MANAGED_GIT_EMAIL = "crew@tablation.dev";
13390
+ function readGlobalGitIdentity() {
13391
+ const get2 = (key) => {
13392
+ const res = spawnSync8("git", ["config", "--global", "--get", key], { encoding: "utf8" });
13393
+ const v = (res.stdout ?? "").trim();
13394
+ return res.status === 0 && v ? v : null;
13395
+ };
13396
+ return { name: get2("user.name"), email: get2("user.email") };
13397
+ }
13398
+ function managedGitIdentityWarnings(kind, identity) {
13399
+ if (kind !== "managed_instance" && kind !== "pooled_instance" && kind !== "pooled") return [];
13400
+ const missing = [identity.name ? "" : "user.name", identity.email ? "" : "user.email"].filter(Boolean);
13401
+ if (missing.length === 0) return [];
13402
+ return [`git ${missing.join(" and ")} not set for this user on a ${kind} ship; every commit crew makes here fails with "Committer identity unknown" (re-run \`crew provision\`, or \`git config --global user.name <ship name>\` and \`user.email ${MANAGED_GIT_EMAIL}\`)`];
13403
+ }
13372
13404
 
13373
13405
  // src/selftest.ts
13374
13406
  import { spawn as spawn6 } from "child_process";
@@ -13596,7 +13628,7 @@ function doctorSelftestLines(stored, toolchains) {
13596
13628
  import { resolve as resolve7, dirname as dirname34, join as join56, basename as basename16 } from "path";
13597
13629
  import { fileURLToPath as fileURLToPath2 } from "url";
13598
13630
  import { hostname as hostname5, homedir as homedir11 } from "os";
13599
- import { spawnSync as spawnSync11 } from "child_process";
13631
+ import { spawnSync as spawnSync12 } from "child_process";
13600
13632
  import { parse as parseYaml2 } from "yaml";
13601
13633
 
13602
13634
  // src/argv.ts
@@ -14451,7 +14483,7 @@ function matchShipRows(ships, shipName2, machineId) {
14451
14483
  }
14452
14484
 
14453
14485
  // src/provision.ts
14454
- import { spawnSync as spawnSync8 } from "child_process";
14486
+ import { spawnSync as spawnSync9 } from "child_process";
14455
14487
  import { existsSync as existsSync34, readFileSync as readFileSync33, writeFileSync as writeFileSync26 } from "fs";
14456
14488
  import { hostname as hostname2 } from "os";
14457
14489
  import { parseDocument as parseDocument3, isMap as isMap2, isScalar } from "yaml";
@@ -14899,6 +14931,11 @@ async function runProvision(o, deps, write, human) {
14899
14931
  emit("connect", `set ${key}`, { setting: key });
14900
14932
  }
14901
14933
  }
14934
+ if (isManagedShipKind(o.shipKind)) {
14935
+ summary.gitIdentity = deps.ensureGitIdentity(shipName2, MANAGED_GIT_EMAIL);
14936
+ if (summary.gitIdentity === "set") summary.changed = true;
14937
+ emit("git_identity", `git identity: ${summary.gitIdentity}`, { action: summary.gitIdentity });
14938
+ }
14902
14939
  for (const host of o.forgeHosts) {
14903
14940
  const token = o.tokens[host];
14904
14941
  const stored = deps.storedToken(host);
@@ -15055,7 +15092,7 @@ function realProvisionDeps(crewHome) {
15055
15092
  const env = { ...process.env, ...o.env };
15056
15093
  delete env.CREW_FORGE_TOKEN;
15057
15094
  for (const k of Object.keys(env)) if (k.startsWith("CREW_CONNECT_KEY_")) delete env[k];
15058
- const res = spawnSync8(bin, [...pre, ...args], { cwd: crewHome, encoding: "utf8", env, input: o.input, maxBuffer: 64 * 1024 * 1024 });
15095
+ const res = spawnSync9(bin, [...pre, ...args], { cwd: crewHome, encoding: "utf8", env, input: o.input, maxBuffer: 64 * 1024 * 1024 });
15059
15096
  return { status: res.status ?? 1, stdout: res.stdout ?? "", stderr: `${res.stderr ?? ""}${res.error ? String(res.error) : ""}` };
15060
15097
  };
15061
15098
  const configFile = () => {
@@ -15103,15 +15140,15 @@ function realProvisionDeps(crewHome) {
15103
15140
  if (mechanism === "systemd") {
15104
15141
  const system = ctx.root || existsSync34(systemUnitPathFor(label));
15105
15142
  const ctl = system ? ["systemctl"] : ["systemctl", "--user"];
15106
- running = spawnSync8(ctl[0], [...ctl.slice(1), "is-active", "--quiet", `${label}.service`]).status === 0;
15143
+ running = spawnSync9(ctl[0], [...ctl.slice(1), "is-active", "--quiet", `${label}.service`]).status === 0;
15107
15144
  if (!system) {
15108
- const out = spawnSync8("loginctl", ["show-user", ctx.user, "-p", "Linger"], { encoding: "utf8" });
15145
+ const out = spawnSync9("loginctl", ["show-user", ctx.user, "-p", "Linger"], { encoding: "utf8" });
15109
15146
  lingering = /Linger=yes/.test(out.stdout ?? "");
15110
15147
  }
15111
15148
  } else if (mechanism === "cron") {
15112
15149
  running = true;
15113
15150
  } else if (mechanism === "launchd") {
15114
- running = spawnSync8("launchctl", ["list", label]).status === 0;
15151
+ running = spawnSync9("launchctl", ["list", label]).status === 0;
15115
15152
  }
15116
15153
  return { mechanism, unitsStale, running, lingering };
15117
15154
  },
@@ -15132,6 +15169,17 @@ function realProvisionDeps(crewHome) {
15132
15169
  if (wrote.length) writeFileSync26(file, doc.toString());
15133
15170
  return wrote;
15134
15171
  },
15172
+ ensureGitIdentity(name, email) {
15173
+ const have = readGlobalGitIdentity();
15174
+ let wrote = false;
15175
+ for (const [key, value2] of [["user.name", have.name ? null : name], ["user.email", have.email ? null : email]]) {
15176
+ if (value2 === null) continue;
15177
+ const res = spawnSync9("git", ["config", "--global", key, value2], { encoding: "utf8" });
15178
+ if (res.status !== 0) throw new Error(`git config --global ${key}: ${(res.stderr ?? "").trim() || `exited ${res.status}`}`);
15179
+ wrote = true;
15180
+ }
15181
+ return wrote ? "set" : "kept";
15182
+ },
15135
15183
  storedToken(host) {
15136
15184
  const res = spawnCrew(["credential", "get"], { input: `protocol=https
15137
15185
  host=${host}
@@ -20721,7 +20769,7 @@ async function showLogEntry(route2, entryId, opts = {}) {
20721
20769
  }
20722
20770
 
20723
20771
  // src/repos-check.ts
20724
- import { spawnSync as spawnSync10 } from "child_process";
20772
+ import { spawnSync as spawnSync11 } from "child_process";
20725
20773
  import { readFileSync as readFileSync39 } from "fs";
20726
20774
  import { basename as basename11 } from "path";
20727
20775
  var POSIX_SHELL = /^(?:ba|z|da|k|a|mk)?sh$|^fish$/;
@@ -20730,10 +20778,10 @@ function checkHookSyntax(script, shell) {
20730
20778
  const name = basename11(spec.bin).replace(/\.exe$/i, "").toLowerCase();
20731
20779
  let run3;
20732
20780
  if (POSIX_SHELL.test(name)) {
20733
- run3 = spawnSync10(spec.bin, ["-n"], { input: script, encoding: "utf8" });
20781
+ run3 = spawnSync11(spec.bin, ["-n"], { input: script, encoding: "utf8" });
20734
20782
  } else if (/^(pwsh|powershell)$/.test(name)) {
20735
20783
  const parse3 = "$e=$null;[void][System.Management.Automation.Language.Parser]::ParseInput($env:CREW_CHECK_SCRIPT,[ref]$null,[ref]$e);if($e){$e|ForEach-Object{[Console]::Error.WriteLine($_.Message)};exit 1}";
20736
- run3 = spawnSync10(spec.bin, ["-NoProfile", "-NonInteractive", "-Command", parse3], {
20784
+ run3 = spawnSync11(spec.bin, ["-NoProfile", "-NonInteractive", "-Command", parse3], {
20737
20785
  encoding: "utf8",
20738
20786
  env: { ...process.env, CREW_CHECK_SCRIPT: script }
20739
20787
  });
@@ -25814,7 +25862,7 @@ if (command === "passenger-sync-daemon") {
25814
25862
  `
25815
25863
  );
25816
25864
  if (!dryRun) {
25817
- spawnSync11("systemctl", ["--user", "disable", "--now", `${label}.timer`], { cwd: CREW_HOME });
25865
+ spawnSync12("systemctl", ["--user", "disable", "--now", `${label}.timer`], { cwd: CREW_HOME });
25818
25866
  try {
25819
25867
  unlinkSync5(staleTimer);
25820
25868
  } catch {
@@ -25865,7 +25913,7 @@ if (command === "passenger-sync-daemon") {
25865
25913
  async function doctorFix(o) {
25866
25914
  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))));
25867
25915
  const unitExists = (p) => existsSync52(p);
25868
- const crontab = o.host === "linux" ? spawnSync11("crontab", ["-l"], { encoding: "utf8" }).stdout ?? "" : "";
25916
+ const crontab = o.host === "linux" ? spawnSync12("crontab", ["-l"], { encoding: "utf8" }).stdout ?? "" : "";
25869
25917
  const schedulerAbsent = o.host !== "windows" ? !schedulerInstalled(planInstall(cfg.ship, CREW_HOME, o.host, detectSystemd(), "run", installContext()), unitExists, crontab) : true;
25870
25918
  const plan = planFix({ routes, schedulerAbsent, host: o.host });
25871
25919
  for (const n of plan.notes) process.stdout.write(`fix: ${n}
@@ -25924,6 +25972,7 @@ ${changed2.map((c) => ` - ${c}
25924
25972
  }
25925
25973
  return [...out.values()];
25926
25974
  }
25975
+ const withShipSandbox = (declared) => cfg.ship.runner === "sandbox" ? [shipSandboxRef(), ...declared] : declared;
25927
25976
  async function resolveOneRepo(c, t) {
25928
25977
  {
25929
25978
  const id = repoIdForName(c, t.name);
@@ -27035,7 +27084,7 @@ ${cu.tail.trimEnd()}
27035
27084
  }
27036
27085
  let failed = false;
27037
27086
  for (const cmd2 of commands) {
27038
- const res2 = spawnSync11(cmd2[0], cmd2.slice(1), { cwd: CREW_HOME, encoding: "utf8" });
27087
+ const res2 = spawnSync12(cmd2[0], cmd2.slice(1), { cwd: CREW_HOME, encoding: "utf8" });
27039
27088
  if (res2.stdout) process.stdout.write(res2.stdout);
27040
27089
  if (res2.stderr) process.stderr.write(res2.stderr);
27041
27090
  if (cmd2 === control.startCommand && (res2.error || res2.status !== 0)) {
@@ -27053,7 +27102,7 @@ ${cu.tail.trimEnd()}
27053
27102
  `);
27054
27103
  break;
27055
27104
  }
27056
- const res = spawnSync11(cmd[0], cmd.slice(1), { cwd: CREW_HOME, encoding: "utf8" });
27105
+ const res = spawnSync12(cmd[0], cmd.slice(1), { cwd: CREW_HOME, encoding: "utf8" });
27057
27106
  if (res.stdout) process.stdout.write(res.stdout);
27058
27107
  if (res.stderr) process.stderr.write(res.stderr);
27059
27108
  if (daemonSub !== "status" && (res.error || res.status !== 0)) {
@@ -27192,7 +27241,7 @@ ${cu.tail.trimEnd()}
27192
27241
  onPass: (result) => {
27193
27242
  if (!sandboxBuilding) {
27194
27243
  sandboxBuilding = true;
27195
- void sandboxReposOf(cfg.routes.filter((r) => r.enabled)).then((repos) => repos.length ? sandboxBuildTick(repos, sandboxTickDeps(emit, route2)) : null).catch((err3) => emit.warn(`sandbox image build tick failed: ${err3 instanceof Error ? err3.message : String(err3)}`)).finally(() => {
27244
+ void sandboxReposOf(cfg.routes.filter((r) => r.enabled)).then(withShipSandbox).then((repos) => repos.length ? sandboxBuildTick(repos, sandboxTickDeps(emit, route2)) : null).catch((err3) => emit.warn(`sandbox image build tick failed: ${err3 instanceof Error ? err3.message : String(err3)}`)).finally(() => {
27196
27245
  sandboxBuilding = false;
27197
27246
  });
27198
27247
  }
@@ -28726,7 +28775,7 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
28726
28775
  }
28727
28776
  throw e;
28728
28777
  }
28729
- const declared = await sandboxReposOf(routesS);
28778
+ const declared = withShipSandbox(await sandboxReposOf(routesS));
28730
28779
  if (subS === "images") {
28731
28780
  const stateS = readSandboxState(cfg.ship.stateDir);
28732
28781
  const rows = declared.map((r) => {
@@ -28753,8 +28802,8 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
28753
28802
  const only = positional[2];
28754
28803
  const targets = only ? declared.filter((r) => r.name === only) : declared;
28755
28804
  if (targets.length === 0) {
28756
- process.stderr.write(only ? `crew: no repository "${only}" declares a sandbox: section
28757
- ` : "crew: no repository declares a sandbox: section in its .crew.yaml\n");
28805
+ process.stderr.write(only ? `crew: no repository "${only}" declares a sandbox: section (${SHIP_SANDBOX_KEY} is the ship's crew layer, built only on a ship.runner: sandbox ship)
28806
+ ` : "crew: this ship is not a sandbox ship and no repository declares a sandbox: section in its .crew.yaml\n");
28758
28807
  process.exit(2);
28759
28808
  }
28760
28809
  const emitS = new Emitter({ route: routesS[0].route, eventFile: eventFileFor(cfg.ship.stateDir), rotateBytes: cfg.ship.eventsRotateBytes, logFile: cfg.ship.logFile, console: (l) => process.stderr.write(`${l}
@@ -29510,7 +29559,7 @@ ${where.join("\n")}`);
29510
29559
  process.stdout.write(upOutput(res, terminal));
29511
29560
  const opener = openerFor(process.platform);
29512
29561
  if (opener && shouldOpen({ open: flag("open"), noOpen: flag("no-open"), json: jsonP, terminal })) {
29513
- const r = spawnSync11(opener, [res.url], { stdio: "ignore" });
29562
+ const r = spawnSync12(opener, [res.url], { stdio: "ignore" });
29514
29563
  if (r.error || r.status !== 0) process.stderr.write(`crew periscope: could not open ${res.url} with ${opener}
29515
29564
  `);
29516
29565
  }
@@ -29758,7 +29807,7 @@ ${where.join("\n")}`);
29758
29807
  }
29759
29808
  const host = hostPlatform();
29760
29809
  const control = host === "windows" ? null : planDaemonControl(CREW_HOME, host, detectSystemd());
29761
- const unitStatus = control?.statusCommand ? spawnSync11(control.statusCommand[0], control.statusCommand.slice(1), { cwd: CREW_HOME, encoding: "utf8" }) : null;
29810
+ const unitStatus = control?.statusCommand ? spawnSync12(control.statusCommand[0], control.statusCommand.slice(1), { cwd: CREW_HOME, encoding: "utf8" }) : null;
29762
29811
  const unitRunning = unitStatus !== null && !unitStatus.error && unitStatus.status === 0;
29763
29812
  if (!unitRunning) {
29764
29813
  process.stdout.write("crew reload: no daemon unit is running \u2014 nothing to reload (the next run reads crew.yaml fresh)\n");
@@ -29772,7 +29821,7 @@ ${where.join("\n")}`);
29772
29821
  );
29773
29822
  if (flag("now") && control?.stopCommand && control.startCommand) {
29774
29823
  for (const cmd of [control.stopCommand, control.startCommand]) {
29775
- const res = spawnSync11(cmd[0], cmd.slice(1), { cwd: CREW_HOME, encoding: "utf8" });
29824
+ const res = spawnSync12(cmd[0], cmd.slice(1), { cwd: CREW_HOME, encoding: "utf8" });
29776
29825
  if (cmd === control.startCommand && (res.error || res.status !== 0)) {
29777
29826
  process.stderr.write(`crew reload: ${cmd.join(" ")} exited ${res.status ?? "error"}
29778
29827
  `);
@@ -29914,7 +29963,7 @@ ${where.join("\n")}`);
29914
29963
  writeFileSync38(join56(dir, "README.md"), SAMPLE_README);
29915
29964
  writeFileSync38(join56(dir, ".crew.yaml"), SAMPLE_CREW_YAML);
29916
29965
  const g = (...a) => {
29917
- const r = spawnSync11("git", a, { cwd: dir, encoding: "utf8" });
29966
+ const r = spawnSync12("git", a, { cwd: dir, encoding: "utf8" });
29918
29967
  if (r.status !== 0) fail2(`git ${a[0]} failed in ${dir}: ${(r.stderr || r.stdout).trim()}`);
29919
29968
  };
29920
29969
  g("init", "-b", "main");
@@ -30346,7 +30395,9 @@ watermark: ${state.watermark()}
30346
30395
  }
30347
30396
  for (const message of roleCapWarnings(cfg.ship)) problems.push({ severity: "note", kind: "role_cap_above_ship_cap", message });
30348
30397
  if (!flag("offline")) {
30349
- for (const message of managedRuntimeWarnings(await doctorShipKind(route2, cfg.ship), cfg.ship)) problems.push({ severity: "attention", kind: "managed_runtime_default", message });
30398
+ const shipKind = await doctorShipKind(route2, cfg.ship);
30399
+ for (const message of managedRuntimeWarnings(shipKind, cfg.ship)) problems.push({ severity: "attention", kind: "managed_runtime_default", message });
30400
+ for (const message of managedGitIdentityWarnings(shipKind, readGlobalGitIdentity())) problems.push({ severity: "attention", kind: "managed_git_identity", message });
30350
30401
  }
30351
30402
  for (const t of rejectedTunnels(cfg.ship.stateDir)) {
30352
30403
  problems.push({ severity: "attention", kind: TUNNEL_REJECTED_KEY, message: tunnelRejectedMessage(t.workspaceId, t.quickExits), remedy: "rotate the slug with `crew rotate-passenger-url`" });
@@ -30597,6 +30648,8 @@ crew manifest: ${seats2.length ? seats2.map((c) => c.name).join(", ") : "non
30597
30648
  `
30598
30649
  );
30599
30650
  for (const w of managedRuntimeWarnings(row.kind, cfg.ship)) process.stdout.write(`managed runtime: WARN ${w}
30651
+ `);
30652
+ for (const w of managedGitIdentityWarnings(row.kind, readGlobalGitIdentity())) process.stdout.write(`managed git: WARN ${w}
30600
30653
  `);
30601
30654
  }
30602
30655
  }
@@ -30853,7 +30906,7 @@ ${foreign.join("\n")}
30853
30906
  }
30854
30907
  }
30855
30908
  {
30856
- const declaredS = await sandboxReposOf(cfg.routes.filter((r) => r.enabled));
30909
+ const declaredS = withShipSandbox(await sandboxReposOf(cfg.routes.filter((r) => r.enabled)));
30857
30910
  const stateS = readSandboxState(cfg.ship.stateDir);
30858
30911
  for (const r of declaredS) {
30859
30912
  const st = sandboxImageStatus(stateS, planSandbox(r.name, r.decl, r.warm, r.bridge));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tablation/crew",
3
- "version": "0.126.12",
3
+ "version": "0.127.1",
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",