@peerbit/server 6.0.42 → 7.0.1

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 (84) hide show
  1. package/dist/src/cli.d.ts.map +1 -1
  2. package/dist/src/cli.js +161 -352
  3. package/dist/src/cli.js.map +1 -1
  4. package/dist/src/client.d.ts +1 -1
  5. package/dist/src/client.d.ts.map +1 -1
  6. package/dist/src/client.js +2 -5
  7. package/dist/src/client.js.map +1 -1
  8. package/dist/src/docker.d.ts +70 -1
  9. package/dist/src/docker.d.ts.map +1 -1
  10. package/dist/src/docker.js +559 -14
  11. package/dist/src/docker.js.map +1 -1
  12. package/dist/src/domain-lease.d.ts +126 -0
  13. package/dist/src/domain-lease.d.ts.map +1 -0
  14. package/dist/src/domain-lease.js +800 -0
  15. package/dist/src/domain-lease.js.map +1 -0
  16. package/dist/src/domain.d.ts +50 -2
  17. package/dist/src/domain.d.ts.map +1 -1
  18. package/dist/src/domain.js +484 -67
  19. package/dist/src/domain.js.map +1 -1
  20. package/dist/src/hetzner.d.ts +4 -21
  21. package/dist/src/hetzner.d.ts.map +1 -1
  22. package/dist/src/hetzner.js +4 -116
  23. package/dist/src/hetzner.js.map +1 -1
  24. package/dist/src/nginx-template.conf +19 -1
  25. package/dist/src/remotes.browser.d.ts +4 -1
  26. package/dist/src/remotes.browser.d.ts.map +1 -1
  27. package/dist/src/remotes.browser.js +1 -2
  28. package/dist/src/remotes.browser.js.map +1 -1
  29. package/dist/src/remotes.d.ts +2 -1
  30. package/dist/src/remotes.d.ts.map +1 -1
  31. package/dist/src/remotes.js +1 -0
  32. package/dist/src/remotes.js.map +1 -1
  33. package/dist/src/server.d.ts.map +1 -1
  34. package/dist/src/server.js +3 -5
  35. package/dist/src/server.js.map +1 -1
  36. package/dist/test/api.spec.js +2 -1
  37. package/dist/test/api.spec.js.map +1 -1
  38. package/dist/test/cli.spec.js +48 -1
  39. package/dist/test/cli.spec.js.map +1 -1
  40. package/dist/test/docker.spec.d.ts +2 -0
  41. package/dist/test/docker.spec.d.ts.map +1 -0
  42. package/dist/test/docker.spec.js +822 -0
  43. package/dist/test/docker.spec.js.map +1 -0
  44. package/dist/test/domain-lease.spec.d.ts +2 -0
  45. package/dist/test/domain-lease.spec.d.ts.map +1 -0
  46. package/dist/test/domain-lease.spec.js +530 -0
  47. package/dist/test/domain-lease.spec.js.map +1 -0
  48. package/dist/test/domain.integration.spec.js +358 -75
  49. package/dist/test/domain.integration.spec.js.map +1 -1
  50. package/dist/test/remotes.spec.d.ts +2 -0
  51. package/dist/test/remotes.spec.d.ts.map +1 -0
  52. package/dist/test/remotes.spec.js +15 -0
  53. package/dist/test/remotes.spec.js.map +1 -0
  54. package/dist/tsconfig.tsbuildinfo +1 -1
  55. package/dist/ui/assets/index-CjZwCewh.js +10 -0
  56. package/dist/ui/assets/index-e3g42MbC.js +9 -0
  57. package/dist/ui/index.html +1 -1
  58. package/package.json +14 -19
  59. package/src/cli.ts +204 -408
  60. package/src/client.ts +2 -6
  61. package/src/docker.ts +841 -20
  62. package/src/domain-lease.ts +1118 -0
  63. package/src/domain.ts +650 -90
  64. package/src/hetzner.ts +4 -184
  65. package/src/nginx-template.conf +19 -1
  66. package/src/remotes.browser.ts +7 -1
  67. package/src/remotes.ts +6 -1
  68. package/src/server.ts +3 -5
  69. package/dist/src/aws.browser.d.ts +0 -2
  70. package/dist/src/aws.browser.d.ts.map +0 -1
  71. package/dist/src/aws.browser.js +0 -3
  72. package/dist/src/aws.browser.js.map +0 -1
  73. package/dist/src/aws.d.ts +0 -30
  74. package/dist/src/aws.d.ts.map +0 -1
  75. package/dist/src/aws.js +0 -240
  76. package/dist/src/aws.js.map +0 -1
  77. package/dist/test/launch.spec.d.ts +0 -2
  78. package/dist/test/launch.spec.d.ts.map +0 -1
  79. package/dist/test/launch.spec.js +0 -23
  80. package/dist/test/launch.spec.js.map +0 -1
  81. package/dist/ui/assets/index-BBTKOM0z.js +0 -6
  82. package/dist/ui/assets/index-DVtj_GOf.js +0 -13
  83. package/src/aws.browser.ts +0 -1
  84. package/src/aws.ts +0 -327
