zooid 0.7.3 → 0.8.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.
- package/dist/bin.js +214 -109
- package/dist/bin.js.map +1 -1
- package/dist/{chunk-3IKBBKGI.js → chunk-R6EDLH23.js} +283 -11
- package/dist/chunk-R6EDLH23.js.map +1 -0
- package/dist/index.js +1 -1
- package/package.json +12 -8
- package/src/bin.ts +1 -1
- package/src/commands/dev.ts +23 -9
- package/src/daemon/start-daemon.ts +20 -1
- package/src/web/fetch.integration.test.ts +61 -0
- package/src/web/fetch.test.ts +139 -0
- package/src/web/fetch.ts +85 -0
- package/src/web/pin.test.ts +24 -0
- package/src/web/pin.ts +13 -0
- package/src/web/resolve.test.ts +54 -26
- package/src/web/resolve.ts +22 -10
- package/src/web/test-helpers.ts +28 -0
- package/dist/chunk-3IKBBKGI.js.map +0 -1
- package/dist/web/assets/geist-cyrillic-wght-normal-CHSlOQsW.woff2 +0 -0
- package/dist/web/assets/geist-latin-ext-wght-normal-DMtmJ5ZE.woff2 +0 -0
- package/dist/web/assets/geist-latin-wght-normal-Dm3htQBi.woff2 +0 -0
- package/dist/web/assets/index-BqOX0Pv4.js +0 -305
- package/dist/web/assets/index-C-ZtBp7U.css +0 -1
- package/dist/web/assets/index-Dc8BJYf_.js +0 -118066
- package/dist/web/assets/reaction-picker-emoji-xD2HkULN.js +0 -716
- package/dist/web/favicon.svg +0 -1
- package/dist/web/index.html +0 -14
package/dist/bin.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import {
|
|
3
3
|
ApprovalCorrelator,
|
|
4
4
|
MatrixClient,
|
|
5
|
+
MediaClient,
|
|
5
6
|
SpawnRegistry,
|
|
6
7
|
buildAcpRegistry,
|
|
7
8
|
createMatrixTransport,
|
|
@@ -15,17 +16,17 @@ import {
|
|
|
15
16
|
renderRegistration,
|
|
16
17
|
startDaemonSocketServer,
|
|
17
18
|
startWorkforcePublisher
|
|
18
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-R6EDLH23.js";
|
|
19
20
|
|
|
20
21
|
// src/bin.ts
|
|
21
|
-
import { resolve as
|
|
22
|
+
import { resolve as resolve6 } from "path";
|
|
22
23
|
import { cac } from "cac";
|
|
23
24
|
|
|
24
25
|
// src/commands/dev.ts
|
|
25
26
|
import chalk from "chalk";
|
|
26
27
|
import { Listr } from "listr2";
|
|
27
|
-
import { existsSync as
|
|
28
|
-
import { dirname as dirname6, resolve as
|
|
28
|
+
import { existsSync as existsSync5, readFileSync as readFileSync5 } from "fs";
|
|
29
|
+
import { dirname as dirname6, join as join10, resolve as resolve3 } from "path";
|
|
29
30
|
import { fileURLToPath } from "url";
|
|
30
31
|
import { serve as serve2 } from "@hono/node-server";
|
|
31
32
|
|
|
@@ -189,7 +190,7 @@ MATRIX_HS_TOKEN=${tokens.hsToken}
|
|
|
189
190
|
import { readFileSync as readFileSync2 } from "fs";
|
|
190
191
|
import { mkdir, unlink } from "fs/promises";
|
|
191
192
|
import { tmpdir } from "os";
|
|
192
|
-
import { dirname as dirname2, join as join3 } from "path";
|
|
193
|
+
import { dirname as dirname2, isAbsolute, join as join3, resolve } from "path";
|
|
193
194
|
import { serve } from "@hono/node-server";
|
|
194
195
|
|
|
195
196
|
// ../transport-http/src/server.ts
|
|
@@ -466,18 +467,18 @@ var defaultExec = async (cmd, args) => {
|
|
|
466
467
|
|
|
467
468
|
// src/daemon/start-daemon.ts
|
|
468
469
|
function listenAsync(server) {
|
|
469
|
-
return new Promise((
|
|
470
|
+
return new Promise((resolve7) => {
|
|
470
471
|
const check = () => {
|
|
471
472
|
const addr = server.address();
|
|
472
|
-
if (addr && typeof addr === "object")
|
|
473
|
+
if (addr && typeof addr === "object") resolve7(addr.port);
|
|
473
474
|
else setImmediate(check);
|
|
474
475
|
};
|
|
475
476
|
check();
|
|
476
477
|
});
|
|
477
478
|
}
|
|
478
479
|
function closeAsync(server) {
|
|
479
|
-
return new Promise((
|
|
480
|
-
server.close(() =>
|
|
480
|
+
return new Promise((resolve7) => {
|
|
481
|
+
server.close(() => resolve7());
|
|
481
482
|
});
|
|
482
483
|
}
|
|
483
484
|
async function startDaemon(opts = {}) {
|
|
@@ -538,6 +539,11 @@ async function startDaemon(opts = {}) {
|
|
|
538
539
|
homeserver: matrix.transport.homeserver,
|
|
539
540
|
asToken: matrix.transport.as_token
|
|
540
541
|
});
|
|
542
|
+
const mediaClient = new MediaClient({
|
|
543
|
+
homeserver: matrix.transport.homeserver,
|
|
544
|
+
asToken: matrix.transport.as_token
|
|
545
|
+
});
|
|
546
|
+
const isContainerRuntime = config.runtime !== "local";
|
|
541
547
|
const bindings = [];
|
|
542
548
|
for (const a of Object.values(config.agents)) {
|
|
543
549
|
if (a.matrix?.transport !== matrix.name) continue;
|
|
@@ -548,6 +554,9 @@ async function startDaemon(opts = {}) {
|
|
|
548
554
|
trigger: a.matrix.trigger
|
|
549
555
|
};
|
|
550
556
|
if (a.matrix.display_name !== void 0) binding.displayName = a.matrix.display_name;
|
|
557
|
+
const workspaceDir = isAbsolute(a.workdir) ? a.workdir : resolve(configDir, a.workdir);
|
|
558
|
+
binding.workspaceDir = workspaceDir;
|
|
559
|
+
binding.agentWorkspacePath = isContainerRuntime ? "/workspace" : workspaceDir;
|
|
551
560
|
bindings.push(binding);
|
|
552
561
|
}
|
|
553
562
|
const transport = createMatrixTransport({
|
|
@@ -556,7 +565,8 @@ async function startDaemon(opts = {}) {
|
|
|
556
565
|
client,
|
|
557
566
|
bindings,
|
|
558
567
|
hsToken: matrix.transport.hs_token,
|
|
559
|
-
adminUserId: opts.adminUserId
|
|
568
|
+
adminUserId: opts.adminUserId,
|
|
569
|
+
media: mediaClient
|
|
560
570
|
});
|
|
561
571
|
const requestedPort = matrix.transport.port ?? 9e3;
|
|
562
572
|
server = serve({ fetch: transport.app.fetch, port: requestedPort, hostname: "0.0.0.0" });
|
|
@@ -573,7 +583,8 @@ async function startDaemon(opts = {}) {
|
|
|
573
583
|
serverName,
|
|
574
584
|
spaceLocalpart,
|
|
575
585
|
preset: "public_chat",
|
|
576
|
-
admins: adminUserIds
|
|
586
|
+
admins: adminUserIds,
|
|
587
|
+
joinRule: opts.publicWorkforceSpace ? "public" : "invite"
|
|
577
588
|
});
|
|
578
589
|
console.log(`[matrix] ensured workforce space #${spaceLocalpart}:${serverName} \u2192 ${spaceRoomId}`);
|
|
579
590
|
} catch (err) {
|
|
@@ -678,6 +689,7 @@ var TuwunelService = class {
|
|
|
678
689
|
constructor(opts) {
|
|
679
690
|
this.opts = opts;
|
|
680
691
|
}
|
|
692
|
+
opts;
|
|
681
693
|
child = null;
|
|
682
694
|
start() {
|
|
683
695
|
this.child = spawn(this.opts.engine, buildRunArgs(this.opts), {
|
|
@@ -688,8 +700,8 @@ var TuwunelService = class {
|
|
|
688
700
|
async stop() {
|
|
689
701
|
if (this.child && this.child.exitCode === null) {
|
|
690
702
|
this.child.kill("SIGTERM");
|
|
691
|
-
await new Promise((
|
|
692
|
-
this.child.on("exit", () =>
|
|
703
|
+
await new Promise((resolve7) => {
|
|
704
|
+
this.child.on("exit", () => resolve7());
|
|
693
705
|
});
|
|
694
706
|
}
|
|
695
707
|
this.child = null;
|
|
@@ -706,7 +718,7 @@ var TuwunelService = class {
|
|
|
706
718
|
`Tuwunel container exited before serving HTTP (exit=${state.exitCode}${state.error ? `, error=${state.error}` : ""}). Check the engine logs (\`${this.opts.engine} logs ${this.opts.name}\`).`
|
|
707
719
|
);
|
|
708
720
|
}
|
|
709
|
-
await new Promise((
|
|
721
|
+
await new Promise((resolve7) => setTimeout(resolve7, 500));
|
|
710
722
|
}
|
|
711
723
|
while (Date.now() < deadline) {
|
|
712
724
|
try {
|
|
@@ -714,7 +726,7 @@ var TuwunelService = class {
|
|
|
714
726
|
if (r.ok) return;
|
|
715
727
|
} catch {
|
|
716
728
|
}
|
|
717
|
-
await new Promise((
|
|
729
|
+
await new Promise((resolve7) => setTimeout(resolve7, 500));
|
|
718
730
|
}
|
|
719
731
|
const finalState = await this.inspectState().catch(() => null);
|
|
720
732
|
const detail = finalState ? ` (container status=${finalState.status}${finalState.exitCode !== void 0 ? `, exit=${finalState.exitCode}` : ""})` : "";
|
|
@@ -747,12 +759,12 @@ var TuwunelService = class {
|
|
|
747
759
|
}
|
|
748
760
|
};
|
|
749
761
|
function execEngine(engine, args) {
|
|
750
|
-
return new Promise((
|
|
762
|
+
return new Promise((resolve7, reject) => {
|
|
751
763
|
const child = spawn(engine, args, { stdio: "pipe" });
|
|
752
764
|
let stderr = "";
|
|
753
765
|
child.stderr.on("data", (b) => stderr += String(b));
|
|
754
766
|
child.on("exit", (code) => {
|
|
755
|
-
if (code === 0)
|
|
767
|
+
if (code === 0) resolve7();
|
|
756
768
|
else reject(new Error(`${engine} ${args.join(" ")} failed: ${stderr.trim()}`));
|
|
757
769
|
});
|
|
758
770
|
child.on("error", reject);
|
|
@@ -760,34 +772,115 @@ function execEngine(engine, args) {
|
|
|
760
772
|
}
|
|
761
773
|
|
|
762
774
|
// src/web/resolve.ts
|
|
763
|
-
import { existsSync as
|
|
764
|
-
import { dirname as dirname3, join as
|
|
775
|
+
import { existsSync as existsSync3 } from "fs";
|
|
776
|
+
import { dirname as dirname3, join as join5, resolve as resolve2 } from "path";
|
|
777
|
+
|
|
778
|
+
// src/web/fetch.ts
|
|
779
|
+
import { mkdirSync as mkdirSync3, renameSync, rmSync, existsSync as existsSync2, readdirSync, writeFileSync as writeFileSync3 } from "fs";
|
|
780
|
+
import { join as join4 } from "path";
|
|
781
|
+
import { createHash, randomUUID as randomUUID2 } from "crypto";
|
|
782
|
+
import * as tar from "tar";
|
|
783
|
+
var PKG = "@zooid/zoon-web";
|
|
784
|
+
var DEFAULT_REGISTRY = "https://registry.npmjs.org";
|
|
785
|
+
async function fetchWebBundle(opts) {
|
|
786
|
+
const target = join4(opts.cacheDir, opts.version);
|
|
787
|
+
if (existsSync2(join4(target, "index.html"))) return target;
|
|
788
|
+
if (existsSync2(target)) rmSync(target, { recursive: true, force: true });
|
|
789
|
+
const f = opts.fetch ?? globalThis.fetch;
|
|
790
|
+
const registry = (opts.registryUrl ?? DEFAULT_REGISTRY).replace(/\/$/, "");
|
|
791
|
+
let tgz;
|
|
792
|
+
try {
|
|
793
|
+
const metaRes = await f(`${registry}/${PKG}`);
|
|
794
|
+
if (!metaRes.ok) throw new Error(`registry metadata: HTTP ${metaRes.status}`);
|
|
795
|
+
const meta = await metaRes.json();
|
|
796
|
+
const dist = meta.versions?.[opts.version]?.dist;
|
|
797
|
+
if (!dist?.tarball || !dist.integrity) {
|
|
798
|
+
throw new Error(`${PKG}@${opts.version} not found on the registry`);
|
|
799
|
+
}
|
|
800
|
+
const tgzRes = await f(dist.tarball);
|
|
801
|
+
if (!tgzRes.ok) throw new Error(`tarball: HTTP ${tgzRes.status}`);
|
|
802
|
+
tgz = Buffer.from(await tgzRes.arrayBuffer());
|
|
803
|
+
const sha = "sha512-" + createHash("sha512").update(tgz).digest("base64");
|
|
804
|
+
if (sha !== dist.integrity) {
|
|
805
|
+
throw new Error(`integrity mismatch for ${PKG}@${opts.version}: got ${sha}`);
|
|
806
|
+
}
|
|
807
|
+
} catch (err) {
|
|
808
|
+
throw new Error(
|
|
809
|
+
`Failed to fetch ${PKG}@${opts.version} from ${registry}.
|
|
810
|
+
Cache: ${opts.cacheDir}
|
|
811
|
+
Offline? Point ZOOID_DEV_WEB_ROOT_OVERRIDE at a built dist as a manual escape hatch.
|
|
812
|
+
Cause: ${err instanceof Error ? err.message : String(err)}`
|
|
813
|
+
);
|
|
814
|
+
}
|
|
815
|
+
const tmp = join4(opts.cacheDir, `.tmp-${randomUUID2().slice(0, 8)}`);
|
|
816
|
+
mkdirSync3(tmp, { recursive: true });
|
|
817
|
+
const tgzPath = join4(tmp, ".bundle.tgz");
|
|
818
|
+
try {
|
|
819
|
+
writeFileSync3(tgzPath, tgz);
|
|
820
|
+
await tar.extract({
|
|
821
|
+
file: tgzPath,
|
|
822
|
+
cwd: tmp,
|
|
823
|
+
strip: 2,
|
|
824
|
+
// package/dist/<file> → <file>
|
|
825
|
+
filter: (p) => p === "package/dist" || p.startsWith("package/dist/")
|
|
826
|
+
});
|
|
827
|
+
rmSync(tgzPath);
|
|
828
|
+
if (!existsSync2(join4(tmp, "index.html"))) {
|
|
829
|
+
throw new Error(`${PKG}@${opts.version} tarball has no dist/index.html`);
|
|
830
|
+
}
|
|
831
|
+
renameSync(tmp, target);
|
|
832
|
+
} catch (err) {
|
|
833
|
+
rmSync(tmp, { recursive: true, force: true });
|
|
834
|
+
throw err;
|
|
835
|
+
}
|
|
836
|
+
for (const entry of readdirSync(opts.cacheDir)) {
|
|
837
|
+
if (entry !== opts.version) {
|
|
838
|
+
rmSync(join4(opts.cacheDir, entry), { recursive: true, force: true });
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
return target;
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
// src/web/resolve.ts
|
|
765
845
|
var ENV_OVERRIDE = "ZOOID_DEV_WEB_ROOT_OVERRIDE";
|
|
766
|
-
function
|
|
846
|
+
async function ensureWebRoot(opts) {
|
|
767
847
|
const override = process.env[ENV_OVERRIDE];
|
|
768
|
-
if (override &&
|
|
769
|
-
const
|
|
770
|
-
if (
|
|
771
|
-
|
|
772
|
-
if (fromSource && existsSync2(join4(fromSource, "dist", "index.html"))) {
|
|
773
|
-
return join4(fromSource, "dist");
|
|
848
|
+
if (override && existsSync3(join5(override, "index.html"))) return resolve2(override);
|
|
849
|
+
const fromSource = webSourcePackage(opts.cliRoot);
|
|
850
|
+
if (fromSource && existsSync3(join5(fromSource, "dist", "index.html"))) {
|
|
851
|
+
return join5(fromSource, "dist");
|
|
774
852
|
}
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
853
|
+
if (!opts.version) {
|
|
854
|
+
throw new Error(
|
|
855
|
+
`No @zooid/zoon-web version pin (zooid.zoonWebVersion) in the cli package.json and no monorepo sibling build found.
|
|
856
|
+
Set ${ENV_OVERRIDE} to a built dist, or run from the monorepo.`
|
|
857
|
+
);
|
|
858
|
+
}
|
|
859
|
+
opts.onProgress?.(`Fetching @zooid/zoon-web ${opts.version}\u2026`);
|
|
860
|
+
const fetchBundleFn = opts.fetchBundle ?? fetchWebBundle;
|
|
861
|
+
return fetchBundleFn({ version: opts.version, cacheDir: opts.cacheDir });
|
|
781
862
|
}
|
|
782
863
|
function webSourcePackage(cliRoot) {
|
|
783
864
|
const workspaceRoot = dirname3(dirname3(dirname3(cliRoot)));
|
|
784
|
-
const candidate =
|
|
785
|
-
return
|
|
865
|
+
const candidate = join5(workspaceRoot, "zoon", "packages", "web");
|
|
866
|
+
return existsSync3(join5(candidate, "package.json")) ? candidate : null;
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
// src/web/pin.ts
|
|
870
|
+
import { readFileSync as readFileSync3 } from "fs";
|
|
871
|
+
import { join as join6 } from "path";
|
|
872
|
+
function readZoonWebPin(cliRoot) {
|
|
873
|
+
try {
|
|
874
|
+
const pkg = JSON.parse(readFileSync3(join6(cliRoot, "package.json"), "utf8"));
|
|
875
|
+
return pkg.zooid?.zoonWebVersion;
|
|
876
|
+
} catch {
|
|
877
|
+
return void 0;
|
|
878
|
+
}
|
|
786
879
|
}
|
|
787
880
|
|
|
788
881
|
// src/web/static.ts
|
|
789
|
-
import { readFileSync as
|
|
790
|
-
import { extname, join as
|
|
882
|
+
import { readFileSync as readFileSync4, statSync } from "fs";
|
|
883
|
+
import { extname, join as join7, normalize } from "path";
|
|
791
884
|
import { Hono as Hono2 } from "hono";
|
|
792
885
|
var MIME = {
|
|
793
886
|
".html": "text/html; charset=utf-8",
|
|
@@ -813,19 +906,19 @@ function webStatic(opts) {
|
|
|
813
906
|
const requested = decodeURIComponent(url.pathname);
|
|
814
907
|
const wantFile = requested === "/" ? "/index.html" : requested;
|
|
815
908
|
const safe = normalize(wantFile).replace(/^(\.\.[/\\])+/g, "");
|
|
816
|
-
const filePath =
|
|
909
|
+
const filePath = join7(opts.webRoot, safe);
|
|
817
910
|
if (!filePath.startsWith(opts.webRoot)) return c.notFound();
|
|
818
911
|
try {
|
|
819
912
|
const stat = statSync(filePath);
|
|
820
913
|
if (stat.isFile()) {
|
|
821
|
-
const body =
|
|
914
|
+
const body = readFileSync4(filePath);
|
|
822
915
|
const ct = MIME[extname(filePath)] ?? "application/octet-stream";
|
|
823
916
|
return c.body(body, 200, { "content-type": ct });
|
|
824
917
|
}
|
|
825
918
|
} catch {
|
|
826
919
|
}
|
|
827
920
|
if (isAssetPath(requested)) return c.notFound();
|
|
828
|
-
const indexBytes =
|
|
921
|
+
const indexBytes = readFileSync4(join7(opts.webRoot, "index.html"));
|
|
829
922
|
return c.body(indexBytes, 200, {
|
|
830
923
|
"content-type": MIME[".html"]
|
|
831
924
|
});
|
|
@@ -835,11 +928,11 @@ function webStatic(opts) {
|
|
|
835
928
|
|
|
836
929
|
// src/web/watch.ts
|
|
837
930
|
import { spawn as spawn2 } from "child_process";
|
|
838
|
-
import { existsSync as
|
|
839
|
-
import { join as
|
|
931
|
+
import { existsSync as existsSync4, statSync as statSync2 } from "fs";
|
|
932
|
+
import { join as join8 } from "path";
|
|
840
933
|
async function startWebWatch(opts) {
|
|
841
|
-
const distPath =
|
|
842
|
-
const indexPath =
|
|
934
|
+
const distPath = join8(opts.webPackageDir, "dist");
|
|
935
|
+
const indexPath = join8(distPath, "index.html");
|
|
843
936
|
const timeoutMs = opts.firstBuildTimeoutMs ?? 6e4;
|
|
844
937
|
const spawnTime = Date.now();
|
|
845
938
|
const child = spawn2(
|
|
@@ -873,7 +966,7 @@ async function startWebWatch(opts) {
|
|
|
873
966
|
attachStdio();
|
|
874
967
|
throw new Error(`vite build --watch exited before first build (code ${child.exitCode})`);
|
|
875
968
|
}
|
|
876
|
-
if (
|
|
969
|
+
if (existsSync4(indexPath)) {
|
|
877
970
|
const m = statSync2(indexPath).mtimeMs;
|
|
878
971
|
if (m >= spawnTime - 500) return makeHandle();
|
|
879
972
|
}
|
|
@@ -893,12 +986,12 @@ async function startWebWatch(opts) {
|
|
|
893
986
|
async function stopChild(child) {
|
|
894
987
|
if (child.exitCode !== null) return;
|
|
895
988
|
child.kill("SIGTERM");
|
|
896
|
-
await new Promise((
|
|
989
|
+
await new Promise((resolve7) => {
|
|
897
990
|
let done = false;
|
|
898
991
|
const finish = () => {
|
|
899
992
|
if (done) return;
|
|
900
993
|
done = true;
|
|
901
|
-
|
|
994
|
+
resolve7();
|
|
902
995
|
};
|
|
903
996
|
child.once("exit", finish);
|
|
904
997
|
setTimeout(() => {
|
|
@@ -913,7 +1006,7 @@ async function stopChild(child) {
|
|
|
913
1006
|
|
|
914
1007
|
// src/observability/paths.ts
|
|
915
1008
|
import { mkdir as mkdir2, readdir, rm, symlink, unlink as unlink2 } from "fs/promises";
|
|
916
|
-
import { join as
|
|
1009
|
+
import { join as join9 } from "path";
|
|
917
1010
|
function localDateSlug(d) {
|
|
918
1011
|
const y = d.getFullYear();
|
|
919
1012
|
const m = String(d.getMonth() + 1).padStart(2, "0");
|
|
@@ -923,19 +1016,19 @@ function localDateSlug(d) {
|
|
|
923
1016
|
var DAY_RE = /^\d{4}-\d{2}-\d{2}$/;
|
|
924
1017
|
function resolveLogPaths({ dataDir, now }) {
|
|
925
1018
|
const slug = localDateSlug(now ?? /* @__PURE__ */ new Date());
|
|
926
|
-
const logsDir =
|
|
927
|
-
const dayDir =
|
|
1019
|
+
const logsDir = join9(dataDir, "logs");
|
|
1020
|
+
const dayDir = join9(logsDir, slug);
|
|
928
1021
|
return {
|
|
929
1022
|
dataDir,
|
|
930
1023
|
logsDir,
|
|
931
1024
|
dayDir,
|
|
932
1025
|
daySlug: slug,
|
|
933
|
-
todayLink:
|
|
934
|
-
tuwunelLog:
|
|
935
|
-
daemonLog:
|
|
936
|
-
devLog:
|
|
937
|
-
agentLog: (n) =>
|
|
938
|
-
agentTap: (n) =>
|
|
1026
|
+
todayLink: join9(logsDir, "today"),
|
|
1027
|
+
tuwunelLog: join9(dayDir, "tuwunel.log"),
|
|
1028
|
+
daemonLog: join9(dayDir, "daemon.log"),
|
|
1029
|
+
devLog: join9(dayDir, "dev.log"),
|
|
1030
|
+
agentLog: (n) => join9(dayDir, `agent-${n}.log`),
|
|
1031
|
+
agentTap: (n) => join9(dayDir, `agent-${n}.acp.jsonl`)
|
|
939
1032
|
};
|
|
940
1033
|
}
|
|
941
1034
|
async function ensureDayFolder(p) {
|
|
@@ -948,7 +1041,7 @@ async function ensureDayFolder(p) {
|
|
|
948
1041
|
}
|
|
949
1042
|
async function pruneOldDays(opts) {
|
|
950
1043
|
if (opts.retainDays <= 0) return [];
|
|
951
|
-
const logsDir =
|
|
1044
|
+
const logsDir = join9(opts.dataDir, "logs");
|
|
952
1045
|
let entries;
|
|
953
1046
|
try {
|
|
954
1047
|
entries = await readdir(logsDir);
|
|
@@ -967,7 +1060,7 @@ async function pruneOldDays(opts) {
|
|
|
967
1060
|
const [y, m, d] = name.split("-").map(Number);
|
|
968
1061
|
const t = new Date(y, m - 1, d).getTime();
|
|
969
1062
|
if (t < cutoff) {
|
|
970
|
-
await rm(
|
|
1063
|
+
await rm(join9(logsDir, name), { recursive: true, force: true });
|
|
971
1064
|
removed.push(name);
|
|
972
1065
|
}
|
|
973
1066
|
}
|
|
@@ -988,6 +1081,7 @@ var JsonlSink = class {
|
|
|
988
1081
|
this.stream = createWriteStream(this.path, { flags: "a" });
|
|
989
1082
|
})();
|
|
990
1083
|
}
|
|
1084
|
+
path;
|
|
991
1085
|
stream = null;
|
|
992
1086
|
maxStringLen;
|
|
993
1087
|
readyPromise;
|
|
@@ -995,14 +1089,14 @@ var JsonlSink = class {
|
|
|
995
1089
|
await this.readyPromise;
|
|
996
1090
|
const capped = capStrings(obj, this.maxStringLen);
|
|
997
1091
|
const line = JSON.stringify(capped) + "\n";
|
|
998
|
-
return new Promise((
|
|
999
|
-
this.stream.write(line, (err) => err ? reject(err) :
|
|
1092
|
+
return new Promise((resolve7, reject) => {
|
|
1093
|
+
this.stream.write(line, (err) => err ? reject(err) : resolve7());
|
|
1000
1094
|
});
|
|
1001
1095
|
}
|
|
1002
1096
|
async close() {
|
|
1003
1097
|
await this.readyPromise;
|
|
1004
|
-
return new Promise((
|
|
1005
|
-
this.stream.end(() =>
|
|
1098
|
+
return new Promise((resolve7) => {
|
|
1099
|
+
this.stream.end(() => resolve7());
|
|
1006
1100
|
});
|
|
1007
1101
|
}
|
|
1008
1102
|
};
|
|
@@ -1077,8 +1171,8 @@ function captureChildToFile(child, path) {
|
|
|
1077
1171
|
const stream = createWriteStream2(path, { flags: "a" });
|
|
1078
1172
|
if (child.stdout) child.stdout.on("data", (b) => stream.write(b));
|
|
1079
1173
|
if (child.stderr) child.stderr.on("data", (b) => stream.write(b));
|
|
1080
|
-
await new Promise((
|
|
1081
|
-
child.on("exit", () => stream.end(() =>
|
|
1174
|
+
await new Promise((resolve7) => {
|
|
1175
|
+
child.on("exit", () => stream.end(() => resolve7()));
|
|
1082
1176
|
});
|
|
1083
1177
|
})();
|
|
1084
1178
|
}
|
|
@@ -1113,10 +1207,10 @@ function buildShutdown(layers) {
|
|
|
1113
1207
|
var CLI_ROOT = (() => {
|
|
1114
1208
|
let dir = dirname6(fileURLToPath(import.meta.url));
|
|
1115
1209
|
for (let i = 0; i < 8; i++) {
|
|
1116
|
-
const pkgPath =
|
|
1117
|
-
if (
|
|
1210
|
+
const pkgPath = resolve3(dir, "package.json");
|
|
1211
|
+
if (existsSync5(pkgPath)) {
|
|
1118
1212
|
try {
|
|
1119
|
-
const pkg = JSON.parse(
|
|
1213
|
+
const pkg = JSON.parse(readFileSync5(pkgPath, "utf8"));
|
|
1120
1214
|
if (pkg.name === "zooid" || pkg.name === "@zooid/cli") return dir;
|
|
1121
1215
|
} catch {
|
|
1122
1216
|
}
|
|
@@ -1132,14 +1226,14 @@ async function runDev(flags) {
|
|
|
1132
1226
|
const found = findConfigFile(cwd);
|
|
1133
1227
|
if (!found) throw new Error(`zooid.yaml not found in ${cwd}`);
|
|
1134
1228
|
loadEnvFiles(cwd);
|
|
1135
|
-
const dataRoot =
|
|
1229
|
+
const dataRoot = resolve3(cwd, flags.dataDir);
|
|
1136
1230
|
const layout = resolveDataLayout(dataRoot);
|
|
1137
1231
|
const paths = resolvePaths(layout.matrixDir);
|
|
1138
1232
|
const logPaths = resolveLogPaths({ dataDir: layout.dataRoot });
|
|
1139
1233
|
const tokens = ensureTokens(paths.envPath);
|
|
1140
1234
|
process.env.MATRIX_AS_TOKEN = tokens.asToken;
|
|
1141
1235
|
process.env.MATRIX_HS_TOKEN = tokens.hsToken;
|
|
1142
|
-
const rawYaml =
|
|
1236
|
+
const rawYaml = readFileSync5(found.path, "utf8");
|
|
1143
1237
|
const preview = loadZooidConfig(rawYaml, { configDir: dirname6(found.path) });
|
|
1144
1238
|
const matrix = findMatrixTransport(preview);
|
|
1145
1239
|
if (!matrix) {
|
|
@@ -1220,6 +1314,10 @@ async function runDev(flags) {
|
|
|
1220
1314
|
installSignalHandlers: false,
|
|
1221
1315
|
adminUserId: `@${flags.adminUser}:${shape.serverName}`,
|
|
1222
1316
|
agentsDir: layout.agentsDir,
|
|
1317
|
+
// Local-only homeserver: make the workforce space publicly joinable
|
|
1318
|
+
// so a self-service-registered dev account lands in #<space> from
|
|
1319
|
+
// the web client without needing an invite.
|
|
1320
|
+
publicWorkforceSpace: true,
|
|
1223
1321
|
onTap: (agentName, event) => captures[agentName]?.onTap(event),
|
|
1224
1322
|
prepullLog: (line) => {
|
|
1225
1323
|
t.output = line.replace(/^\[zooid\]\s+/, "").trim();
|
|
@@ -1229,13 +1327,13 @@ async function runDev(flags) {
|
|
|
1229
1327
|
},
|
|
1230
1328
|
...flags.watchWeb ? [
|
|
1231
1329
|
{
|
|
1232
|
-
title: "Start @zoon
|
|
1330
|
+
title: "Start @zooid/zoon-web watcher (vite build --watch)",
|
|
1233
1331
|
task: async () => {
|
|
1234
|
-
const pkgDir = typeof flags.watchWeb === "string" ?
|
|
1332
|
+
const pkgDir = typeof flags.watchWeb === "string" ? resolve3(flags.watchWeb) : webSourcePackage(CLI_ROOT);
|
|
1235
1333
|
if (!pkgDir) {
|
|
1236
1334
|
const defaultPath = dirname6(dirname6(dirname6(CLI_ROOT))) + "/zoon/packages/web";
|
|
1237
1335
|
throw new Error(
|
|
1238
|
-
`--watch-web: @zoon
|
|
1336
|
+
`--watch-web: @zooid/zoon-web not found at ${defaultPath}.
|
|
1239
1337
|
Pass an explicit path: --watch-web=/path/to/zoon/packages/web`
|
|
1240
1338
|
);
|
|
1241
1339
|
}
|
|
@@ -1244,9 +1342,16 @@ Pass an explicit path: --watch-web=/path/to/zoon/packages/web`
|
|
|
1244
1342
|
}
|
|
1245
1343
|
] : [],
|
|
1246
1344
|
{
|
|
1247
|
-
title: `Serve @zoon
|
|
1248
|
-
task: () => {
|
|
1249
|
-
const webRoot = ctx.webWatch?.distPath ??
|
|
1345
|
+
title: `Serve @zooid/zoon-web on http://localhost:${flags.uiPort}`,
|
|
1346
|
+
task: async (_, t) => {
|
|
1347
|
+
const webRoot = ctx.webWatch?.distPath ?? await ensureWebRoot({
|
|
1348
|
+
cliRoot: CLI_ROOT,
|
|
1349
|
+
cacheDir: join10(layout.dataRoot, "web"),
|
|
1350
|
+
version: readZoonWebPin(CLI_ROOT),
|
|
1351
|
+
onProgress: (msg) => {
|
|
1352
|
+
t.output = msg;
|
|
1353
|
+
}
|
|
1354
|
+
});
|
|
1250
1355
|
const app = webStatic({ webRoot, homeserverUrl: homeserver });
|
|
1251
1356
|
ctx.uiServer = serve2({ fetch: app.fetch, port: flags.uiPort });
|
|
1252
1357
|
}
|
|
@@ -1295,7 +1400,7 @@ Pass an explicit path: --watch-web=/path/to/zoon/packages/web`
|
|
|
1295
1400
|
chalk.bold("UI: ") + ` http://localhost:${flags.uiPort}`,
|
|
1296
1401
|
` ${chalk.cyan("admin user:")} ${flags.adminUser} / ${flags.adminPassword}`,
|
|
1297
1402
|
` ${chalk.cyan("data dir:")} ${layout.dataRoot}`,
|
|
1298
|
-
...ctx.webWatch ? [` ${chalk.cyan("web watcher:")} live (vite build --watch on @zoon
|
|
1403
|
+
...ctx.webWatch ? [` ${chalk.cyan("web watcher:")} live (vite build --watch on @zooid/zoon-web)`] : [],
|
|
1299
1404
|
"",
|
|
1300
1405
|
chalk.dim("Press Ctrl-C to stop."),
|
|
1301
1406
|
""
|
|
@@ -1308,8 +1413,8 @@ Pass an explicit path: --watch-web=/path/to/zoon/packages/web`
|
|
|
1308
1413
|
}
|
|
1309
1414
|
function loadEnvFiles(cwd) {
|
|
1310
1415
|
for (const name of [".env.local", ".env"]) {
|
|
1311
|
-
const path =
|
|
1312
|
-
if (!
|
|
1416
|
+
const path = resolve3(cwd, name);
|
|
1417
|
+
if (!existsSync5(path)) continue;
|
|
1313
1418
|
try {
|
|
1314
1419
|
process.loadEnvFile(path);
|
|
1315
1420
|
} catch (err) {
|
|
@@ -1319,8 +1424,8 @@ function loadEnvFiles(cwd) {
|
|
|
1319
1424
|
}
|
|
1320
1425
|
|
|
1321
1426
|
// src/commands/init.ts
|
|
1322
|
-
import { existsSync as
|
|
1323
|
-
import { dirname as dirname7, join as
|
|
1427
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync4, readdirSync as readdirSync2, writeFileSync as writeFileSync4 } from "fs";
|
|
1428
|
+
import { dirname as dirname7, join as join12, resolve as resolve4 } from "path";
|
|
1324
1429
|
|
|
1325
1430
|
// src/commands/init/generators.ts
|
|
1326
1431
|
function generateZooidYaml(opts) {
|
|
@@ -1473,14 +1578,14 @@ function findOpencodeProvider(id) {
|
|
|
1473
1578
|
}
|
|
1474
1579
|
|
|
1475
1580
|
// src/commands/init/sniff.ts
|
|
1476
|
-
import { existsSync as
|
|
1581
|
+
import { existsSync as existsSync6 } from "fs";
|
|
1477
1582
|
import { homedir } from "os";
|
|
1478
|
-
import { join as
|
|
1583
|
+
import { join as join11 } from "path";
|
|
1479
1584
|
function sniffCredentials(preset, home = homedir()) {
|
|
1480
1585
|
const meta = findSimplePreset(preset);
|
|
1481
1586
|
if (!meta) return { found: false };
|
|
1482
|
-
const full =
|
|
1483
|
-
if (
|
|
1587
|
+
const full = join11(home, meta.credentialDir);
|
|
1588
|
+
if (existsSync6(full)) return { found: true, path: full };
|
|
1484
1589
|
return { found: false };
|
|
1485
1590
|
}
|
|
1486
1591
|
|
|
@@ -1497,10 +1602,10 @@ var IGNORED_PREEXISTING = /* @__PURE__ */ new Set([
|
|
|
1497
1602
|
"yarn.lock"
|
|
1498
1603
|
]);
|
|
1499
1604
|
async function runInit(opts) {
|
|
1500
|
-
const dir =
|
|
1501
|
-
|
|
1605
|
+
const dir = resolve4(opts.dir);
|
|
1606
|
+
mkdirSync4(dir, { recursive: true });
|
|
1502
1607
|
if (!opts.force) {
|
|
1503
|
-
const blocking =
|
|
1608
|
+
const blocking = readdirSync2(dir).filter((n) => !IGNORED_PREEXISTING.has(n));
|
|
1504
1609
|
if (blocking.length > 0) {
|
|
1505
1610
|
throw new Error(
|
|
1506
1611
|
`${dir} is non-empty (use --force to allow scaffolding into it). Conflicting entries: ${blocking.join(", ")}`
|
|
@@ -1567,14 +1672,14 @@ async function runInit(opts) {
|
|
|
1567
1672
|
}
|
|
1568
1673
|
writes.push({ path: ".gitignore", content: generateGitignore() });
|
|
1569
1674
|
for (const w of writes) {
|
|
1570
|
-
const full =
|
|
1571
|
-
const exists =
|
|
1675
|
+
const full = join12(dir, w.path);
|
|
1676
|
+
const exists = existsSync7(full);
|
|
1572
1677
|
if (exists && !opts.overwrite) {
|
|
1573
1678
|
console.warn(`\u26A0 ${w.path} exists; left as-is (use --force --overwrite to replace)`);
|
|
1574
1679
|
continue;
|
|
1575
1680
|
}
|
|
1576
|
-
|
|
1577
|
-
|
|
1681
|
+
mkdirSync4(dirname7(full), { recursive: true });
|
|
1682
|
+
writeFileSync4(full, w.content);
|
|
1578
1683
|
console.log(`\u2713 Created ${w.path}`);
|
|
1579
1684
|
}
|
|
1580
1685
|
if ((opts.preset === "claude" || opts.preset === "codex") && opts.auth === "subscription") {
|
|
@@ -1691,8 +1796,8 @@ async function resolveOptions(flags) {
|
|
|
1691
1796
|
|
|
1692
1797
|
// src/commands/logs.ts
|
|
1693
1798
|
import { readFile, readdir as readdir2, readlink } from "fs/promises";
|
|
1694
|
-
import { existsSync as
|
|
1695
|
-
import { join as
|
|
1799
|
+
import { existsSync as existsSync8 } from "fs";
|
|
1800
|
+
import { join as join13 } from "path";
|
|
1696
1801
|
var KNOWN_SOURCES = ["tuwunel", "daemon", "dev"];
|
|
1697
1802
|
async function runLogs(flags) {
|
|
1698
1803
|
const writer = flags.writer ?? ((s) => process.stdout.write(s));
|
|
@@ -1711,8 +1816,8 @@ async function runLogs(flags) {
|
|
|
1711
1816
|
writer("no logs yet\n");
|
|
1712
1817
|
return;
|
|
1713
1818
|
}
|
|
1714
|
-
const dayDir =
|
|
1715
|
-
if (!
|
|
1819
|
+
const dayDir = join13(flags.dataDir, "logs", day);
|
|
1820
|
+
if (!existsSync8(dayDir)) {
|
|
1716
1821
|
writer(`no logs for ${day}
|
|
1717
1822
|
`);
|
|
1718
1823
|
return;
|
|
@@ -1732,7 +1837,7 @@ async function runLogs(flags) {
|
|
|
1732
1837
|
return;
|
|
1733
1838
|
}
|
|
1734
1839
|
const path = resolveSourcePath(dayDir, flags.source);
|
|
1735
|
-
if (!
|
|
1840
|
+
if (!existsSync8(path)) {
|
|
1736
1841
|
writer(`no such source: ${flags.source}
|
|
1737
1842
|
`);
|
|
1738
1843
|
return;
|
|
@@ -1740,7 +1845,7 @@ async function runLogs(flags) {
|
|
|
1740
1845
|
writer(await readFile(path, "utf8"));
|
|
1741
1846
|
}
|
|
1742
1847
|
async function resolveTodaySlug(dataDir) {
|
|
1743
|
-
const link =
|
|
1848
|
+
const link = join13(dataDir, "logs", "today");
|
|
1744
1849
|
try {
|
|
1745
1850
|
return await readlink(link);
|
|
1746
1851
|
} catch {
|
|
@@ -1749,18 +1854,18 @@ async function resolveTodaySlug(dataDir) {
|
|
|
1749
1854
|
}
|
|
1750
1855
|
function resolveSourcePath(dayDir, source) {
|
|
1751
1856
|
if (source.startsWith("agent-")) {
|
|
1752
|
-
if (source.endsWith(".acp")) return
|
|
1753
|
-
return
|
|
1857
|
+
if (source.endsWith(".acp")) return join13(dayDir, `${source.slice(0, -4)}.acp.jsonl`);
|
|
1858
|
+
return join13(dayDir, `${source}.log`);
|
|
1754
1859
|
}
|
|
1755
1860
|
if (KNOWN_SOURCES.includes(source))
|
|
1756
|
-
return
|
|
1757
|
-
return
|
|
1861
|
+
return join13(dayDir, `${source}.log`);
|
|
1862
|
+
return join13(dayDir, source);
|
|
1758
1863
|
}
|
|
1759
1864
|
async function dumpByTurn(dayDir, turnId, writer) {
|
|
1760
1865
|
const entries = await readdir2(dayDir);
|
|
1761
1866
|
const taps = entries.filter((e) => e.endsWith(".acp.jsonl")).sort();
|
|
1762
1867
|
for (const f of taps) {
|
|
1763
|
-
const text = await readFile(
|
|
1868
|
+
const text = await readFile(join13(dayDir, f), "utf8");
|
|
1764
1869
|
for (const line of text.split("\n")) {
|
|
1765
1870
|
if (!line) continue;
|
|
1766
1871
|
try {
|
|
@@ -1774,14 +1879,14 @@ async function dumpByTurn(dayDir, turnId, writer) {
|
|
|
1774
1879
|
|
|
1775
1880
|
// src/commands/start.ts
|
|
1776
1881
|
import { randomBytes as randomBytes2 } from "crypto";
|
|
1777
|
-
import { resolve as
|
|
1882
|
+
import { resolve as resolve5 } from "path";
|
|
1778
1883
|
async function runStart(flags) {
|
|
1779
1884
|
if (flags.printToken) {
|
|
1780
1885
|
process.stdout.write(`${randomBytes2(32).toString("hex")}
|
|
1781
1886
|
`);
|
|
1782
1887
|
return;
|
|
1783
1888
|
}
|
|
1784
|
-
const dataRoot =
|
|
1889
|
+
const dataRoot = resolve5(process.cwd(), flags.dataDir ?? "./data");
|
|
1785
1890
|
const layout = resolveDataLayout(dataRoot);
|
|
1786
1891
|
const handle = await startDaemon({
|
|
1787
1892
|
cliFlags: flags,
|
|
@@ -1794,7 +1899,7 @@ async function runStart(flags) {
|
|
|
1794
1899
|
}
|
|
1795
1900
|
|
|
1796
1901
|
// src/commands/status.ts
|
|
1797
|
-
import { readFileSync as
|
|
1902
|
+
import { readFileSync as readFileSync6 } from "fs";
|
|
1798
1903
|
import { dirname as dirname8 } from "path";
|
|
1799
1904
|
import chalk2 from "chalk";
|
|
1800
1905
|
async function probe(url, timeoutMs = 2e3) {
|
|
@@ -1819,7 +1924,7 @@ async function collectStatus(opts) {
|
|
|
1819
1924
|
agents: []
|
|
1820
1925
|
};
|
|
1821
1926
|
}
|
|
1822
|
-
const cfg = loadZooidConfig(
|
|
1927
|
+
const cfg = loadZooidConfig(readFileSync6(found.path, "utf8"), {
|
|
1823
1928
|
configDir: dirname8(found.path)
|
|
1824
1929
|
});
|
|
1825
1930
|
const matrixEntry = Object.entries(cfg.transports).find(
|
|
@@ -1850,7 +1955,7 @@ async function runStatus(flags) {
|
|
|
1850
1955
|
if (port === void 0) {
|
|
1851
1956
|
const found = findConfigFile(cwd);
|
|
1852
1957
|
if (found) {
|
|
1853
|
-
const cfg = loadZooidConfig(
|
|
1958
|
+
const cfg = loadZooidConfig(readFileSync6(found.path, "utf8"), {
|
|
1854
1959
|
configDir: dirname8(found.path)
|
|
1855
1960
|
});
|
|
1856
1961
|
const matrix = findMatrixTransport(cfg);
|
|
@@ -1887,7 +1992,7 @@ cli.command("start", "Run the daemon (production entry-point)").option("--data <
|
|
|
1887
1992
|
});
|
|
1888
1993
|
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(
|
|
1889
1994
|
"--watch-web [path]",
|
|
1890
|
-
"Run vite build --watch on @zoon
|
|
1995
|
+
"Run vite build --watch on @zooid/zoon-web. Path defaults to sibling ../zoon/packages/web."
|
|
1891
1996
|
).action(async (flags) => {
|
|
1892
1997
|
await runDev({
|
|
1893
1998
|
dataDir: flags.data,
|
|
@@ -1904,14 +2009,14 @@ cli.command(
|
|
|
1904
2009
|
).option("--data <dir>", "Persistent data root dir", { default: "./data" }).option("--day <YYYY-MM-DD>", "Day partition (defaults to today)").option("--turn <id>", "Filter ACP taps to a single turn id").option("-f, --follow", "Tail the file (not yet implemented)").option("--keep <n>", "For `logs prune`: days to retain", { default: 14 }).action(async (source, flags) => {
|
|
1905
2010
|
if (source === "prune") {
|
|
1906
2011
|
await runLogs({
|
|
1907
|
-
dataDir:
|
|
2012
|
+
dataDir: resolve6(process.cwd(), flags.data),
|
|
1908
2013
|
subcommand: "prune",
|
|
1909
2014
|
keep: Number(flags.keep)
|
|
1910
2015
|
});
|
|
1911
2016
|
return;
|
|
1912
2017
|
}
|
|
1913
2018
|
await runLogs({
|
|
1914
|
-
dataDir:
|
|
2019
|
+
dataDir: resolve6(process.cwd(), flags.data),
|
|
1915
2020
|
source,
|
|
1916
2021
|
day: flags.day,
|
|
1917
2022
|
turn: flags.turn,
|