relight-cli 0.1.0 → 0.3.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.
Files changed (45) hide show
  1. package/README.md +77 -34
  2. package/package.json +12 -4
  3. package/src/cli.js +350 -1
  4. package/src/commands/apps.js +128 -0
  5. package/src/commands/auth.js +13 -4
  6. package/src/commands/config.js +282 -0
  7. package/src/commands/cost.js +593 -0
  8. package/src/commands/db.js +775 -0
  9. package/src/commands/deploy.js +264 -0
  10. package/src/commands/doctor.js +69 -13
  11. package/src/commands/domains.js +223 -0
  12. package/src/commands/logs.js +111 -0
  13. package/src/commands/open.js +42 -0
  14. package/src/commands/ps.js +121 -0
  15. package/src/commands/scale.js +132 -0
  16. package/src/commands/service.js +227 -0
  17. package/src/lib/clouds/aws.js +309 -35
  18. package/src/lib/clouds/cf.js +401 -2
  19. package/src/lib/clouds/gcp.js +255 -4
  20. package/src/lib/clouds/neon.js +147 -0
  21. package/src/lib/clouds/slicervm.js +139 -0
  22. package/src/lib/config.js +200 -2
  23. package/src/lib/docker.js +34 -0
  24. package/src/lib/link.js +31 -5
  25. package/src/lib/providers/aws/app.js +481 -0
  26. package/src/lib/providers/aws/db.js +504 -0
  27. package/src/lib/providers/aws/dns.js +232 -0
  28. package/src/lib/providers/aws/registry.js +59 -0
  29. package/src/lib/providers/cf/app.js +596 -0
  30. package/src/lib/providers/cf/bundle.js +70 -0
  31. package/src/lib/providers/cf/db.js +181 -0
  32. package/src/lib/providers/cf/dns.js +148 -0
  33. package/src/lib/providers/cf/registry.js +17 -0
  34. package/src/lib/providers/gcp/app.js +429 -0
  35. package/src/lib/providers/gcp/db.js +372 -0
  36. package/src/lib/providers/gcp/dns.js +166 -0
  37. package/src/lib/providers/gcp/registry.js +30 -0
  38. package/src/lib/providers/neon/db.js +306 -0
  39. package/src/lib/providers/resolve.js +79 -0
  40. package/src/lib/providers/slicervm/app.js +396 -0
  41. package/src/lib/providers/slicervm/db.js +33 -0
  42. package/src/lib/providers/slicervm/dns.js +58 -0
  43. package/src/lib/providers/slicervm/registry.js +7 -0
  44. package/worker-template/package.json +10 -0
  45. package/worker-template/src/index.js +260 -0
@@ -1,4 +1,7 @@
1
1
  var CF_API = "https://api.cloudflare.com/client/v4";
2
+ var CF_REGISTRY = "registry.cloudflare.com";
3
+
4
+ export { CF_REGISTRY };
2
5
 
3
6
  export var TOKEN_URL =
4
7
  "https://dash.cloudflare.com/profile/api-tokens?" +
@@ -13,12 +16,16 @@ export var TOKEN_URL =
13
16
  ) +
14
17
  "&name=relight-cli";
15
18
 