@@ -1 +1 @@
1
- {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../src/cli.ts"],"names":[],"mappings":"AA+HA,eAAO,MAAM,GAAG,GAAU,OAAO,MAAM,EAAE;;;;;;;;EA0yCxC,CAAC"}
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../src/cli.ts"],"names":[],"mappings":"AA+HA,eAAO,MAAM,GAAG,GAAU,OAAO,MAAM,EAAE;;;;;;;;EA8lCxC,CAAC"}
package/dist/src/cli.js CHANGED
@@ -4,15 +4,14 @@ import chalk from "chalk";
4
4
  import fs from "fs";
5
5
  import sodium from "libsodium-wrappers";
6
6
  import path from "path";
7
- import { exit } from "process";
8
7
  import readline from "readline";
9
8
  import Table from "tty-table";
10
- import { AWS_LINUX_ARM_AMIs, createRecord, launchNodes, terminateNode, } from "./aws.js";
11
- import { createClient, waitForDomain } from "./client.js";
9
+ import { createClient } from "./client.js";
12
10
  import { getHomeConfigDir, getKeypair, getPackageName, getRemotesPath, } from "./config.js";
13
- import { createTestDomain, getDomainFromConfig, loadConfig, startCertbot, } from "./domain.js";
14
- import { HETZNER_SERVER_TYPES, launchNodes as launchHetznerNodes, terminateNode as terminateHetznerNode, } from "./hetzner.js";
15
- import { DEFAULT_REMOTE_GROUP, Remotes } from "./remotes.js";
11
+ import { DNS_LEASE_ACCESS_TOKEN_ENV, DNS_LEASE_SERVICE_URL_ENV, DNS_LEASE_STATE_FILE_ENV, getDnsLeaseStatePath, provisionDnsLease, readDnsLeaseState, releaseDnsLease, renewDnsLease, startDnsLeaseRenewal, } from "./domain-lease.js";
12
+ import { getDomainFromConfig, loadConfig, startCertbot } from "./domain.js";
13
+ import { terminateNode as terminateHetznerNode } from "./hetzner.js";
14
+ import { DEFAULT_REMOTE_GROUP, Remotes, getRetiredAWSManagementError, } from "./remotes.js";
16
15
  import { LOCAL_API_PORT } from "./routes.js";
17
16
  import { startServerWithNode } from "./server.js";
18
17
  await sodium.ready;
@@ -139,18 +138,40 @@ export const cli = async (args) => {
139
138
  describe: "Set Libp2p listen port. Only modify this when testing locally, since NGINX config depends on the default value",
140
139
  type: "number",
141
140
  default: undefined,
141
+ })
142
+ .option("dns-lease-state-file", {
143
+ describe: `Managed DNS lease state file (or ${DNS_LEASE_STATE_FILE_ENV})`,
144
+ type: "string",
142
145
  });
143
146
  return yargs;
144
147
  },
