@peerbit/server 2.0.0 → 4.0.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 (59) hide show
  1. package/lib/esm/aws.browser.d.ts +0 -0
  2. package/lib/esm/aws.browser.js +3 -0
  3. package/lib/esm/aws.browser.js.map +1 -0
  4. package/lib/esm/aws.d.ts +19 -0
  5. package/lib/esm/aws.js +185 -1
  6. package/lib/esm/aws.js.map +1 -1
  7. package/lib/esm/cli.js +614 -299
  8. package/lib/esm/cli.js.map +1 -1
  9. package/lib/esm/client.d.ts +13 -1
  10. package/lib/esm/client.js +101 -31
  11. package/lib/esm/client.js.map +1 -1
  12. package/lib/esm/config.d.ts +3 -3
  13. package/lib/esm/config.js +18 -16
  14. package/lib/esm/config.js.map +1 -1
  15. package/lib/esm/docker.browser.d.ts +0 -0
  16. package/lib/esm/docker.browser.js +3 -0
  17. package/lib/esm/docker.browser.js.map +1 -0
  18. package/lib/esm/domain.js +1 -1
  19. package/lib/esm/domain.js.map +1 -1
  20. package/lib/esm/peerbit.d.ts +2 -0
  21. package/lib/esm/peerbit.js +1 -0
  22. package/lib/esm/peerbit.js.map +1 -1
  23. package/lib/esm/remotes.d.ts +15 -3
  24. package/lib/esm/remotes.js +8 -9
  25. package/lib/esm/remotes.js.map +1 -1
  26. package/lib/esm/routes.d.ts +4 -2
  27. package/lib/esm/routes.js +6 -4
  28. package/lib/esm/routes.js.map +1 -1
  29. package/lib/esm/server.d.ts +5 -7
  30. package/lib/esm/server.js +183 -185
  31. package/lib/esm/server.js.map +1 -1
  32. package/lib/esm/signes-request.d.ts +5 -0
  33. package/lib/esm/signes-request.js +54 -0
  34. package/lib/esm/signes-request.js.map +1 -0
  35. package/lib/esm/trust.browser.d.ts +0 -0
  36. package/lib/esm/trust.browser.js +3 -0
  37. package/lib/esm/trust.browser.js.map +1 -0
  38. package/lib/esm/trust.d.ts +9 -0
  39. package/lib/esm/trust.js +36 -0
  40. package/lib/esm/trust.js.map +1 -0
  41. package/lib/ui/assets/aws.browser-4ed993c7.js +1 -0
  42. package/lib/ui/assets/index-5ed0229d.js +77 -0
  43. package/lib/ui/index.html +1 -1
  44. package/package.json +13 -7
  45. package/src/aws.browser.ts +1 -0
  46. package/src/aws.ts +250 -1
  47. package/src/cli.ts +726 -348
  48. package/src/client.ts +145 -38
  49. package/src/config.ts +21 -23
  50. package/src/docker.browser.ts +1 -0
  51. package/src/domain.ts +1 -1
  52. package/src/peerbit.ts +3 -0
  53. package/src/remotes.ts +24 -12
  54. package/src/routes.ts +6 -5
  55. package/src/server.ts +238 -254
  56. package/src/signes-request.ts +84 -0
  57. package/src/trust.browser.ts +1 -0
  58. package/src/trust.ts +39 -0
  59. package/lib/ui/assets/index-73eaa3bc.js +0 -53
package/src/cli.ts CHANGED
@@ -5,10 +5,20 @@ import {
5
5
  startCertbot,
6
6
  } from "./domain.js";
7
7
  import { startServerWithNode } from "./server.js";
8
- import { createRecord } from "./aws.js";
9
- import { getHomeConfigDir, getPackageName, getRemotesPath } from "./config.js";
8
+ import {
9
+ AWS_LINUX_ARM_AMIs,
10
+ createRecord,
11
+ launchNodes,
12
+ terminateNode,
13
+ } from "./aws.js";
14
+ import {
15
+ getHomeConfigDir,
16
+ getKeypair,
17
+ getPackageName,
18
+ getRemotesPath,
19
+ } from "./config.js";
10
20
  import chalk from "chalk";
11
- import { client } from "./client.js";
21
+ import { createClient, waitForDomain } from "./client.js";
12
22
  import { InstallDependency, StartProgram } from "./types.js";
13
23
  import { exit } from "process";
14
24
  import yargs from "yargs";
@@ -16,14 +26,88 @@ import readline from "readline";
16
26
  import fs from "fs";
17
27
  import path from "path";
18
28
  import { toBase64 } from "@peerbit/crypto";
19
- import { Remotes } from "./remotes.js";
29
+ import { DEFAULT_REMOTE_GROUP, RemoteObject, Remotes } from "./remotes.js";
30
+ import { peerIdFromString } from "@libp2p/peer-id";
31
+ import { LOCAL_API_PORT } from "./routes.js";
32
+ import { type PeerId } from "@libp2p/interface/peer-id";
20
33
 
