@tablation/crew 0.126.12 → 0.127.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.
Files changed (2) hide show
  1. package/dist/cli.js +24 -6
  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];
@@ -25924,6 +25941,7 @@ ${changed2.map((c) => ` - ${c}
25924
25941
  }
25925
25942
  return [...out.values()];
25926
25943
  }
25944
+ const withShipSandbox = (declared) => cfg.ship.runner === "sandbox" ? [shipSandboxRef(), ...declared] : declared;
25927
25945
  async function resolveOneRepo(c, t) {
25928
25946
  {
25929
25947
  const id = repoIdForName(c, t.name);
@@ -27192,7 +27210,7 @@ ${cu.tail.trimEnd()}
27192
27210
  onPass: (result) => {
27193
27211
  if (!sandboxBuilding) {
27194
27212
  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(() => {
27213
+ 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
27214
  sandboxBuilding = false;
27197
27215
  });
27198
27216
  }
@@ -28726,7 +28744,7 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
28726
28744
  }
28727
28745
  throw e;
28728
28746
  }
28729
- const declared = await sandboxReposOf(routesS);
28747
+ const declared = withShipSandbox(await sandboxReposOf(routesS));
28730
28748
  if (subS === "images") {
28731
28749
  const stateS = readSandboxState(cfg.ship.stateDir);
28732
28750
  const rows = declared.map((r) => {
@@ -28753,8 +28771,8 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
28753
28771
  const only = positional[2];
28754
28772
  const targets = only ? declared.filter((r) => r.name === only) : declared;
28755
28773
  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");
28774
+ 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)
28775
+ ` : "crew: this ship is not a sandbox ship and no repository declares a sandbox: section in its .crew.yaml\n");
28758
28776
  process.exit(2);
28759
28777
  }
28760
28778
  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}
@@ -30853,7 +30871,7 @@ ${foreign.join("\n")}
30853
30871
  }
30854
30872
  }
30855
30873
  {
30856
- const declaredS = await sandboxReposOf(cfg.routes.filter((r) => r.enabled));
30874
+ const declaredS = withShipSandbox(await sandboxReposOf(cfg.routes.filter((r) => r.enabled)));
30857
30875
  const stateS = readSandboxState(cfg.ship.stateDir);
30858
30876
  for (const r of declaredS) {
30859
30877
  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.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",