zooid 0.0.1 → 0.0.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.

Potentially problematic release.


This version of zooid might be problematic. Click here for more details.

Files changed (2) hide show
  1. package/dist/index.js +217 -67
  2. package/package.json +3 -3
package/dist/index.js CHANGED
@@ -37,7 +37,10 @@ function loadConfigFile() {
37
37
  };
38
38
  const dir = getConfigDir();
39
39
  fs.mkdirSync(dir, { recursive: true });
40
- fs.writeFileSync(getConfigPath(), JSON.stringify(migrated, null, 2) + "\n");
40
+ fs.writeFileSync(
41
+ getConfigPath(),
42
+ JSON.stringify(migrated, null, 2) + "\n"
43
+ );
41
44
  return migrated;
42
45
  }
43
46
  return parsed;
@@ -59,7 +62,9 @@ function resolveServer() {
59
62
  }
60
63
  if (cwdUrl) {
61
64
  if (file.current && file.current !== cwdUrl) {
62
- console.log(` Note: using server from zooid.json (${cwdUrl}), current is ${file.current}`);
65
+ console.log(
66
+ ` Note: using server from zooid.json (${cwdUrl}), current is ${file.current}`
67
+ );
63
68
  }
64
69
  return cwdUrl;
65
70
  }
@@ -82,7 +87,9 @@ function saveConfig(partial, serverUrl) {
82
87
  const file = loadConfigFile();
83
88
  const url = serverUrl ?? resolveServer();
84
89
  if (!url) {
85
- throw new Error("No server URL to save config for. Deploy first or set url in zooid.json.");
90
+ throw new Error(
91
+ "No server URL to save config for. Deploy first or set url in zooid.json."
92
+ );
86
93
  }
87
94
  if (!file.servers) file.servers = {};
88
95
  const existing = file.servers[url] ?? {};
@@ -281,7 +288,9 @@ function runConfigSet(key, value) {
281
288
  const enabled = value === "on" || value === "true" || value === "1";
282
289
  writeTelemetryFlag(enabled);
283
290
  } else {
284
- throw new Error(`Unknown config key: "${key}". Valid keys: ${VALID_KEYS.join(", ")}`);
291
+ throw new Error(
292
+ `Unknown config key: "${key}". Valid keys: ${VALID_KEYS.join(", ")}`
293
+ );
285
294
  }
286
295
  }
287
296
  function runConfigGet(key) {
@@ -289,7 +298,9 @@ function runConfigGet(key) {
289
298
  if (key === "server") return config.server;
290
299
  if (key === "admin-token") return config.admin_token;
291
300
  if (key === "telemetry") return isEnabled() ? "on" : "off";
292
- throw new Error(`Unknown config key: "${key}". Valid keys: ${VALID_KEYS.join(", ")}`);
301
+ throw new Error(
302
+ `Unknown config key: "${key}". Valid keys: ${VALID_KEYS.join(", ")}`
303
+ );
293
304
  }
294
305
 
295
306
  // src/lib/client.ts
@@ -484,7 +495,9 @@ async function deviceAuth() {
484
495
  const cmd = platform === "darwin" ? "open" : platform === "win32" ? "start" : "xdg-open";
485
496
  exec(`${cmd} "${verification_url}"`);
486
497
  } catch {
487
- console.log(" Could not open browser automatically. Please visit the URL above.\n");
498
+ console.log(
499
+ " Could not open browser automatically. Please visit the URL above.\n"
500
+ );
488
501
  }
489
502
  const deadline = Date.now() + POLL_TIMEOUT_MS;
490
503
  while (Date.now() < deadline) {
@@ -503,7 +516,9 @@ async function deviceAuth() {
503
516
  throw new Error("Device authorization expired. Please try again.");
504
517
  }
505
518
  }
506
- throw new Error("Authorization timed out. You need your human to authorize you. Run `npx zooid share` again to retry.");
519
+ throw new Error(
520
+ "Authorization timed out. You need your human to authorize you. Run `npx zooid share` again to retry."
521
+ );
507
522
  }