21
- const padString = function (string: string, padding: number, padChar = " ") {
34
+ const colors = [
35
+ "#00FF00",
36
+ "#0000FF",
37
+ "#FF0000",
38
+ "#01FFFE",
39
+ "#FFA6FE",
40
+ "#FFDB66",
41
+ "#006401",
42
+ "#010067",
43
+ "#95003A",
44
+ "#007DB5",
45
+ "#FF00F6",
46
+ "#FFEEE8",
47
+ "#774D00",
48
+ "#90FB92",
49
+ "#0076FF",
50
+ "#D5FF00",
51
+ "#FF937E",
52
+ "#6A826C",
53
+ "#FF029D",
54
+ "#FE8900",
55
+ "#7A4782",
56
+ "#7E2DD2",
57
+ "#85A900",
58
+ "#FF0056",
59
+ "#A42400",
60
+ "#00AE7E",
61
+ "#683D3B",
62
+ "#BDC6FF",
63
+ "#263400",
64
+ "#BDD393",
65
+ "#00B917",
66
+ "#9E008E",
67
+ "#001544",
68
+ "#C28C9F",
69
+ "#FF74A3",
70
+ "#01D0FF",
71
+ "#004754",
72
+ "#E56FFE",
73
+ "#788231",
74
+ "#0E4CA1",
75
+ "#91D0CB",
76
+ "#BE9970",
77
+ "#968AE8",
78
+ "#BB8800",
79
+ "#43002C",
80
+ "#DEFF74",
81
+ "#00FFC6",
82
+ "#FFE502",
83
+ "#620E00",
84
+ "#008F9C",
85
+ "#98FF52",
86
+ "#7544B1",
87
+ "#B500FF",
88
+ "#00FF78",
89
+ "#FF6E41",
90
+ "#005F39",
91
+ "#6B6882",
92
+ "#5FAD4E",
93
+ "#A75740",
94
+ "#A5FFD2",
95
+ "#FFB167",
96
+ "#009BFF",
97
+ "#E85EBE",
98
+ ];
99
+ const padString = function (
100
+ string: string,
101
+ padding: number,
102
+ padChar = " ",
103
+ stringLength = string.valueOf().length
104
+ ) {
22
105
  const val = string.valueOf();
23
- if (Math.abs(padding) <= val.length) {
106
+ if (Math.abs(padding) <= stringLength) {
24
107
  return val;
25
108
  }
26
- const m = Math.max(Math.abs(padding) - string.length || 0, 0);
109
+
110
+ const m = Math.max(Math.abs(padding) - stringLength || 0, 0);
27
111
  const pad = Array(m + 1).join(String(padChar).charAt(0));
28
112
  // var pad = String(c || ' ').charAt(0).repeat(Math.abs(n) - this.length);
29
113
  return padding < 0 ? pad + val : val + pad;
@@ -57,6 +141,14 @@ export const cli = async (args?: string[]) => {
57
141
  type: "boolean",
58
142
  default: false,
59
143
  })
144
+ .option("grant-access", {
145
+ describe: "Grant access to public keys on start",
146
+ defaultDescription:
147
+ "The publickey of this device located in 'directory'",
148
+ type: "string",
149
+ array: true,
150
+ alias: "ga",
151
+ })
60
152
  .option("reset", {
61
153
  describe:
62
154
  "If true, then programs opened during last session will not be opened",
@@ -64,14 +156,6 @@ export const cli = async (args?: string[]) => {
64
156
  default: false,
65
157
  alias: "r",
66
158
  })
67
- .option("password", {
68
- describe:
69
- "Setup password so you can interact with the node remotely",
70
- type: "string",
71
- defaultDescription:
72
- "The password from the last session will be used or a password will be generated",
73
- default: undefined,
74
- })
75
159
  .option("port-api", {
76
160
  describe:
77
161
  "Set API server port. Only modify this when testing locally, since NGINX config depends on the default value",
@@ -94,12 +178,29 @@ export const cli = async (args?: string[]) => {
94
178
  ),
95
179
  ports: { api: args["port-api"], node: args["port-node"] },
96
180
  bootstrap: args.bootstrap,
97
- password: args.password,
98
181
  newSession: args.reset,
182
+ grantAccess: args["grant-access"],
99
183
  });
100
184
  },
101
185
  })
