@peerbit/server 1.1.2 → 3.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 (54) hide show
  1. package/lib/esm/cli.js +518 -144
  2. package/lib/esm/cli.js.map +1 -1
  3. package/lib/esm/client.d.ts +11 -3
  4. package/lib/esm/client.js +61 -27
  5. package/lib/esm/client.js.map +1 -1
  6. package/lib/esm/config.d.ts +8 -5
  7. package/lib/esm/config.js +44 -19
  8. package/lib/esm/config.js.map +1 -1
  9. package/lib/esm/peerbit.d.ts +4 -0
  10. package/lib/esm/peerbit.js +15 -2
  11. package/lib/esm/peerbit.js.map +1 -1
  12. package/lib/esm/remotes.browser.d.ts +0 -0
  13. package/lib/esm/remotes.browser.js +3 -0
  14. package/lib/esm/remotes.browser.js.map +1 -0
  15. package/lib/esm/remotes.d.ts +16 -0
  16. package/lib/esm/remotes.js +51 -0
  17. package/lib/esm/remotes.js.map +1 -0
  18. package/lib/esm/routes.d.ts +3 -0
  19. package/lib/esm/routes.js +3 -0
  20. package/lib/esm/routes.js.map +1 -1
  21. package/lib/esm/server.d.ts +14 -4
  22. package/lib/esm/server.js +297 -144
  23. package/lib/esm/server.js.map +1 -1
  24. package/lib/esm/session.d.ts +19 -0
  25. package/lib/esm/session.js +49 -0
  26. package/lib/esm/session.js.map +1 -0
  27. package/lib/esm/signes-request.d.ts +5 -0
  28. package/lib/esm/signes-request.js +54 -0
  29. package/lib/esm/signes-request.js.map +1 -0
  30. package/lib/esm/trust.browser.d.ts +0 -0
  31. package/lib/esm/trust.browser.js +3 -0
  32. package/lib/esm/trust.browser.js.map +1 -0
  33. package/lib/esm/trust.d.ts +9 -0
  34. package/lib/esm/trust.js +36 -0
  35. package/lib/esm/trust.js.map +1 -0
  36. package/lib/esm/types.d.ts +10 -0
  37. package/lib/ui/assets/index-cac7195d.js +77 -0
  38. package/lib/ui/index.html +1 -1
  39. package/package.json +9 -5
  40. package/src/cli.ts +705 -271
  41. package/src/client.ts +105 -30
  42. package/src/config.ts +52 -25
  43. package/src/peerbit.ts +27 -3
  44. package/src/remotes.browser.ts +1 -0
  45. package/src/remotes.ts +63 -0
  46. package/src/routes.ts +3 -1
  47. package/src/server.ts +381 -190
  48. package/src/session.ts +69 -0
  49. package/src/signes-request.ts +84 -0
  50. package/src/trust.browser.ts +1 -0
  51. package/src/trust.ts +39 -0
  52. package/src/types.ts +13 -0
  53. package/lib/ui/assets/config.browser-4ed993c7.js +0 -1
  54. package/lib/ui/assets/index-a8188422.js +0 -53
package/lib/esm/cli.js CHANGED
@@ -1,9 +1,27 @@
1
1
  import { createTestDomain, getDomainFromConfig, loadConfig, startCertbot, } from "./domain.js";
2
2
  import { startServerWithNode } from "./server.js";
3
3
  import { createRecord } from "./aws.js";
4
- import { getHomeConfigDir } from "./config.js";
4
+ import { getHomeConfigDir, getKeypair, getPackageName, getRemotesPath, } from "./config.js";
5
5
  import chalk from "chalk";
6
6
  import { client } from "./client.js";
