@tablation/crew 0.113.0 → 0.114.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 +87 -10
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -3030,7 +3030,9 @@ var SHIP_KEYS = /* @__PURE__ */ new Set([
3030
3030
  "maxPeriscopes",
3031
3031
  "periscopeIdleMinutes",
3032
3032
  "secretStore",
3033
- "env"
3033
+ "env",
3034
+ "runner",
3035
+ "sandbox"
3034
3036
  ]);
3035
3037
  var ROUTE_KEYS = /* @__PURE__ */ new Set([
3036
3038
  "route",
@@ -12164,6 +12166,17 @@ function roleCapWarnings(ship) {
12164
12166
  if (!ship.maxConcurrentAgentsSet) return [];
12165
12167
  return Object.entries(ship.roleConcurrency).filter(([, n]) => (n ?? 0) > ship.maxConcurrentAgents).map(([role, n]) => `ship.roleConcurrency.${role} is ${n}, above ship.maxConcurrentAgents (${ship.maxConcurrentAgents}); the ship-wide cap wins`);
12166
12168
  }
12169
+ function managedRuntimeWarnings(kind, ship) {
12170
+ if (kind !== "managed_instance" && kind !== "pooled_instance" && kind !== "pooled") return [];
12171
+ const out = [];
12172
+ const explicit = ship.toolchains ?? [];
12173
+ const effectiveToolchains = explicit.some((t) => t.name === "claude") ? explicit : [{ name: "claude", harness: "claude" }, ...explicit];
12174
+ if ((ship.runner ?? "host") !== "sandbox") out.push(`ship.runner is "${ship.runner ?? "host"}" on a ${kind} ship; a managed ship should run agents in the sandbox (set ship.runner: sandbox)`);
12175
+ for (const t of effectiveToolchains) {
12176
+ if (t.harness === "claude" && t.auth !== "workspace") out.push(`ship.toolchains.${t.name}.auth is "${t.auth ?? "inherit"}" on a ${kind} ship; a managed ship reaches a model only through the workspace endpoint (set auth: workspace)`);
12177
+ }
12178
+ return out;
12179
+ }
12167
12180
 
12168
12181
  // src/selftest.ts
12169
12182
  import { spawn as spawn5 } from "child_process";
@@ -13248,7 +13261,7 @@ function matchShipRows(ships, shipName2, machineId) {
13248
13261
  import { spawnSync as spawnSync7 } from "child_process";
13249
13262
  import { existsSync as existsSync28, readFileSync as readFileSync27, writeFileSync as writeFileSync21 } from "fs";
13250
13263
  import { hostname as hostname2 } from "os";
13251
- import { parseDocument as parseDocument3 } from "yaml";
13264
+ import { parseDocument as parseDocument3, isMap as isMap2, isScalar } from "yaml";
13252
13265
 
13253
13266
  // src/doctor-fix.ts
13254
13267
  import { parseDocument as parseDocument2 } from "yaml";
@@ -13684,6 +13697,15 @@ async function runProvision(o, deps, write, human) {
13684
13697
  emit("connect", `set ship.${key}`, { setting: key });
13685
13698
  }
13686
13699
  }
13700
+ if (o.runner === "host") {
13701
+ emit("connect", "runner: host chosen explicitly (--runner host); not applying the managed sandbox defaults", { setting: "ship.runner", value: "host" });
13702
+ if (deps.setShipSetting("runner", "host")) summary.changed = true;
13703
+ } else if (o.runner === "sandbox" || isManagedShipKind(o.shipKind)) {
13704
+ for (const key of deps.applyManagedDefaults()) {
13705
+ summary.changed = true;
13706
+ emit("connect", `set ${key}`, { setting: key });
13707
+ }
13708
+ }
13687
13709
  for (const host of o.forgeHosts) {
13688
13710
  const token = o.tokens[host];
13689
13711
  const stored = deps.storedToken(host);
@@ -13727,6 +13749,38 @@ async function runProvision(o, deps, write, human) {
13727
13749
  emit("done", summary.state, { summary });
13728
13750
  return { code: PROVISION_EXIT.ok, summary };
13729
13751
  }
13752
+ function isManagedShipKind(kind) {
13753
+ return kind === "managed_instance" || kind === "pooled_instance" || kind === "pooled";
13754
+ }
13755
+ var MANAGED_CACHE_VOLUME = "crew-cache";
13756
+ function applyManagedDefaultsToDoc(doc) {
13757
+ const wrote = [];
13758
+ if (doc.getIn(["ship", "runner"]) === void 0) {
13759
+ doc.setIn(["ship", "runner"], "sandbox");
13760
+ wrote.push("ship.runner");
13761
+ }
13762
+ if (doc.getIn(["ship", "sandbox", "cacheVolume"]) === void 0) {
13763
+ doc.setIn(["ship", "sandbox", "cacheVolume"], MANAGED_CACHE_VOLUME);
13764
+ wrote.push("ship.sandbox.cacheVolume");
13765
+ }
13766
+ const existing = doc.getIn(["ship", "toolchains"]);
13767
+ const hasClaude = isMap2(existing) && existing.items.some((i) => (isScalar(i.key) ? i.key.value : i.key) === "claude");
13768
+ if (!hasClaude) {
13769
+ doc.setIn(["ship", "toolchains", "claude", "harness"], "claude");
13770
+ for (const k of ["bin", "model", "maxThinkingTokens"]) {
13771
+ const v = doc.getIn(["ship", "agent", k]);
13772
+ if (v !== void 0) doc.setIn(["ship", "toolchains", "claude", k], v);
13773
+ }
13774
+ }
13775
+ const toolchains = doc.getIn(["ship", "toolchains"]);
13776
+ for (const name of isMap2(toolchains) ? toolchains.items.map((i) => String(isScalar(i.key) ? i.key.value : i.key)) : []) {
13777
+ if (doc.getIn(["ship", "toolchains", name, "harness"]) !== "claude") continue;
13778
+ if (doc.getIn(["ship", "toolchains", name, "auth"]) !== void 0) continue;
13779
+ doc.setIn(["ship", "toolchains", name, "auth"], "workspace");
13780
+ wrote.push(`ship.toolchains.${name}.auth`);
13781
+ }
13782
+ return wrote;
13783
+ }
13730
13784
  function envKey(host) {
13731
13785
  return host.toUpperCase().replace(/[^A-Z0-9]/g, "_");
13732
13786
  }
@@ -13778,6 +13832,7 @@ function provisionOptionsFrom(argv2, env) {
13778
13832
  relayHost: one("relay-host"),
13779
13833
  maxConcurrentAgents: Number.isInteger(Number(one("max-concurrent-agents"))) && Number(one("max-concurrent-agents")) > 0 ? Number(one("max-concurrent-agents")) : void 0,
13780
13834
  agentBin: one("agent-bin"),
13835
+ runner: one("runner") === "host" || one("runner") === "sandbox" ? one("runner") : void 0,
13781
13836
  extraPath: one("extra-path"),
13782
13837
  baseUrl: one("base-url"),
13783
13838
  forgeHosts,
@@ -13862,6 +13917,14 @@ function realProvisionDeps(crewHome) {
13862
13917
  writeFileSync21(file, doc.toString());
13863
13918
  return true;
13864
13919
  },
13920
+ applyManagedDefaults() {
13921
+ const file = configFile();
13922
+ if (!file) return [];
13923
+ const doc = parseDocument3(readFileSync27(file, "utf8"));
13924
+ const wrote = applyManagedDefaultsToDoc(doc);
13925
+ if (wrote.length) writeFileSync21(file, doc.toString());
13926
+ return wrote;
13927
+ },
13865
13928
  storedToken(host) {
13866
13929
  const res = spawnCrew(["credential", "get"], { input: `protocol=https
13867
13930
  host=${host}
@@ -19371,7 +19434,7 @@ function renderRepoCheck(r) {
19371
19434
  }
19372
19435
 
19373
19436
  // src/repos-hooks.ts
19374
- import { isMap as isMap2, parseDocument as parseDocument6, YAMLMap } from "yaml";
19437
+ import { isMap as isMap3, parseDocument as parseDocument6, YAMLMap } from "yaml";
19375
19438
  function packageManager(f) {
19376
19439
  if (f.exists("pnpm-lock.yaml")) return "pnpm";
19377
19440
  if (f.exists("yarn.lock")) return "yarn";
@@ -19517,9 +19580,9 @@ function planRouteHooks(text2, route2, name, hooks, opts = {}) {
19517
19580
  const m = routeMapOf(doc, route2);
19518
19581
  const repos = m.get("repos", true);
19519
19582
  let target = m;
19520
- if (isMap2(repos)) {
19583
+ if (isMap3(repos)) {
19521
19584
  const entry = repos.get(name, true);
19522
- if (isMap2(entry)) target = entry;
19585
+ if (isMap3(entry)) target = entry;
19523
19586
  else {
19524
19587
  const created = new YAMLMap();
19525
19588
  created.set("dir", String(repos.get(name)));
@@ -19529,7 +19592,7 @@ function planRouteHooks(text2, route2, name, hooks, opts = {}) {
19529
19592
  }
19530
19593
  const current = target.get("hooks", true);
19531
19594
  let hooksMap;
19532
- if (isMap2(current)) hooksMap = current;
19595
+ if (isMap3(current)) hooksMap = current;
19533
19596
  else {
19534
19597
  hooksMap = new YAMLMap();
19535
19598
  target.set("hooks", hooksMap);
@@ -19554,13 +19617,13 @@ function clearRouteHooks(text2, route2, name) {
19554
19617
  const m = routeMapOf(doc, route2);
19555
19618
  const repos = m.get("repos", true);
19556
19619
  let target = m;
19557
- if (isMap2(repos)) {
19620
+ if (isMap3(repos)) {
19558
19621
  const entry = repos.get(name, true);
19559
- if (!isMap2(entry)) return { text: text2, removed: [] };
19622
+ if (!isMap3(entry)) return { text: text2, removed: [] };
19560
19623
  target = entry;
19561
19624
  }
19562
19625
  const current = target.get("hooks", true);
19563
- if (!isMap2(current)) return { text: text2, removed: [] };
19626
+ if (!isMap3(current)) return { text: text2, removed: [] };
19564
19627
  const removed = current.items.map((i) => String(i.key.value ?? i.key));
19565
19628
  target.delete("hooks");
19566
19629
  return { text: String(doc), removed };
@@ -23881,6 +23944,15 @@ if (command === "passenger-sync-daemon") {
23881
23944
  }
23882
23945
  return { problems, readiness };
23883
23946
  }
23947
+ async function doctorShipKind(route3, ship) {
23948
+ try {
23949
+ const rows = await new Tracker(route3, ship).shipRows();
23950
+ const mine = matchShipRows(rows, ship.name, doctorMachineId(ship.stateDir)).rows;
23951
+ return mine.length === 1 ? mine[0].kind ?? null : null;
23952
+ } catch {
23953
+ return null;
23954
+ }
23955
+ }
23884
23956
  async function doctorFix(o) {
23885
23957
  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))));
23886
23958
  const unitExists = (p) => existsSync45(p);
@@ -26327,7 +26399,7 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
26327
26399
  }
26328
26400
  let apiKeyFile;
26329
26401
  const apiKeyVar = "CREW_API_KEY";
26330
- if (firstRunFromFlags && agentBinSearched && !dryRun) {
26402
+ if (firstRunFromFlags && agentBinSearched && !dryRun && !isManagedShipKind(value("ship-kind"))) {
26331
26403
  const msg = `no agent binary: "claude" was not found on PATH or in ${agentBinSearched.join(", ")} \u2014 pass --agent-bin /absolute/path`;
26332
26404
  process.stderr.write(`crew connect: ${msg}
26333
26405
  `);
@@ -28234,6 +28306,9 @@ watermark: ${state.watermark()}
28234
28306
  }
28235
28307
  }
28236
28308
  for (const message of roleCapWarnings(cfg.ship)) problems.push({ severity: "note", kind: "role_cap_above_ship_cap", message });
28309
+ if (!flag("offline")) {
28310
+ for (const message of managedRuntimeWarnings(await doctorShipKind(route2, cfg.ship), cfg.ship)) problems.push({ severity: "attention", kind: "managed_runtime_default", message });
28311
+ }
28237
28312
  const previewProblem2 = await previewHostProblem(cfg);
28238
28313
  if (previewProblem2) problems.push(previewHostFinding(previewProblem2));
28239
28314
  if (cfg.routes.some((x) => x.route === route2.route && x.enabled && x.hostPeriscopes)) {
@@ -28472,6 +28547,8 @@ routes: ${cfg.routes.map((c) => c.route).join(", ")}
28472
28547
  crew manifest: ${seats2.length ? seats2.map((c) => c.name).join(", ") : "none"}
28473
28548
  `
28474
28549
  );
28550
+ for (const w of managedRuntimeWarnings(row.kind, cfg.ship)) process.stdout.write(`managed runtime: WARN ${w}
28551
+ `);
28475
28552
  }
28476
28553
  }
28477
28554
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tablation/crew",
3
- "version": "0.113.0",
3
+ "version": "0.114.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",