@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/lib/esm/cli.js CHANGED
@@ -1,22 +1,88 @@
1
1
  import { createTestDomain, getDomainFromConfig, loadConfig, startCertbot, } from "./domain.js";
2
2
  import { startServerWithNode } from "./server.js";
3
- import { createRecord } from "./aws.js";
3
+ import { AWS_LINUX_ARM_AMIs, createRecord, launchNodes, terminateNode, } from "./aws.js";
4
4
  import { getHomeConfigDir, getKeypair, getPackageName, getRemotesPath, } from "./config.js";
5
5
  import chalk from "chalk";
6
- import { client } from "./client.js";
6
+ import { createClient, waitForDomain } from "./client.js";
7
7
  import { exit } from "process";
8
8
  import readline from "readline";
9
9
  import fs from "fs";
10
10
  import path from "path";
11
11
  import { toBase64 } from "@peerbit/crypto";
12
- import { Remotes } from "./remotes.js";
12
+ import { DEFAULT_REMOTE_GROUP, Remotes } from "./remotes.js";
13
13
  import { peerIdFromString } from "@libp2p/peer-id";
14
- const padString = function (string, padding, padChar = " ") {
14
+ import { LOCAL_API_PORT } from "./routes.js";
15
+ const colors = [
16
+ "#00FF00",
17
+ "#0000FF",
18
+ "#FF0000",
19
+ "#01FFFE",
20
+ "#FFA6FE",
21
+ "#FFDB66",
22
+ "#006401",
23
+ "#010067",
24
+ "#95003A",
25
+ "#007DB5",
26
+ "#FF00F6",
27
+ "#FFEEE8",
28
+ "#774D00",
29
+ "#90FB92",
30
+ "#0076FF",
31
+ "#D5FF00",
32
+ "#FF937E",
33
+ "#6A826C",
34
+ "#FF029D",
35
+ "#FE8900",
36
+ "#7A4782",
37
+ "#7E2DD2",
38
+ "#85A900",
39
+ "#FF0056",
40
+ "#A42400",
41
+ "#00AE7E",
42
+ "#683D3B",
43
+ "#BDC6FF",
44
+ "#263400",
45
+ "#BDD393",
46
+ "#00B917",
47
+ "#9E008E",
48
+ "#001544",
49
+ "#C28C9F",
50
+ "#FF74A3",
51
+ "#01D0FF",
52
+ "#004754",
53
+ "#E56FFE",
54
+ "#788231",
55
+ "#0E4CA1",
56
+ "#91D0CB",
57
+ "#BE9970",
58
+ "#968AE8",
59
+ "#BB8800",
60
+ "#43002C",
61
+ "#DEFF74",
62
+ "#00FFC6",
63
+ "#FFE502",
64
+ "#620E00",
65
+ "#008F9C",
66
+ "#98FF52",
67
+ "#7544B1",
68
+ "#B500FF",
69
+ "#00FF78",
70
+ "#FF6E41",
71
+ "#005F39",
72
+ "#6B6882",
73
+ "#5FAD4E",
74
+ "#A75740",
75
+ "#A5FFD2",
76
+ "#FFB167",
77
+ "#009BFF",
78
+ "#E85EBE",
79
+ ];
80
+ const padString = function (string, padding, padChar = " ", stringLength = string.valueOf().length) {
15
81
  const val = string.valueOf();
16
- if (Math.abs(padding) <= val.length) {
82
+ if (Math.abs(padding) <= stringLength) {
17
83
  return val;
18
84
  }
19
- const m = Math.max(Math.abs(padding) - string.length || 0, 0);
85
+ const m = Math.max(Math.abs(padding) - stringLength || 0, 0);
20
86
  const pad = Array(m + 1).join(String(padChar).charAt(0));
21
87
  // var pad = String(c || ' ').charAt(0).repeat(Math.abs(n) - this.length);
22
88
  return padding < 0 ? pad + val : val + pad;
@@ -46,6 +112,13 @@ export const cli = async (args) => {
46
112
  describe: "Whether to connect to bootstap nodes on startup",
47
113
  type: "boolean",
48
114
  default: false,
115
+ })
116
+ .option("grant-access", {
117
+ describe: "Grant access to public keys on start",
118
+ defaultDescription: "The publickey of this device located in 'directory'",
119
+ type: "string",
120
+ array: true,
121
+ alias: "ga",
49
122
  })
50
123
  .option("reset", {
51
124
  describe: "If true, then programs opened during last session will not be opened",
@@ -72,6 +145,7 @@ export const cli = async (args) => {
72
145
  ports: { api: args["port-api"], node: args["port-node"] },
73
146
  bootstrap: args.bootstrap,
74
147
  newSession: args.reset,
148
+ grantAccess: args["grant-access"],
75
149
  });
76
150
  },
77
151
  })