7
+ import { exit } from "process";
8
+ import readline from "readline";
9
+ import fs from "fs";
10
+ import path from "path";
11
+ import { toBase64 } from "@peerbit/crypto";
12
+ import { Remotes } from "./remotes.js";
13
+ import { peerIdFromString } from "@libp2p/peer-id";
14
+ const padString = function (string, padding, padChar = " ") {
15
+ const val = string.valueOf();
16
+ if (Math.abs(padding) <= val.length) {
17
+ return val;
18
+ }
19
+ const m = Math.max(Math.abs(padding) - string.length || 0, 0);
20
+ const pad = Array(m + 1).join(String(padChar).charAt(0));
21
+ // var pad = String(c || ' ').charAt(0).repeat(Math.abs(n) - this.length);
22
+ return padding < 0 ? pad + val : val + pad;
23
+ // return (n < 0) ? val + pad : pad + val;
24
+ };
7
25
  export const cli = async (args) => {
8
26
  const yargs = await import("yargs");
9
27
  if (!args) {
@@ -15,28 +33,67 @@ export const cli = async (args) => {
15
33
  .command({
16
34
  command: "start",
17
35
  describe: "Start node",
18
- builder: {
19
- directory: {
20
- describe: "Directory for all data created by the node",
36
+ builder: (yargs) => {
37
+ yargs
38
+ .option("directory", {
39
+ describe: "Peerbit directory",
21
40
  defaultDescription: "~.peerbit",
22
41
  type: "string",
23
- default: await getHomeConfigDir(),
24
- },
25
- bootstrap: {
42
+ alias: "d",
43
+ default: getHomeConfigDir(),
44
+ })
45
+ .option("bootstrap", {
26
46
  describe: "Whether to connect to bootstap nodes on startup",
27
47
  type: "boolean",
28
48
  default: false,
29
- },
49
+ })
50
+ .option("reset", {
51
+ describe: "If true, then programs opened during last session will not be opened",
52
+ type: "boolean",
53
+ default: false,
54
+ alias: "r",
55
+ })
56
+ .option("port-api", {
57
+ describe: "Set API server port. Only modify this when testing locally, since NGINX config depends on the default value",
58
+ type: "number",
59
+ default: undefined,
60
+ })
61
+ .option("port-node", {
62
+ describe: "Set Libp2p listen port. Only modify this when testing locally, since NGINX config depends on the default value",
63
+ type: "number",
64
+ default: undefined,
65
+ });
66
+ return yargs;
30
67
  },
31
68
  handler: async (args) => {
32
69
  await startServerWithNode({
33
70
  directory: args.directory,
34
71
  domain: await loadConfig().then((config) => config ? getDomainFromConfig(config) : undefined),
72
+ ports: { api: args["port-api"], node: args["port-node"] },
35
73
  bootstrap: args.bootstrap,
74
+ newSession: args.reset,
36
75
  });
37
76
  },
38
77
  })
39
- .command("domain", "Setup a domain and certificate", (yargs) => {
78
+ .command({
79
+ command: "id",
80
+ describe: "Get peer id",
81
+ builder: (yargs) => {
82
+ yargs.option("directory", {
83
+ describe: "Peerbit directory",
84
+ defaultDescription: "~.peerbit",
85
+ type: "string",
86
+ alias: "d",
87
+ default: getHomeConfigDir(),
88
+ });
89
+ return yargs;
90
+ },
91
+ handler: async (args) => {
92
+ const kp = await getKeypair(args.directory);
93
+ console.log((await kp.toPeerId()).toString());
94
+ },
95
+ })
96
+ .command("domain", "Setup a domain and certificate for this node", (yargs) => {
40
97
  yargs
41
98
  .command({
42
99
  command: "test",
@@ -62,7 +119,6 @@ export const cli = async (args) => {
62
119
  handler: async (args) => {
63
120
  const domain = await createTestDomain();
64
121
  await startCertbot(domain, args.email, args.outdir, args.wait);
65
- const { exit } = await import("process");
66
122
  exit();
67
123
  },
68
124
  })
@@ -131,193 +187,511 @@ export const cli = async (args) => {
131
187
  : undefined,
132
188
  });
133
189
  await startCertbot(args.domain, args.email, args.outdir, args.wait);
134
- const { exit } = await import("process");
135
190
  exit();
136
191
  },
137
192
  })
138
193
  .strict()
139
194
  .demandCommand();
140
195
  })
141
- .command("network", "Manage network", (yargs) => {
142
- yargs
143
- .command({
144
- command: "bootstrap",
145
- describe: "Connect to bootstrap nodes",
146
- handler: async () => {
147
- const c = await client();
148
- await c.network.bootstrap();
149
- },
150
- })
151
- .strict()
152
- .demandCommand();
153
- })
154
- .command("topic", "Manage topics the node is listening to", (yargs) => {
155
- yargs
196
+ .command("remote", "Handle remote nodes", (innerYargs) => {
197
+ innerYargs
156
198
  .command({
157
199
  command: "list",
158
200
  aliases: "ls",
159
- describe: "List all topics",
160
- builder: (yargs) => {
161
- yargs.option("replicate", {
162
- type: "boolean",
163
- describe: "Replicate data on this topic",
164
- alias: "r",
165
- default: false,
166
- });
167
- return yargs;
168
- },
169
- handler: async (args) => {
170
- /* const c = await client();
171
- const topics = await c.topics.get(args.replicate);
172
- if (topics?.length > 0) {
173
- console.log("Topic (" + topics.length + "):");
174
- for (const t of topics) {
175
- console.log(t);
176
- }
177
- } else {
178
- console.log("Not subscribed to any topics");
179
- } */
180
- console.error("Not implemented");
181
- },
182
- })
183
- .strict()
184
- .demandCommand();
185
- return yargs;
186
- })
187
- .command("program", "Manage programs", (yargs) => {
188
- yargs
189
- .command({
190
- command: "status <address>",
191
- describe: "Is a program open",
201
+ describe: "List remotes",
192
202
  builder: (yargs) => {
193
- yargs.positional("address", {
203
+ yargs.option("directory", {
204
+ describe: "Peerbit directory",
205
+ defaultDescription: "~.peerbit",
194
206
  type: "string",
195
- describe: "Program address",
196
- demandOption: true,
207
+ alias: "d",
208
+ default: getHomeConfigDir(),
197
209
  });
198
210
  return yargs;
199
211
  },
200
212
  handler: async (args) => {
201
- const c = await client();
202
- const program = await c.program.has(args.address);
203
- if (!program) {
204
- console.log(chalk.red("Closed"));
213
+ const remotes = new Remotes(getRemotesPath(args.directory));
214
+ const allRemotes = await remotes.all();
215
+ const maxNameLength = allRemotes
216
+ .map((x) => x.name.length)
217
+ .reduce((prev, c, i) => {
218
+ return Math.max(prev, c);
219
+ }, 0);
220
+ const all = await remotes.all();
221
+ if (all.length > 0) {
222
+ for (const remote of all) {
223
+ console.log(padString(remote.name, maxNameLength + 10), remote.address);
224
+ }
205
225
  }
206
226
  else {
207
- console.log(chalk.green("Open"));
227
+ console.log("No remotes found!");
208
228
  }
209
229
  },
210
230
  })
211
231
  .command({
212
- command: "drop <address>",
213
- describe: "Drop a program",
232
+ command: "add <name> <address>",
233
+ describe: "Add remote",
214
234
  builder: (yargs) => {
215
- yargs.positional("address", {
235
+ yargs
236
+ .positional("address", {
216
237
  type: "string",
217
- describe: "Program address",
238
+ describe: "Remote name",
218
239
  demandOption: true,
240
+ })
241
+ .positional("name", {
242
+ type: "string",
243
+ describe: "Remote address",
244
+ demandOption: true,
245
+ })
246
+ .option("directory", {
247
+ describe: "Peerbit directory",
248
+ defaultDescription: "~.peerbit",
249
+ type: "string",
250
+ alias: "d",
251
+ default: getHomeConfigDir(),
219
252
  });
220
253
  return yargs;
221
254
  },
222
255
  handler: async (args) => {
223
- const c = await client();
224
- await c.program.drop(args.address);
256
+ if (args.name === "localhost") {
257
+ throw new Error("Remote can not be named 'localhost'");
258
+ }
259
+ const api = await client(await getKeypair(args.directory), args.address);
260
+ try {
261
+ await api.program.list();
262
+ }
263
+ catch (error) {
264
+ throw new Error("Failed to add remote: " + error?.toString());
265
+ }
266
+ if (!fs.existsSync(args.directory)) {
267
+ fs.mkdirSync(args.directory, { recursive: true });
268
+ }
269
+ const remotes = new Remotes(getRemotesPath(args.directory));
270
+ remotes.add(args.name, args.address);
225
271
  },
226
272
  })
227
273
  .command({
228
- command: "close <address>",
229
- describe: "Close a program",
274
+ command: "remove <name>",
275
+ describe: "Remove a remote",
230
276
  builder: (yargs) => {
231
- yargs.positional("address", {
277
+ yargs
278
+ .positional("name", {
232
279
  type: "string",
233
- describe: "Program address",
280
+ describe: "Remote address",
234
281
  demandOption: true,
282
+ })
283
+ .option("directory", {
284
+ describe: "Peerbit directory",
285
+ defaultDescription: "~.peerbit",
286
+ type: "string",
287
+ alias: "d",
288
+ default: getHomeConfigDir(),
235
289
  });
236
290
  return yargs;
237
291
  },
238
292
  handler: async (args) => {
239
- const c = await client();
240
- await c.program.close(args.address);
241
- },
242
- })
243
- .command({
244
- command: "list",
245
- describe: "List all running programs",
246
- aliases: "ls",
247
- handler: async (args) => {
248
- const c = await client();
249
- const list = await c.program.list();
250
- console.log(`Running programs (${list.length}):`);
251
- list.forEach((p) => {
252
- console.log(chalk.green(p));
253
- });
293
+ const remotes = new Remotes(getRemotesPath(args.directory));
294
+ if (remotes.remove(args.name)) {
295
+ console.log(chalk.green("Removed remote with name: " + args.name));
296
+ }
297
+ else {
298
+ console.log(chalk.red("Did not find any remote with name: " + args.name));
299
+ }
254
300
  },
255
301
  })
256
302
  .command({
257
- command: "open [program]",
258
- describe: "Open program",
303
+ command: "connect [name...]",
304
+ describe: "Connect to remote(s)",
259
305
  builder: (yargs) => {
260
- yargs.positional("program", {
306
+ yargs
307
+ .positional("name", {
261
308
  type: "string",
262
- describe: "Identifier",
263
- demandOption: true,
264
- });
265
- yargs.option("base64", {
309
+ describe: "Remote name",
310
+ default: "localhost",
311
+ demandOption: false,
312
+ array: true,
313
+ })
314
+ .option("directory", {
315
+ describe: "Peerbit directory",
316
+ defaultDescription: "~.peerbit",
266
317
  type: "string",
267
- describe: "Base64 encoded serialized",
268
- alias: "b",
269
- });
270
- yargs.option("variant", {
271
- type: "string",
272
- describe: "Variant name",
273
- alias: "v",
318
+ alias: "d",
319
+ default: getHomeConfigDir(),
274
320
  });
275
321
  return yargs;
276
322
  },
277
- handler: async (args) => {
278
- const c = await client();
279
- if (!args.base64 && !args.variant) {
280
- throw new Error("Either base64 or variant argument needs to be provided");
281
- }
282
- let startArg;
283
- if (args.base64) {
284
- startArg = {
285
- base64: args.base64,
286
- };
323
+ handler: async (connectArgs) => {
324
+ const names = connectArgs.name;
325
+ const apis = [];
326
+ console.log(getRemotesPath(connectArgs.directory));
327
+ const config = await import("./config.js");
328
+ const keypair = await config.getKeypair(connectArgs.directory);
329
+ if (names.length > 0) {
330
+ const remotes = new Remotes(getRemotesPath(connectArgs.directory));
331
+ for (const name of names) {
332
+ if (name === "localhost") {
333
+ apis.push({
334
+ log: (string) => console.log("localhost: " + string),
335
+ name: "localhost",
336
+ api: await client(keypair),
337
+ });
338
+ }
339
+ else {
340
+ const remote = remotes.getByName(name);
341
+ if (!remote) {
342
+ throw new Error("Missing remote with name: " + name);
343
+ }
344
+ let logFn;
345
+ if (names.length > 0) {
346
+ logFn = (string) => console.log(name + ": " + string);
347
+ }
348
+ else {
349
+ logFn = (string) => console.log(string);
350
+ }
351
+ apis.push({
352
+ log: logFn,
353
+ name,
354
+ api: await client(keypair, remote.address),
355
+ });
356
+ }
357
+ }
287
358
  }
288
- else {
289
- startArg = {
290
- variant: args.variant,
291
- };
359
+ // try if authenticated
360
+ for (const api of apis) {
361
+ try {
362
+ await api.api.program.list();
363
+ }
364
+ catch (error) {
365
+ throw new Error(`Failed to connect to '${api.name}': ${error?.toString()}`);
366
+ }
292
367
  }
293
- const address = await c.program.open(startArg);
294
- console.log("Started program with address: ");
295
- console.log(chalk.green(address.toString()));
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
+ },
413
+ })
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
+ },
430
+ })
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
+ },
444
+ })
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
+ },
476
+ })
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"));
499
+ }
500
+ else {
501
+ api.log(chalk.green("Open"));
502
+ }
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);
521
+ }
522
+ catch (error) {
523
+ api.log(chalk.red(`Failed to drop ${args.address}: ${error.toString()}`));
524
+ }
525
+ }
526
+ },
527
+ })
528
+ .command({
529
+ command: "close <address>",
530
+ describe: "Close a program",
531
+ builder: (yargs) => {
532
+ yargs.positional("address", {
533
+ type: "string",
534
+ describe: "Program address",
535
+ demandOption: true,
536
+ });
537
+ return yargs;
538
+ },
539
+ handler: async (args) => {
540
+ for (const api of apis) {
541
+ await api.api.program.close(args.address);
542
+ }
543
+ },
544
+ })
545
+ .command({
546
+ command: "list",
547
+ describe: "List all running programs",
548
+ aliases: "ls",
549
+ handler: async (args) => {
550
+ 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
+ });
556
+ }
557
+ },
558
+ })
559
+ .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
+ }
595
+ 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()));
599
+ }
600
+ },
601
+ })
602
+ .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
+ };
632
+ }
633
+ else {
634
+ installCommand = { type: "npm", name: packageName };
635
+ }
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
+ });
296
689
  },
297
690
  })
691
+ .help()
298
692
  .strict()
299
693
  .demandCommand();
300
- return yargs;
301
- })
302
- .command({
303
- command: "install <package-spec>",
304
- describe: "install and import a dependency",
305
- builder: (yargs) => {
306
- yargs.positional("package-spec", {
307
- type: "string",
308
- describe: "Installed dependency will be loaded with js import(...)",
309
- demandOption: true,
310
- });
311
- return yargs;
312
- },
313
- handler: async (args) => {
314
- const c = await client();
315
- const newPrograms = await c.dependency.install(args["package-spec"]);
316
- console.log(`New programs available (${newPrograms.length}):`);
317
- newPrograms.forEach((p) => {
318
- console.log(chalk.green(p));
319
- });
320
- },
694
+ return innerYargs;
321
695
  })
322
696
  .help()
323
697
  .strict()