16
- export async function cfApi(method, path, body, apiToken) {
19
+ // --- CF API base ---
20
+
21
+ export async function cfApi(method, path, body, apiToken, contentType) {
17
22
  var headers = {
18
23
  Authorization: `Bearer ${apiToken}`,
19
24
  };
20
25
 
21
- if (body && typeof body === "object") {
26
+ if (contentType) {
27
+ headers["Content-Type"] = contentType;
28
+ } else if (body && typeof body === "object") {
22
29
  headers["Content-Type"] = "application/json";
23
30
  body = JSON.stringify(body);
24
31
  }
@@ -41,6 +48,32 @@ export async function cfApi(method, path, body, apiToken) {
41
48
  return res.text();
42
49
  }
43
50
 
51
+ // --- CF GraphQL Analytics API ---
52
+
53
+ export async function cfGraphQL(apiToken, query, variables) {
54
+ var res = await fetch("https://api.cloudflare.com/client/v4/graphql", {
55
+ method: "POST",
56
+ headers: {
57
+ Authorization: `Bearer ${apiToken}`,
58
+ "Content-Type": "application/json",
59
+ },
60
+ body: JSON.stringify({ query, variables }),
61
+ });
62
+
63
+ if (!res.ok) {
64
+ var text = await res.text();
65
+ throw new Error(`CF GraphQL: ${res.status} ${text}`);
66
+ }
67
+
68
+ var json = await res.json();
69
+ if (json.errors && json.errors.length > 0) {
70
+ throw new Error(`CF GraphQL: ${json.errors.map((e) => e.message).join(", ")}`);
71
+ }
72
+ return json.data;
73
+ }
74
+
75
+ // --- Auth verification ---
76
+
44
77
  export async function verifyToken(apiToken) {
45
78
  return cfApi("GET", "/user/tokens/verify", null, apiToken);
46
79
  }
@@ -50,6 +83,299 @@ export async function listAccounts(apiToken) {
50
83
  return res.result || [];
51
84
  }
52
85
 
86
+ // --- Registry ---
87
+
88
+ export async function getRegistryCredentials(accountId, apiToken) {
89
+ var res = await cfApi(
90
+ "POST",
91
+ `/accounts/${accountId}/containers/registries/${CF_REGISTRY}/credentials`,
92
+ { permissions: ["push", "pull"], expiration_minutes: 15 },
93
+ apiToken
94
+ );
95
+ return res.result;
96
+ }
97
+
98
+ // --- Worker scripts ---
99
+
100
+ export async function uploadWorker(accountId, apiToken, scriptName, code, metadata) {
101
+ var form = new FormData();
102
+ form.append(
103
+ "metadata",
104
+ new Blob([JSON.stringify(metadata)], { type: "application/json" })
105
+ );
106
+ form.append(
107
+ "index.js",
108
+ new Blob([code], { type: "application/javascript+module" }),
109
+ "index.js"
110
+ );
111
+
112
+ var res = await fetch(
113
+ `${CF_API}/accounts/${accountId}/workers/scripts/${scriptName}`,
114
+ {
115
+ method: "PUT",
116
+ headers: { Authorization: `Bearer ${apiToken}` },
117
+ body: form,
118
+ }
119
+ );
120
+
121
+ if (!res.ok) {
122
+ var text = await res.text();
123
+ throw new Error(`Worker upload failed: ${res.status} ${text}`);
124
+ }
125
+
126
+ return res.json();
127
+ }
128
+
129
+ export async function deleteWorker(accountId, apiToken, scriptName) {
130
+ return cfApi(
131
+ "DELETE",
132
+ `/accounts/${accountId}/workers/scripts/${scriptName}`,
133
+ null,
134
+ apiToken
135
+ );
136
+ }
137
+
138
+ export async function patchWorkerSettings(accountId, apiToken, scriptName, settings) {
139
+ var form = new FormData();
140
+ form.append(
141
+ "settings",
142
+ new Blob([JSON.stringify(settings)], { type: "application/json" })
143
+ );
144
+
145
+ var res = await fetch(
146
+ `${CF_API}/accounts/${accountId}/workers/scripts/${scriptName}/settings`,
147
+ {
148
+ method: "PATCH",
149
+ headers: { Authorization: `Bearer ${apiToken}` },
150
+ body: form,
151
+ }
152
+ );
153
+
154
+ if (!res.ok) {
155
+ var text = await res.text();
156
+ throw new Error(`Settings update failed: ${res.status} ${text}`);
157
+ }
158
+
159
+ return res.json();
160
+ }
161
+
162
+ export async function listWorkerScripts(accountId, apiToken) {
163
+ var res = await cfApi(
164
+ "GET",
165
+ `/accounts/${accountId}/workers/scripts`,
166
+ null,
167
+ apiToken
168
+ );
169
+ return res.result || [];
170
+ }
171
+
172
+ export async function getWorkerSettings(accountId, apiToken, scriptName) {
173
+ var res = await cfApi(
174
+ "GET",
175
+ `/accounts/${accountId}/workers/scripts/${scriptName}/settings`,
176
+ null,
177
+ apiToken
178
+ );
179
+ return res.result;
180
+ }
181
+
182
+ // --- Container applications ---
183
+
184
+ export async function getDONamespaceId(accountId, apiToken, scriptName, className) {
185
+ var res = await cfApi(
186
+ "GET",
187
+ `/accounts/${accountId}/workers/durable_objects/namespaces`,
188
+ null,
189
+ apiToken
190
+ );
191
+ var namespaces = res.result || [];
192
+ var ns = namespaces.find(
193
+ (n) => n.script === scriptName && n.class === className
194
+ );
195
+ return ns ? ns.id : null;
196
+ }
197
+
198
+ export async function listContainerApps(accountId, apiToken) {
199
+ var res = await cfApi(
200
+ "GET",
201
+ `/accounts/${accountId}/containers/applications`,
202
+ null,
203
+ apiToken
204
+ );
205
+ return res.result || [];
206
+ }
207
+
208
+ export async function findContainerApp(accountId, apiToken, name) {
209
+ var apps = await listContainerApps(accountId, apiToken);
210
+ return apps.find((a) => a.name === name) || null;
211
+ }
212
+
213
+ export async function createContainerApp(accountId, apiToken, app) {
214
+ var res = await cfApi(
215
+ "POST",
216
+ `/accounts/${accountId}/containers/applications`,
217
+ app,
218
+ apiToken
219
+ );
220
+ return res.result;
221
+ }
222
+
223
+ export async function deleteContainerApp(accountId, apiToken, appId) {
224
+ return cfApi(
225
+ "DELETE",
226
+ `/accounts/${accountId}/containers/applications/${appId}`,
227
+ null,
228
+ apiToken
229
+ );
230
+ }
231
+
232
+ export async function modifyContainerApp(accountId, apiToken, appId, changes) {
233
+ var res = await cfApi(
234
+ "PATCH",
235
+ `/accounts/${accountId}/containers/applications/${appId}`,
236
+ changes,
237
+ apiToken
238
+ );
239
+ return res.result;
240
+ }
241
+
242
+ export async function createRollout(accountId, apiToken, appId, rollout) {
243
+ var res = await cfApi(
244
+ "POST",
245
+ `/accounts/${accountId}/containers/applications/${appId}/rollouts`,
246
+ rollout,
247
+ apiToken
248
+ );
249
+ return res.result;
250
+ }
251
+
252
+ // --- Tail/logs ---
253
+
254
+ export async function createTail(accountId, apiToken, scriptName) {
255
+ var res = await cfApi(
256
+ "POST",
257
+ `/accounts/${accountId}/workers/scripts/${scriptName}/tails`,
258
+ {},
259
+ apiToken
260
+ );
261
+ return res.result;
262
+ }
263
+
264
+ export async function deleteTail(accountId, apiToken, scriptName, tailId) {
265
+ return cfApi(
266
+ "DELETE",
267
+ `/accounts/${accountId}/workers/scripts/${scriptName}/tails/${tailId}`,
268
+ null,
269
+ apiToken
270
+ );
271
+ }
272
+
273
+ // --- Zones ---
274
+
275
+ export async function listZones(accountId, apiToken) {
276
+ var all = [];
277
+ var page = 1;
278
+ while (true) {
279
+ var res = await cfApi(
280
+ "GET",
281
+ `/zones?account.id=${accountId}&per_page=50&status=active&page=${page}`,
282
+ null,
283
+ apiToken
284
+ );
285
+ var zones = res.result || [];
286
+ all.push(...zones);
287
+ if (zones.length < 50) break;
288
+ page++;
289
+ }
290
+ return all;
291
+ }
292
+
293
+ export function findZoneForHostname(zones, hostname) {
294
+ var match = null;
295
+ for (var zone of zones) {
296
+ if (hostname === zone.name || hostname.endsWith("." + zone.name)) {
297
+ if (!match || zone.name.length > match.name.length) {
298
+ match = zone;
299
+ }
300
+ }
301
+ }
302
+ return match;
303
+ }
304
+
305
+ // --- DNS records ---
306
+
307
+ export async function listDnsRecords(accountId, apiToken, zoneId, params) {
308
+ var qs = new URLSearchParams(params || {}).toString();
309
+ var path = `/zones/${zoneId}/dns_records${qs ? "?" + qs : ""}`;
310
+ var res = await cfApi("GET", path, null, apiToken);
311
+ return res.result || [];
312
+ }
313
+
314
+ export async function createDnsRecord(accountId, apiToken, zoneId, record) {
315
+ return cfApi(
316
+ "POST",
317
+ `/zones/${zoneId}/dns_records`,
318
+ record,
319
+ apiToken
320
+ );
321
+ }
322
+
323
+ export async function deleteDnsRecord(accountId, apiToken, zoneId, recordId) {
324
+ return cfApi(
325
+ "DELETE",
326
+ `/zones/${zoneId}/dns_records/${recordId}`,
327
+ null,
328
+ apiToken
329
+ );
330
+ }
331
+
332
+ // --- Workers custom domains ---
333
+
334
+ export async function addWorkerDomain(accountId, apiToken, scriptName, hostname, zoneId) {
335
+ return cfApi(
336
+ "PUT",
337
+ `/accounts/${accountId}/workers/domains`,
338
+ {
339
+ hostname,
340
+ zone_id: zoneId,
341
+ service: scriptName,
342
+ environment: "production",
343
+ },
344
+ apiToken
345
+ );
346
+ }
347
+
348
+ export async function removeWorkerDomain(accountId, apiToken, hostname) {
349
+ var res = await cfApi(
350
+ "GET",
351
+ `/accounts/${accountId}/workers/domains`,
352
+ null,
353
+ apiToken
354
+ );
355
+ var domains = res.result || [];
356
+ var domain = domains.find((d) => d.hostname === hostname);
357
+ if (!domain) return;
358
+
359
+ return cfApi(
360
+ "DELETE",
361
+ `/accounts/${accountId}/workers/domains/${domain.id}`,
362
+ null,
363
+ apiToken
364
+ );
365
+ }
366
+
367
+ export async function listWorkerDomainsForService(accountId, apiToken, scriptName) {
368
+ var res = await cfApi(
369
+ "GET",
370
+ `/accounts/${accountId}/workers/domains`,
371
+ null,
372
+ apiToken
373
+ );
374
+ return (res.result || []).filter((d) => d.service === scriptName);
375
+ }
376
+
377
+ // --- Workers subdomain ---
378
+
53
379
  export async function getWorkersSubdomain(accountId, apiToken) {
54
380
  try {
55
381
  var res = await cfApi(
@@ -63,3 +389,76 @@ export async function getWorkersSubdomain(accountId, apiToken) {
63
389
  return null;
64
390
  }
65
391
  }
392
+
393
+ export async function enableWorkerSubdomain(accountId, apiToken, scriptName) {
394
+ return cfApi(
395
+ "POST",
396
+ `/accounts/${accountId}/workers/services/${scriptName}/environments/production/subdomain`,
397
+ { enabled: true },
398
+ apiToken
399
+ );
400
+ }
401
+
402
+ // --- D1 databases ---
403
+
404
+ export async function createD1Database(accountId, apiToken, name, { locationHint, jurisdiction } = {}) {
405
+ var body = { name };
406
+ if (jurisdiction) body.jurisdiction = jurisdiction;
407
+ else if (locationHint) body.primary_location_hint = locationHint;
408
+ var res = await cfApi(
409
+ "POST",
410
+ `/accounts/${accountId}/d1/database`,
411
+ body,
412
+ apiToken
413
+ );
414
+ return res.result;
415
+ }
416
+
417
+ export async function deleteD1Database(accountId, apiToken, dbId) {
418
+ return cfApi(
419
+ "DELETE",
420
+ `/accounts/${accountId}/d1/database/${dbId}`,
421
+ null,
422
+ apiToken
423
+ );
424
+ }
425
+
426
+ export async function getD1Database(accountId, apiToken, dbId) {
427
+ var res = await cfApi(
428
+ "GET",
429
+ `/accounts/${accountId}/d1/database/${dbId}`,
430
+ null,
431
+ apiToken
432
+ );
433
+ return res.result;
434
+ }
435
+
436
+ export async function queryD1(accountId, apiToken, dbId, sql, params) {
437
+ var body = { sql };
438
+ if (params) body.params = params;
439
+ var res = await cfApi(
440
+ "POST",
441
+ `/accounts/${accountId}/d1/database/${dbId}/query`,
442
+ body,
443
+ apiToken
444
+ );
445
+ return res.result;
446
+ }
447
+
448
+ export async function exportD1(accountId, apiToken, dbId, body) {
449
+ return cfApi(
450
+ "POST",
451
+ `/accounts/${accountId}/d1/database/${dbId}/export`,
452
+ body,
453
+ apiToken
454
+ );
455
+ }
456
+
457
+ export async function importD1(accountId, apiToken, dbId, body) {
458
+ return cfApi(
459
+ "POST",
460
+ `/accounts/${accountId}/d1/database/${dbId}/import`,
461
+ body,
462
+ apiToken
463
+ );
464
+ }