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.
- package/dist/index.js +217 -67
- 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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
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(
|
|
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(
|
|
790
|
-
|
|
791
|
-
|
|
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(
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
|
922
|
-
if (
|
|
923
|
-
fs6.symlinkSync(
|
|
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(
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
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(
|
|
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, [
|
|
1105
|
-
|
|
1106
|
-
|
|
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, {
|
|
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, {
|
|
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, {
|
|
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(
|
|
1124
|
-
|
|
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(
|
|
1131
|
-
|
|
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(
|
|
1140
|
-
|
|
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({
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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)
|
|
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(
|
|
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.
|
|
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",
|