@peerbit/server 3.0.0 → 4.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 (44) 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 +590 -324
  8. package/lib/esm/cli.js.map +1 -1
  9. package/lib/esm/client.d.ts +11 -5
  10. package/lib/esm/client.js +64 -11
  11. package/lib/esm/client.js.map +1 -1
  12. package/lib/esm/docker.browser.d.ts +0 -0
  13. package/lib/esm/docker.browser.js +3 -0
  14. package/lib/esm/docker.browser.js.map +1 -0
  15. package/lib/esm/domain.js +1 -1
  16. package/lib/esm/domain.js.map +1 -1
  17. package/lib/esm/peerbit.d.ts +1 -1
  18. package/lib/esm/remotes.d.ts +15 -2
  19. package/lib/esm/remotes.js +8 -8
  20. package/lib/esm/remotes.js.map +1 -1
  21. package/lib/esm/routes.d.ts +3 -2
  22. package/lib/esm/routes.js +5 -4
  23. package/lib/esm/routes.js.map +1 -1
  24. package/lib/esm/server.d.ts +3 -1
  25. package/lib/esm/server.js +32 -19
  26. package/lib/esm/server.js.map +1 -1
  27. package/lib/esm/trust.d.ts +1 -1
  28. package/lib/esm/trust.js.map +1 -1
  29. package/lib/ui/assets/aws.browser-4ed993c7.js +1 -0
  30. package/lib/ui/assets/index-5ed0229d.js +77 -0
  31. package/lib/ui/index.html +1 -1
  32. package/package.json +13 -7
  33. package/src/aws.browser.ts +1 -0
  34. package/src/aws.ts +250 -1
  35. package/src/cli.ts +677 -354
  36. package/src/client.ts +76 -11
  37. package/src/docker.browser.ts +1 -0
  38. package/src/domain.ts +1 -1
  39. package/src/peerbit.ts +1 -1
  40. package/src/remotes.ts +24 -10
  41. package/src/routes.ts +5 -4
  42. package/src/server.ts +43 -23
  43. package/src/trust.ts +1 -1
  44. package/lib/ui/assets/index-cac7195d.js +0 -77
package/src/cli.ts CHANGED
@@ -5,7 +5,12 @@ import {
5
5
  startCertbot,
6
6
  } from "./domain.js";
7
7
  import { startServerWithNode } from "./server.js";
8
- import { createRecord } from "./aws.js";
8
+ import {
9
+ AWS_LINUX_ARM_AMIs,
10
+ createRecord,
11
+ launchNodes,
12
+ terminateNode,
13
+ } from "./aws.js";
9
14
  import {
10
15
  getHomeConfigDir,
11
16
  getKeypair,
@@ -13,7 +18,7 @@ import {
13
18
  getRemotesPath,
14
19
  } from "./config.js";
15
20
  import chalk from "chalk";
16
- import { client } from "./client.js";
21
+ import { createClient, waitForDomain } from "./client.js";
17
22
  import { InstallDependency, StartProgram } from "./types.js";
18
23
  import { exit } from "process";
19
24
  import yargs from "yargs";
@@ -21,15 +26,88 @@ import readline from "readline";
21
26
  import fs from "fs";
22
27
  import path from "path";
23
28
  import { toBase64 } from "@peerbit/crypto";
24
- import { Remotes } from "./remotes.js";
29
+ import { DEFAULT_REMOTE_GROUP, RemoteObject, Remotes } from "./remotes.js";
25
30
  import { peerIdFromString } from "@libp2p/peer-id";
31
+ import { LOCAL_API_PORT } from "./routes.js";
32
+ import { type PeerId } from "@libp2p/interface/peer-id";
26
33
 
27
- 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
+ ) {
28
105
  const val = string.valueOf();
29
- if (Math.abs(padding) <= val.length) {
106
+ if (Math.abs(padding) <= stringLength) {
30
107
  return val;
31
108
  }
32
- const m = Math.max(Math.abs(padding) - string.length || 0, 0);
109
+
110
+ const m = Math.max(Math.abs(padding) - stringLength || 0, 0);
33
111
  const pad = Array(m + 1).join(String(padChar).charAt(0));
34
112
  // var pad = String(c || ' ').charAt(0).repeat(Math.abs(n) - this.length);
35
113
  return padding < 0 ? pad + val : val + pad;
@@ -63,6 +141,14 @@ export const cli = async (args?: string[]) => {
63
141
  type: "boolean",
64
142
  default: false,
65
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
+ })
66
152
  .option("reset", {
67
153
  describe:
68
154
  "If true, then programs opened during last session will not be opened",
@@ -93,6 +179,7 @@ export const cli = async (args?: string[]) => {
93
179
  ports: { api: args["port-api"], node: args["port-node"] },
94
180
  bootstrap: args.bootstrap,
95
181
  newSession: args.reset,
182
+ grantAccess: args["grant-access"],
96
183
  });
97
184
  },
98
185
  })
