zooid 0.0.1 → 0.0.3

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/index.js +227 -68
  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);
@@ -592,10 +609,19 @@ async function runShare(channelIds, options = {}) {
592
609
  console.log("");
593
610
  for (const ch of selected) {
594
611
  const dirChannel = result.channels?.find((c) => c.channel_id === ch.id);
595
- const url = dirChannel?.directory_url ?? `${DIRECTORY_BASE_URL}/servers/${encodeURIComponent(serverUrl)}/${ch.id}`;
612
+ const url = dirChannel?.directory_url ?? `${serverUrl}/${ch.id}`;
596
613
  console.log(` ${ch.id} \u2192 ${url}`);
597
614
  }
598
615
  console.log("");
616
+ console.log(` Any zooid can find your channel using:`);
617
+ console.log(` npx zooid discover --query ${selected[0].id}`);
618
+ const tags = [
619
+ ...new Set(selected.flatMap((ch) => channelDetails.get(ch.id)?.tags ?? []))
620
+ ];
621
+ if (tags.length > 0) {
622
+ console.log(` npx zooid discover --tag ${tags[0]}`);
623
+ }
624
+ console.log("");
599
625
  }
600
626
  async function pickChannels(channels) {
601
627
  const { default: checkbox } = await import("@inquirer/checkbox");
@@ -636,7 +662,11 @@ async function promptChannelDetails(channels, skipPrompt) {
636
662
  for (const ch of channels) {
637
663
  console.log(` ${ch.id}:`);
638
664
  const desc = await ask(rl, "Description", ch.description ?? "");
639
- const tagsRaw = await ask(rl, "Tags (comma-separated)", ch.tags.join(", "));
665
+ const tagsRaw = await ask(
666
+ rl,
667
+ "Tags (comma-separated)",
668
+ ch.tags.join(", ")
669
+ );
640
670
  const tags = tagsRaw.split(",").map((t) => t.trim()).filter(Boolean);
641
671
  result.set(ch.id, { description: desc, tags });
642
672
  console.log("");
@@ -670,7 +700,9 @@ async function runUnshare(channelId) {
670
700
  const config = loadConfig();
671
701
  const serverUrl = config.server;
672
702
  if (!serverUrl) {
673
- throw new Error("No server configured. Run: npx zooid config set server <url>");
703
+ throw new Error(
704
+ "No server configured. Run: npx zooid config set server <url>"
705
+ );
674
706
  }
675
707
  const { claim, signature } = await client.getClaim([channelId], "delete");
676
708
  const res = await directoryFetch("/api/servers/channels", {
@@ -752,11 +784,17 @@ function base64url(buf) {
752
784
  return buf.toString("base64url");
753
785
  }
754
786
  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
- })));
787
+ const header = base64url(
788
+ Buffer.from(JSON.stringify({ alg: "HS256", typ: "JWT" }))
789
+ );
790
+ const payload = base64url(
791
+ Buffer.from(
792
+ JSON.stringify({
793
+ scope: "admin",
794
+ iat: Math.floor(Date.now() / 1e3)
795
+ })
796
+ )
797
+ );
760
798
  const data = `${header}.${payload}`;
761
799
  const key = await crypto.subtle.importKey(
762
800
  "raw",
@@ -765,7 +803,11 @@ async function createAdminToken(secret) {
765
803
  false,
766
804
  ["sign"]
767
805
  );
768
- const sig = await crypto.subtle.sign("HMAC", key, new TextEncoder().encode(data));
806
+ const sig = await crypto.subtle.sign(
807
+ "HMAC",
808
+ key,
809
+ new TextEncoder().encode(data)
810
+ );
769
811
  const signature = base64url(Buffer.from(sig));
770
812
  return `${data}.${signature}`;
771
813
  }
@@ -786,10 +828,13 @@ async function runDev(port = 8787) {
786
828
  const schemaPath = path3.join(serverDir, "src/db/schema.sql");
787
829
  if (fs4.existsSync(schemaPath)) {
788
830
  try {
789
- execSync(`npx wrangler d1 execute zooid-db --local --file=${schemaPath}`, {
790
- cwd: serverDir,
791
- stdio: "pipe"
792
- });
831
+ execSync(
832
+ `npx wrangler d1 execute zooid-db --local --file=${schemaPath}`,
833
+ {
834
+ cwd: serverDir,
835
+ stdio: "pipe"
836
+ }
837
+ );
793
838
  printSuccess("Database schema initialized");
794
839
  } catch {
795
840
  printSuccess("Database ready (schema already exists)");
@@ -802,11 +847,15 @@ async function runDev(port = 8787) {
802
847
  console.log("");
803
848
  console.log("Starting wrangler dev...");
804
849
  console.log("");
805
- const child = spawn("npx", ["wrangler", "dev", "--local", "--port", String(port)], {
806
- cwd: serverDir,
807
- stdio: "inherit",
808
- shell: true
809
- });
850
+ const child = spawn(
851
+ "npx",
852
+ ["wrangler", "dev", "--local", "--port", String(port)],
853
+ {
854
+ cwd: serverDir,
855
+ stdio: "inherit",
856
+ shell: true
857
+ }
858
+ );
810
859
  child.on("error", (err) => {
811
860
  console.error(`Failed to start local server: ${err.message}`);
812
861
  process.exit(1);
@@ -850,11 +899,19 @@ async function runInit() {
850
899
  console.log(" Configure your Zooid server");
851
900
  console.log(" Press Enter to accept defaults shown in [brackets].\n");
852
901
  const name = await ask2(rl, "Server name", existing?.name ?? "");
853
- const description = await ask2(rl, "Description", existing?.description ?? "");
902
+ const description = await ask2(
903
+ rl,
904
+ "Description",
905
+ existing?.description ?? ""
906
+ );
854
907
  const owner = await ask2(rl, "Owner", existing?.owner ?? "");
855
908
  const company = await ask2(rl, "Company", existing?.company ?? "");
856
909
  const email = await ask2(rl, "Email", existing?.email ?? "");
857
- const tagsRaw = await ask2(rl, "Tags (comma-separated)", existing?.tags?.join(", ") ?? "");
910
+ const tagsRaw = await ask2(
911
+ rl,
912
+ "Tags (comma-separated)",
913
+ existing?.tags?.join(", ") ?? ""
914
+ );
858
915
  const url = await ask2(rl, "URL", existing?.url ?? "");
859
916
  const tags = tagsRaw.split(",").map((t) => t.trim()).filter(Boolean);
860
917
  const config = {
@@ -875,7 +932,10 @@ async function runInit() {
875
932
  printInfo("Owner", config.owner || "(not set)");
876
933
  printInfo("Company", config.company || "(not set)");
877
934
  printInfo("Email", config.email || "(not set)");
878
- printInfo("Tags", config.tags.length > 0 ? config.tags.join(", ") : "(none)");
935
+ printInfo(
936
+ "Tags",
937
+ config.tags.length > 0 ? config.tags.join(", ") : "(none)"
938
+ );
879
939
  printInfo("URL", config.url || "(not set)");
880
940
  console.log("");
881
941
  } finally {
@@ -918,12 +978,26 @@ function prepareStagingDir() {
918
978
  let toml = fs6.readFileSync(path5.join(serverDir, "wrangler.toml"), "utf-8");
919
979
  toml = toml.replace(/directory\s*=\s*"[^"]*"/, 'directory = "./web-dist/"');
920
980
  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");
981
+ const nodeModules = findServerNodeModules(serverDir);
982
+ if (nodeModules) {
983
+ fs6.symlinkSync(nodeModules, path5.join(tmpDir, "node_modules"), "junction");
924
984
  }
925
985
  return tmpDir;
926
986
  }
987
+ function findServerNodeModules(serverDir) {
988
+ const local = path5.join(serverDir, "node_modules");
989
+ if (fs6.existsSync(path5.join(local, "hono"))) return local;
990
+ const storeNodeModules = path5.resolve(serverDir, "..", "..");
991
+ if (fs6.existsSync(path5.join(storeNodeModules, "hono")))
992
+ return storeNodeModules;
993
+ let dir = serverDir;
994
+ while (dir !== path5.dirname(dir)) {
995
+ dir = path5.dirname(dir);
996
+ const nm = path5.join(dir, "node_modules");
997
+ if (fs6.existsSync(path5.join(nm, "hono"))) return nm;
998
+ }
999
+ return null;
1000
+ }
927
1001
  function copyDirSync(src, dest) {
928
1002
  fs6.mkdirSync(dest, { recursive: true });
929
1003
  for (const entry of fs6.readdirSync(src, { withFileTypes: true })) {
@@ -940,11 +1014,17 @@ function base64url2(buf) {
940
1014
  return buf.toString("base64url");
941
1015
  }
942
1016
  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
- })));
1017
+ const header = base64url2(
1018
+ Buffer.from(JSON.stringify({ alg: "HS256", typ: "JWT" }))
1019
+ );
1020
+ const payload = base64url2(
1021
+ Buffer.from(
1022
+ JSON.stringify({
1023
+ scope: "admin",
1024
+ iat: Math.floor(Date.now() / 1e3)
1025
+ })
1026
+ )
1027
+ );
948
1028
  const data = `${header}.${payload}`;
949
1029
  const key = await crypto2.subtle.importKey(
950
1030
  "raw",
@@ -953,7 +1033,11 @@ async function createAdminToken2(secret) {
953
1033
  false,
954
1034
  ["sign"]
955
1035
  );
956
- const sig = await crypto2.subtle.sign("HMAC", key, new TextEncoder().encode(data));
1036
+ const sig = await crypto2.subtle.sign(
1037
+ "HMAC",
1038
+ key,
1039
+ new TextEncoder().encode(data)
1040
+ );
957
1041
  const signature = base64url2(Buffer.from(sig));
958
1042
  return `${data}.${signature}`;
959
1043
  }
@@ -975,7 +1059,9 @@ function wrangler(cmd, cwd, creds, opts) {
975
1059
  }
976
1060
  function parseDeployUrls(output) {
977
1061
  const workersDev = output.match(/https:\/\/[^\s]+\.workers\.dev/);
978
- const custom = output.match(/^\s+(\S+\.(?!workers\.dev)\S+)\s+\(custom domain\)/m);
1062
+ const custom = output.match(
1063
+ /^\s+(\S+\.(?!workers\.dev)\S+)\s+\(custom domain\)/m
1064
+ );
979
1065
  return {
980
1066
  workerUrl: workersDev ? workersDev[0] : null,
981
1067
  customDomain: custom ? `https://${custom[1]}` : null
@@ -1011,11 +1097,15 @@ async function getCfCredentials() {
1011
1097
  console.log("");
1012
1098
  console.log(" Cloudflare API token required for deployment.");
1013
1099
  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.');
1100
+ console.log(
1101
+ ' Use the "Edit Cloudflare Workers" template, then add D1 Edit permission.'
1102
+ );
1015
1103
  console.log(" Tip: save credentials in .env to skip this prompt.");
1016
1104
  console.log("");
1017
1105
  const token = await rl.question(" API token: ");
1018
- const accountId = await rl.question(" Account ID (from the dashboard URL or Workers overview): ");
1106
+ const accountId = await rl.question(
1107
+ " Account ID (from the dashboard URL or Workers overview): "
1108
+ );
1019
1109
  return {
1020
1110
  apiToken: token.trim(),
1021
1111
  accountId: accountId.trim() || void 0
@@ -1087,30 +1177,61 @@ async function runDeploy() {
1087
1177
  printSuccess(`D1 database created (${databaseId})`);
1088
1178
  const wranglerTomlPath = path5.join(stagingDir, "wrangler.toml");
1089
1179
  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}"`);
1180
+ tomlContent = tomlContent.replace(
1181
+ /name = "[^"]*"/,
1182
+ `name = "${workerName}"`
1183
+ );
1184
+ tomlContent = tomlContent.replace(
1185
+ /database_name = "[^"]*"/,
1186
+ `database_name = "${dbName}"`
1187
+ );
1188
+ tomlContent = tomlContent.replace(
1189
+ /database_id = "[^"]*"/,
1190
+ `database_id = "${databaseId}"`
1191
+ );
1192
+ tomlContent = tomlContent.replace(
1193
+ /ZOOID_SERVER_ID = "[^"]*"/,
1194
+ `ZOOID_SERVER_ID = "${serverSlug}"`
1195
+ );
1094
1196
  fs6.writeFileSync(wranglerTomlPath, tomlContent);
1095
1197
  printSuccess("Configured wrangler.toml");
1096
1198
  const schemaPath = path5.join(stagingDir, "src/db/schema.sql");
1097
1199
  if (fs6.existsSync(schemaPath)) {
1098
1200
  console.log("Running database schema migration...");
1099
- wrangler(`d1 execute ${dbName} --remote --file=${schemaPath}`, stagingDir, creds);
1201
+ wrangler(
1202
+ `d1 execute ${dbName} --remote --file=${schemaPath}`,
1203
+ stagingDir,
1204
+ creds
1205
+ );
1100
1206
  printSuccess("Database schema initialized");
1101
1207
  }
1102
1208
  console.log("Generating secrets...");
1103
1209
  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);
1210
+ const keyPair = await crypto2.subtle.generateKey("Ed25519", true, [
1211
+ "sign",
1212
+ "verify"
1213
+ ]);
1214
+ const privateKeyRaw = await crypto2.subtle.exportKey(
1215
+ "pkcs8",
1216
+ keyPair.privateKey
1217
+ );
1218
+ const publicKeyRaw = await crypto2.subtle.exportKey(
1219
+ "raw",
1220
+ keyPair.publicKey
1221
+ );
1107
1222
  const privateKeyB64 = Buffer.from(privateKeyRaw).toString("base64");
1108
1223
  const publicKeyB64 = Buffer.from(publicKeyRaw).toString("base64");
1109
- wrangler("secret put ZOOID_JWT_SECRET", stagingDir, creds, { input: jwtSecret });
1224
+ wrangler("secret put ZOOID_JWT_SECRET", stagingDir, creds, {
1225
+ input: jwtSecret
1226
+ });
1110
1227
  printSuccess("Set ZOOID_JWT_SECRET");
1111
- wrangler("secret put ZOOID_SIGNING_KEY", stagingDir, creds, { input: privateKeyB64 });
1228
+ wrangler("secret put ZOOID_SIGNING_KEY", stagingDir, creds, {
1229
+ input: privateKeyB64
1230
+ });
1112
1231
  printSuccess("Set ZOOID_SIGNING_KEY (Ed25519 private)");
1113
- wrangler("secret put ZOOID_PUBLIC_KEY", stagingDir, creds, { input: publicKeyB64 });
1232
+ wrangler("secret put ZOOID_PUBLIC_KEY", stagingDir, creds, {
1233
+ input: publicKeyB64
1234
+ });
1114
1235
  printSuccess("Set ZOOID_PUBLIC_KEY (Ed25519 public)");
1115
1236
  adminToken = await createAdminToken2(jwtSecret);
1116
1237
  printSuccess("Admin token generated");
@@ -1120,15 +1241,27 @@ async function runDeploy() {
1120
1241
  console.log("");
1121
1242
  const wranglerTomlPath = path5.join(stagingDir, "wrangler.toml");
1122
1243
  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}"`);
1244
+ tomlContent = tomlContent.replace(
1245
+ /name = "[^"]*"/,
1246
+ `name = "${workerName}"`
1247
+ );
1248
+ tomlContent = tomlContent.replace(
1249
+ /ZOOID_SERVER_ID = "[^"]*"/,
1250
+ `ZOOID_SERVER_ID = "${serverSlug}"`
1251
+ );
1125
1252
  try {
1126
1253
  const output = wrangler("d1 list --json", stagingDir, creds);
1127
1254
  const databases = JSON.parse(output);
1128
1255
  const db = databases.find((d) => d.name === dbName);
1129
1256
  if (db) {
1130
- tomlContent = tomlContent.replace(/database_name = "[^"]*"/, `database_name = "${dbName}"`);
1131
- tomlContent = tomlContent.replace(/database_id = "[^"]*"/, `database_id = "${db.uuid}"`);
1257
+ tomlContent = tomlContent.replace(
1258
+ /database_name = "[^"]*"/,
1259
+ `database_name = "${dbName}"`
1260
+ );
1261
+ tomlContent = tomlContent.replace(
1262
+ /database_id = "[^"]*"/,
1263
+ `database_id = "${db.uuid}"`
1264
+ );
1132
1265
  }
1133
1266
  } catch {
1134
1267
  }
@@ -1136,8 +1269,12 @@ async function runDeploy() {
1136
1269
  const existingConfig = loadConfig();
1137
1270
  adminToken = existingConfig.admin_token;
1138
1271
  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.");
1272
+ printError(
1273
+ "No admin token found in ~/.zooid/config.json for this server"
1274
+ );
1275
+ console.log(
1276
+ "If this is a first deploy, remove the D1 database and try again."
1277
+ );
1141
1278
  cleanup(stagingDir);
1142
1279
  process.exit(1);
1143
1280
  }
@@ -1156,7 +1293,10 @@ async function runDeploy() {
1156
1293
  await new Promise((r) => setTimeout(r, 2e3));
1157
1294
  if (canonicalUrl && adminToken) {
1158
1295
  try {
1159
- const client = new ZooidClient2({ server: canonicalUrl, token: adminToken });
1296
+ const client = new ZooidClient2({
1297
+ server: canonicalUrl,
1298
+ token: adminToken
1299
+ });
1160
1300
  await client.updateServerMeta({
1161
1301
  name: config.name || void 0,
1162
1302
  description: config.description || void 0,
@@ -1167,7 +1307,9 @@ async function runDeploy() {
1167
1307
  });
1168
1308
  printSuccess("Server identity updated");
1169
1309
  } catch (err) {
1170
- printError(`Failed to push server identity: ${err instanceof Error ? err.message : err}`);
1310
+ printError(
1311
+ `Failed to push server identity: ${err instanceof Error ? err.message : err}`
1312
+ );
1171
1313
  }
1172
1314
  }
1173
1315
  if (!config.url && (customDomain || workerUrl)) {
@@ -1202,7 +1344,9 @@ async function runDeploy() {
1202
1344
  if (isFirstDeploy) {
1203
1345
  console.log(" Next steps:");
1204
1346
  console.log(" npx zooid channel create my-channel");
1205
- console.log(` npx zooid publish my-channel --data='{"hello": "world"}'`);
1347
+ console.log(
1348
+ ` npx zooid publish my-channel --data='{"hello": "world"}'`
1349
+ );
1206
1350
  console.log("");
1207
1351
  }
1208
1352
  }
@@ -1340,11 +1484,15 @@ channelCmd.command("list").description("List all channels").action(async () => {
1340
1484
  try {
1341
1485
  const channels = await runChannelList();
1342
1486
  if (channels.length === 0) {
1343
- console.log("No channels yet. Create one with: npx zooid channel create <name>");
1487
+ console.log(
1488
+ "No channels yet. Create one with: npx zooid channel create <name>"
1489
+ );
1344
1490
  } else {
1345
1491
  for (const ch of channels) {
1346
1492
  const visibility = ch.is_public ? "public" : "private";
1347
- console.log(` ${ch.id} \u2014 ${ch.name} (${visibility}, ${ch.event_count} events)`);
1493
+ console.log(
1494
+ ` ${ch.id} \u2014 ${ch.name} (${visibility}, ${ch.event_count} events)`
1495
+ );
1348
1496
  }
1349
1497
  }
1350
1498
  } catch (err) {
@@ -1373,13 +1521,19 @@ program.command("publish <channel>").description("Publish an event to a channel"
1373
1521
  process.exit(1);
1374
1522
  }
1375
1523
  });
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) => {
1524
+ 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(
1525
+ "--mode <mode>",
1526
+ "Transport mode for follow: auto, ws, or poll",
1527
+ "auto"
1528
+ ).option("--interval <ms>", "Poll interval in ms for follow mode", "5000").action(async (channel, opts) => {
1377
1529
  setTelemetryChannel(channel);
1378
1530
  try {
1379
1531
  if (opts.follow) {
1380
1532
  const mode = opts.mode;
1381
1533
  const transport = mode === "auto" ? "auto (WebSocket \u2192 poll fallback)" : mode;
1382
- console.log(`Tailing ${channel} [${transport}]${opts.type ? ` type=${opts.type}` : ""}...`);
1534
+ console.log(
1535
+ `Tailing ${channel} [${transport}]${opts.type ? ` type=${opts.type}` : ""}...`
1536
+ );
1383
1537
  console.log("Press Ctrl+C to stop.\n");
1384
1538
  await runTail(channel, {
1385
1539
  follow: true,
@@ -1421,7 +1575,9 @@ program.command("subscribe <channel>").description("Subscribe to a channel").opt
1421
1575
  } else {
1422
1576
  const mode = opts.mode;
1423
1577
  const transport = mode === "auto" ? "auto (WebSocket \u2192 poll fallback)" : mode;
1424
- console.log(`Subscribing to ${channel} [${transport}]${opts.type ? ` type=${opts.type}` : ""}...`);
1578
+ console.log(
1579
+ `Subscribing to ${channel} [${transport}]${opts.type ? ` type=${opts.type}` : ""}...`
1580
+ );
1425
1581
  console.log("Press Ctrl+C to stop.\n");
1426
1582
  await runSubscribePoll(channel, {
1427
1583
  interval: parseInt(opts.interval, 10),
@@ -1460,12 +1616,15 @@ serverCmd.command("set").description("Update server metadata").option("--name <n
1460
1616
  const fields = {};
1461
1617
  if (opts.name !== void 0) fields.name = opts.name;
1462
1618
  if (opts.description !== void 0) fields.description = opts.description;
1463
- if (opts.tags !== void 0) fields.tags = opts.tags.split(",").map((t) => t.trim());
1619
+ if (opts.tags !== void 0)
1620
+ fields.tags = opts.tags.split(",").map((t) => t.trim());
1464
1621
  if (opts.owner !== void 0) fields.owner = opts.owner;
1465
1622
  if (opts.company !== void 0) fields.company = opts.company;
1466
1623
  if (opts.email !== void 0) fields.email = opts.email;
1467
1624
  if (Object.keys(fields).length === 0) {
1468
- printError("No fields specified. Use --name, --description, --tags, --owner, --company, or --email.");
1625
+ printError(
1626
+ "No fields specified. Use --name, --description, --tags, --owner, --company, or --email."
1627
+ );
1469
1628
  process.exit(1);
1470
1629
  }
1471
1630
  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.3",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "author": "Ori Ben",
@@ -19,8 +19,8 @@
19
19
  "@inquirer/checkbox": "^5.0.7",
20
20
  "commander": "^14.0.3",
21
21
  "@zooid/sdk": "0.0.1",
22
- "@zooid/types": "0.0.1",
23
- "@zooid/server": "0.0.1"
22
+ "@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",