145
148
  handler: async (args) => {
146
- await startServerWithNode({
147
- directory: args.directory,
148
- domain: await loadConfig().then((config) => config ? getDomainFromConfig(config) : undefined),
149
- ports: { api: args["port-api"], node: args["port-node"] },
150
- bootstrap: args.bootstrap,
151
- newSession: args.reset,
152
- grantAccess: args["grant-access"],
153
- });
149
+ let stopDnsLeaseRenewal = () => undefined;
150
+ try {
151
+ stopDnsLeaseRenewal = await startDnsLeaseRenewal({
152
+ statePath: args["dns-lease-state-file"] ||
153
+ process.env[DNS_LEASE_STATE_FILE_ENV],
154
+ onError: (message) => console.warn(message),
155
+ });
156
+ }
157
+ catch {
158
+ console.warn("Managed DNS lease renewal could not start; use 'peerbit domain lease renew' to inspect it");
159
+ }
160
+ try {
161
+ const started = await startServerWithNode({
162
+ directory: args.directory,
163
+ domain: await loadConfig().then((config) => config ? getDomainFromConfig(config) : undefined),
164
+ ports: { api: args["port-api"], node: args["port-node"] },
165
+ bootstrap: args.bootstrap,
166
+ newSession: args.reset,
167
+ grantAccess: args["grant-access"],
168
+ });
169
+ started.server.once("close", stopDnsLeaseRenewal);
170
+ }
171
+ catch (error) {
172
+ stopDnsLeaseRenewal();
173
+ throw error;
174
+ }
154
175
  },
155
176
  })
156
177
  .command({
@@ -171,96 +192,133 @@ export const cli = async (args) => {
171
192
  console.log((await kp.toPeerId()).toString());
172
193
  },
173
194
  })