102
-
186
+ .command({
187
+ command: "id",
188
+ describe: "Get peer id",
189
+ builder: (yargs: yargs.Argv) => {
190
+ yargs.option("directory", {
191
+ describe: "Peerbit directory",
192
+ defaultDescription: "~.peerbit",
193
+ type: "string",
194
+ alias: "d",
195
+ default: getHomeConfigDir(),
196
+ });
197
+ return yargs;
198
+ },
199
+ handler: async (args) => {
200
+ const kp = await getKeypair(args.directory);
201
+ console.log((await kp.toPeerId()).toString());
202
+ },
203
+ })
103
204
  .command(
104
205
  "domain",
105
206
  "Setup a domain and certificate for this node",
@@ -217,6 +318,167 @@ export const cli = async (args?: string[]) => {
217
318
  )
218
319
  .command("remote", "Handle remote nodes", (innerYargs) => {
219
320
  innerYargs
321
+ .command("spawn", "Spawn remote nodes", (spawnYargs) => {
322
+ spawnYargs
323
+ .command({
324
+ command: "aws",
325
+ describe: "Spawn remote nodes on AWS",
326
+ builder: (awsArgs: yargs.Argv) => {
327
+ awsArgs.option("count", {
328
+ describe: "Amount of nodes to spawn",
329
+ defaultDescription: "One node",
330
+ type: "number",
331
+ alias: "c",
332
+ default: 1,
333
+ });
334
+ awsArgs.option("region", {
335
+ describe: "Region",
336
+ type: "string",
337
+ defaultDescription: "Region defined in ~.aws/config",
338
+ choices: Object.keys(AWS_LINUX_ARM_AMIs),
339
+ });
340
+ awsArgs.option("group", {
341
+ describe: "Remote group to launch nodes in",
342
+ type: "string",
343
+ alias: "g",
344
+ default: DEFAULT_REMOTE_GROUP,
345
+ });
346
+ awsArgs.option("size", {
347
+ describe: "Instance size",
348
+ type: "string",
349
+ alias: "s",
350
+ choices: [
351
+ "micro",
352
+ "small",
353
+ "medium",
354
+ "large",
355
+ "xlarge",
356
+ "2xlarge",
357
+ ],
358
+ default: "micro",
359
+ });
360
+
361
+ awsArgs.option("name", {
362
+ describe: "Name prefix for spawned nodes",
363
+ type: "string",
364
+ alias: "n",
365
+ default: "peerbit-node",
366
+ });
367
+ awsArgs.option("grant-access", {
368
+ describe: "Grant access to public keys on start",
369
+ defaultDescription:
370
+ "The publickey of this device located in 'directory'",
371
+ type: "string",
372
+ array: true,
373
+ alias: "ga",
374
+ });
375
+ awsArgs.option("directory", {
376
+ describe: "Peerbit directory",
377
+ defaultDescription: "~.peerbit",
378
+ type: "string",
379
+ alias: "d",
380
+ default: getHomeConfigDir(),
381
+ });
382
+ return awsArgs;
383
+ },
384
+ handler: async (args) => {
385
+ const accessGrant: PeerId[] =
386
+ args.access?.length > 0
387
+ ? args.access.map((x) => peerIdFromString(x))
388
+ : [
389
+ await (
390
+ await getKeypair(args.directory)
391
+ ).publicKey.toPeerId(),
392
+ ];
393
+ const nodes = await launchNodes({
394
+ email: "marcus@dao.xyz",
395
+ count: args.number,
396
+ namePrefix: args.name,
397
+ region: args.region,
398
+ grantAccess: accessGrant,
399
+ size: args.size,
400
+ });
401
+ console.log(
402
+ `Waiting for ${args.count} ${
403
+ args.count > 1 ? "nodes" : "node"
404
+ } to spawn. This might take a few minutes. You can watch the progress in your AWS console.`
405
+ );
406
+ const twirlTimer = (function () {
407
+ const P = ["\\", "|", "/", "-"];
408
+ let x = 0;
409
+ return setInterval(function () {
410
+ process.stdout.write(
411
+ "\r" + "Loading: " + chalk.hex(colors[x])(P[x++])
412
+ );
413
+ x &= 3;
414
+ }, 250);
415
+ })();
416
+ try {
417
+ for (const node of nodes) {
418
+ const domain = await waitForDomain(node.publicIp);
419
+ const remotes = new Remotes(getRemotesPath(args.directory));
420
+ remotes.add({
421
+ name: node.name,
422
+ address: domain,
423
+ group: args.group,
424
+ origin: {
425
+ type: "aws",
426
+ instanceId: node.instanceId,
427
+ region: node.region,
428
+ },
429
+ });
430
+ }
431
+ } catch (error: any) {
432
+ console.error(
433
+ "Error waiting for domains to be available: " +
434
+ error?.toString()
435
+ );
436
+ } finally {
437
+ clearInterval(twirlTimer);
438
+ process.stdout.write("\r");
439
+ }
440
+ console.log(`New nodes available ${nodes.length}:`);
441
+ for (const node of nodes) {
442
+ console.log(chalk.green(node.name));
443
+ }
444
+ },
445
+ })
446
+ .strict()
447
+ .demandCommand();
448
+ })
449
+ .command({
450
+ command: "terminate [name...]",
451
+ describe: "Terminate remote instances that was previously spawned",
452
+ builder: (killArgs: yargs.Argv) => {
453
+ killArgs.option("all", {
454
+ describe: "Kill all nodes",
455
+ type: "boolean",
456
+ default: false,
457
+ });
458
+ killArgs.positional("name", {
459
+ type: "string",
460
+ describe: "Remote name",
461
+ default: "localhost",
462
+ demandOption: false,
463
+ array: true,
464
+ });
465
+ return killArgs;
466
+ },
467
+ handler: async (args) => {
468
+ const remotes = new Remotes(getRemotesPath(args.directory));
469
+ const allRemotes = await remotes.all();
470
+ for (const remote of allRemotes) {
471
+ if (args.all || args.name.includes(remote.name)) {
472
+ if (remote.origin?.type === "aws") {
473
+ await terminateNode({
474
+ instanceId: remote.origin.instanceId,
475
+ region: remote.origin.region,
476
+ });
477
+ }
478
+ }
479
+ }
480
+ },
481
+ })
220
482
  .command({
221
483
  command: "list",
222
484
  aliases: "ls",
@@ -240,11 +502,19 @@ export const cli = async (args?: string[]) => {
240
502
  .reduce((prev, c, i) => {
241
503
  return Math.max(prev, c);
242
504
  }, 0);
505
+
243
506
  const all = await remotes.all();
244
507
  if (all.length > 0) {
508
+ console.log(
509
+ padString("Name", maxNameLength + 10),
510
+ padString("Group" || "", 10),
511
+ "Address"
512
+ );
513
+
245
514
  for (const remote of all) {
246
515
  console.log(
247
516
  padString(remote.name, maxNameLength + 10),
517
+ padString(remote.group || "", 10),
248
518
  remote.address
249
519
  );
250
520
  }
@@ -254,25 +524,26 @@ export const cli = async (args?: string[]) => {
254
524
  },
255
525
  })
256
526
  .command({
257
- command: "add <name> <address> <password>",
527
+ command: "add <name> <address>",
258
528
  describe: "Add remote",
259
529
  builder: (yargs: yargs.Argv) => {
260
530
  yargs
261
- .positional("address", {
262
- type: "string",
263
- describe: "Remote name",
264
- demandOption: true,
265
- })
266
531
  .positional("name", {
267
532
  type: "string",
268
533
  describe: "Remote address",
269
534
  demandOption: true,
270
535
  })
271
- .positional("password", {
536
+ .positional("address", {
272
537
  type: "string",
273
- describe: "Password",
538
+ describe: "Remote name",
274
539
  demandOption: true,
275
540
  })
541
+ .option("group", {
542
+ describe: "Group name",
543
+ type: "string",
544
+ alias: "g",
545
+ default: DEFAULT_REMOTE_GROUP,
546
+ })
276
547
  .option("directory", {
277
548
  describe: "Peerbit directory",
278
549
  defaultDescription: "~.peerbit",
@@ -287,7 +558,9 @@ export const cli = async (args?: string[]) => {
287
558
  if (args.name === "localhost") {
288
559
  throw new Error("Remote can not be named 'localhost'");
289
560
  }
290
- const api = await client(args.password, args.address);
561
+ const api = await createClient(await getKeypair(args.directory), {
562
+ address: args.address,
563
+ });
291
564
  try {
292
565
  await api.program.list();
293
566
  } catch (error) {
@@ -297,7 +570,11 @@ export const cli = async (args?: string[]) => {
297
570
  fs.mkdirSync(args.directory, { recursive: true });
298
571
  }
299
572
  const remotes = new Remotes(getRemotesPath(args.directory));
300
- remotes.add(args.name, args.address, args.password);
573
+ remotes.add({
574
+ name: args.name,
575
+ address: args.address,
576
+ group: args.group,
577
+ });
301
578
  },
302
579
  })
303
580
  .command({
@@ -346,6 +623,18 @@ export const cli = async (args?: string[]) => {
346
623
  demandOption: false,
347
624
  array: true,
348
625
  })
626
+ .option("all", {
627
+ type: "boolean",
628
+ describe: "Connect to all nodes",
629
+ default: false,
630
+ })
631
+ .option("group", {
632
+ type: "string",
633
+ describe: "Remote group name",
634
+ alias: "g",
635
+ default: [],
636
+ array: true,
637
+ })
349
638
  .option("directory", {
350
639
  describe: "Peerbit directory",
351
640
  defaultDescription: "~.peerbit",
@@ -356,368 +645,457 @@ export const cli = async (args?: string[]) => {
356
645
  return yargs;
357
646
  },
358
647
  handler: async (connectArgs) => {
359
- const names = connectArgs.name;
648
+ const remotes = new Remotes(getRemotesPath(connectArgs.directory));
649
+ let names: string[] = connectArgs.name;
650
+ if (
651
+ names.length === 0 ||
652
+ connectArgs.all ||
653
+ connectArgs.group.length > 0
654
+ ) {
655
+ names = (await remotes.all()).map((x) => x.name);
656
+ }
657
+
360
658
  const apis: {
361
659
  log: (string: string) => void;
362
660
  name: string;
363
- api: Awaited<ReturnType<typeof client>>;
661
+ api: Awaited<ReturnType<typeof createClient>>;
364
662
  }[] = [];
365
- console.log(getRemotesPath(connectArgs.directory));
663
+
664
+ const config = await import("./config.js");
665
+ const keypair = await config.getKeypair(connectArgs.directory);
666
+
667
+ const selectedRemotes: RemoteObject[] = [];
366
668
  if (names.length > 0) {
367
- const remotes = new Remotes(
368
- getRemotesPath(connectArgs.directory)
369
- );
370
- for (const name of names) {
669
+ for (const [ix, name] of names.entries()) {
371
670
  if (name === "localhost") {
372
- const config = await import("./config.js");
373
- const adminPassword = await config.loadPassword(
374
- config.getServerConfigPath(connectArgs.directory)
375
- );
376
- apis.push({
377
- log: (string) => console.log("localhost: " + string),
671
+ selectedRemotes.push({
672
+ address: "http://localhost:" + LOCAL_API_PORT,
378
673
  name: "localhost",
379
- api: await client(adminPassword),
674
+ group: DEFAULT_REMOTE_GROUP,
380
675
  });
381
676
  } else {
382
677
  const remote = remotes.getByName(name);
678
+
383
679
  if (!remote) {
384
680
  throw new Error("Missing remote with name: " + name);
385
681
  }
386
- let logFn: (name: string) => void;
387
- if (names.length > 0) {
388
- logFn = (string) => console.log(name + ": " + string);
389
- } else {
390
- logFn = (string) => console.log(string);
682
+
683
+ if (
684
+ connectArgs.group.length > 0 &&
685
+ !connectArgs.group.includes(remote.group)
686
+ ) {
687
+ continue;
391
688
  }
392
- apis.push({
393
- log: logFn,
394
- name,
395
- api: await client(remote.password, remote.address),
396
- });
689
+
690
+ selectedRemotes.push(remote);
397
691
  }
398
692
  }
399
693
  }
400
694
 
401
- // try if authenticated
402
- for (const api of apis) {
403
- try {
404
- await api.api.program.list();
405
- } catch (error) {
406
- throw new Error(
407
- `Failed to connect to '${api.name}': ${error?.toString()}`
408
- );
695
+ const maxNameLength = selectedRemotes
696
+ .map((x) => x.name.length)
697
+ .reduce((prev, c, i) => {
698
+ return Math.max(prev, c);
699
+ }, 0);
700
+
701
+ if (selectedRemotes.length === 0) {
702
+ console.log(
703
+ chalk.red("No remotes matched your connection condition")
704
+ );
705
+ } else {
706
+ console.log(`Connected to (${selectedRemotes.length}):`);
707
+
708
+ for (const [ix, remote] of selectedRemotes.entries()) {
709
+ const chalkBg = chalk.bgHex(colors[ix]);
710
+ console.log(chalkBg(remote.name));
711
+ const logFn: (name: string) => void = (string) =>
712
+ console.log(
713
+ padString(
714
+ chalkBg(remote.name),
715
+ maxNameLength,
716
+ " ",
717
+ remote.name.length
718
+ ) +
719
+ ": " +
720
+ string
721
+ );
722
+
723
+ apis.push({
724
+ log: logFn,
725
+ name: remote.name,
726
+ api: await createClient(keypair, remote),
727
+ });
409
728
  }
410
- }
411
- const capi = () =>
412
- yargs
413
- .default()
414
- .command("peer", "Peer info", (yargs) => {
415
- yargs
416
- .command({
417
- command: "id",
418
- describe: "Get peer id",
419
- handler: async (args) => {
420
- for (const api of apis) {
421
- api.log((await api.api.peer.id.get()).toString());
422
- }
423
- },
424
- })
425
- .command({
426
- command: "address",
427
- describe: "Get addresses",
428
- handler: async (args) => {
429
- for (const api of apis) {
430
- (await api.api.peer.addresses.get()).forEach((x) =>
431
- api.log(x.toString())
432
- );
433
- }
434
- },
435
- })
436
- .strict()
437
- .demandCommand();
438
- return yargs;
439
- })
440
- .command("network", "Manage network", (yargs) => {
441
- yargs
442
- .command({
443
- command: "bootstrap",
444
- describe: "Connect to bootstrap nodes",
445
- handler: async () => {
446
- for (const api of apis) {
447
- await api.api.network.bootstrap();
729
+
730
+ // try if authenticated
731
+ for (const api of apis) {
732
+ try {
733
+ await api.api.program.list();
734
+ } catch (error) {
735
+ throw new Error(
736
+ `Failed to connect to '${api.name}': ${error?.toString()}`
737
+ );
738
+ }
739
+ }
740
+
741
+ const rl = readline.createInterface({
742
+ input: process.stdin,
743
+ output: process.stdout,
744
+ terminal: true,
745
+ historySize: 100,
746
+ });
747
+
748
+ console.log("Write 'help' to show commands.\n");
749
+ rl.prompt(false);
750
+ const capi = () =>
751
+ yargs
752
+ .default()
753
+ .command("peer", "Peer info", (yargs) => {
754
+ yargs
755
+ .command({
756
+ command: "id",
757
+ describe: "Get peer id",
758
+ handler: async (args) => {
759
+ for (const api of apis) {
760
+ api.log((await api.api.peer.id.get()).toString());
761
+ }
762
+ },
763
+ })
764
+ .command({
765
+ command: "address",
766
+ describe: "Get addresses",
767
+ handler: async (args) => {
768
+ for (const api of apis) {
769
+ (await api.api.peer.addresses.get()).forEach((x) =>
770
+ api.log(x.toString())
771
+ );
772
+ }
773
+ },
774
+ })
775
+ .strict()
776
+ .demandCommand();
777
+ return yargs;
778
+ })
779
+ .command(
780
+ "access",
781
+ "Modify access control for this node",
782
+ (yargs) => {
783
+ yargs
784
+ .command({
785
+ command: "grant <peer-id>",
786
+ describe: "Give a peer-id admin capabilities",
787
+ builder: (yargs: yargs.Argv) => {
788
+ yargs.positional("peer-id", {
789
+ describe: "Peer id",
790
+ type: "string",
791
+ demandOption: true,
792
+ });
793
+ return yargs;
794
+ },
795
+ handler: async (args) => {
796
+ const peerId: PeerId = peerIdFromString(
797
+ args["peer-id"]
798
+ );
799
+ for (const api of apis) {
800
+ await api.api.access.allow(peerId);
801
+ }
802
+ },
803
+ })
804
+ .command({
805
+ command: "deny <peer-id>",
806
+ describe: "Remove admin capabilities from peer-id",
807
+ builder: (yargs: yargs.Argv) => {
808
+ yargs.positional("peer-id", {
809
+ describe: "Peer id",
810
+ demandOption: true,
811
+ });
812
+ return yargs;
813
+ },
814
+ handler: async (args) => {
815
+ const peerId = peerIdFromString(args["peer-id"]);
816
+ for (const api of apis) {
817
+ await api.api.access.deny(peerId);
818
+ }
819
+ },
820
+ })
821
+ .strict()
822
+ .demandCommand();
823
+ }
824
+ )
825
+ .command("network", "Manage network", (yargs) => {
826
+ yargs
827
+ .command({
828
+ command: "bootstrap",
829
+ describe: "Connect to bootstrap nodes",
830
+ handler: async () => {
831
+ for (const api of apis) {
832
+ await api.api.network.bootstrap();
833
+ }
834
+ },
835
+ })
836
+ .strict()
837
+ .demandCommand();
838
+ })
839
+
840
+ .command(
841
+ "topic",
842
+ "Manage topics the node is listening to",
843
+ (yargs) => {
844
+ yargs
845
+ .command({
846
+ command: "list",
847
+ aliases: "ls",
848
+ describe: "List all topics",
849
+ builder: (yargs: any) => {
850
+ yargs.option("replicate", {
851
+ type: "boolean",
852
+ describe: "Replicate data on this topic",
853
+ aliases: "r",
854
+ default: false,
855
+ });
856
+ return yargs;
857
+ },
858
+ handler: async (args) => {
859
+ /* const c = await client();
860
+ const topics = await c.topics.get(args.replicate);
861
+ if (topics?.length > 0) {
862
+ console.log("Topic (" + topics.length + "):");
863
+ for (const t of topics) {
864
+ console.log(t);
448
865
  }
449
- },
450
- })
451
- .strict()
452
- .demandCommand();
453
- })
454
-
455
- .command(
456
- "topic",
457
- "Manage topics the node is listening to",
458
- (yargs) => {
866
+ } else {
867
+ console.log("Not subscribed to any topics");
868
+ } */
869
+ console.error("Not implemented");
870
+ },
871
+ })
872
+ .strict()
873
+ .demandCommand();
874
+ return yargs;
875
+ }
876
+ )
877
+ .command("program", "Manage programs", (yargs) => {
459
878
  yargs
879
+ .command({
880
+ command: "status <address>",
881
+ describe: "Is a program open",
882
+ builder: (yargs: any) => {
883
+ yargs.positional("address", {
884
+ type: "string",
885
+ describe: "Program address",
886
+ demandOption: true,
887
+ });
888
+ return yargs;
889
+ },
890
+
891
+ handler: async (args) => {
892
+ for (const api of apis) {
893
+ const program = await api.api.program.has(
894
+ args.address
895
+ );
896
+ if (!program) {
897
+ api.log(chalk.red("Closed"));
898
+ } else {
899
+ api.log(chalk.green("Open"));
900
+ }
901
+ }
902
+ },
903
+ })
904
+ .command({
905
+ command: "drop <address>",
906
+ describe: "Drop a program",
907
+ builder: (yargs: any) => {
908
+ yargs.positional("address", {
909
+ type: "string",
910
+ describe: "Program address",
911
+ demandOption: true,
912
+ });
913
+ return yargs;
914
+ },
915
+
916
+ handler: async (args) => {
917
+ for (const api of apis) {
918
+ try {
919
+ await api.api.program.drop(args.address);
920
+ } catch (error: any) {
921
+ api.log(
922
+ chalk.red(
923
+ `Failed to drop ${
924
+ args.address
925
+ }: ${error.toString()}`
926
+ )
927
+ );
928
+ }
929
+ }
930
+ },
931
+ })
932
+ .command({
933
+ command: "close <address>",
934
+ describe: "Close a program",
935
+ builder: (yargs: any) => {
936
+ yargs.positional("address", {
937
+ type: "string",
938
+ describe: "Program address",
939
+ demandOption: true,
940
+ });
941
+ return yargs;
942
+ },
943
+
944
+ handler: async (args) => {
945
+ for (const api of apis) {
946
+ await api.api.program.close(args.address);
947
+ }
948
+ },
949
+ })
460
950
  .command({
461
951
  command: "list",
952
+ describe: "List all running programs",
462
953
  aliases: "ls",
463
- describe: "List all topics",
954
+ handler: async (args) => {
955
+ for (const api of apis) {
956
+ const list = await api.api.program.list();
957
+ api.log(`Running programs (${list.length}):`);
958
+ list.forEach((p) => {
959
+ api.log(chalk.green(p));
960
+ });
961
+ }
962
+ },
963
+ })
964
+
965
+ .command({
966
+ command: "open [program]",
967
+ describe: "Open program",
464
968
  builder: (yargs: any) => {
465
- yargs.option("replicate", {
466
- type: "boolean",
467
- describe: "Replicate data on this topic",
468
- aliases: "r",
469
- default: false,
969
+ yargs.positional("program", {
970
+ type: "string",
971
+ describe: "Identifier",
972
+ demandOption: true,
973
+ });
974
+ yargs.option("base64", {
975
+ type: "string",
976
+ describe: "Base64 encoded serialized",
977
+ aliases: "b",
978
+ });
979
+ yargs.option("variant", {
980
+ type: "string",
981
+ describe: "Variant name",
982
+ aliases: "v",
470
983
  });
471
984
  return yargs;
472
985
  },
473
986
  handler: async (args) => {
474
- /* const c = await client();
475
- const topics = await c.topics.get(args.replicate);
476
- if (topics?.length > 0) {
477
- console.log("Topic (" + topics.length + "):");
478
- for (const t of topics) {
479
- console.log(t);
480
- }
481
- } else {
482
- console.log("Not subscribed to any topics");
483
- } */
484
- console.error("Not implemented");
987
+ if (!args.base64 && !args.variant) {
988
+ throw new Error(
989
+ "Either base64 or variant argument needs to be provided"
990
+ );
991
+ }
992
+ let startArg: StartProgram;
993
+ if (args.base64) {
994
+ startArg = {
995
+ base64: args.base64,
996
+ };
997
+ } else {
998
+ startArg = {
999
+ variant: args.variant,
1000
+ };
1001
+ }
1002
+ for (const api of apis) {
1003
+ const address = await api.api.program.open(
1004
+ startArg
1005
+ );
1006
+ api.log("Started program with address: ");
1007
+ api.log(chalk.green(address.toString()));
1008
+ }
485
1009
  },
486
1010
  })
487
1011
  .strict()
488
1012
  .demandCommand();
489
1013
  return yargs;
490
- }
491
- )
492
- .command("program", "Manage programs", (yargs) => {
493
- yargs
494
- .command({
495
- command: "status <address>",
496
- describe: "Is a program open",
497
- builder: (yargs: any) => {
498
- yargs.positional("address", {
499
- type: "string",
500
- describe: "Program address",
501
- demandOption: true,
502
- });
503
- return yargs;
504
- },
1014
+ })
1015
+ .command({
1016
+ command: "install <package-spec>",
1017
+ describe: "install and import a dependency",
1018
+ builder: (yargs: any) => {
1019
+ yargs.positional("package-spec", {
1020
+ type: "string",
1021
+ describe:
1022
+ "Installed dependency will be loaded with js import(...)",
1023
+ demandOption: true,
1024
+ });
505
1025
 
506
- handler: async (args) => {
507
- for (const api of apis) {
508
- const program = await api.api.program.has(
509
- args.address
510
- );
511
- if (!program) {
512
- api.log(chalk.red("Closed"));
513
- } else {
514
- api.log(chalk.green("Open"));
515
- }
516
- }
517
- },
518
- })
519
- .command({
520
- command: "drop <address>",
521
- describe: "Drop a program",
522
- builder: (yargs: any) => {
523
- yargs.positional("address", {
524
- type: "string",
525
- describe: "Program address",
526
- demandOption: true,
527
- });
528
- return yargs;
529
- },
1026
+ return yargs;
1027
+ },
1028
+ handler: async (args) => {
1029
+ // if ends with .tgz assume it is a file
530
1030
 
531
- handler: async (args) => {
532
- for (const api of apis) {
533
- try {
534
- await api.api.program.drop(args.address);
535
- } catch (error: any) {
536
- api.log(
537
- chalk.red(
538
- `Failed to drop ${
539
- args.address
540
- }: ${error.toString()}`
541
- )
542
- );
543
- }
544
- }
545
- },
546
- })
547
- .command({
548
- command: "close <address>",
549
- describe: "Close a program",
550
- builder: (yargs: any) => {
551
- yargs.positional("address", {
552
- type: "string",
553
- describe: "Program address",
554
- demandOption: true,
555
- });
556
- return yargs;
557
- },
1031
+ let installCommand: InstallDependency;
1032
+ const packageName: string = args["package-spec"];
1033
+ if (packageName.endsWith(".tgz")) {
1034
+ const packagePath = path.isAbsolute(packageName)
1035
+ ? packageName
1036
+ : path.join(process.cwd(), packageName);
558
1037
 
559
- handler: async (args) => {
560
- for (const api of apis) {
561
- await api.api.program.close(args.address);
562
- }
563
- },
564
- })
565
- .command({
566
- command: "list",
567
- describe: "List all running programs",
568
- aliases: "ls",
569
- handler: async (args) => {
570
- for (const api of apis) {
571
- const list = await api.api.program.list();
572
- api.log(`Running programs (${list.length}):`);
573
- list.forEach((p) => {
574
- api.log(chalk.green(p));
575
- });
576
- }
577
- },
578
- })
579
-
580
- .command({
581
- command: "open [program]",
582
- describe: "Open program",
583
- builder: (yargs: any) => {
584
- yargs.positional("program", {
585
- type: "string",
586
- describe: "Identifier",
587
- demandOption: true,
588
- });
589
- yargs.option("base64", {
590
- type: "string",
591
- describe: "Base64 encoded serialized",
592
- aliases: "b",
593
- });
594
- yargs.option("variant", {
595
- type: "string",
596
- describe: "Variant name",
597
- aliases: "v",
598
- });
599
- return yargs;
600
- },
601
- handler: async (args) => {
602
- if (!args.base64 && !args.variant) {
603
- throw new Error(
604
- "Either base64 or variant argument needs to be provided"
605
- );
606
- }
607
- let startArg: StartProgram;
608
- if (args.base64) {
609
- startArg = {
610
- base64: args.base64,
611
- };
612
- } else {
613
- startArg = {
614
- variant: args.variant,
615
- };
616
- }
617
- for (const api of apis) {
618
- const address = await api.api.program.open(startArg);
619
- api.log("Started program with address: ");
620
- api.log(chalk.green(address.toString()));
621
- }
622
- },
623
- })
624
- .strict()
625
- .demandCommand();
626
- return yargs;
627
- })
628
- .command({
629
- command: "install <package-spec>",
630
- describe: "install and import a dependency",
631
- builder: (yargs: any) => {
632
- yargs.positional("package-spec", {
633
- type: "string",
634
- describe:
635
- "Installed dependency will be loaded with js import(...)",
636
- demandOption: true,
637
- });
1038
+ const buffer = fs.readFileSync(packagePath);
1039
+ const base64 = toBase64(buffer);
1040
+ installCommand = {
1041
+ type: "tgz",
1042
+ name: await getPackageName(packageName),
1043
+ base64,
1044
+ };
1045
+ } else {
1046
+ installCommand = { type: "npm", name: packageName };
1047
+ }
638
1048
 
639
- return yargs;
640
- },
641
- handler: async (args) => {
642
- // if ends with .tgz assume it is a file
643
-
644
- let installCommand: InstallDependency;
645
- const packageName: string = args["package-spec"];
646
- if (packageName.endsWith(".tgz")) {
647
- const packagePath = path.isAbsolute(packageName)
648
- ? packageName
649
- : path.join(process.cwd(), packageName);
650
-
651
- const buffer = fs.readFileSync(packagePath);
652
- const base64 = toBase64(buffer);
653
- installCommand = {
654
- type: "tgz",
655
- name: await getPackageName(packageName),
656
- base64,
657
- };
658
- } else {
659
- installCommand = { type: "npm", name: packageName };
660
- }
1049
+ for (const api of apis) {
1050
+ const newPrograms = await api.api.dependency.install(
1051
+ installCommand
1052
+ );
1053
+ api.log(
1054
+ `New programs available (${newPrograms.length}):`
1055
+ );
1056
+ newPrograms.forEach((p) => {
1057
+ api.log(chalk.green(p));
1058
+ });
1059
+ }
1060
+ },
1061
+ })
1062
+ .command({
1063
+ command: "restart",
1064
+ describe: "Restart the server",
1065
+ handler: async () => {
1066
+ for (const api of apis) {
1067
+ await api.api.restart();
1068
+ }
1069
+ },
1070
+ })
1071
+ .command({
1072
+ command: "stop",
1073
+ describe: "Stop the server",
1074
+ handler: async () => {
1075
+ for (const api of apis) {
1076
+ await api.api.stop();
1077
+ }
1078
+ },
1079
+ })
1080
+ .help()
1081
+ .strict()
1082
+ .scriptName("")
1083
+ .demandCommand()
1084
+ .showHelpOnFail(true)
1085
+ .exitProcess(false);
661
1086
 
662
- for (const api of apis) {
663
- const newPrograms = await api.api.dependency.install(
664
- installCommand
665
- );
666
- api.log(
667
- `New programs available (${newPrograms.length}):`
668
- );
669
- newPrograms.forEach((p) => {
670
- api.log(chalk.green(p));
671
- });
672
- }
673
- },
674
- })
675
- .command({
676
- command: "restart",
677
- describe: "Restart the server",
678
- handler: async () => {
679
- for (const api of apis) {
680
- await api.api.restart();
681
- }
682
- },
683
- })
684
- .command({
685
- command: "terminate",
686
- describe: "Terminate the server",
687
- handler: async () => {
688
- for (const api of apis) {
689
- await api.api.terminate();
690
- }
691
- },
692
- })
693
- .help()
694
- .strict()
695
- .scriptName("")
696
- .demandCommand()
697
- .showHelpOnFail(true)
698
- .exitProcess(false);
699
- const rl = readline.createInterface({
700
- input: process.stdin,
701
- output: process.stdout,
702
- terminal: true,
703
- historySize: 100,
704
- });
705
- console.log(chalk.green("Connected"));
706
- console.log("Write 'help' to show commands.\n");
707
- const first = true;
708
- rl.prompt(false);
709
- rl.on("line", async (cargs) => {
710
- const cmds = capi();
711
- try {
712
- await cmds.parse(cargs);
713
- } catch (error: any) {
714
- /* console.log(chalk.red("Error parsing command: " + cargs))*/
715
- }
716
- rl.prompt(true);
717
- });
1087
+ rl.on("line", async (cargs) => {
1088
+ const cmds = capi();
1089
+ try {
1090
+ await cmds.parse(cargs);
1091
+ } catch (error: any) {
1092
+ /* console.log(chalk.red("Error parsing command: " + cargs))*/
1093
+ }
1094
+ rl.prompt(true);
1095
+ });
1096
+ }
718
1097
  },
719
1098
  })
720
-
721
1099
  .help()
722
1100
  .strict()
723
1101
  .demandCommand();