@peerbit/server 3.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 (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 +592 -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 +676 -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,167 @@ 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
+ 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
+ })
234
482
  .command({
235
483
  command: "list",
236
484
  aliases: "ls",
@@ -254,11 +502,19 @@ export const cli = async (args?: string[]) => {
254
502
  .reduce((prev, c, i) => {
255
503
  return Math.max(prev, c);
256
504
  }, 0);
505
+
257
506
  const all = await remotes.all();
258
507
  if (all.length > 0) {
508
+ console.log(
509
+ padString("Name", maxNameLength + 10),
510
+ padString("Group" || "", 10),
511
+ "Address"
512
+ );
513
+
259
514
  for (const remote of all) {
260
515
  console.log(
261
516
  padString(remote.name, maxNameLength + 10),
517
+ padString(remote.group || "", 10),
262
518
  remote.address
263
519
  );
264
520
  }
@@ -272,15 +528,21 @@ export const cli = async (args?: string[]) => {
272
528
  describe: "Add remote",
273
529
  builder: (yargs: yargs.Argv) => {
274
530
  yargs
531
+ .positional("name", {
532
+ type: "string",
533
+ describe: "Remote address",
534
+ demandOption: true,
535
+ })
275
536
  .positional("address", {
276
537
  type: "string",
277
538
  describe: "Remote name",
278
539
  demandOption: true,
279
540
  })
280
- .positional("name", {
541
+ .option("group", {
542
+ describe: "Group name",
281
543
  type: "string",
282
- describe: "Remote address",
283
- demandOption: true,
544
+ alias: "g",
545
+ default: DEFAULT_REMOTE_GROUP,
284
546
  })
285
547
  .option("directory", {
286
548
  describe: "Peerbit directory",
@@ -296,10 +558,9 @@ export const cli = async (args?: string[]) => {
296
558
  if (args.name === "localhost") {
297
559
  throw new Error("Remote can not be named 'localhost'");
298
560
  }
299
- const api = await client(
300
- await getKeypair(args.directory),
301
- args.address
302
- );
561
+ const api = await createClient(await getKeypair(args.directory), {
562
+ address: args.address,
563
+ });
303
564
  try {
304
565
  await api.program.list();
305
566
  } catch (error) {
@@ -309,7 +570,11 @@ export const cli = async (args?: string[]) => {
309
570
  fs.mkdirSync(args.directory, { recursive: true });
310
571
  }
311
572
  const remotes = new Remotes(getRemotesPath(args.directory));
312
- remotes.add(args.name, args.address);
573
+ remotes.add({
574
+ name: args.name,
575
+ address: args.address,
576
+ group: args.group,
577
+ });
313
578
  },
314
579
  })
315
580
  .command({
@@ -358,6 +623,18 @@ export const cli = async (args?: string[]) => {
358
623
  demandOption: false,
359
624
  array: true,
360
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
+ })
361
638
  .option("directory", {
362
639
  describe: "Peerbit directory",
363
640
  defaultDescription: "~.peerbit",
@@ -368,412 +645,457 @@ export const cli = async (args?: string[]) => {
368
645
  return yargs;
369
646
  },
370
647
  handler: async (connectArgs) => {
371
- 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
+
372
658
  const apis: {
373
659
  log: (string: string) => void;
374
660
  name: string;
375
- api: Awaited<ReturnType<typeof client>>;
661
+ api: Awaited<ReturnType<typeof createClient>>;
376
662
  }[] = [];
377
- console.log(getRemotesPath(connectArgs.directory));
663
+
378
664
  const config = await import("./config.js");
379
665
  const keypair = await config.getKeypair(connectArgs.directory);
380
666
 
667
+ const selectedRemotes: RemoteObject[] = [];
381
668
  if (names.length > 0) {
382
- const remotes = new Remotes(
383
- getRemotesPath(connectArgs.directory)
384
- );
385
- for (const name of names) {
669
+ for (const [ix, name] of names.entries()) {
386
670
  if (name === "localhost") {
387
- apis.push({
388
- log: (string) => console.log("localhost: " + string),
671
+ selectedRemotes.push({
672
+ address: "http://localhost:" + LOCAL_API_PORT,
389
673
  name: "localhost",
390
- api: await client(keypair),
674
+ group: DEFAULT_REMOTE_GROUP,
391
675
  });
392
676
  } else {
393
677
  const remote = remotes.getByName(name);
678
+
394
679
  if (!remote) {
395
680
  throw new Error("Missing remote with name: " + name);
396
681
  }
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);
682
+
683
+ if (
684
+ connectArgs.group.length > 0 &&
685
+ !connectArgs.group.includes(remote.group)
686
+ ) {
687
+ continue;
402
688
  }
403
689
 
404
- apis.push({
405
- log: logFn,
406
- name,
407
- api: await client(keypair, remote.address),
408
- });
690
+ selectedRemotes.push(remote);
409
691
  }
410
692
  }
411
693
  }
412
694
 
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
- );
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
+ });
421
728
  }
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
- );
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);
445
865
  }
446
- },
447
- })
448
- .strict()
449
- .demandCommand();
450
- return yargs;
451
- })
452
- .command(
453
- "access",
454
- "Modify access control for this node",
455
- (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) => {
456
878
  yargs
457
879
  .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",
880
+ command: "status <address>",
881
+ describe: "Is a program open",
882
+ builder: (yargs: any) => {
883
+ yargs.positional("address", {
463
884
  type: "string",
885
+ describe: "Program address",
464
886
  demandOption: true,
465
887
  });
466
888
  return yargs;
467
889
  },
890
+
468
891
  handler: async (args) => {
469
- const peerId = peerIdFromString(args["peer-id"]);
470
892
  for (const api of apis) {
471
- await api.api.trust.add(peerId);
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
+ }
472
901
  }
473
902
  },
474
903
  })
475
904
  .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",
905
+ command: "drop <address>",
906
+ describe: "Drop a program",
907
+ builder: (yargs: any) => {
908
+ yargs.positional("address", {
909
+ type: "string",
910
+ describe: "Program address",
481
911
  demandOption: true,
482
912
  });
483
913
  return yargs;
484
914
  },
915
+
485
916
  handler: async (args) => {
486
- const peerId = peerIdFromString(args["peer-id"]);
487
917
  for (const api of apis) {
488
- await api.api.trust.remove(peerId);
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);
489
947
  }
490
948
  },
491
949
  })
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
950
  .command({
517
951
  command: "list",
952
+ describe: "List all running programs",
518
953
  aliases: "ls",
519
- 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",
520
968
  builder: (yargs: any) => {
521
- yargs.option("replicate", {
522
- type: "boolean",
523
- describe: "Replicate data on this topic",
524
- aliases: "r",
525
- 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",
526
983
  });
527
984
  return yargs;
528
985
  },
529
986
  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");
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
+ }
541
1009
  },
542
1010
  })
543
1011
  .strict()
544
1012
  .demandCommand();
545
1013
  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
- },
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
+ });
561
1025
 
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
- },
1026
+ return yargs;
1027
+ },
1028
+ handler: async (args) => {
1029
+ // if ends with .tgz assume it is a file
586
1030
 
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
- },
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);
614
1037
 
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
- });
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
+ }
694
1048
 
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
- }
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);
717
1086
 
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
- });
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
+ }
774
1097
  },
775
1098
  })
776
-
777
1099
  .help()
778
1100
  .strict()
779
1101
  .demandCommand();