174
- .command("domain", "Setup a domain and certificate for this node", (yargs) => {
195
+ .command("domain", "Configure a domain and certificate for this node", (yargs) => {
175
196
  yargs
176
197
  .command({
177
- command: "test",
178
- describe: "Setup a testing domain with SSL (no guarantess on how long the domain will be available)",
179
- builder: {
180
- email: {
181
- describe: "Email for Lets security messages",
182
- type: "string",
183
- demandOption: true,
184
- },
185
- outdir: {
186
- describe: "Output path for Nginx config",
187
- type: "string",
188
- alias: "o",
189
- },
190
- wait: {
191
- alias: "w",
192
- describe: "Wait for setup to succeed (or fail)",
193
- type: "boolean",
194
- default: false,
195
- },
196
- },
198
+ command: "lease [operation]",
199
+ describe: "Manage a temporary peerchecker.com DNS lease",
200
+ builder: (leaseArgs) => leaseArgs
201
+ .positional("operation", {
202
+ choices: ["claim", "renew", "release", "status"],
203
+ default: "claim",
204
+ describe: "Lease operation",
205
+ type: "string",
206
+ })
207
+ .option("service-url", {
208
+ describe: `Lease service URL (or ${DNS_LEASE_SERVICE_URL_ENV})`,
209
+ type: "string",
210
+ })
211
+ .option("access-token", {
212
+ describe: `Claim access token (prefer ${DNS_LEASE_ACCESS_TOKEN_ENV} to avoid shell history)`,
213
+ type: "string",
214
+ })
215
+ .option("address", {
216
+ describe: "Public IPv4 or IPv6 address for a new lease",
217
+ type: "string",
218
+ })
219
+ .option("email", {
220
+ describe: "Email for Let's Encrypt security messages",
221
+ type: "string",
222
+ })
223
+ .option("configure", {
224
+ default: true,
225
+ describe: "Configure NGINX and Let's Encrypt after claiming",
226
+ type: "boolean",
227
+ })
228
+ .option("wait", {
229
+ alias: "w",
230
+ default: true,
231
+ describe: "Wait for HTTPS setup to succeed",
232
+ type: "boolean",
233
+ })
234
+ .option("state-file", {
235
+ default: getDnsLeaseStatePath(),
236
+ describe: "Managed DNS lease state file",
237
+ type: "string",
238
+ }),
197
239
  handler: async (args) => {
198
- const domain = await createTestDomain();
199
- await startCertbot(domain, args.email, args.wait);
200
- exit();
240
+ const operation = args.operation || "claim";
241
+ const statePath = args["state-file"];
242
+ const serviceUrl = args["service-url"] ||
243
+ process.env[DNS_LEASE_SERVICE_URL_ENV];
244
+ if (operation === "status") {
245
+ const state = readDnsLeaseState(statePath);
246
+ console.log(JSON.stringify(state
247
+ ? {
248
+ address: state.address,
249
+ configuredAt: state.configuredAt,
250
+ domain: state.domain,
251
+ expiresAt: state.expiresAt,
252
+ status: state.status,
253
+ }
254
+ : { status: "none" }, undefined, 2));
255
+ return;
256
+ }
257
+ if (operation === "renew") {
258
+ const active = await renewDnsLease({ statePath, serviceUrl });
259
+ console.log(`DNS lease renewed until ${active.expiresAt}`);
260
+ return;
261
+ }
262
+ if (operation === "release") {
263
+ await releaseDnsLease({ statePath, serviceUrl });
264
+ console.log("DNS lease released");
265
+ return;
266
+ }
267
+ if (args.configure && !args.email) {
268
+ throw new Error("--email is required when configuring the leased domain");
269
+ }
270
+ const active = await provisionDnsLease({
271
+ accessToken: args["access-token"] ||
272
+ process.env[DNS_LEASE_ACCESS_TOKEN_ENV],
273
+ address: args.address,
274
+ configure: args.configure
275
+ ? (domain) => startCertbot(domain, args.email, args.wait)
276
+ : undefined,
277
+ serviceUrl,
278
+ statePath,
279
+ });
280
+ console.log(`DNS lease active until ${active.expiresAt}`);
201
281
  },
202
282
  })
203
283
  .command({
204
- command: "aws",
205
- describe: "Setup a domain with an AWS account. You either have to setup you AWS credentials in the .aws folder, or pass the credentials in the cli",
284
+ command: "configure <domain>",
285
+ describe: "Configure NGINX and Let's Encrypt for a domain you control",
206
286
  builder: {
207
287
  domain: {
208
- describe: "domain, e.g. abc.example.com, example.com",
209
- alias: "d",
288
+ describe: "Domain whose DNS record already points to this server",
210
289
  type: "string",
211
290
  demandOption: true,
212
291
  },
213
- hostedZoneId: {
214
- describe: 'The id of the hosted zone "HostedZoneId"',
215
- alias: "hz",
216
- type: "string",
217
- require: true,
218
- },
219
- accessKeyId: {
220
- describe: "Access key id of the AWS user",
221
- alias: "ak",
222
- type: "string",
223
- },
224
- region: {
225
- describe: "AWS region",
226
- alias: "r",
227
- type: "string",
228
- },
229
- secretAccessKey: {
230
- describe: "Secret key id of the AWS user",
231
- alias: "sk",
232
- type: "string",
233
- },
234
- outdir: {
235
- describe: "Output path for Nginx config",
292
+ email: {
293
+ describe: "Email for Let's Encrypt security messages",
236
294
  type: "string",
237
- alias: "o",
295
+ demandOption: true,
238
296
  },
239
297
  wait: {
240
298
  alias: "w",
241
- describe: "Wait for setup to succeed (or fail)",
299
+ describe: "Wait for HTTPS setup to succeed (use --no-wait to return early)",
242
300
  type: "boolean",
243
- default: false,
301
+ default: true,
244
302
  },
245
303
  },
246
304
  handler: async (args) => {
247
- if (!!args.accessKeyId !== !!args.secretAccessKey ||
248
- !!args.region !== !!args.secretAccessKey) {
249
- throw new Error("Expecting either all 'accessKeyId', 'region' and 'secretAccessKey' to be provided or none");
250
- }
251
- await createRecord({
252
- domain: args.domain,
253
- hostedZoneId: args.hostedZoneId,
254
- region: args.region,
255
- credentials: args.accessKeyId
256
- ? {
257
- accessKeyId: args.accessKeyId,
258
- secretAccessKey: args.secretAccessKey,
259
- }
260
- : undefined,
261
- });
262
305
  await startCertbot(args.domain, args.email, args.wait);
263
- exit();
306
+ },
307
+ })
308
+ .command({
309
+ command: "test",
310
+ describe: false,
311
+ builder: (retiredArgs) => retiredArgs.strict(false),
312
+ handler: () => {
313
+ throw new Error("Automatic test domains have been retired. Point a domain you control to this server, then run 'peerbit domain configure <domain> --email <email>'.");
314
+ },
315
+ })
316
+ .command({
317
+ command: "aws",
318
+ describe: false,
319
+ builder: (retiredArgs) => retiredArgs.strict(false),
320
+ handler: () => {
321
+ throw new Error("Automatic AWS DNS configuration has been retired. Configure DNS with your provider, then run 'peerbit domain configure <domain> --email <email>'.");
264
322
  },
265
323
  })
266
324
  .strict()
@@ -268,263 +326,17 @@ export const cli = async (args) => {
268
326
  })
269
327
  .command("remote", "Handle remote nodes", (innerYargs) => {
270
328
  innerYargs
271
- .command("spawn", "Spawn remote nodes", (spawnYargs) => {
272
- spawnYargs
273
- .command({
274
- command: "aws",
275
- describe: "Spawn remote nodes on AWS",
276
- builder: (awsArgs) => {
277
- awsArgs.option("count", {
278
- describe: "Amount of nodes to spawn",
279
- defaultDescription: "One node",
280
- type: "number",
281
- alias: "c",
282
- default: 1,
283
- });
284
- awsArgs.option("region", {
285
- describe: "Region",
286
- type: "string",
287
- defaultDescription: "Region defined in ~.aws/config",
288
- choices: Object.keys(AWS_LINUX_ARM_AMIs),
289
- });
290
- awsArgs.option("group", {
291
- describe: "Remote group to launch nodes in",
292
- type: "string",
293
- alias: "g",
294
- default: DEFAULT_REMOTE_GROUP,
295
- });
296
- awsArgs.option("size", {
297
- describe: "Instance size",
298
- type: "string",
299
- alias: "s",
300
- choices: [
301
- "micro",
302
- "small",
303
- "medium",
304
- "large",
305
- "xlarge",
306
- "2xlarge",
307
- ],
308
- default: "micro",
309
- });
310
- awsArgs.option("name", {
311
- describe: "Name prefix for spawned nodes",
312
- type: "string",
313
- alias: "n",
314
- default: "peerbit-node",
315
- });
316
- awsArgs.option("grant-access", {
317
- describe: "Grant access to public keys on start",
318
- defaultDescription: "The publickey of this device located in 'directory'",
319
- type: "string",
320
- array: true,
321
- alias: "ga",
322
- });
323
- awsArgs.option("directory", {
324
- describe: "Peerbit directory",
325
- defaultDescription: "~.peerbit",
326
- type: "string",
327
- alias: "d",
328
- default: getHomeConfigDir(),
329
- });
330
- awsArgs.option("email", {
331
- describe: "Email for Let's security messages",
332
- type: "string",
333
- alias: "e",
334
- demandOption: true,
335
- });
336
- awsArgs.option("server-version", {
337
- describe: "@peerbit/server version or tag to install on the instance (e.g. 5.7.0-58d3d09)",
338
- type: "string",
339
- alias: ["sv"],
340
- });
341
- return awsArgs;
342
- },
343
- handler: async (args) => {
344
- const self = (await getKeypair(args.directory)).publicKey.toPeerId();
345
- const accessGrant = args["grant-access"]?.length > 0
346
- ? args["grant-access"].map((x) => peerIdFromString(x))
347
- : [];
348
- accessGrant.push(self);
349
- const nodes = await launchNodes({
350
- email: args.email,
351
- count: args.count,
352
- namePrefix: args.name,
353
- region: args.region,
354
- grantAccess: accessGrant,
355
- size: args.size,
356
- serverVersion: args["server-version"] || undefined,
357
- });
358
- console.log(`Waiting for ${args.count} ${args.count > 1 ? "nodes" : "node"} to spawn. This might take a few minutes. You can watch the progress in your AWS console.`);
359
- const twirlTimer = (function () {
360
- const P = ["\\", "|", "/", "-"];
361
- let x = 0;
362
- return setInterval(function () {
363
- process.stdout.write("\r" + "Loading: " + chalk.hex(colors[x])(P[x++]));
364
- x &= 3;
365
- }, 250);
366
- })();
367
- for (const node of nodes) {
368
- try {
369
- const domain = await waitForDomain(node.publicIp);
370
- const remotes = new Remotes(getRemotesPath(args.directory));
371
- remotes.add({
372
- name: node.name,
373
- address: domain,
374
- group: args.group,
375
- origin: {
376
- type: "aws",
377
- instanceId: node.instanceId,
378
- region: node.region,
379
- },
380
- });
381
- }
382
- catch (error) {
383
- process.stdout.write("\r");
384
- console.error(`Error waiting for domain for ip: ${node.publicIp} to be available: ${error?.toString()}`);
385
- }
386
- }
387
- process.stdout.write("\r");
388
- clearInterval(twirlTimer);
389
- console.log(`New nodes available (${nodes.length}):`);
390
- for (const node of nodes) {
391
- console.log(chalk.green(node.name));
392
- }
393
- },
394
- })
395
- .command({
396
- command: "hetzner",
397
- describe: "Spawn remote nodes on Hetzner Cloud",
398
- builder: (hetznerArgs) => {
399
- hetznerArgs.option("count", {
400
- describe: "Amount of nodes to spawn",
401
- defaultDescription: "One node",
402
- type: "number",
403
- alias: "c",
404
- default: 1,
405
- });
406
- hetznerArgs.option("location", {
407
- describe: "Location (e.g. fsn1, nbg1, hel1, ash, hil)",
408
- type: "string",
409
- alias: "l",
410
- default: "fsn1",
411
- });
412
- hetznerArgs.option("group", {
413
- describe: "Remote group to launch nodes in",
414
- type: "string",
415
- alias: "g",
416
- default: DEFAULT_REMOTE_GROUP,
417
- });
418
- hetznerArgs.option("server-type", {
419
- describe: "Server type",
420
- type: "string",
421
- alias: "t",
422
- choices: [...HETZNER_SERVER_TYPES],
423
- default: "cx11",
424
- });
425
- hetznerArgs.option("name", {
426
- describe: "Name prefix for spawned nodes",
427
- type: "string",
428
- alias: "n",
429
- default: "peerbit-node",
430
- });
431
- hetznerArgs.option("grant-access", {
432
- describe: "Grant access to public keys on start",
433
- defaultDescription: "The publickey of this device located in 'directory'",
434
- type: "string",
435
- array: true,
436
- alias: "ga",
437
- });
438
- hetznerArgs.option("directory", {
439
- describe: "Peerbit directory",
440
- defaultDescription: "~.peerbit",
441
- type: "string",
442
- alias: "d",
443
- default: getHomeConfigDir(),
444
- });
445
- hetznerArgs.option("email", {
446
- describe: "Email for Let's security messages",
447
- type: "string",
448
- alias: "e",
449
- demandOption: true,
450
- });
451
- hetznerArgs.option("token", {
452
- describe: "Hetzner Cloud API token (or set HCLOUD_TOKEN)",
453
- type: "string",
454
- alias: ["tok"],
455
- });
456
- hetznerArgs.option("image", {
457
- describe: "Image to use (e.g. ubuntu-22.04)",
458
- type: "string",
459
- default: "ubuntu-22.04",
460
- });
461
- hetznerArgs.option("server-version", {
462
- describe: "@peerbit/server version or tag to install on the instance (e.g. 5.7.0-58d3d09)",
463
- type: "string",
464
- alias: ["sv"],
465
- });
466
- return hetznerArgs;
467
- },
468
- handler: async (args) => {
469
- const self = (await getKeypair(args.directory)).publicKey.toPeerId();
470
- const accessGrant = args["grant-access"]?.length > 0
471
- ? args["grant-access"].map((x) => peerIdFromString(x))
472
- : [];
473
- accessGrant.push(self);
474
- const nodes = await launchHetznerNodes({
475
- token: args.token || undefined,
476
- email: args.email,
477
- count: args.count,
478
- namePrefix: args.name,
479
- location: args.location,
480
- serverType: args["server-type"],
481
- grantAccess: accessGrant,
482
- image: args.image,
483
- serverVersion: args["server-version"] || undefined,
484
- });
485
- console.log(`Waiting for ${args.count} ${args.count > 1 ? "nodes" : "node"} to spawn. This might take a few minutes. You can watch the progress in your Hetzner Cloud console.`);
486
- const twirlTimer = (function () {
487
- const P = ["\\", "|", "/", "-"];
488
- let x = 0;
489
- return setInterval(function () {
490
- process.stdout.write("\r" + "Loading: " + chalk.hex(colors[x])(P[x++]));
491
- x &= 3;
492
- }, 250);
493
- })();
494
- for (const node of nodes) {
495
- try {
496
- const domain = await waitForDomain(node.publicIp);
497
- const remotes = new Remotes(getRemotesPath(args.directory));
498
- remotes.add({
499
- name: node.name,
500
- address: domain,
501
- group: args.group,
502
- origin: {
503
- type: "hetzner",
504
- serverId: node.serverId,
505
- location: node.location,
506
- },
507
- });
508
- }
509
- catch (error) {
510
- process.stdout.write("\r");
511
- console.error(`Error waiting for domain for ip: ${node.publicIp} to be available: ${error?.toString()}`);
512
- }
513
- }
514
- process.stdout.write("\r");
515
- clearInterval(twirlTimer);
516
- console.log(`New nodes available (${nodes.length}):`);
517
- for (const node of nodes) {
518
- console.log(chalk.green(node.name));
519
- }
520
- },
521
- })
522
- .strict()
523
- .demandCommand();
329
+ .command({
330
+ command: "spawn [provider]",
331
+ describe: false,
332
+ builder: (spawnArgs) => spawnArgs.positional("provider", { type: "string" }).strict(false),
333
+ handler: () => {
334
+ throw new Error("Automatic cloud provisioning has been retired. Provision a server with your provider, configure a domain you control, then register it with 'peerbit remote add <name> <address>'.");
335
+ },
524
336
  })
525
337
  .command({
526
338
  command: "terminate [name...]",
527
- describe: "Terminate remote instances that was previously spawned",
339
+ describe: "Terminate legacy Hetzner instances recorded by older releases",
528
340
  builder: (killArgs) => {
529
341
  killArgs.option("all", {
530
342
  describe: "Kill all nodes",
@@ -555,20 +367,17 @@ export const cli = async (args) => {
555
367
  handler: async (args) => {
556
368
  const remotes = new Remotes(getRemotesPath(args.directory));
557
369
  const allRemotes = await remotes.all();
558
- for (const remote of allRemotes) {
559
- if (args.all || args.name.includes(remote.name)) {
560
- if (remote.origin?.type === "aws") {
561
- await terminateNode({
562
- instanceId: remote.origin.instanceId,
563
- region: remote.origin.region,
564
- });
565
- }
566
- else if (remote.origin?.type === "hetzner") {
567
- await terminateHetznerNode({
568
- serverId: remote.origin.serverId,
569
- token: args.token || undefined,
570
- });
571
- }
370
+ const selectedRemotes = allRemotes.filter((remote) => args.all || args.name.includes(remote.name));
371
+ const retiredAWSRemote = selectedRemotes.find((remote) => remote.origin?.type === "aws");
372
+ if (retiredAWSRemote?.origin?.type === "aws") {
373
+ throw getRetiredAWSManagementError(retiredAWSRemote.origin);
374
+ }
375
+ for (const remote of selectedRemotes) {
376
+ if (remote.origin?.type === "hetzner") {
377
+ await terminateHetznerNode({
378
+ serverId: remote.origin.serverId,
379
+ token: args.token || undefined,
380
+ });
572
381
  }
573
382
  }
574
383
  },