@@ -231,6 +318,168 @@ export const cli = async (args?: string[]) => {
231
318
  )
232
319
  .command("remote", "Handle remote nodes", (innerYargs) => {
233
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
+ for (const node of nodes) {
417
+ try {
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
+ } catch (error: any) {
431
+ process.stdout.write("\r");
432
+ console.error(
433
+ `Error waiting for domain for ip: ${
434
+ node.publicIp
435
+ } to be available: ${error?.toString()}`
436
+ );
437
+ }
438
+ }
439
+ process.stdout.write("\r");
440
+ clearInterval(twirlTimer);
441
+ console.log(`New nodes available (${nodes.length}):`);
442
+ for (const node of nodes) {
443
+ console.log(chalk.green(node.name));
444
+ }
445
+ },
446
+ })
447
+ .strict()
448
+ .demandCommand();
449
+ })
450
+ .command({
451
+ command: "terminate [name...]",
452
+ describe: "Terminate remote instances that was previously spawned",
453
+ builder: (killArgs: yargs.Argv) => {
454
+ killArgs.option("all", {
455
+ describe: "Kill all nodes",
456
+ type: "boolean",
457
+ default: false,
458
+ });
459
+ killArgs.positional("name", {
460
+ type: "string",
461
+ describe: "Remote name",
462
+ default: "localhost",
463
+ demandOption: false,
464
+ array: true,
465
+ });
466
+ return killArgs;
467
+ },
468
+ handler: async (args) => {
469
+ const remotes = new Remotes(getRemotesPath(args.directory));
470
+ const allRemotes = await remotes.all();
471
+ for (const remote of allRemotes) {
472
+ if (args.all || args.name.includes(remote.name)) {
473
+ if (remote.origin?.type === "aws") {
474
+ await terminateNode({
475
+ instanceId: remote.origin.instanceId,
476
+ region: remote.origin.region,
477
+ });
478
+ }
479
+ }
480
+ }
481
+ },
482
+ })
234
483
  .command({
235
484
  command: "list",
236
485
  aliases: "ls",
@@ -254,11 +503,19 @@ export const cli = async (args?: string[]) => {
254
503
  .reduce((prev, c, i) => {
255
504
  return Math.max(prev, c);
256
505
  }, 0);
506
+
257
507
  const all = await remotes.all();
258
508
  if (all.length > 0) {
509
+ console.log(
510
+ padString("Name", maxNameLength + 10),
511
+ padString("Group" || "", 10),
512
+ "Address"
513
+ );
514
+
259
515
  for (const remote of all) {
260
516
  console.log(
261
517
  padString(remote.name, maxNameLength + 10),
518
+ padString(remote.group || "", 10),
262
519
  remote.address
263
520
  );
264
521
  }
@@ -272,15 +529,21 @@ export const cli = async (args?: string[]) => {
272
529
  describe: "Add remote",
273
530
  builder: (yargs: yargs.Argv) => {
274
531
  yargs
532
+ .positional("name", {
533
+ type: "string",
534
+ describe: "Remote address",
535
+ demandOption: true,
536
+ })
275
537
  .positional("address", {
276
538
  type: "string",
277
539
  describe: "Remote name",
278
540
  demandOption: true,
279
541
  })
280
- .positional("name", {
542
+ .option("group", {
543
+ describe: "Group name",
281
544
  type: "string",
282
- describe: "Remote address",
283
- demandOption: true,
545
+ alias: "g",
546
+ default: DEFAULT_REMOTE_GROUP,
284
547
  })
285
548
  .option("directory", {
286
549
  describe: "Peerbit directory",
@@ -296,10 +559,9 @@ export const cli = async (args?: string[]) => {
296
559
  if (args.name === "localhost") {
297
560
  throw new Error("Remote can not be named 'localhost'");
298
561
  }
299
- const api = await client(
300
- await getKeypair(args.directory),
301
- args.address
302
- );
562
+ const api = await createClient(await getKeypair(args.directory), {
563
+ address: args.address,
564
+ });
303
565
  try {
304
566
  await api.program.list();
305
567
  } catch (error) {
@@ -309,7 +571,11 @@ export const cli = async (args?: string[]) => {
309
571
  fs.mkdirSync(args.directory, { recursive: true });
310
572
  }
311
573
  const remotes = new Remotes(getRemotesPath(args.directory));
312
- remotes.add(args.name, args.address);
574
+ remotes.add({
575
+ name: args.name,
576
+ address: args.address,
577
+ group: args.group,
578
+ });
313
579
  },
314
580
  })
315
581
  .command({
@@ -358,6 +624,18 @@ export const cli = async (args?: string[]) => {
358
624
  demandOption: false,
359
625
  array: true,
360
626
  })
627
+ .option("all", {
628
+ type: "boolean",
629
+ describe: "Connect to all nodes",
630
+ default: false,
631
+ })
632
+ .option("group", {
633
+ type: "string",
634
+ describe: "Remote group name",
635
+ alias: "g",
636
+ default: [],
637
+ array: true,
638
+ })
361
639
  .option("directory", {
362
640
  describe: "Peerbit directory",
363
641
  defaultDescription: "~.peerbit",
@@ -368,412 +646,457 @@ export const cli = async (args?: string[]) => {
368
646
  return yargs;
369
647
  },
370
648
  handler: async (connectArgs) => {
371
- const names = connectArgs.name;
649
+ const remotes = new Remotes(getRemotesPath(connectArgs.directory));
650
+ let names: string[] = connectArgs.name;
651
+ if (
652
+ names.length === 0 ||
653
+ connectArgs.all ||
654
+ connectArgs.group.length > 0
655
+ ) {
656
+ names = (await remotes.all()).map((x) => x.name);
657
+ }
658
+
372
659
  const apis: {
373
660
  log: (string: string) => void;
374
661
  name: string;
375
- api: Awaited<ReturnType<typeof client>>;
662
+ api: Awaited<ReturnType<typeof createClient>>;
376
663
  }[] = [];
377
- console.log(getRemotesPath(connectArgs.directory));
664
+
378
665
  const config = await import("./config.js");
379
666
  const keypair = await config.getKeypair(connectArgs.directory);
380
667
 
668
+ const selectedRemotes: RemoteObject[] = [];
381
669
  if (names.length > 0) {
382
- const remotes = new Remotes(
383
- getRemotesPath(connectArgs.directory)
384
- );
385
- for (const name of names) {
670
+ for (const [ix, name] of names.entries()) {
386
671
  if (name === "localhost") {
387
- apis.push({
388
- log: (string) => console.log("localhost: " + string),
672
+ selectedRemotes.push({
673
+ address: "http://localhost:" + LOCAL_API_PORT,
389
674
  name: "localhost",
390
- api: await client(keypair),
675
+ group: DEFAULT_REMOTE_GROUP,
391
676
  });
392
677
  } else {
393
678
  const remote = remotes.getByName(name);
679
+
394
680
  if (!remote) {
395
681
  throw new Error("Missing remote with name: " + name);
396
682
  }
397
- let logFn: (name: string) => void;
398
- if (names.length > 0) {
399
- logFn = (string) => console.log(name + ": " + string);
400
- } else {
401
- logFn = (string) => console.log(string);
683
+
684
+ if (
685
+ connectArgs.group.length > 0 &&
686
+ !connectArgs.group.includes(remote.group)
687
+ ) {
688
+ continue;
402
689
  }
403
690
 
404
- apis.push({
405
- log: logFn,
406
- name,
407
- api: await client(keypair, remote.address),
408
- });
691
+ selectedRemotes.push(remote);
409
692
  }
410
693
  }
411
694
  }
412
695
 
413
- // try if authenticated
414
- for (const api of apis) {
415
- try {
416
- await api.api.program.list();
417
- } catch (error) {
418
- throw new Error(
419
- `Failed to connect to '${api.name}': ${error?.toString()}`
420
- );
696
+ const maxNameLength = selectedRemotes
697
+ .map((x) => x.name.length)
698
+ .reduce((prev, c, i) => {
699
+ return Math.max(prev, c);
700
+ }, 0);
701
+
702
+ if (selectedRemotes.length === 0) {
703
+ console.log(
704
+ chalk.red("No remotes matched your connection condition")
705
+ );
706
+ } else {
707
+ console.log(`Connected to (${selectedRemotes.length}):`);
708
+
709
+ for (const [ix, remote] of selectedRemotes.entries()) {
710
+ const chalkBg = chalk.bgHex(colors[ix]);
711
+ console.log(chalkBg(remote.name));
712
+ const logFn: (name: string) => void = (string) =>
713
+ console.log(
714
+ padString(
715
+ chalkBg(remote.name),
716
+ maxNameLength,
717
+ " ",
718
+ remote.name.length
719
+ ) +
720
+ ": " +
721
+ string
722
+ );
723
+
724
+ apis.push({
725
+ log: logFn,
726
+ name: remote.name,
727
+ api: await createClient(keypair, remote),
728
+ });
421
729
  }
422
- }
423
- const capi = () =>
424
- yargs
425
- .default()
426
- .command("peer", "Peer info", (yargs) => {
427
- yargs
428
- .command({
429
- command: "id",
430
- describe: "Get peer id",
431
- handler: async (args) => {
432
- for (const api of apis) {
433
- api.log((await api.api.peer.id.get()).toString());
434
- }
435
- },
436
- })
437
- .command({
438
- command: "address",
439
- describe: "Get addresses",
440
- handler: async (args) => {
441
- for (const api of apis) {
442
- (await api.api.peer.addresses.get()).forEach((x) =>
443
- api.log(x.toString())
444
- );
730
+
731
+ // try if authenticated
732
+ for (const api of apis) {
733
+ try {
734
+ await api.api.program.list();
735
+ } catch (error) {
736
+ throw new Error(
737
+ `Failed to connect to '${api.name}': ${error?.toString()}`
738
+ );
739
+ }
740
+ }
741
+
742
+ const rl = readline.createInterface({
743
+ input: process.stdin,
744
+ output: process.stdout,
745
+ terminal: true,
746
+ historySize: 100,
747
+ });
748
+
749
+ console.log("Write 'help' to show commands.\n");
750
+ rl.prompt(false);
751
+ const capi = () =>
752
+ yargs
753
+ .default()
754
+ .command("peer", "Peer info", (yargs) => {
755
+ yargs
756
+ .command({
757
+ command: "id",
758
+ describe: "Get peer id",
759
+ handler: async (args) => {
760
+ for (const api of apis) {
761
+ api.log((await api.api.peer.id.get()).toString());
762
+ }
763
+ },
764
+ })
765
+ .command({
766
+ command: "address",
767
+ describe: "Get addresses",
768
+ handler: async (args) => {
769
+ for (const api of apis) {
770
+ (await api.api.peer.addresses.get()).forEach((x) =>
771
+ api.log(x.toString())
772
+ );
773
+ }
774
+ },
775
+ })
776
+ .strict()
777
+ .demandCommand();
778
+ return yargs;
779
+ })
780
+ .command(
781
+ "access",
782
+ "Modify access control for this node",
783
+ (yargs) => {
784
+ yargs
785
+ .command({
786
+ command: "grant <peer-id>",
787
+ describe: "Give a peer-id admin capabilities",
788
+ builder: (yargs: yargs.Argv) => {
789
+ yargs.positional("peer-id", {
790
+ describe: "Peer id",
791
+ type: "string",
792
+ demandOption: true,
793
+ });
794
+ return yargs;
795
+ },
796
+ handler: async (args) => {
797
+ const peerId: PeerId = peerIdFromString(
798
+ args["peer-id"]
799
+ );
800
+ for (const api of apis) {
801
+ await api.api.access.allow(peerId);
802
+ }
803
+ },
804
+ })
805
+ .command({
806
+ command: "deny <peer-id>",
807
+ describe: "Remove admin capabilities from peer-id",
808
+ builder: (yargs: yargs.Argv) => {
809
+ yargs.positional("peer-id", {
810
+ describe: "Peer id",
811
+ demandOption: true,
812
+ });
813
+ return yargs;
814
+ },
815
+ handler: async (args) => {
816
+ const peerId = peerIdFromString(args["peer-id"]);
817
+ for (const api of apis) {
818
+ await api.api.access.deny(peerId);
819
+ }
820
+ },
821
+ })
822
+ .strict()
823
+ .demandCommand();
824
+ }
825
+ )
826
+ .command("network", "Manage network", (yargs) => {
827
+ yargs
828
+ .command({
829
+ command: "bootstrap",
830
+ describe: "Connect to bootstrap nodes",
831
+ handler: async () => {
832
+ for (const api of apis) {
833
+ await api.api.network.bootstrap();
834
+ }
835
+ },
836
+ })
837
+ .strict()
838
+ .demandCommand();
839
+ })
840
+
841
+ .command(
842
+ "topic",
843
+ "Manage topics the node is listening to",
844
+ (yargs) => {
845
+ yargs
846
+ .command({
847
+ command: "list",
848
+ aliases: "ls",
849
+ describe: "List all topics",
850
+ builder: (yargs: any) => {
851
+ yargs.option("replicate", {
852
+ type: "boolean",
853
+ describe: "Replicate data on this topic",
854
+ aliases: "r",
855
+ default: false,
856
+ });
857
+ return yargs;
858
+ },
859
+ handler: async (args) => {
860
+ /* const c = await client();
861
+ const topics = await c.topics.get(args.replicate);
862
+ if (topics?.length > 0) {
863
+ console.log("Topic (" + topics.length + "):");
864
+ for (const t of topics) {
865
+ console.log(t);
445
866
  }
446
- },
447
- })
448
- .strict()
449
- .demandCommand();
450
- return yargs;
451
- })
452
- .command(
453
- "access",
454
- "Modify access control for this node",
455
- (yargs) => {
867
+ } else {
868
+ console.log("Not subscribed to any topics");
869
+ } */
870
+ console.error("Not implemented");
871
+ },
872
+ })
873
+ .strict()
874
+ .demandCommand();
875
+ return yargs;
876
+ }
877
+ )
878
+ .command("program", "Manage programs", (yargs) => {
456
879
  yargs
457
880
  .command({
458
- command: "grant <peer-id>",
459
- describe: "Give a peer-id admin capabilities",
460
- builder: (yargs: yargs.Argv) => {
461
- yargs.positional("peer-id", {
462
- describe: "Peer id",
881
+ command: "status <address>",
882
+ describe: "Is a program open",
883
+ builder: (yargs: any) => {
884
+ yargs.positional("address", {
463
885
  type: "string",
886
+ describe: "Program address",
464
887
  demandOption: true,
465
888
  });
466
889
  return yargs;
467
890
  },
891
+
468
892
  handler: async (args) => {
469
- const peerId = peerIdFromString(args["peer-id"]);
470
893
  for (const api of apis) {
471
- await api.api.trust.add(peerId);
894
+ const program = await api.api.program.has(
895
+ args.address
896
+ );
897
+ if (!program) {
898
+ api.log(chalk.red("Closed"));
899
+ } else {
900
+ api.log(chalk.green("Open"));
901
+ }
472
902
  }
473
903
  },
474
904
  })
475
905
  .command({
476
- command: "deny <peer-id>",
477
- describe: "Remove admin capabilities from peer-id",
478
- builder: (yargs: yargs.Argv) => {
479
- yargs.positional("peer-id", {
480
- describe: "Peer id",
906
+ command: "drop <address>",
907
+ describe: "Drop a program",
908
+ builder: (yargs: any) => {
909
+ yargs.positional("address", {
910
+ type: "string",
911
+ describe: "Program address",
481
912
  demandOption: true,
482
913
  });
483
914
  return yargs;
484
915
  },
916
+
485
917
  handler: async (args) => {
486
- const peerId = peerIdFromString(args["peer-id"]);
487
918
  for (const api of apis) {
488
- await api.api.trust.remove(peerId);
919
+ try {
920
+ await api.api.program.drop(args.address);
921
+ } catch (error: any) {
922
+ api.log(
923
+ chalk.red(
924
+ `Failed to drop ${
925
+ args.address
926
+ }: ${error.toString()}`
927
+ )
928
+ );
929
+ }
930
+ }
931
+ },
932
+ })
933
+ .command({
934
+ command: "close <address>",
935
+ describe: "Close a program",
936
+ builder: (yargs: any) => {
937
+ yargs.positional("address", {
938
+ type: "string",
939
+ describe: "Program address",
940
+ demandOption: true,
941
+ });
942
+ return yargs;
943
+ },
944
+
945
+ handler: async (args) => {
946
+ for (const api of apis) {
947
+ await api.api.program.close(args.address);
489
948
  }
490
949
  },
491
950
  })
492
- .strict()
493
- .demandCommand();
494
- }
495
- )
496
- .command("network", "Manage network", (yargs) => {
497
- yargs
498
- .command({
499
- command: "bootstrap",
500
- describe: "Connect to bootstrap nodes",
501
- handler: async () => {
502
- for (const api of apis) {
503
- await api.api.network.bootstrap();
504
- }
505
- },
506
- })
507
- .strict()
508
- .demandCommand();
509
- })
510
-
511
- .command(
512
- "topic",
513
- "Manage topics the node is listening to",
514
- (yargs) => {
515
- yargs
516
951
  .command({
517
952
  command: "list",
953
+ describe: "List all running programs",
518
954
  aliases: "ls",
519
- describe: "List all topics",
955
+ handler: async (args) => {
956
+ for (const api of apis) {
957
+ const list = await api.api.program.list();
958
+ api.log(`Running programs (${list.length}):`);
959
+ list.forEach((p) => {
960
+ api.log(chalk.green(p));
961
+ });
962
+ }
963
+ },
964
+ })
965
+
966
+ .command({
967
+ command: "open [program]",
968
+ describe: "Open program",
520
969
  builder: (yargs: any) => {
521
- yargs.option("replicate", {
522
- type: "boolean",
523
- describe: "Replicate data on this topic",
524
- aliases: "r",
525
- default: false,
970
+ yargs.positional("program", {
971
+ type: "string",
972
+ describe: "Identifier",
973
+ demandOption: true,
974
+ });
975
+ yargs.option("base64", {
976
+ type: "string",
977
+ describe: "Base64 encoded serialized",
978
+ aliases: "b",
979
+ });
980
+ yargs.option("variant", {
981
+ type: "string",
982
+ describe: "Variant name",
983
+ aliases: "v",
526
984
  });
527
985
  return yargs;
528
986
  },
529
987
  handler: async (args) => {
530
- /* const c = await client();
531
- const topics = await c.topics.get(args.replicate);
532
- if (topics?.length > 0) {
533
- console.log("Topic (" + topics.length + "):");
534
- for (const t of topics) {
535
- console.log(t);
536
- }
537
- } else {
538
- console.log("Not subscribed to any topics");
539
- } */
540
- console.error("Not implemented");
988
+ if (!args.base64 && !args.variant) {
989
+ throw new Error(
990
+ "Either base64 or variant argument needs to be provided"
991
+ );
992
+ }
993
+ let startArg: StartProgram;
994
+ if (args.base64) {
995
+ startArg = {
996
+ base64: args.base64,
997
+ };
998
+ } else {
999
+ startArg = {
1000
+ variant: args.variant,
1001
+ };
1002
+ }
1003
+ for (const api of apis) {
1004
+ const address = await api.api.program.open(
1005
+ startArg
1006
+ );
1007
+ api.log("Started program with address: ");
1008
+ api.log(chalk.green(address.toString()));
1009
+ }
541
1010
  },
542
1011
  })
543
1012
  .strict()
544
1013
  .demandCommand();
545
1014
  return yargs;
546
- }
547
- )
548
- .command("program", "Manage programs", (yargs) => {
549
- yargs
550
- .command({
551
- command: "status <address>",
552
- describe: "Is a program open",
553
- builder: (yargs: any) => {
554
- yargs.positional("address", {
555
- type: "string",
556
- describe: "Program address",
557
- demandOption: true,
558
- });
559
- return yargs;
560
- },
1015
+ })
1016
+ .command({
1017
+ command: "install <package-spec>",
1018
+ describe: "install and import a dependency",
1019
+ builder: (yargs: any) => {
1020
+ yargs.positional("package-spec", {
1021
+ type: "string",
1022
+ describe:
1023
+ "Installed dependency will be loaded with js import(...)",
1024
+ demandOption: true,
1025
+ });
561
1026
 
562
- handler: async (args) => {
563
- for (const api of apis) {
564
- const program = await api.api.program.has(
565
- args.address
566
- );
567
- if (!program) {
568
- api.log(chalk.red("Closed"));
569
- } else {
570
- api.log(chalk.green("Open"));
571
- }
572
- }
573
- },
574
- })
575
- .command({
576
- command: "drop <address>",
577
- describe: "Drop a program",
578
- builder: (yargs: any) => {
579
- yargs.positional("address", {
580
- type: "string",
581
- describe: "Program address",
582
- demandOption: true,
583
- });
584
- return yargs;
585
- },
1027
+ return yargs;
1028
+ },
1029
+ handler: async (args) => {
1030
+ // if ends with .tgz assume it is a file
586
1031
 
587
- handler: async (args) => {
588
- for (const api of apis) {
589
- try {
590
- await api.api.program.drop(args.address);
591
- } catch (error: any) {
592
- api.log(
593
- chalk.red(
594
- `Failed to drop ${
595
- args.address
596
- }: ${error.toString()}`
597
- )
598
- );
599
- }
600
- }
601
- },
602
- })
603
- .command({
604
- command: "close <address>",
605
- describe: "Close a program",
606
- builder: (yargs: any) => {
607
- yargs.positional("address", {
608
- type: "string",
609
- describe: "Program address",
610
- demandOption: true,
611
- });
612
- return yargs;
613
- },
1032
+ let installCommand: InstallDependency;
1033
+ const packageName: string = args["package-spec"];
1034
+ if (packageName.endsWith(".tgz")) {
1035
+ const packagePath = path.isAbsolute(packageName)
1036
+ ? packageName
1037
+ : path.join(process.cwd(), packageName);
614
1038
 
615
- handler: async (args) => {
616
- for (const api of apis) {
617
- await api.api.program.close(args.address);
618
- }
619
- },
620
- })
621
- .command({
622
- command: "list",
623
- describe: "List all running programs",
624
- aliases: "ls",
625
- handler: async (args) => {
626
- for (const api of apis) {
627
- const list = await api.api.program.list();
628
- api.log(`Running programs (${list.length}):`);
629
- list.forEach((p) => {
630
- api.log(chalk.green(p));
631
- });
632
- }
633
- },
634
- })
635
-
636
- .command({
637
- command: "open [program]",
638
- describe: "Open program",
639
- builder: (yargs: any) => {
640
- yargs.positional("program", {
641
- type: "string",
642
- describe: "Identifier",
643
- demandOption: true,
644
- });
645
- yargs.option("base64", {
646
- type: "string",
647
- describe: "Base64 encoded serialized",
648
- aliases: "b",
649
- });
650
- yargs.option("variant", {
651
- type: "string",
652
- describe: "Variant name",
653
- aliases: "v",
654
- });
655
- return yargs;
656
- },
657
- handler: async (args) => {
658
- if (!args.base64 && !args.variant) {
659
- throw new Error(
660
- "Either base64 or variant argument needs to be provided"
661
- );
662
- }
663
- let startArg: StartProgram;
664
- if (args.base64) {
665
- startArg = {
666
- base64: args.base64,
667
- };
668
- } else {
669
- startArg = {
670
- variant: args.variant,
671
- };
672
- }
673
- for (const api of apis) {
674
- const address = await api.api.program.open(startArg);
675
- api.log("Started program with address: ");
676
- api.log(chalk.green(address.toString()));
677
- }
678
- },
679
- })
680
- .strict()
681
- .demandCommand();
682
- return yargs;
683
- })
684
- .command({
685
- command: "install <package-spec>",
686
- describe: "install and import a dependency",
687
- builder: (yargs: any) => {
688
- yargs.positional("package-spec", {
689
- type: "string",
690
- describe:
691
- "Installed dependency will be loaded with js import(...)",
692
- demandOption: true,
693
- });
1039
+ const buffer = fs.readFileSync(packagePath);
1040
+ const base64 = toBase64(buffer);
1041
+ installCommand = {
1042
+ type: "tgz",
1043
+ name: await getPackageName(packageName),
1044
+ base64,
1045
+ };
1046
+ } else {
1047
+ installCommand = { type: "npm", name: packageName };
1048
+ }
694
1049
 
695
- return yargs;
696
- },
697
- handler: async (args) => {
698
- // if ends with .tgz assume it is a file
699
-
700
- let installCommand: InstallDependency;
701
- const packageName: string = args["package-spec"];
702
- if (packageName.endsWith(".tgz")) {
703
- const packagePath = path.isAbsolute(packageName)
704
- ? packageName
705
- : path.join(process.cwd(), packageName);
706
-
707
- const buffer = fs.readFileSync(packagePath);
708
- const base64 = toBase64(buffer);
709
- installCommand = {
710
- type: "tgz",
711
- name: await getPackageName(packageName),
712
- base64,
713
- };
714
- } else {
715
- installCommand = { type: "npm", name: packageName };
716
- }
1050
+ for (const api of apis) {
1051
+ const newPrograms = await api.api.dependency.install(
1052
+ installCommand
1053
+ );
1054
+ api.log(
1055
+ `New programs available (${newPrograms.length}):`
1056
+ );
1057
+ newPrograms.forEach((p) => {
1058
+ api.log(chalk.green(p));
1059
+ });
1060
+ }
1061
+ },
1062
+ })
1063
+ .command({
1064
+ command: "restart",
1065
+ describe: "Restart the server",
1066
+ handler: async () => {
1067
+ for (const api of apis) {
1068
+ await api.api.restart();
1069
+ }
1070
+ },
1071
+ })
1072
+ .command({
1073
+ command: "stop",
1074
+ describe: "Stop the server",
1075
+ handler: async () => {
1076
+ for (const api of apis) {
1077
+ await api.api.stop();
1078
+ }
1079
+ },
1080
+ })
1081
+ .help()
1082
+ .strict()
1083
+ .scriptName("")
1084
+ .demandCommand()
1085
+ .showHelpOnFail(true)
1086
+ .exitProcess(false);
717
1087
 
718
- for (const api of apis) {
719
- const newPrograms = await api.api.dependency.install(
720
- installCommand
721
- );
722
- api.log(
723
- `New programs available (${newPrograms.length}):`
724
- );
725
- newPrograms.forEach((p) => {
726
- api.log(chalk.green(p));
727
- });
728
- }
729
- },
730
- })
731
- .command({
732
- command: "restart",
733
- describe: "Restart the server",
734
- handler: async () => {
735
- for (const api of apis) {
736
- await api.api.restart();
737
- }
738
- },
739
- })
740
- .command({
741
- command: "terminate",
742
- describe: "Terminate the server",
743
- handler: async () => {
744
- for (const api of apis) {
745
- await api.api.terminate();
746
- }
747
- },
748
- })
749
- .help()
750
- .strict()
751
- .scriptName("")
752
- .demandCommand()
753
- .showHelpOnFail(true)
754
- .exitProcess(false);
755
- const rl = readline.createInterface({
756
- input: process.stdin,
757
- output: process.stdout,
758
- terminal: true,
759
- historySize: 100,
760
- });
761
- console.log(chalk.green("Connected"));
762
- console.log("Write 'help' to show commands.\n");
763
- const first = true;
764
- rl.prompt(false);
765
- rl.on("line", async (cargs) => {
766
- const cmds = capi();
767
- try {
768
- await cmds.parse(cargs);
769
- } catch (error: any) {
770
- /* console.log(chalk.red("Error parsing command: " + cargs))*/
771
- }
772
- rl.prompt(true);
773
- });
1088
+ rl.on("line", async (cargs) => {
1089
+ const cmds = capi();
1090
+ try {
1091
+ await cmds.parse(cargs);
1092
+ } catch (error: any) {
1093
+ /* console.log(chalk.red("Error parsing command: " + cargs))*/
1094
+ }
1095
+ rl.prompt(true);
1096
+ });
1097
+ }
774
1098
  },
775
1099
  })
776
-
777
1100
  .help()
778
1101
  .strict()
779
1102
  .demandCommand();