zooid 0.7.0 → 0.7.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.
package/dist/bin.js CHANGED
@@ -14,7 +14,7 @@ import {
14
14
  renderRegistration,
15
15
  startDaemonSocketServer,
16
16
  startWorkforcePublisher
17
- } from "./chunk-N5POSZX5.js";
17
+ } from "./chunk-SUPTPSN3.js";
18
18
 
19
19
  // src/bin.ts
20
20
  import { resolve as resolve5 } from "path";
@@ -401,6 +401,68 @@ function createApp({
401
401
  return app;
402
402
  }
403
403
 
404
+ // src/prepull-images.ts
405
+ import { execFile } from "child_process";
406
+ import { promisify } from "util";
407
+ var pExecFile = promisify(execFile);
408
+ async function prepullImages(registry, opts) {
409
+ if (opts.skip || opts.runtime === "local") return;
410
+ const exec = opts.exec ?? defaultExec;
411
+ const log = opts.log ?? ((l) => console.log(l));
412
+ const images = /* @__PURE__ */ new Set();
413
+ for (const name of registry.agentNames()) {
414
+ const img = registry.resolveSpawnImage(name);
415
+ if (img) images.add(img);
416
+ }
417
+ const toPull = [];
418
+ let cached = 0;
419
+ if (opts.refresh) {
420
+ toPull.push(...images);
421
+ } else {
422
+ await Promise.all(
423
+ [...images].map(async (img) => {
424
+ const r = await exec(opts.engine, ["image", "inspect", img]);
425
+ if (r.code === 0) cached++;
426
+ else toPull.push(img);
427
+ })
428
+ );
429
+ }
430
+ log(
431
+ `[zooid] image prepull: ${images.size} unique images (${cached} cached, ${toPull.length} to pull)`
432
+ );
433
+ if (toPull.length === 0) return;
434
+ await Promise.all(
435
+ toPull.map(async (img) => {
436
+ log(`[zooid] ${img} pulling\u2026`);
437
+ const start = Date.now();
438
+ const r = await exec(opts.engine, ["pull", img]);
439
+ const secs = ((Date.now() - start) / 1e3).toFixed(1);
440
+ if (r.code !== 0) {
441
+ throw new Error(
442
+ `image prepull failed for ${img}:
443
+ ${r.stderr.trim() || "(no stderr)"}`
444
+ );
445
+ }
446
+ log(`[zooid] ${img} \u2713 ${secs}s`);
447
+ })
448
+ );
449
+ }
450
+ var defaultExec = async (cmd, args) => {
451
+ try {
452
+ const { stdout, stderr } = await pExecFile(cmd, args, {
453
+ maxBuffer: 16 * 1024 * 1024
454
+ });
455
+ return { code: 0, stdout, stderr };
456
+ } catch (err) {
457
+ const e = err;
458
+ return {
459
+ code: typeof e.code === "number" ? e.code : 1,
460
+ stdout: e.stdout ?? "",
461
+ stderr: e.stderr ?? String(e)
462
+ };
463
+ }
464
+ };
465
+
404
466
  // src/daemon/start-daemon.ts
405
467
  function listenAsync(server) {
406
468
  return new Promise((resolve6) => {
@@ -421,7 +483,8 @@ async function startDaemon(opts = {}) {
421
483
  const cwd = opts.cwd ?? process.cwd();
422
484
  const found = opts.configPath ? { path: opts.configPath } : findConfigFile(cwd);
423
485
  if (!found) throw new Error("zooid.yaml is required");
424
- const base = loadZooidConfig(readFileSync2(found.path, "utf8"));
486
+ const configDir = dirname2(found.path);
487
+ const base = loadZooidConfig(readFileSync2(found.path, "utf8"), { configDir });
425
488
  const config = mergeCliFlags(base, opts.cliFlags ?? {});
426
489
  const approvals = new ApprovalCorrelator();
427
490
  const daemonSockPath = opts.agentsDir ? join3(opts.agentsDir, "..", "run", "context.sock") : join3(tmpdir(), `zooid-context-${process.pid}.sock`);
@@ -437,14 +500,27 @@ async function startDaemon(opts = {}) {
437
500
  } catch (err) {
438
501
  console.warn("[context] daemon socket startup failed; zooid-context MCP disabled:", err);
439
502
  }
503
+ const dataDir = opts.agentsDir ? dirname2(opts.agentsDir) : void 0;
440
504
  const registry = buildAcpRegistry(config, {
441
505
  approvals,
442
506
  onTap: opts.onTap,
443
507
  agentsDir: opts.agentsDir,
444
508
  contextSpawnRegistry: contextSocket ? contextSpawnRegistry : void 0,
445
- daemonSockPath: contextSocket ? daemonSockPath : void 0
509
+ daemonSockPath: contextSocket ? daemonSockPath : void 0,
510
+ configDir,
511
+ dataDir,
512
+ daemonHome: process.env.HOME
446
513
  });
447
514
  const agentNames = Object.keys(config.agents);
515
+ if (config.runtime !== "local") {
516
+ await prepullImages(registry, {
517
+ engine: config.runtime === "podman" ? "podman" : "docker",
518
+ runtime: config.runtime,
519
+ skip: opts.noPrepull ?? process.env.ZOOID_NO_PREPULL === "1",
520
+ refresh: opts.refreshImages ?? process.env.ZOOID_REFRESH_IMAGES === "1",
521
+ log: opts.prepullLog
522
+ });
523
+ }
448
524
  console.log(
449
525
  `[context] socket=${daemonSockPath} status=${contextSocket ? "listening" : "disabled"} agents={${agentNames.map((n) => `${n}:${registry.hasContextSpawn(n) ? "yes" : "no"}`).join(", ")}}`
450
526
  );
@@ -1011,7 +1087,7 @@ async function runDev(flags) {
1011
1087
  process.env.MATRIX_AS_TOKEN = tokens.asToken;
1012
1088
  process.env.MATRIX_HS_TOKEN = tokens.hsToken;
1013
1089
  const rawYaml = readFileSync4(found.path, "utf8");
1014
- const preview = loadZooidConfig(rawYaml);
1090
+ const preview = loadZooidConfig(rawYaml, { configDir: dirname6(found.path) });
1015
1091
  const matrix = findMatrixTransport(preview);
1016
1092
  if (!matrix) {
1017
1093
  throw new Error("zooid.yaml: zooid dev requires at least one matrix transport");
@@ -1072,7 +1148,7 @@ async function runDev(flags) {
1072
1148
  },
1073
1149
  {
1074
1150
  title: "Start daemon",
1075
- task: async () => {
1151
+ task: async (_ctx, t) => {
1076
1152
  const captures = {};
1077
1153
  for (const name of Object.keys(preview.agents)) {
1078
1154
  captures[name] = wireAgentCapture({
@@ -1091,7 +1167,10 @@ async function runDev(flags) {
1091
1167
  installSignalHandlers: false,
1092
1168
  adminUserId: `@${flags.adminUser}:${shape.serverName}`,
1093
1169
  agentsDir: layout.agentsDir,
1094
- onTap: (agentName, event) => captures[agentName]?.onTap(event)
1170
+ onTap: (agentName, event) => captures[agentName]?.onTap(event),
1171
+ prepullLog: (line) => {
1172
+ t.output = line.replace(/^\[zooid\]\s+/, "").trim();
1173
+ }
1095
1174
  });
1096
1175
  }
1097
1176
  },
@@ -1099,10 +1178,12 @@ async function runDev(flags) {
1099
1178
  {
1100
1179
  title: "Start @zoon/web watcher (vite build --watch)",
1101
1180
  task: async () => {
1102
- const pkgDir = webSourcePackage(CLI_ROOT);
1181
+ const pkgDir = typeof flags.watchWeb === "string" ? resolve2(flags.watchWeb) : webSourcePackage(CLI_ROOT);
1103
1182
  if (!pkgDir) {
1183
+ const defaultPath = dirname6(dirname6(dirname6(CLI_ROOT))) + "/zoon/packages/web";
1104
1184
  throw new Error(
1105
- "--watch-web requires running from the monorepo source (no zoon/packages/web found)."
1185
+ `--watch-web: @zoon/web not found at ${defaultPath}.
1186
+ Pass an explicit path: --watch-web=/path/to/zoon/packages/web`
1106
1187
  );
1107
1188
  }
1108
1189
  ctx.webWatch = await startWebWatch({ webPackageDir: pkgDir });
@@ -1218,6 +1299,11 @@ You are the zooid setup assistant. Your job is to help the operator get a workfo
1218
1299
  - Zooid documentation: https://zooid.dev/docs/
1219
1300
  - Use your web fetch tool to read documentation pages when answering questions.
1220
1301
 
1302
+ ## Stack facts
1303
+
1304
+ - **Tuwunel** is a lightweight Rust Matrix homeserver (Conduit fork) backed by RocksDB. Single binary, low resource floor \u2014 not "heavy" for typical workforces.
1305
+ - **Logs land on disk** in \`./data/\` inside this zooid home dir: tuwunel logs, the daemon, and each agent's ACP stream. Use \`zooid logs <source>\` to tail them (sources: \`tuwunel\`, \`daemon\`, \`dev\`, \`agent-<name>\`, \`agent-<name>.acp\`). Debugging is mostly "read the log file" \u2014 don't tell the operator it's opaque.
1306
+
1221
1307
  ## Behavior
1222
1308
 
1223
1309
  - Be terse. Give the answer; link to the doc page; stop.
@@ -1656,6 +1742,7 @@ async function runStart(flags) {
1656
1742
 
1657
1743
  // src/commands/status.ts
1658
1744
  import { readFileSync as readFileSync5 } from "fs";
1745
+ import { dirname as dirname8 } from "path";
1659
1746
  import chalk2 from "chalk";
1660
1747
  async function probe(url, timeoutMs = 2e3) {
1661
1748
  try {
@@ -1679,7 +1766,9 @@ async function collectStatus(opts) {
1679
1766
  agents: []
1680
1767
  };
1681
1768
  }
1682
- const cfg = loadZooidConfig(readFileSync5(found.path, "utf8"));
1769
+ const cfg = loadZooidConfig(readFileSync5(found.path, "utf8"), {
1770
+ configDir: dirname8(found.path)
1771
+ });
1683
1772
  const matrixEntry = Object.entries(cfg.transports).find(
1684
1773
  ([, t]) => t.type === "matrix"
1685
1774
  );
@@ -1708,7 +1797,9 @@ async function runStatus(flags) {
1708
1797
  if (port === void 0) {
1709
1798
  const found = findConfigFile(cwd);
1710
1799
  if (found) {
1711
- const cfg = loadZooidConfig(readFileSync5(found.path, "utf8"));
1800
+ const cfg = loadZooidConfig(readFileSync5(found.path, "utf8"), {
1801
+ configDir: dirname8(found.path)
1802
+ });
1712
1803
  const matrix = findMatrixTransport(cfg);
1713
1804
  if (matrix) {
1714
1805
  const userIds = Object.values(cfg.agents).filter((a) => a.matrix?.transport === matrix.name).map((a) => a.matrix.user_id);
@@ -1741,14 +1832,17 @@ cli.command("start", "Run the daemon (production entry-point)").option("--data <
1741
1832
  printToken: flags.printToken
1742
1833
  });
1743
1834
  });
1744
- cli.command("dev", "Tuwunel + daemon + UI for local development").option("--data <dir>", "Persistent data root dir", { default: "./data" }).option("--engine <docker|podman>", "Container engine", { default: "docker" }).option("--ui-port <n>", "UI HTTP port", { default: 5173 }).option("--admin-user <name>", "Admin username", { default: "admin" }).option("--admin-password <pw>", "Admin password", { default: "admin" }).option("--watch-web", "Run vite build --watch on @zoon/web (monorepo source only)").action(async (flags) => {
1835
+ cli.command("dev", "Tuwunel + daemon + UI for local development").option("--data <dir>", "Persistent data root dir", { default: "./data" }).option("--engine <docker|podman>", "Container engine", { default: "docker" }).option("--ui-port <n>", "UI HTTP port", { default: 5173 }).option("--admin-user <name>", "Admin username", { default: "admin" }).option("--admin-password <pw>", "Admin password", { default: "admin" }).option(
1836
+ "--watch-web [path]",
1837
+ "Run vite build --watch on @zoon/web. Path defaults to sibling ../zoon/packages/web."
1838
+ ).action(async (flags) => {
1745
1839
  await runDev({
1746
1840
  dataDir: flags.data,
1747
1841
  engine: flags.engine,
1748
1842
  uiPort: Number(flags.uiPort),
1749
1843
  adminUser: flags.adminUser,
1750
1844
  adminPassword: flags.adminPassword,
1751
- watchWeb: Boolean(flags.watchWeb)
1845
+ watchWeb: flags.watchWeb
1752
1846
  });
1753
1847
  });
1754
1848
  cli.command(