508
523
  async function directoryFetch(path6, options = {}) {
509
524
  let token = await ensureDirectoryToken();
@@ -531,7 +546,9 @@ async function runShare(channelIds, options = {}) {
531
546
  const config = loadConfig();
532
547
  const serverUrl = config.server;
533
548
  if (!serverUrl) {
534
- throw new Error("No server configured. Run: npx zooid config set server <url>");
549
+ throw new Error(
550
+ "No server configured. Run: npx zooid config set server <url>"
551
+ );
535
552
  }
536
553
  const allChannels = await client.listChannels();
537
554
  const publicChannels = allChannels.filter((ch) => ch.is_public);
@@ -636,7 +653,11 @@ async function promptChannelDetails(channels, skipPrompt) {
636
653
  for (const ch of channels) {
637
654
  console.log(` ${ch.id}:`);
638
655
  const desc = await ask(rl, "Description", ch.description ?? "");
639
- const tagsRaw = await ask(rl, "Tags (comma-separated)", ch.tags.join(", "));
656
+ const tagsRaw = await ask(
657
+ rl,
658
+ "Tags (comma-separated)",
659
+ ch.tags.join(", ")
660
+ );
640
661
  const tags = tagsRaw.split(",").map((t) => t.trim()).filter(Boolean);
641
662
  result.set(ch.id, { description: desc, tags });
642
663
  console.log("");
@@ -670,7 +691,9 @@ async function runUnshare(channelId) {
670
691
  const config = loadConfig();
671
692
  const serverUrl = config.server;
672
693
  if (!serverUrl) {
673
- throw new Error("No server configured. Run: npx zooid config set server <url>");
694
+ throw new Error(
695
+ "No server configured. Run: npx zooid config set server <url>"
696
+ );
674
697
  }
675
698
  const { claim, signature } = await client.getClaim([channelId], "delete");
676
699
  const res = await directoryFetch("/api/servers/channels", {
@@ -752,11 +775,17 @@ function base64url(buf) {
752
775
  return buf.toString("base64url");
753
776
  }
754
777
  async function createAdminToken(secret) {
755
- const header = base64url(Buffer.from(JSON.stringify({ alg: "HS256", typ: "JWT" })));
756
- const payload = base64url(Buffer.from(JSON.stringify({
757
- scope: "admin",
758
- iat: Math.floor(Date.now() / 1e3)
759
- })));
778
+ const header = base64url(
779
+ Buffer.from(JSON.stringify({ alg: "HS256", typ: "JWT" }))
780
+ );
781
+ const payload = base64url(
782
+ Buffer.from(
783
+ JSON.stringify({
784
+ scope: "admin",
785
+ iat: Math.floor(Date.now() / 1e3)
786
+ })
787
+ )
788
+ );
760
789
  const data = `${header}.${payload}`;
761
790
  const key = await crypto.subtle.importKey(
762
791
  "raw",
@@ -765,7 +794,11 @@ async function createAdminToken(secret) {
765
794
  false,
766
795
  ["sign"]
767
796
  );
768
- const sig = await crypto.subtle.sign("HMAC", key, new TextEncoder().encode(data));
797
+ const sig = await crypto.subtle.sign(
798
+ "HMAC",
799
+ key,
800
+ new TextEncoder().encode(data)
801
+ );
769
802
  const signature = base64url(Buffer.from(sig));
770
803
  return `${data}.${signature}`;
771
804
  }
@@ -786,10 +819,13 @@ async function runDev(port = 8787) {
786
819
  const schemaPath = path3.join(serverDir, "src/db/schema.sql");
787
820
  if (fs4.existsSync(schemaPath)) {
788
821
  try {
789
- execSync(`npx wrangler d1 execute zooid-db --local --file=${schemaPath}`, {
790
- cwd: serverDir,
791
- stdio: "pipe"
792
- });
822
+ execSync(
823
+ `npx wrangler d1 execute zooid-db --local --file=${schemaPath}`,
824
+ {
825
+ cwd: serverDir,
826
+ stdio: "pipe"
827
+ }
828
+ );
793
829
  printSuccess("Database schema initialized");
794
830
  } catch {
795
831
  printSuccess("Database ready (schema already exists)");
@@ -802,11 +838,15 @@ async function runDev(port = 8787) {
802
838
  console.log("");
803
839
  console.log("Starting wrangler dev...");
804
840
  console.log("");
805
- const child = spawn("npx", ["wrangler", "dev", "--local", "--port", String(port)], {
806
- cwd: serverDir,
807
- stdio: "inherit",
808
- shell: true
809
- });
841
+ const child = spawn(
842
+ "npx",
843
+ ["wrangler", "dev", "--local", "--port", String(port)],
844
+ {
845
+ cwd: serverDir,
846
+ stdio: "inherit",
847
+ shell: true
848
+ }
849
+ );
810
850
  child.on("error", (err) => {
811
851
  console.error(`Failed to start local server: ${err.message}`);
812
852
  process.exit(1);
@@ -850,11 +890,19 @@ async function runInit() {
850
890
  console.log(" Configure your Zooid server");
851
891
  console.log(" Press Enter to accept defaults shown in [brackets].\n");
852
892
  const name = await ask2(rl, "Server name", existing?.name ?? "");
853
- const description = await ask2(rl, "Description", existing?.description ?? "");
893
+ const description = await ask2(
894
+ rl,
895
+ "Description",
896
+ existing?.description ?? ""
897
+ );
854
898
  const owner = await ask2(rl, "Owner", existing?.owner ?? "");
855
899
  const company = await ask2(rl, "Company", existing?.company ?? "");
856
900
  const email = await ask2(rl, "Email", existing?.email ?? "");
857
- const tagsRaw = await ask2(rl, "Tags (comma-separated)", existing?.tags?.join(", ") ?? "");
901
+ const tagsRaw = await ask2(
902
+ rl,
903
+ "Tags (comma-separated)",
904
+ existing?.tags?.join(", ") ?? ""
905
+ );
858
906
  const url = await ask2(rl, "URL", existing?.url ?? "");
859
907
  const tags = tagsRaw.split(",").map((t) => t.trim()).filter(Boolean);
860
908
  const config = {
@@ -875,7 +923,10 @@ async function runInit() {
875
923
  printInfo("Owner", config.owner || "(not set)");
876
924
  printInfo("Company", config.company || "(not set)");
877
925
  printInfo("Email", config.email || "(not set)");
878
- printInfo("Tags", config.tags.length > 0 ? config.tags.join(", ") : "(none)");
926
+ printInfo(
927
+ "Tags",
928
+ config.tags.length > 0 ? config.tags.join(", ") : "(none)"
929
+ );
879
930
  printInfo("URL", config.url || "(not set)");
880
931
  console.log("");
881
932
  } finally {
@@ -918,12 +969,26 @@ function prepareStagingDir() {
918
969
  let toml = fs6.readFileSync(path5.join(serverDir, "wrangler.toml"), "utf-8");
919
970
  toml = toml.replace(/directory\s*=\s*"[^"]*"/, 'directory = "./web-dist/"');
920
971
  fs6.writeFileSync(path5.join(tmpDir, "wrangler.toml"), toml);
921
- const serverNodeModules = path5.join(serverDir, "node_modules");
922
- if (fs6.existsSync(serverNodeModules)) {
923
- fs6.symlinkSync(serverNodeModules, path5.join(tmpDir, "node_modules"), "junction");
972
+ const nodeModules = findServerNodeModules(serverDir);
973
+ if (nodeModules) {
974
+ fs6.symlinkSync(nodeModules, path5.join(tmpDir, "node_modules"), "junction");
924
975
  }
925
976
  return tmpDir;
926
977
  }
978
+ function findServerNodeModules(serverDir) {
979
+ const local = path5.join(serverDir, "node_modules");
980
+ if (fs6.existsSync(path5.join(local, "hono"))) return local;
981
+ const storeNodeModules = path5.resolve(serverDir, "..", "..");
982
+ if (fs6.existsSync(path5.join(storeNodeModules, "hono")))
983
+ return storeNodeModules;
984
+ let dir = serverDir;
985
+ while (dir !== path5.dirname(dir)) {
986
+ dir = path5.dirname(dir);
987
+ const nm = path5.join(dir, "node_modules");
988
+ if (fs6.existsSync(path5.join(nm, "hono"))) return nm;
989
+ }
990
+ return null;
991
+ }
927
992
  function copyDirSync(src, dest) {
928
993
  fs6.mkdirSync(dest, { recursive: true });
929
994
  for (const entry of fs6.readdirSync(src, { withFileTypes: true })) {
@@ -940,11 +1005,17 @@ function base64url2(buf) {
940
1005
  return buf.toString("base64url");
941
1006
  }
942
1007
  async function createAdminToken2(secret) {
943
- const header = base64url2(Buffer.from(JSON.stringify({ alg: "HS256", typ: "JWT" })));
944
- const payload = base64url2(Buffer.from(JSON.stringify({
945
- scope: "admin",
946
- iat: Math.floor(Date.now() / 1e3)
947
- })));
1008
+ const header = base64url2(
1009
+ Buffer.from(JSON.stringify({ alg: "HS256", typ: "JWT" }))
1010
+ );
1011
+ const payload = base64url2(
1012
+ Buffer.from(
1013
+ JSON.stringify({
1014
+ scope: "admin",
1015
+ iat: Math.floor(Date.now() / 1e3)
1016
+ })
1017
+ )
1018
+ );
948
1019
  const data = `${header}.${payload}`;
949
1020
  const key = await crypto2.subtle.importKey(
950
1021
  "raw",
@@ -953,7 +1024,11 @@ async function createAdminToken2(secret) {
953
1024
  false,
954
1025
  ["sign"]
955
1026
  );
956
- const sig = await crypto2.subtle.sign("HMAC", key, new TextEncoder().encode(data));
1027
+ const sig = await crypto2.subtle.sign(
1028
+ "HMAC",
1029
+ key,
1030
+ new TextEncoder().encode(data)
1031
+ );
957
1032
  const signature = base64url2(Buffer.from(sig));
958
1033
  return `${data}.${signature}`;
959
1034
  }
@@ -975,7 +1050,9 @@ function wrangler(cmd, cwd, creds, opts) {
975
1050
  }
976
1051
  function parseDeployUrls(output) {
977
1052
  const workersDev = output.match(/https:\/\/[^\s]+\.workers\.dev/);
978
- const custom = output.match(/^\s+(\S+\.(?!workers\.dev)\S+)\s+\(custom domain\)/m);
1053
+ const custom = output.match(
1054
+ /^\s+(\S+\.(?!workers\.dev)\S+)\s+\(custom domain\)/m
1055
+ );
979
1056
  return {
980
1057
  workerUrl: workersDev ? workersDev[0] : null,
981
1058
  customDomain: custom ? `https://${custom[1]}` : null
@@ -1011,11 +1088,15 @@ async function getCfCredentials() {
1011
1088
  console.log("");
1012
1089
  console.log(" Cloudflare API token required for deployment.");
1013
1090
  console.log(" Go to: https://dash.cloudflare.com/profile/api-tokens");
1014
- console.log(' Use the "Edit Cloudflare Workers" template, then add D1 Edit permission.');
1091
+ console.log(
1092
+ ' Use the "Edit Cloudflare Workers" template, then add D1 Edit permission.'
1093
+ );
1015
1094
  console.log(" Tip: save credentials in .env to skip this prompt.");
1016
1095
  console.log("");
1017
1096
  const token = await rl.question(" API token: ");
1018
- const accountId = await rl.question(" Account ID (from the dashboard URL or Workers overview): ");
1097
+ const accountId = await rl.question(
1098
+ " Account ID (from the dashboard URL or Workers overview): "
1099
+ );
1019
1100
  return {
1020
1101
  apiToken: token.trim(),
1021
1102
  accountId: accountId.trim() || void 0
@@ -1087,30 +1168,61 @@ async function runDeploy() {
1087
1168
  printSuccess(`D1 database created (${databaseId})`);
1088
1169
  const wranglerTomlPath = path5.join(stagingDir, "wrangler.toml");
1089
1170
  let tomlContent = fs6.readFileSync(wranglerTomlPath, "utf-8");
1090
- tomlContent = tomlContent.replace(/name = "[^"]*"/, `name = "${workerName}"`);
1091
- tomlContent = tomlContent.replace(/database_name = "[^"]*"/, `database_name = "${dbName}"`);
1092
- tomlContent = tomlContent.replace(/database_id = "[^"]*"/, `database_id = "${databaseId}"`);
1093
- tomlContent = tomlContent.replace(/ZOOID_SERVER_ID = "[^"]*"/, `ZOOID_SERVER_ID = "${serverSlug}"`);
1171
+ tomlContent = tomlContent.replace(
1172
+ /name = "[^"]*"/,
1173
+ `name = "${workerName}"`
1174
+ );
1175
+ tomlContent = tomlContent.replace(
1176
+ /database_name = "[^"]*"/,
1177
+ `database_name = "${dbName}"`
1178
+ );
1179
+ tomlContent = tomlContent.replace(
1180
+ /database_id = "[^"]*"/,
1181
+ `database_id = "${databaseId}"`
1182
+ );
1183
+ tomlContent = tomlContent.replace(
1184
+ /ZOOID_SERVER_ID = "[^"]*"/,
1185
+ `ZOOID_SERVER_ID = "${serverSlug}"`
1186
+ );
1094
1187
  fs6.writeFileSync(wranglerTomlPath, tomlContent);
1095
1188
  printSuccess("Configured wrangler.toml");
1096
1189
  const schemaPath = path5.join(stagingDir, "src/db/schema.sql");
1097
1190
  if (fs6.existsSync(schemaPath)) {
1098
1191
  console.log("Running database schema migration...");
1099
- wrangler(`d1 execute ${dbName} --remote --file=${schemaPath}`, stagingDir, creds);
1192
+ wrangler(
1193
+ `d1 execute ${dbName} --remote --file=${schemaPath}`,
1194
+ stagingDir,
1195
+ creds
1196
+ );
1100
1197
  printSuccess("Database schema initialized");
1101
1198
  }
1102
1199
  console.log("Generating secrets...");
1103
1200
  const jwtSecret = crypto2.randomBytes(32).toString("base64");
1104
- const keyPair = await crypto2.subtle.generateKey("Ed25519", true, ["sign", "verify"]);
1105
- const privateKeyRaw = await crypto2.subtle.exportKey("pkcs8", keyPair.privateKey);
1106
- const publicKeyRaw = await crypto2.subtle.exportKey("raw", keyPair.publicKey);
1201
+ const keyPair = await crypto2.subtle.generateKey("Ed25519", true, [
1202
+ "sign",
1203
+ "verify"
1204
+ ]);
1205
+ const privateKeyRaw = await crypto2.subtle.exportKey(
1206
+ "pkcs8",
1207
+ keyPair.privateKey
1208
+ );
1209
+ const publicKeyRaw = await crypto2.subtle.exportKey(
1210
+ "raw",
1211
+ keyPair.publicKey
1212
+ );
1107
1213
  const privateKeyB64 = Buffer.from(privateKeyRaw).toString("base64");
1108
1214
  const publicKeyB64 = Buffer.from(publicKeyRaw).toString("base64");
1109
- wrangler("secret put ZOOID_JWT_SECRET", stagingDir, creds, { input: jwtSecret });
1215
+ wrangler("secret put ZOOID_JWT_SECRET", stagingDir, creds, {
1216
+ input: jwtSecret
1217
+ });
1110
1218
  printSuccess("Set ZOOID_JWT_SECRET");
1111
- wrangler("secret put ZOOID_SIGNING_KEY", stagingDir, creds, { input: privateKeyB64 });
1219
+ wrangler("secret put ZOOID_SIGNING_KEY", stagingDir, creds, {
1220
+ input: privateKeyB64
1221
+ });
1112
1222
  printSuccess("Set ZOOID_SIGNING_KEY (Ed25519 private)");
1113
- wrangler("secret put ZOOID_PUBLIC_KEY", stagingDir, creds, { input: publicKeyB64 });
1223
+ wrangler("secret put ZOOID_PUBLIC_KEY", stagingDir, creds, {
1224
+ input: publicKeyB64
1225
+ });
1114
1226
  printSuccess("Set ZOOID_PUBLIC_KEY (Ed25519 public)");
1115
1227
  adminToken = await createAdminToken2(jwtSecret);
1116
1228
  printSuccess("Admin token generated");
@@ -1120,15 +1232,27 @@ async function runDeploy() {
1120
1232
  console.log("");
1121
1233
  const wranglerTomlPath = path5.join(stagingDir, "wrangler.toml");
1122
1234
  let tomlContent = fs6.readFileSync(wranglerTomlPath, "utf-8");
1123
- tomlContent = tomlContent.replace(/name = "[^"]*"/, `name = "${workerName}"`);
1124
- tomlContent = tomlContent.replace(/ZOOID_SERVER_ID = "[^"]*"/, `ZOOID_SERVER_ID = "${serverSlug}"`);
1235
+ tomlContent = tomlContent.replace(
1236
+ /name = "[^"]*"/,
1237
+ `name = "${workerName}"`
1238
+ );
1239
+ tomlContent = tomlContent.replace(
1240
+ /ZOOID_SERVER_ID = "[^"]*"/,
1241
+ `ZOOID_SERVER_ID = "${serverSlug}"`
1242
+ );
1125
1243
  try {
1126
1244
  const output = wrangler("d1 list --json", stagingDir, creds);
1127
1245
  const databases = JSON.parse(output);
1128
1246
  const db = databases.find((d) => d.name === dbName);
1129
1247
  if (db) {
1130
- tomlContent = tomlContent.replace(/database_name = "[^"]*"/, `database_name = "${dbName}"`);
1131
- tomlContent = tomlContent.replace(/database_id = "[^"]*"/, `database_id = "${db.uuid}"`);
1248
+ tomlContent = tomlContent.replace(
1249
+ /database_name = "[^"]*"/,
1250
+ `database_name = "${dbName}"`
1251
+ );
1252
+ tomlContent = tomlContent.replace(
1253
+ /database_id = "[^"]*"/,
1254
+ `database_id = "${db.uuid}"`
1255
+ );
1132
1256
  }
1133
1257
  } catch {
1134
1258
  }
@@ -1136,8 +1260,12 @@ async function runDeploy() {
1136
1260
  const existingConfig = loadConfig();
1137
1261
  adminToken = existingConfig.admin_token;
1138
1262
  if (!adminToken) {
1139
- printError("No admin token found in ~/.zooid/config.json for this server");
1140
- console.log("If this is a first deploy, remove the D1 database and try again.");
1263
+ printError(
1264
+ "No admin token found in ~/.zooid/config.json for this server"
1265
+ );
1266
+ console.log(
1267
+ "If this is a first deploy, remove the D1 database and try again."
1268
+ );
1141
1269
  cleanup(stagingDir);
1142
1270
  process.exit(1);
1143
1271
  }
@@ -1156,7 +1284,10 @@ async function runDeploy() {
1156
1284
  await new Promise((r) => setTimeout(r, 2e3));
1157
1285
  if (canonicalUrl && adminToken) {
1158
1286
  try {
1159
- const client = new ZooidClient2({ server: canonicalUrl, token: adminToken });
1287
+ const client = new ZooidClient2({
1288
+ server: canonicalUrl,
1289
+ token: adminToken
1290
+ });
1160
1291
  await client.updateServerMeta({
1161
1292
  name: config.name || void 0,
1162
1293
  description: config.description || void 0,
@@ -1167,7 +1298,9 @@ async function runDeploy() {
1167
1298
  });
1168
1299
  printSuccess("Server identity updated");
1169
1300
  } catch (err) {
1170
- printError(`Failed to push server identity: ${err instanceof Error ? err.message : err}`);
1301
+ printError(
1302
+ `Failed to push server identity: ${err instanceof Error ? err.message : err}`
1303
+ );
1171
1304
  }
1172
1305
  }
1173
1306
  if (!config.url && (customDomain || workerUrl)) {
@@ -1202,7 +1335,9 @@ async function runDeploy() {
1202
1335
  if (isFirstDeploy) {
1203
1336
  console.log(" Next steps:");
1204
1337
  console.log(" npx zooid channel create my-channel");
1205
- console.log(` npx zooid publish my-channel --data='{"hello": "world"}'`);
1338
+ console.log(
1339
+ ` npx zooid publish my-channel --data='{"hello": "world"}'`
1340
+ );
1206
1341
  console.log("");
1207
1342
  }
1208
1343
  }
@@ -1340,11 +1475,15 @@ channelCmd.command("list").description("List all channels").action(async () => {
1340
1475
  try {
1341
1476
  const channels = await runChannelList();
1342
1477
  if (channels.length === 0) {
1343
- console.log("No channels yet. Create one with: npx zooid channel create <name>");
1478
+ console.log(
1479
+ "No channels yet. Create one with: npx zooid channel create <name>"
1480
+ );
1344
1481
  } else {
1345
1482
  for (const ch of channels) {
1346
1483
  const visibility = ch.is_public ? "public" : "private";
1347
- console.log(` ${ch.id} \u2014 ${ch.name} (${visibility}, ${ch.event_count} events)`);
1484
+ console.log(
1485
+ ` ${ch.id} \u2014 ${ch.name} (${visibility}, ${ch.event_count} events)`
1486
+ );
1348
1487
  }
1349
1488
  }
1350
1489
  } catch (err) {
@@ -1373,13 +1512,19 @@ program.command("publish <channel>").description("Publish an event to a channel"
1373
1512
  process.exit(1);
1374
1513
  }
1375
1514
  });
1376
- program.command("tail <channel>").description("Fetch latest events, or stream live with -f").option("-n, --limit <n>", "Max events to return", "50").option("-f, --follow", "Follow mode \u2014 stream new events as they arrive").option("--type <type>", "Filter events by type").option("--since <iso>", "Only events after this ISO 8601 timestamp").option("--cursor <cursor>", "Resume from a previous cursor").option("--mode <mode>", "Transport mode for follow: auto, ws, or poll", "auto").option("--interval <ms>", "Poll interval in ms for follow mode", "5000").action(async (channel, opts) => {
1515
+ program.command("tail <channel>").description("Fetch latest events, or stream live with -f").option("-n, --limit <n>", "Max events to return", "50").option("-f, --follow", "Follow mode \u2014 stream new events as they arrive").option("--type <type>", "Filter events by type").option("--since <iso>", "Only events after this ISO 8601 timestamp").option("--cursor <cursor>", "Resume from a previous cursor").option(
1516
+ "--mode <mode>",
1517
+ "Transport mode for follow: auto, ws, or poll",
1518
+ "auto"
1519
+ ).option("--interval <ms>", "Poll interval in ms for follow mode", "5000").action(async (channel, opts) => {
1377
1520
  setTelemetryChannel(channel);
1378
1521
  try {
1379
1522
  if (opts.follow) {
1380
1523
  const mode = opts.mode;
1381
1524
  const transport = mode === "auto" ? "auto (WebSocket \u2192 poll fallback)" : mode;
1382
- console.log(`Tailing ${channel} [${transport}]${opts.type ? ` type=${opts.type}` : ""}...`);
1525
+ console.log(
1526
+ `Tailing ${channel} [${transport}]${opts.type ? ` type=${opts.type}` : ""}...`
1527
+ );
1383
1528
  console.log("Press Ctrl+C to stop.\n");
1384
1529
  await runTail(channel, {
1385
1530
  follow: true,
@@ -1421,7 +1566,9 @@ program.command("subscribe <channel>").description("Subscribe to a channel").opt
1421
1566
  } else {
1422
1567
  const mode = opts.mode;
1423
1568
  const transport = mode === "auto" ? "auto (WebSocket \u2192 poll fallback)" : mode;
1424
- console.log(`Subscribing to ${channel} [${transport}]${opts.type ? ` type=${opts.type}` : ""}...`);
1569
+ console.log(
1570
+ `Subscribing to ${channel} [${transport}]${opts.type ? ` type=${opts.type}` : ""}...`
1571
+ );
1425
1572
  console.log("Press Ctrl+C to stop.\n");
1426
1573
  await runSubscribePoll(channel, {
1427
1574
  interval: parseInt(opts.interval, 10),
@@ -1460,12 +1607,15 @@ serverCmd.command("set").description("Update server metadata").option("--name <n
1460
1607
  const fields = {};
1461
1608
  if (opts.name !== void 0) fields.name = opts.name;
1462
1609
  if (opts.description !== void 0) fields.description = opts.description;
1463
- if (opts.tags !== void 0) fields.tags = opts.tags.split(",").map((t) => t.trim());
1610
+ if (opts.tags !== void 0)
1611
+ fields.tags = opts.tags.split(",").map((t) => t.trim());
1464
1612
  if (opts.owner !== void 0) fields.owner = opts.owner;
1465
1613
  if (opts.company !== void 0) fields.company = opts.company;
1466
1614
  if (opts.email !== void 0) fields.email = opts.email;
1467
1615
  if (Object.keys(fields).length === 0) {
1468
- printError("No fields specified. Use --name, --description, --tags, --owner, --company, or --email.");
1616
+ printError(
1617
+ "No fields specified. Use --name, --description, --tags, --owner, --company, or --email."
1618
+ );
1469
1619
  process.exit(1);
1470
1620
  }
1471
1621
  const meta = await runServerSet(fields);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zooid",
3
- "version": "0.0.1",
3
+ "version": "0.0.2",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "author": "Ori Ben",
@@ -18,9 +18,9 @@
18
18
  "dependencies": {
19
19
  "@inquirer/checkbox": "^5.0.7",
20
20
  "commander": "^14.0.3",
21
+ "@zooid/server": "0.0.1",
21
22
  "@zooid/sdk": "0.0.1",
22
- "@zooid/types": "0.0.1",
23
- "@zooid/server": "0.0.1"
23
+ "@zooid/types": "0.0.1"
24
24
  },
25
25
  "devDependencies": {
26
26
  "@cloudflare/vitest-pool-workers": "^0.8.34",