@@ -195,6 +269,154 @@ export const cli = async (args) => {
195
269
  })
196
270
  .command("remote", "Handle remote nodes", (innerYargs) => {
197
271
  innerYargs
272
+ .command("spawn", "Spawn remote nodes", (spawnYargs) => {
273
+ spawnYargs
274
+ .command({
275
+ command: "aws",
276
+ describe: "Spawn remote nodes on AWS",
277
+ builder: (awsArgs) => {
278
+ awsArgs.option("count", {
279
+ describe: "Amount of nodes to spawn",
280
+ defaultDescription: "One node",
281
+ type: "number",
282
+ alias: "c",
283
+ default: 1,
284
+ });
285
+ awsArgs.option("region", {
286
+ describe: "Region",
287
+ type: "string",
288
+ defaultDescription: "Region defined in ~.aws/config",
289
+ choices: Object.keys(AWS_LINUX_ARM_AMIs),
290
+ });
291
+ awsArgs.option("group", {
292
+ describe: "Remote group to launch nodes in",
293
+ type: "string",
294
+ alias: "g",
295
+ default: DEFAULT_REMOTE_GROUP,
296
+ });
297
+ awsArgs.option("size", {
298
+ describe: "Instance size",
299
+ type: "string",
300
+ alias: "s",
301
+ choices: [
302
+ "micro",
303
+ "small",
304
+ "medium",
305
+ "large",
306
+ "xlarge",
307
+ "2xlarge",
308
+ ],
309
+ default: "micro",
310
+ });
311
+ awsArgs.option("name", {
312
+ describe: "Name prefix for spawned nodes",
313
+ type: "string",
314
+ alias: "n",
315
+ default: "peerbit-node",
316
+ });
317
+ awsArgs.option("grant-access", {
318
+ describe: "Grant access to public keys on start",
319
+ defaultDescription: "The publickey of this device located in 'directory'",
320
+ type: "string",
321
+ array: true,
322
+ alias: "ga",
323
+ });
324
+ awsArgs.option("directory", {
325
+ describe: "Peerbit directory",
326
+ defaultDescription: "~.peerbit",
327
+ type: "string",
328
+ alias: "d",
329
+ default: getHomeConfigDir(),
330
+ });
331
+ return awsArgs;
332
+ },
333
+ handler: async (args) => {
334
+ const accessGrant = args.access?.length > 0
335
+ ? args.access.map((x) => peerIdFromString(x))
336
+ : [
337
+ await (await getKeypair(args.directory)).publicKey.toPeerId(),
338
+ ];
339
+ const nodes = await launchNodes({
340
+ email: "marcus@dao.xyz",
341
+ count: args.number,
342
+ namePrefix: args.name,
343
+ region: args.region,
344
+ grantAccess: accessGrant,
345
+ size: args.size,
346
+ });
347
+ console.log(`Waiting for ${args.count} ${args.count > 1 ? "nodes" : "node"} to spawn. This might take a few minutes. You can watch the progress in your AWS console.`);
348
+ const twirlTimer = (function () {
349
+ const P = ["\\", "|", "/", "-"];
350
+ let x = 0;
351
+ return setInterval(function () {
352
+ process.stdout.write("\r" + "Loading: " + chalk.hex(colors[x])(P[x++]));
353
+ x &= 3;
354
+ }, 250);
355
+ })();
356
+ for (const node of nodes) {
357
+ try {
358
+ const domain = await waitForDomain(node.publicIp);
359
+ const remotes = new Remotes(getRemotesPath(args.directory));
360
+ remotes.add({
361
+ name: node.name,
362
+ address: domain,
363
+ group: args.group,
364
+ origin: {
365
+ type: "aws",
366
+ instanceId: node.instanceId,
367
+ region: node.region,
368
+ },
369
+ });
370
+ }
371
+ catch (error) {
372
+ process.stdout.write("\r");
373
+ console.error(`Error waiting for domain for ip: ${node.publicIp} to be available: ${error?.toString()}`);
374
+ }
375
+ }
376
+ process.stdout.write("\r");
377
+ clearInterval(twirlTimer);
378
+ console.log(`New nodes available (${nodes.length}):`);
379
+ for (const node of nodes) {
380
+ console.log(chalk.green(node.name));
381
+ }
382
+ },
383
+ })
384
+ .strict()
385
+ .demandCommand();
386
+ })
387
+ .command({
388
+ command: "terminate [name...]",
389
+ describe: "Terminate remote instances that was previously spawned",
390
+ builder: (killArgs) => {
391
+ killArgs.option("all", {
392
+ describe: "Kill all nodes",
393
+ type: "boolean",
394
+ default: false,
395
+ });
396
+ killArgs.positional("name", {
397
+ type: "string",
398
+ describe: "Remote name",
399
+ default: "localhost",
400
+ demandOption: false,
401
+ array: true,
402
+ });
403
+ return killArgs;
404
+ },
405
+ handler: async (args) => {
406
+ const remotes = new Remotes(getRemotesPath(args.directory));
407
+ const allRemotes = await remotes.all();
408
+ for (const remote of allRemotes) {
409
+ if (args.all || args.name.includes(remote.name)) {
410
+ if (remote.origin?.type === "aws") {
411
+ await terminateNode({
412
+ instanceId: remote.origin.instanceId,
413
+ region: remote.origin.region,
414
+ });
415
+ }
416
+ }
417
+ }
418
+ },
419
+ })
198
420
  .command({
199
421
  command: "list",
200
422
  aliases: "ls",
@@ -219,8 +441,9 @@ export const cli = async (args) => {
219
441
  }, 0);
220
442
  const all = await remotes.all();
221
443
  if (all.length > 0) {
444
+ console.log(padString("Name", maxNameLength + 10), padString("Group" || "", 10), "Address");
222
445
  for (const remote of all) {
223
- console.log(padString(remote.name, maxNameLength + 10), remote.address);
446
+ console.log(padString(remote.name, maxNameLength + 10), padString(remote.group || "", 10), remote.address);
224
447
  }
225
448
  }
226
449
  else {
@@ -233,15 +456,21 @@ export const cli = async (args) => {
233
456
  describe: "Add remote",
234
457
  builder: (yargs) => {
235
458
  yargs
459
+ .positional("name", {
460
+ type: "string",
461
+ describe: "Remote address",
462
+ demandOption: true,
463
+ })
236
464
  .positional("address", {
237
465
  type: "string",
238
466
  describe: "Remote name",
239
467
  demandOption: true,
240
468
  })
241
- .positional("name", {
469
+ .option("group", {
470
+ describe: "Group name",
242
471
  type: "string",
243
- describe: "Remote address",
244
- demandOption: true,
472
+ alias: "g",
473
+ default: DEFAULT_REMOTE_GROUP,
245
474
  })
246
475
  .option("directory", {
247
476
  describe: "Peerbit directory",
@@ -256,7 +485,9 @@ export const cli = async (args) => {
256
485
  if (args.name === "localhost") {
257
486
  throw new Error("Remote can not be named 'localhost'");
258
487
  }
259
- const api = await client(await getKeypair(args.directory), args.address);
488
+ const api = await createClient(await getKeypair(args.directory), {
489
+ address: args.address,
490
+ });
260
491
  try {
261
492
  await api.program.list();
262
493
  }
@@ -267,7 +498,11 @@ export const cli = async (args) => {
267
498
  fs.mkdirSync(args.directory, { recursive: true });
268
499
  }
269
500
  const remotes = new Remotes(getRemotesPath(args.directory));
270
- remotes.add(args.name, args.address);
501
+ remotes.add({
502
+ name: args.name,
503
+ address: args.address,
504
+ group: args.group,
505
+ });
271
506
  },
272
507
  })
273
508
  .command({
@@ -310,6 +545,18 @@ export const cli = async (args) => {
310
545
  default: "localhost",
311
546
  demandOption: false,
312
547
  array: true,
548
+ })
549
+ .option("all", {
550
+ type: "boolean",
551
+ describe: "Connect to all nodes",
552
+ default: false,
553
+ })
554
+ .option("group", {
555
+ type: "string",
556
+ describe: "Remote group name",
557
+ alias: "g",
558
+ default: [],
559
+ array: true,
313
560
  })
314
561
  .option("directory", {
315
562
  describe: "Peerbit directory",
@@ -321,19 +568,24 @@ export const cli = async (args) => {
321
568
  return yargs;
322
569
  },
323
570
  handler: async (connectArgs) => {
324
- const names = connectArgs.name;
571
+ const remotes = new Remotes(getRemotesPath(connectArgs.directory));
572
+ let names = connectArgs.name;
573
+ if (names.length === 0 ||
574
+ connectArgs.all ||
575
+ connectArgs.group.length > 0) {
576
+ names = (await remotes.all()).map((x) => x.name);
577
+ }
325
578
  const apis = [];
326
- console.log(getRemotesPath(connectArgs.directory));
327
579
  const config = await import("./config.js");
328
580
  const keypair = await config.getKeypair(connectArgs.directory);
581
+ const selectedRemotes = [];
329
582
  if (names.length > 0) {
330
- const remotes = new Remotes(getRemotesPath(connectArgs.directory));
331
- for (const name of names) {
583
+ for (const [ix, name] of names.entries()) {
332
584
  if (name === "localhost") {
333
- apis.push({
334
- log: (string) => console.log("localhost: " + string),
585
+ selectedRemotes.push({
586
+ address: "http://localhost:" + LOCAL_API_PORT,
335
587
  name: "localhost",
336
- api: await client(keypair),
588
+ group: DEFAULT_REMOTE_GROUP,
337
589
  });
338
590
  }
339
591
  else {
@@ -341,351 +593,365 @@ export const cli = async (args) => {
341
593
  if (!remote) {
342
594
  throw new Error("Missing remote with name: " + name);
343
595
  }
344
- let logFn;
345
- if (names.length > 0) {
346
- logFn = (string) => console.log(name + ": " + string);
347
- }
348
- else {
349
- logFn = (string) => console.log(string);
596
+ if (connectArgs.group.length > 0 &&
597
+ !connectArgs.group.includes(remote.group)) {
598
+ continue;
350
599
  }
351
- apis.push({
352
- log: logFn,
353
- name,
354
- api: await client(keypair, remote.address),
355
- });
600
+ selectedRemotes.push(remote);
356
601
  }
357
602
  }
358
603
  }
359
- // try if authenticated
360
- for (const api of apis) {
361
- try {
362
- await api.api.program.list();
604
+ const maxNameLength = selectedRemotes
605
+ .map((x) => x.name.length)
606
+ .reduce((prev, c, i) => {
607
+ return Math.max(prev, c);
608
+ }, 0);
609
+ if (selectedRemotes.length === 0) {
610
+ console.log(chalk.red("No remotes matched your connection condition"));
611
+ }
612
+ else {
613
+ console.log(`Connected to (${selectedRemotes.length}):`);
614
+ for (const [ix, remote] of selectedRemotes.entries()) {
615
+ const chalkBg = chalk.bgHex(colors[ix]);
616
+ console.log(chalkBg(remote.name));
617
+ const logFn = (string) => console.log(padString(chalkBg(remote.name), maxNameLength, " ", remote.name.length) +
618
+ ": " +
619
+ string);
620
+ apis.push({
621
+ log: logFn,
622
+ name: remote.name,
623
+ api: await createClient(keypair, remote),
624
+ });
363
625
  }
364
- catch (error) {
365
- throw new Error(`Failed to connect to '${api.name}': ${error?.toString()}`);
626
+ // try if authenticated
627
+ for (const api of apis) {
628
+ try {
629
+ await api.api.program.list();
630
+ }
631
+ catch (error) {
632
+ throw new Error(`Failed to connect to '${api.name}': ${error?.toString()}`);
633
+ }
366
634
  }
367
- }
368
- const capi = () => yargs
369
- .default()
370
- .command("peer", "Peer info", (yargs) => {
371
- yargs
372
- .command({
373
- command: "id",
374
- describe: "Get peer id",
375
- handler: async (args) => {
376
- for (const api of apis) {
377
- api.log((await api.api.peer.id.get()).toString());
378
- }
379
- },
380
- })
381
- .command({
382
- command: "address",
383
- describe: "Get addresses",
384
- handler: async (args) => {
385
- for (const api of apis) {
386
- (await api.api.peer.addresses.get()).forEach((x) => api.log(x.toString()));
387
- }
388
- },
389
- })
390
- .strict()
391
- .demandCommand();
392
- return yargs;
393
- })
394
- .command("access", "Modify access control for this node", (yargs) => {
395
- yargs
396
- .command({
397
- command: "grant <peer-id>",
398
- describe: "Give a peer-id admin capabilities",
399
- builder: (yargs) => {
400
- yargs.positional("peer-id", {
401
- describe: "Peer id",
402
- type: "string",
403
- demandOption: true,
404
- });
405
- return yargs;
406
- },
407
- handler: async (args) => {
408
- const peerId = peerIdFromString(args["peer-id"]);
409
- for (const api of apis) {
410
- await api.api.trust.add(peerId);
411
- }
412
- },
635
+ const rl = readline.createInterface({
636
+ input: process.stdin,
637
+ output: process.stdout,
638
+ terminal: true,
639
+ historySize: 100,
640
+ });
641
+ console.log("Write 'help' to show commands.\n");
642
+ rl.prompt(false);
643
+ const capi = () => yargs
644
+ .default()
645
+ .command("peer", "Peer info", (yargs) => {
646
+ yargs
647
+ .command({
648
+ command: "id",
649
+ describe: "Get peer id",
650
+ handler: async (args) => {
651
+ for (const api of apis) {
652
+ api.log((await api.api.peer.id.get()).toString());
653
+ }
654
+ },
655
+ })
656
+ .command({
657
+ command: "address",
658
+ describe: "Get addresses",
659
+ handler: async (args) => {
660
+ for (const api of apis) {
661
+ (await api.api.peer.addresses.get()).forEach((x) => api.log(x.toString()));
662
+ }
663
+ },
664
+ })
665
+ .strict()
666
+ .demandCommand();
667
+ return yargs;
413
668
  })
414
- .command({
415
- command: "deny <peer-id>",
416
- describe: "Remove admin capabilities from peer-id",
417
- builder: (yargs) => {
418
- yargs.positional("peer-id", {
419
- describe: "Peer id",
420
- demandOption: true,
421
- });
422
- return yargs;
423
- },
424
- handler: async (args) => {
425
- const peerId = peerIdFromString(args["peer-id"]);
426
- for (const api of apis) {
427
- await api.api.trust.remove(peerId);
428
- }
429
- },
669
+ .command("access", "Modify access control for this node", (yargs) => {
670
+ yargs
671
+ .command({
672
+ command: "grant <peer-id>",
673
+ describe: "Give a peer-id admin capabilities",
674
+ builder: (yargs) => {
675
+ yargs.positional("peer-id", {
676
+ describe: "Peer id",
677
+ type: "string",
678
+ demandOption: true,
679
+ });
680
+ return yargs;
681
+ },
682
+ handler: async (args) => {
683
+ const peerId = peerIdFromString(args["peer-id"]);
684
+ for (const api of apis) {
685
+ await api.api.access.allow(peerId);
686
+ }
687
+ },
688
+ })
689
+ .command({
690
+ command: "deny <peer-id>",
691
+ describe: "Remove admin capabilities from peer-id",
692
+ builder: (yargs) => {
693
+ yargs.positional("peer-id", {
694
+ describe: "Peer id",
695
+ demandOption: true,
696
+ });
697
+ return yargs;
698
+ },
699
+ handler: async (args) => {
700
+ const peerId = peerIdFromString(args["peer-id"]);
701
+ for (const api of apis) {
702
+ await api.api.access.deny(peerId);
703
+ }
704
+ },
705
+ })
706
+ .strict()
707
+ .demandCommand();
430
708
  })
431
- .strict()
432
- .demandCommand();
433
- })
434
- .command("network", "Manage network", (yargs) => {
435
- yargs
436
- .command({
437
- command: "bootstrap",
438
- describe: "Connect to bootstrap nodes",
439
- handler: async () => {
440
- for (const api of apis) {
441
- await api.api.network.bootstrap();
442
- }
443
- },
709
+ .command("network", "Manage network", (yargs) => {
710
+ yargs
711
+ .command({
712
+ command: "bootstrap",
713
+ describe: "Connect to bootstrap nodes",
714
+ handler: async () => {
715
+ for (const api of apis) {
716
+ await api.api.network.bootstrap();
717
+ }
718
+ },
719
+ })
720
+ .strict()
721
+ .demandCommand();
444
722
  })
445
- .strict()
446
- .demandCommand();
447
- })
448
- .command("topic", "Manage topics the node is listening to", (yargs) => {
449
- yargs
450
- .command({
451
- command: "list",
452
- aliases: "ls",
453
- describe: "List all topics",
454
- builder: (yargs) => {
455
- yargs.option("replicate", {
456
- type: "boolean",
457
- describe: "Replicate data on this topic",
458
- aliases: "r",
459
- default: false,
460
- });
461
- return yargs;
462
- },
463
- handler: async (args) => {
464
- /* const c = await client();
465
- const topics = await c.topics.get(args.replicate);
466
- if (topics?.length > 0) {
467
- console.log("Topic (" + topics.length + "):");
468
- for (const t of topics) {
469
- console.log(t);
470
- }
471
- } else {
472
- console.log("Not subscribed to any topics");
473
- } */
474
- console.error("Not implemented");
475
- },
723
+ .command("topic", "Manage topics the node is listening to", (yargs) => {
724
+ yargs
725
+ .command({
726
+ command: "list",
727
+ aliases: "ls",
728
+ describe: "List all topics",
729
+ builder: (yargs) => {
730
+ yargs.option("replicate", {
731
+ type: "boolean",
732
+ describe: "Replicate data on this topic",
733
+ aliases: "r",
734
+ default: false,
735
+ });
736
+ return yargs;
737
+ },
738
+ handler: async (args) => {
739
+ /* const c = await client();
740
+ const topics = await c.topics.get(args.replicate);
741
+ if (topics?.length > 0) {
742
+ console.log("Topic (" + topics.length + "):");
743
+ for (const t of topics) {
744
+ console.log(t);
745
+ }
746
+ } else {
747
+ console.log("Not subscribed to any topics");
748
+ } */
749
+ console.error("Not implemented");
750
+ },
751
+ })
752
+ .strict()
753
+ .demandCommand();
754
+ return yargs;
476
755
  })
477
- .strict()
478
- .demandCommand();
479
- return yargs;
480
- })
481
- .command("program", "Manage programs", (yargs) => {
482
- yargs
483
- .command({
484
- command: "status <address>",
485
- describe: "Is a program open",
486
- builder: (yargs) => {
487
- yargs.positional("address", {
488
- type: "string",
489
- describe: "Program address",
490
- demandOption: true,
491
- });
492
- return yargs;
493
- },
494
- handler: async (args) => {
495
- for (const api of apis) {
496
- const program = await api.api.program.has(args.address);
497
- if (!program) {
498
- api.log(chalk.red("Closed"));
756
+ .command("program", "Manage programs", (yargs) => {
757
+ yargs
758
+ .command({
759
+ command: "status <address>",
760
+ describe: "Is a program open",
761
+ builder: (yargs) => {
762
+ yargs.positional("address", {
763
+ type: "string",
764
+ describe: "Program address",
765
+ demandOption: true,
766
+ });
767
+ return yargs;
768
+ },
769
+ handler: async (args) => {
770
+ for (const api of apis) {
771
+ const program = await api.api.program.has(args.address);
772
+ if (!program) {
773
+ api.log(chalk.red("Closed"));
774
+ }
775
+ else {
776
+ api.log(chalk.green("Open"));
777
+ }
499
778
  }
500
- else {
501
- api.log(chalk.green("Open"));
779
+ },
780
+ })
781
+ .command({
782
+ command: "drop <address>",
783
+ describe: "Drop a program",
784
+ builder: (yargs) => {
785
+ yargs.positional("address", {
786
+ type: "string",
787
+ describe: "Program address",
788
+ demandOption: true,
789
+ });
790
+ return yargs;
791
+ },
792
+ handler: async (args) => {
793
+ for (const api of apis) {
794
+ try {
795
+ await api.api.program.drop(args.address);
796
+ }
797
+ catch (error) {
798
+ api.log(chalk.red(`Failed to drop ${args.address}: ${error.toString()}`));
799
+ }
502
800
  }
503
- }
504
- },
505
- })
506
- .command({
507
- command: "drop <address>",
508
- describe: "Drop a program",
509
- builder: (yargs) => {
510
- yargs.positional("address", {
511
- type: "string",
512
- describe: "Program address",
513
- demandOption: true,
514
- });
515
- return yargs;
516
- },
517
- handler: async (args) => {
518
- for (const api of apis) {
519
- try {
520
- await api.api.program.drop(args.address);
801
+ },
802
+ })
803
+ .command({
804
+ command: "close <address>",
805
+ describe: "Close a program",
806
+ builder: (yargs) => {
807
+ yargs.positional("address", {
808
+ type: "string",
809
+ describe: "Program address",
810
+ demandOption: true,
811
+ });
812
+ return yargs;
813
+ },
814
+ handler: async (args) => {
815
+ for (const api of apis) {
816
+ await api.api.program.close(args.address);
521
817
  }
522
- catch (error) {
523
- api.log(chalk.red(`Failed to drop ${args.address}: ${error.toString()}`));
818
+ },
819
+ })
820
+ .command({
821
+ command: "list",
822
+ describe: "List all running programs",
823
+ aliases: "ls",
824
+ handler: async (args) => {
825
+ for (const api of apis) {
826
+ const list = await api.api.program.list();
827
+ api.log(`Running programs (${list.length}):`);
828
+ list.forEach((p) => {
829
+ api.log(chalk.green(p));
830
+ });
524
831
  }
525
- }
526
- },
832
+ },
833
+ })
834
+ .command({
835
+ command: "open [program]",
836
+ describe: "Open program",
837
+ builder: (yargs) => {
838
+ yargs.positional("program", {
839
+ type: "string",
840
+ describe: "Identifier",
841
+ demandOption: true,
842
+ });
843
+ yargs.option("base64", {
844
+ type: "string",
845
+ describe: "Base64 encoded serialized",
846
+ aliases: "b",
847
+ });
848
+ yargs.option("variant", {
849
+ type: "string",
850
+ describe: "Variant name",
851
+ aliases: "v",
852
+ });
853
+ return yargs;
854
+ },
855
+ handler: async (args) => {
856
+ if (!args.base64 && !args.variant) {
857
+ throw new Error("Either base64 or variant argument needs to be provided");
858
+ }
859
+ let startArg;
860
+ if (args.base64) {
861
+ startArg = {
862
+ base64: args.base64,
863
+ };
864
+ }
865
+ else {
866
+ startArg = {
867
+ variant: args.variant,
868
+ };
869
+ }
870
+ for (const api of apis) {
871
+ const address = await api.api.program.open(startArg);
872
+ api.log("Started program with address: ");
873
+ api.log(chalk.green(address.toString()));
874
+ }
875
+ },
876
+ })
877
+ .strict()
878
+ .demandCommand();
879
+ return yargs;
527
880
  })
528
881
  .command({
529
- command: "close <address>",
530
- describe: "Close a program",
882
+ command: "install <package-spec>",
883
+ describe: "install and import a dependency",
531
884
  builder: (yargs) => {
532
- yargs.positional("address", {
885
+ yargs.positional("package-spec", {
533
886
  type: "string",
534
- describe: "Program address",
887
+ describe: "Installed dependency will be loaded with js import(...)",
535
888
  demandOption: true,
536
889
  });
537
890
  return yargs;
538
891
  },
539
892
  handler: async (args) => {
893
+ // if ends with .tgz assume it is a file
894
+ let installCommand;
895
+ const packageName = args["package-spec"];
896
+ if (packageName.endsWith(".tgz")) {
897
+ const packagePath = path.isAbsolute(packageName)
898
+ ? packageName
899
+ : path.join(process.cwd(), packageName);
900
+ const buffer = fs.readFileSync(packagePath);
901
+ const base64 = toBase64(buffer);
902
+ installCommand = {
903
+ type: "tgz",
904
+ name: await getPackageName(packageName),
905
+ base64,
906
+ };
907
+ }
908
+ else {
909
+ installCommand = { type: "npm", name: packageName };
910
+ }
540
911
  for (const api of apis) {
541
- await api.api.program.close(args.address);
912
+ const newPrograms = await api.api.dependency.install(installCommand);
913
+ api.log(`New programs available (${newPrograms.length}):`);
914
+ newPrograms.forEach((p) => {
915
+ api.log(chalk.green(p));
916
+ });
542
917
  }
543
918
  },
544
919
  })
545
920
  .command({
546
- command: "list",
547
- describe: "List all running programs",
548
- aliases: "ls",
549
- handler: async (args) => {
921
+ command: "restart",
922
+ describe: "Restart the server",
923
+ handler: async () => {
550
924
  for (const api of apis) {
551
- const list = await api.api.program.list();
552
- api.log(`Running programs (${list.length}):`);
553
- list.forEach((p) => {
554
- api.log(chalk.green(p));
555
- });
925
+ await api.api.restart();
556
926
  }
557
927
  },
558
928
  })
559
929
  .command({
560
- command: "open [program]",
561
- describe: "Open program",
562
- builder: (yargs) => {
563
- yargs.positional("program", {
564
- type: "string",
565
- describe: "Identifier",
566
- demandOption: true,
567
- });
568
- yargs.option("base64", {
569
- type: "string",
570
- describe: "Base64 encoded serialized",
571
- aliases: "b",
572
- });
573
- yargs.option("variant", {
574
- type: "string",
575
- describe: "Variant name",
576
- aliases: "v",
577
- });
578
- return yargs;
579
- },
580
- handler: async (args) => {
581
- if (!args.base64 && !args.variant) {
582
- throw new Error("Either base64 or variant argument needs to be provided");
583
- }
584
- let startArg;
585
- if (args.base64) {
586
- startArg = {
587
- base64: args.base64,
588
- };
589
- }
590
- else {
591
- startArg = {
592
- variant: args.variant,
593
- };
594
- }
930
+ command: "stop",
931
+ describe: "Stop the server",
932
+ handler: async () => {
595
933
  for (const api of apis) {
596
- const address = await api.api.program.open(startArg);
597
- api.log("Started program with address: ");
598
- api.log(chalk.green(address.toString()));
934
+ await api.api.stop();
599
935
  }
600
936
  },
601
937
  })
938
+ .help()
602
939
  .strict()
603
- .demandCommand();
604
- return yargs;
605
- })
606
- .command({
607
- command: "install <package-spec>",
608
- describe: "install and import a dependency",
609
- builder: (yargs) => {
610
- yargs.positional("package-spec", {
611
- type: "string",
612
- describe: "Installed dependency will be loaded with js import(...)",
613
- demandOption: true,
614
- });
615
- return yargs;
616
- },
617
- handler: async (args) => {
618
- // if ends with .tgz assume it is a file
619
- let installCommand;
620
- const packageName = args["package-spec"];
621
- if (packageName.endsWith(".tgz")) {
622
- const packagePath = path.isAbsolute(packageName)
623
- ? packageName
624
- : path.join(process.cwd(), packageName);
625
- const buffer = fs.readFileSync(packagePath);
626
- const base64 = toBase64(buffer);
627
- installCommand = {
628
- type: "tgz",
629
- name: await getPackageName(packageName),
630
- base64,
631
- };
940
+ .scriptName("")
941
+ .demandCommand()
942
+ .showHelpOnFail(true)
943
+ .exitProcess(false);
944
+ rl.on("line", async (cargs) => {
945
+ const cmds = capi();
946
+ try {
947
+ await cmds.parse(cargs);
632
948
  }
633
- else {
634
- installCommand = { type: "npm", name: packageName };
949
+ catch (error) {
950
+ /* console.log(chalk.red("Error parsing command: " + cargs))*/
635
951
  }
636
- for (const api of apis) {
637
- const newPrograms = await api.api.dependency.install(installCommand);
638
- api.log(`New programs available (${newPrograms.length}):`);
639
- newPrograms.forEach((p) => {
640
- api.log(chalk.green(p));
641
- });
642
- }
643
- },
644
- })
645
- .command({
646
- command: "restart",
647
- describe: "Restart the server",
648
- handler: async () => {
649
- for (const api of apis) {
650
- await api.api.restart();
651
- }
652
- },
653
- })
654
- .command({
655
- command: "terminate",
656
- describe: "Terminate the server",
657
- handler: async () => {
658
- for (const api of apis) {
659
- await api.api.terminate();
660
- }
661
- },
662
- })
663
- .help()
664
- .strict()
665
- .scriptName("")
666
- .demandCommand()
667
- .showHelpOnFail(true)
668
- .exitProcess(false);
669
- const rl = readline.createInterface({
670
- input: process.stdin,
671
- output: process.stdout,
672
- terminal: true,
673
- historySize: 100,
674
- });
675
- console.log(chalk.green("Connected"));
676
- console.log("Write 'help' to show commands.\n");
677
- const first = true;
678
- rl.prompt(false);
679
- rl.on("line", async (cargs) => {
680
- const cmds = capi();
681
- try {
682
- await cmds.parse(cargs);
683
- }
684
- catch (error) {
685
- /* console.log(chalk.red("Error parsing command: " + cargs))*/
686
- }
687
- rl.prompt(true);
688
- });
952
+ rl.prompt(true);
953
+ });
954
+ }
689
955
  },
690
956
  })
691
957
  .help()