zooid 0.7.0 → 0.7.2
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 +124 -15
- package/dist/bin.js.map +1 -1
- package/dist/{chunk-N5POSZX5.js → chunk-O6E4CDTV.js} +805 -218
- package/dist/chunk-O6E4CDTV.js.map +1 -0
- package/dist/index.d.ts +27 -3
- package/dist/index.js +1 -1
- package/dist/web/assets/{index-BT_v3DKu.js → index-1pU3tgkr.js} +85 -75
- package/dist/web/assets/index-C-ZtBp7U.css +1 -0
- package/dist/web/assets/{index-JZMMlqDP.js → index-CXOPATwH.js} +1 -1
- package/dist/web/assets/{reaction-picker-emoji-Kh7emgFa.js → reaction-picker-emoji-DrlHZ5qt.js} +1 -1
- package/dist/web/index.html +2 -2
- package/package.json +8 -7
- package/src/bin.ts +5 -2
- package/src/build-registry.context.test.ts +1 -1
- package/src/build-registry.ts +211 -10
- package/src/build-registry.zod043.test.ts +1 -1
- package/src/build-registry.zod044.test.ts +261 -0
- package/src/commands/dev.ts +15 -7
- package/src/commands/init/generators.ts +5 -0
- package/src/commands/status.ts +7 -2
- package/src/daemon/start-daemon.ts +45 -3
- package/src/prepull-images.test.ts +149 -0
- package/src/prepull-images.ts +100 -0
- package/dist/chunk-N5POSZX5.js.map +0 -1
- package/dist/web/assets/index-DJTghnF-.css +0 -1
package/dist/bin.js
CHANGED
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
SpawnRegistry,
|
|
6
6
|
buildAcpRegistry,
|
|
7
7
|
createMatrixTransport,
|
|
8
|
+
ensureDefaultChannel,
|
|
8
9
|
ensureWorkforceSpace,
|
|
9
10
|
findConfigFile,
|
|
10
11
|
findHttpTransport,
|
|
@@ -14,7 +15,7 @@ import {
|
|
|
14
15
|
renderRegistration,
|
|
15
16
|
startDaemonSocketServer,
|
|
16
17
|
startWorkforcePublisher
|
|
17
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-O6E4CDTV.js";
|
|
18
19
|
|
|
19
20
|
// src/bin.ts
|
|
20
21
|
import { resolve as resolve5 } from "path";
|
|
@@ -401,6 +402,68 @@ function createApp({
|
|
|
401
402
|
return app;
|
|
402
403
|
}
|
|
403
404
|
|
|
405
|
+
// src/prepull-images.ts
|
|
406
|
+
import { execFile } from "child_process";
|
|
407
|
+
import { promisify } from "util";
|
|
408
|
+
var pExecFile = promisify(execFile);
|
|
409
|
+
async function prepullImages(registry, opts) {
|
|
410
|
+
if (opts.skip || opts.runtime === "local") return;
|
|
411
|
+
const exec = opts.exec ?? defaultExec;
|
|
412
|
+
const log = opts.log ?? ((l) => console.log(l));
|
|
413
|
+
const images = /* @__PURE__ */ new Set();
|
|
414
|
+
for (const name of registry.agentNames()) {
|
|
415
|
+
const img = registry.resolveSpawnImage(name);
|
|
416
|
+
if (img) images.add(img);
|
|
417
|
+
}
|
|
418
|
+
const toPull = [];
|
|
419
|
+
let cached = 0;
|
|
420
|
+
if (opts.refresh) {
|
|
421
|
+
toPull.push(...images);
|
|
422
|
+
} else {
|
|
423
|
+
await Promise.all(
|
|
424
|
+
[...images].map(async (img) => {
|
|
425
|
+
const r = await exec(opts.engine, ["image", "inspect", img]);
|
|
426
|
+
if (r.code === 0) cached++;
|
|
427
|
+
else toPull.push(img);
|
|
428
|
+
})
|
|
429
|
+
);
|
|
430
|
+
}
|
|
431
|
+
log(
|
|
432
|
+
`[zooid] image prepull: ${images.size} unique images (${cached} cached, ${toPull.length} to pull)`
|
|
433
|
+
);
|
|
434
|
+
if (toPull.length === 0) return;
|
|
435
|
+
await Promise.all(
|
|
436
|
+
toPull.map(async (img) => {
|
|
437
|
+
log(`[zooid] ${img} pulling\u2026`);
|
|
438
|
+
const start = Date.now();
|
|
439
|
+
const r = await exec(opts.engine, ["pull", img]);
|
|
440
|
+
const secs = ((Date.now() - start) / 1e3).toFixed(1);
|
|
441
|
+
if (r.code !== 0) {
|
|
442
|
+
throw new Error(
|
|
443
|
+
`image prepull failed for ${img}:
|
|
444
|
+
${r.stderr.trim() || "(no stderr)"}`
|
|
445
|
+
);
|
|
446
|
+
}
|
|
447
|
+
log(`[zooid] ${img} \u2713 ${secs}s`);
|
|
448
|
+
})
|
|
449
|
+
);
|
|
450
|
+
}
|
|
451
|
+
var defaultExec = async (cmd, args) => {
|
|
452
|
+
try {
|
|
453
|
+
const { stdout, stderr } = await pExecFile(cmd, args, {
|
|
454
|
+
maxBuffer: 16 * 1024 * 1024
|
|
455
|
+
});
|
|
456
|
+
return { code: 0, stdout, stderr };
|
|
457
|
+
} catch (err) {
|
|
458
|
+
const e = err;
|
|
459
|
+
return {
|
|
460
|
+
code: typeof e.code === "number" ? e.code : 1,
|
|
461
|
+
stdout: e.stdout ?? "",
|
|
462
|
+
stderr: e.stderr ?? String(e)
|
|
463
|
+
};
|
|
464
|
+
}
|
|
465
|
+
};
|
|
466
|
+
|
|
404
467
|
// src/daemon/start-daemon.ts
|
|
405
468
|
function listenAsync(server) {
|
|
406
469
|
return new Promise((resolve6) => {
|
|
@@ -421,7 +484,8 @@ async function startDaemon(opts = {}) {
|
|
|
421
484
|
const cwd = opts.cwd ?? process.cwd();
|
|
422
485
|
const found = opts.configPath ? { path: opts.configPath } : findConfigFile(cwd);
|
|
423
486
|
if (!found) throw new Error("zooid.yaml is required");
|
|
424
|
-
const
|
|
487
|
+
const configDir = dirname2(found.path);
|
|
488
|
+
const base = loadZooidConfig(readFileSync2(found.path, "utf8"), { configDir });
|
|
425
489
|
const config = mergeCliFlags(base, opts.cliFlags ?? {});
|
|
426
490
|
const approvals = new ApprovalCorrelator();
|
|
427
491
|
const daemonSockPath = opts.agentsDir ? join3(opts.agentsDir, "..", "run", "context.sock") : join3(tmpdir(), `zooid-context-${process.pid}.sock`);
|
|
@@ -437,14 +501,27 @@ async function startDaemon(opts = {}) {
|
|
|
437
501
|
} catch (err) {
|
|
438
502
|
console.warn("[context] daemon socket startup failed; zooid-context MCP disabled:", err);
|
|
439
503
|
}
|
|
504
|
+
const dataDir = opts.agentsDir ? dirname2(opts.agentsDir) : void 0;
|
|
440
505
|
const registry = buildAcpRegistry(config, {
|
|
441
506
|
approvals,
|
|
442
507
|
onTap: opts.onTap,
|
|
443
508
|
agentsDir: opts.agentsDir,
|
|
444
509
|
contextSpawnRegistry: contextSocket ? contextSpawnRegistry : void 0,
|
|
445
|
-
daemonSockPath: contextSocket ? daemonSockPath : void 0
|
|
510
|
+
daemonSockPath: contextSocket ? daemonSockPath : void 0,
|
|
511
|
+
configDir,
|
|
512
|
+
dataDir,
|
|
513
|
+
daemonHome: process.env.HOME
|
|
446
514
|
});
|
|
447
515
|
const agentNames = Object.keys(config.agents);
|
|
516
|
+
if (config.runtime !== "local") {
|
|
517
|
+
await prepullImages(registry, {
|
|
518
|
+
engine: config.runtime === "podman" ? "podman" : "docker",
|
|
519
|
+
runtime: config.runtime,
|
|
520
|
+
skip: opts.noPrepull ?? process.env.ZOOID_NO_PREPULL === "1",
|
|
521
|
+
refresh: opts.refreshImages ?? process.env.ZOOID_REFRESH_IMAGES === "1",
|
|
522
|
+
log: opts.prepullLog
|
|
523
|
+
});
|
|
524
|
+
}
|
|
448
525
|
console.log(
|
|
449
526
|
`[context] socket=${daemonSockPath} status=${contextSocket ? "listening" : "disabled"} agents={${agentNames.map((n) => `${n}:${registry.hasContextSpawn(n) ? "yes" : "no"}`).join(", ")}}`
|
|
450
527
|
);
|
|
@@ -481,12 +558,13 @@ async function startDaemon(opts = {}) {
|
|
|
481
558
|
hsToken: matrix.transport.hs_token,
|
|
482
559
|
adminUserId: opts.adminUserId
|
|
483
560
|
});
|
|
484
|
-
const requestedPort = matrix.transport.port ??
|
|
561
|
+
const requestedPort = matrix.transport.port ?? 9e3;
|
|
485
562
|
server = serve({ fetch: transport.app.fetch, port: requestedPort, hostname: "0.0.0.0" });
|
|
486
563
|
port = await listenAsync(server);
|
|
487
564
|
const serverName = matrix.transport.user_namespace.split(":").slice(1).join(":").replace(/\\?\)?$/, "") || new URL(matrix.transport.homeserver).hostname;
|
|
488
565
|
const asUserId = `@${matrix.transport.sender_localpart}:${serverName}`;
|
|
489
566
|
const spaceLocalpart = matrix.transport.space ?? "dev";
|
|
567
|
+
const adminUserIds = opts.adminUserId ? [opts.adminUserId] : [];
|
|
490
568
|
let spaceRoomId;
|
|
491
569
|
try {
|
|
492
570
|
spaceRoomId = await ensureWorkforceSpace({
|
|
@@ -494,14 +572,27 @@ async function startDaemon(opts = {}) {
|
|
|
494
572
|
asUserId,
|
|
495
573
|
serverName,
|
|
496
574
|
spaceLocalpart,
|
|
497
|
-
preset: "public_chat"
|
|
575
|
+
preset: "public_chat",
|
|
576
|
+
admins: adminUserIds
|
|
498
577
|
});
|
|
499
578
|
console.log(`[matrix] ensured workforce space #${spaceLocalpart}:${serverName} \u2192 ${spaceRoomId}`);
|
|
500
579
|
} catch (err) {
|
|
501
580
|
console.warn("[matrix] workforce space provisioning failed:", err);
|
|
502
581
|
}
|
|
503
|
-
await transport.bootstrap({ spaceRoomId, asUserId });
|
|
582
|
+
await transport.bootstrap({ spaceRoomId, asUserId, adminUserIds });
|
|
504
583
|
if (spaceRoomId) {
|
|
584
|
+
try {
|
|
585
|
+
const generalRoomId = await ensureDefaultChannel({
|
|
586
|
+
client,
|
|
587
|
+
asUserId,
|
|
588
|
+
serverName,
|
|
589
|
+
spaceId: spaceRoomId,
|
|
590
|
+
admins: adminUserIds
|
|
591
|
+
});
|
|
592
|
+
console.log(`[matrix] ensured default channel #general:${serverName} \u2192 ${generalRoomId}`);
|
|
593
|
+
} catch (err) {
|
|
594
|
+
console.warn("[matrix] default channel provisioning failed:", err);
|
|
595
|
+
}
|
|
505
596
|
try {
|
|
506
597
|
const publisher = await startWorkforcePublisher({
|
|
507
598
|
client,
|
|
@@ -1011,7 +1102,7 @@ async function runDev(flags) {
|
|
|
1011
1102
|
process.env.MATRIX_AS_TOKEN = tokens.asToken;
|
|
1012
1103
|
process.env.MATRIX_HS_TOKEN = tokens.hsToken;
|
|
1013
1104
|
const rawYaml = readFileSync4(found.path, "utf8");
|
|
1014
|
-
const preview = loadZooidConfig(rawYaml);
|
|
1105
|
+
const preview = loadZooidConfig(rawYaml, { configDir: dirname6(found.path) });
|
|
1015
1106
|
const matrix = findMatrixTransport(preview);
|
|
1016
1107
|
if (!matrix) {
|
|
1017
1108
|
throw new Error("zooid.yaml: zooid dev requires at least one matrix transport");
|
|
@@ -1072,7 +1163,7 @@ async function runDev(flags) {
|
|
|
1072
1163
|
},
|
|
1073
1164
|
{
|
|
1074
1165
|
title: "Start daemon",
|
|
1075
|
-
task: async () => {
|
|
1166
|
+
task: async (_ctx, t) => {
|
|
1076
1167
|
const captures = {};
|
|
1077
1168
|
for (const name of Object.keys(preview.agents)) {
|
|
1078
1169
|
captures[name] = wireAgentCapture({
|
|
@@ -1091,7 +1182,10 @@ async function runDev(flags) {
|
|
|
1091
1182
|
installSignalHandlers: false,
|
|
1092
1183
|
adminUserId: `@${flags.adminUser}:${shape.serverName}`,
|
|
1093
1184
|
agentsDir: layout.agentsDir,
|
|
1094
|
-
onTap: (agentName, event) => captures[agentName]?.onTap(event)
|
|
1185
|
+
onTap: (agentName, event) => captures[agentName]?.onTap(event),
|
|
1186
|
+
prepullLog: (line) => {
|
|
1187
|
+
t.output = line.replace(/^\[zooid\]\s+/, "").trim();
|
|
1188
|
+
}
|
|
1095
1189
|
});
|
|
1096
1190
|
}
|
|
1097
1191
|
},
|
|
@@ -1099,10 +1193,12 @@ async function runDev(flags) {
|
|
|
1099
1193
|
{
|
|
1100
1194
|
title: "Start @zoon/web watcher (vite build --watch)",
|
|
1101
1195
|
task: async () => {
|
|
1102
|
-
const pkgDir = webSourcePackage(CLI_ROOT);
|
|
1196
|
+
const pkgDir = typeof flags.watchWeb === "string" ? resolve2(flags.watchWeb) : webSourcePackage(CLI_ROOT);
|
|
1103
1197
|
if (!pkgDir) {
|
|
1198
|
+
const defaultPath = dirname6(dirname6(dirname6(CLI_ROOT))) + "/zoon/packages/web";
|
|
1104
1199
|
throw new Error(
|
|
1105
|
-
|
|
1200
|
+
`--watch-web: @zoon/web not found at ${defaultPath}.
|
|
1201
|
+
Pass an explicit path: --watch-web=/path/to/zoon/packages/web`
|
|
1106
1202
|
);
|
|
1107
1203
|
}
|
|
1108
1204
|
ctx.webWatch = await startWebWatch({ webPackageDir: pkgDir });
|
|
@@ -1218,6 +1314,11 @@ You are the zooid setup assistant. Your job is to help the operator get a workfo
|
|
|
1218
1314
|
- Zooid documentation: https://zooid.dev/docs/
|
|
1219
1315
|
- Use your web fetch tool to read documentation pages when answering questions.
|
|
1220
1316
|
|
|
1317
|
+
## Stack facts
|
|
1318
|
+
|
|
1319
|
+
- **Tuwunel** is a lightweight Rust Matrix homeserver (Conduit fork) backed by RocksDB. Single binary, low resource floor \u2014 not "heavy" for typical workforces.
|
|
1320
|
+
- **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.
|
|
1321
|
+
|
|
1221
1322
|
## Behavior
|
|
1222
1323
|
|
|
1223
1324
|
- Be terse. Give the answer; link to the doc page; stop.
|
|
@@ -1656,6 +1757,7 @@ async function runStart(flags) {
|
|
|
1656
1757
|
|
|
1657
1758
|
// src/commands/status.ts
|
|
1658
1759
|
import { readFileSync as readFileSync5 } from "fs";
|
|
1760
|
+
import { dirname as dirname8 } from "path";
|
|
1659
1761
|
import chalk2 from "chalk";
|
|
1660
1762
|
async function probe(url, timeoutMs = 2e3) {
|
|
1661
1763
|
try {
|
|
@@ -1679,7 +1781,9 @@ async function collectStatus(opts) {
|
|
|
1679
1781
|
agents: []
|
|
1680
1782
|
};
|
|
1681
1783
|
}
|
|
1682
|
-
const cfg = loadZooidConfig(readFileSync5(found.path, "utf8")
|
|
1784
|
+
const cfg = loadZooidConfig(readFileSync5(found.path, "utf8"), {
|
|
1785
|
+
configDir: dirname8(found.path)
|
|
1786
|
+
});
|
|
1683
1787
|
const matrixEntry = Object.entries(cfg.transports).find(
|
|
1684
1788
|
([, t]) => t.type === "matrix"
|
|
1685
1789
|
);
|
|
@@ -1708,7 +1812,9 @@ async function runStatus(flags) {
|
|
|
1708
1812
|
if (port === void 0) {
|
|
1709
1813
|
const found = findConfigFile(cwd);
|
|
1710
1814
|
if (found) {
|
|
1711
|
-
const cfg = loadZooidConfig(readFileSync5(found.path, "utf8")
|
|
1815
|
+
const cfg = loadZooidConfig(readFileSync5(found.path, "utf8"), {
|
|
1816
|
+
configDir: dirname8(found.path)
|
|
1817
|
+
});
|
|
1712
1818
|
const matrix = findMatrixTransport(cfg);
|
|
1713
1819
|
if (matrix) {
|
|
1714
1820
|
const userIds = Object.values(cfg.agents).filter((a) => a.matrix?.transport === matrix.name).map((a) => a.matrix.user_id);
|
|
@@ -1741,14 +1847,17 @@ cli.command("start", "Run the daemon (production entry-point)").option("--data <
|
|
|
1741
1847
|
printToken: flags.printToken
|
|
1742
1848
|
});
|
|
1743
1849
|
});
|
|
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(
|
|
1850
|
+
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(
|
|
1851
|
+
"--watch-web [path]",
|
|
1852
|
+
"Run vite build --watch on @zoon/web. Path defaults to sibling ../zoon/packages/web."
|
|
1853
|
+
).action(async (flags) => {
|
|
1745
1854
|
await runDev({
|
|
1746
1855
|
dataDir: flags.data,
|
|
1747
1856
|
engine: flags.engine,
|
|
1748
1857
|
uiPort: Number(flags.uiPort),
|
|
1749
1858
|
adminUser: flags.adminUser,
|
|
1750
1859
|
adminPassword: flags.adminPassword,
|
|
1751
|
-
watchWeb:
|
|
1860
|
+
watchWeb: flags.watchWeb
|
|
1752
1861
|
});
|
|
1753
1862
|
});
|
|
1754
1863
|
cli.command(
|