@peerbit/server 2.0.0 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/esm/aws.browser.d.ts +0 -0
- package/lib/esm/aws.browser.js +3 -0
- package/lib/esm/aws.browser.js.map +1 -0
- package/lib/esm/aws.d.ts +19 -0
- package/lib/esm/aws.js +185 -1
- package/lib/esm/aws.js.map +1 -1
- package/lib/esm/cli.js +614 -299
- package/lib/esm/cli.js.map +1 -1
- package/lib/esm/client.d.ts +13 -1
- package/lib/esm/client.js +101 -31
- package/lib/esm/client.js.map +1 -1
- package/lib/esm/config.d.ts +3 -3
- package/lib/esm/config.js +18 -16
- package/lib/esm/config.js.map +1 -1
- package/lib/esm/docker.browser.d.ts +0 -0
- package/lib/esm/docker.browser.js +3 -0
- package/lib/esm/docker.browser.js.map +1 -0
- package/lib/esm/domain.js +1 -1
- package/lib/esm/domain.js.map +1 -1
- package/lib/esm/peerbit.d.ts +2 -0
- package/lib/esm/peerbit.js +1 -0
- package/lib/esm/peerbit.js.map +1 -1
- package/lib/esm/remotes.d.ts +15 -3
- package/lib/esm/remotes.js +8 -9
- package/lib/esm/remotes.js.map +1 -1
- package/lib/esm/routes.d.ts +4 -2
- package/lib/esm/routes.js +6 -4
- package/lib/esm/routes.js.map +1 -1
- package/lib/esm/server.d.ts +5 -7
- package/lib/esm/server.js +183 -185
- package/lib/esm/server.js.map +1 -1
- package/lib/esm/signes-request.d.ts +5 -0
- package/lib/esm/signes-request.js +54 -0
- package/lib/esm/signes-request.js.map +1 -0
- package/lib/esm/trust.browser.d.ts +0 -0
- package/lib/esm/trust.browser.js +3 -0
- package/lib/esm/trust.browser.js.map +1 -0
- package/lib/esm/trust.d.ts +9 -0
- package/lib/esm/trust.js +36 -0
- package/lib/esm/trust.js.map +1 -0
- package/lib/ui/assets/aws.browser-4ed993c7.js +1 -0
- package/lib/ui/assets/index-5ed0229d.js +77 -0
- package/lib/ui/index.html +1 -1
- package/package.json +13 -7
- package/src/aws.browser.ts +1 -0
- package/src/aws.ts +250 -1
- package/src/cli.ts +726 -348
- package/src/client.ts +145 -38
- package/src/config.ts +21 -23
- package/src/docker.browser.ts +1 -0
- package/src/domain.ts +1 -1
- package/src/peerbit.ts +3 -0
- package/src/remotes.ts +24 -12
- package/src/routes.ts +6 -5
- package/src/server.ts +238 -254
- package/src/signes-request.ts +84 -0
- package/src/trust.browser.ts +1 -0
- package/src/trust.ts +39 -0
- package/lib/ui/assets/index-73eaa3bc.js +0 -53
package/lib/esm/cli.js
CHANGED
|
@@ -1,21 +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";
|
|
4
|
-
import { getHomeConfigDir, getPackageName, getRemotesPath } from "./config.js";
|
|
3
|
+
import { AWS_LINUX_ARM_AMIs, createRecord, launchNodes, terminateNode, } from "./aws.js";
|
|
4
|
+
import { getHomeConfigDir, getKeypair, getPackageName, getRemotesPath, } from "./config.js";
|
|
5
5
|
import chalk from "chalk";
|
|
6
|
-
import {
|
|
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";
|
|
13
|
-
|
|
12
|
+
import { DEFAULT_REMOTE_GROUP, Remotes } from "./remotes.js";
|
|
13
|
+
import { peerIdFromString } from "@libp2p/peer-id";
|
|
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) {
|
|
14
81
|
const val = string.valueOf();
|
|
15
|
-
if (Math.abs(padding) <=
|
|
82
|
+
if (Math.abs(padding) <= stringLength) {
|
|
16
83
|
return val;
|
|
17
84
|
}
|
|
18
|
-
const m = Math.max(Math.abs(padding) -
|
|
85
|
+
const m = Math.max(Math.abs(padding) - stringLength || 0, 0);
|
|
19
86
|
const pad = Array(m + 1).join(String(padChar).charAt(0));
|
|
20
87
|
// var pad = String(c || ' ').charAt(0).repeat(Math.abs(n) - this.length);
|
|
21
88
|
return padding < 0 ? pad + val : val + pad;
|
|
@@ -45,18 +112,19 @@ export const cli = async (args) => {
|
|
|
45
112
|
describe: "Whether to connect to bootstap nodes on startup",
|
|
46
113
|
type: "boolean",
|
|
47
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",
|
|
48
122
|
})
|
|
49
123
|
.option("reset", {
|
|
50
124
|
describe: "If true, then programs opened during last session will not be opened",
|
|
51
125
|
type: "boolean",
|
|
52
126
|
default: false,
|
|
53
127
|
alias: "r",
|
|
54
|
-
})
|
|
55
|
-
.option("password", {
|
|
56
|
-
describe: "Setup password so you can interact with the node remotely",
|
|
57
|
-
type: "string",
|
|
58
|
-
defaultDescription: "The password from the last session will be used or a password will be generated",
|
|
59
|
-
default: undefined,
|
|
60
128
|
})
|
|
61
129
|
.option("port-api", {
|
|
62
130
|
describe: "Set API server port. Only modify this when testing locally, since NGINX config depends on the default value",
|
|
@@ -76,9 +144,27 @@ export const cli = async (args) => {
|
|
|
76
144
|
domain: await loadConfig().then((config) => config ? getDomainFromConfig(config) : undefined),
|
|
77
145
|
ports: { api: args["port-api"], node: args["port-node"] },
|
|
78
146
|
bootstrap: args.bootstrap,
|
|
79
|
-
password: args.password,
|
|
80
147
|
newSession: args.reset,
|
|
148
|
+
grantAccess: args["grant-access"],
|
|
149
|
+
});
|
|
150
|
+
},
|
|
151
|
+
})
|
|
152
|
+
.command({
|
|
153
|
+
command: "id",
|
|
154
|
+
describe: "Get peer id",
|
|
155
|
+
builder: (yargs) => {
|
|
156
|
+
yargs.option("directory", {
|
|
157
|
+
describe: "Peerbit directory",
|
|
158
|
+
defaultDescription: "~.peerbit",
|
|
159
|
+
type: "string",
|
|
160
|
+
alias: "d",
|
|
161
|
+
default: getHomeConfigDir(),
|
|
81
162
|
});
|
|
163
|
+
return yargs;
|
|
164
|
+
},
|
|
165
|
+
handler: async (args) => {
|
|
166
|
+
const kp = await getKeypair(args.directory);
|
|
167
|
+
console.log((await kp.toPeerId()).toString());
|
|
82
168
|
},
|
|
83
169
|
})
|
|
84
170
|
.command("domain", "Setup a domain and certificate for this node", (yargs) => {
|
|
@@ -183,6 +269,156 @@ export const cli = async (args) => {
|
|
|
183
269
|
})
|
|
184
270
|
.command("remote", "Handle remote nodes", (innerYargs) => {
|
|
185
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
|
+
try {
|
|
357
|
+
for (const node of nodes) {
|
|
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
|
+
}
|
|
372
|
+
catch (error) {
|
|
373
|
+
console.error("Error waiting for domains to be available: " +
|
|
374
|
+
error?.toString());
|
|
375
|
+
}
|
|
376
|
+
finally {
|
|
377
|
+
clearInterval(twirlTimer);
|
|
378
|
+
process.stdout.write("\r");
|
|
379
|
+
}
|
|
380
|
+
console.log(`New nodes available ${nodes.length}:`);
|
|
381
|
+
for (const node of nodes) {
|
|
382
|
+
console.log(chalk.green(node.name));
|
|
383
|
+
}
|
|
384
|
+
},
|
|
385
|
+
})
|
|
386
|
+
.strict()
|
|
387
|
+
.demandCommand();
|
|
388
|
+
})
|
|
389
|
+
.command({
|
|
390
|
+
command: "terminate [name...]",
|
|
391
|
+
describe: "Terminate remote instances that was previously spawned",
|
|
392
|
+
builder: (killArgs) => {
|
|
393
|
+
killArgs.option("all", {
|
|
394
|
+
describe: "Kill all nodes",
|
|
395
|
+
type: "boolean",
|
|
396
|
+
default: false,
|
|
397
|
+
});
|
|
398
|
+
killArgs.positional("name", {
|
|
399
|
+
type: "string",
|
|
400
|
+
describe: "Remote name",
|
|
401
|
+
default: "localhost",
|
|
402
|
+
demandOption: false,
|
|
403
|
+
array: true,
|
|
404
|
+
});
|
|
405
|
+
return killArgs;
|
|
406
|
+
},
|
|
407
|
+
handler: async (args) => {
|
|
408
|
+
const remotes = new Remotes(getRemotesPath(args.directory));
|
|
409
|
+
const allRemotes = await remotes.all();
|
|
410
|
+
for (const remote of allRemotes) {
|
|
411
|
+
if (args.all || args.name.includes(remote.name)) {
|
|
412
|
+
if (remote.origin?.type === "aws") {
|
|
413
|
+
await terminateNode({
|
|
414
|
+
instanceId: remote.origin.instanceId,
|
|
415
|
+
region: remote.origin.region,
|
|
416
|
+
});
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
},
|
|
421
|
+
})
|
|
186
422
|
.command({
|
|
187
423
|
command: "list",
|
|
188
424
|
aliases: "ls",
|
|
@@ -207,8 +443,9 @@ export const cli = async (args) => {
|
|
|
207
443
|
}, 0);
|
|
208
444
|
const all = await remotes.all();
|
|
209
445
|
if (all.length > 0) {
|
|
446
|
+
console.log(padString("Name", maxNameLength + 10), padString("Group" || "", 10), "Address");
|
|
210
447
|
for (const remote of all) {
|
|
211
|
-
console.log(padString(remote.name, maxNameLength + 10), remote.address);
|
|
448
|
+
console.log(padString(remote.name, maxNameLength + 10), padString(remote.group || "", 10), remote.address);
|
|
212
449
|
}
|
|
213
450
|
}
|
|
214
451
|
else {
|
|
@@ -217,24 +454,25 @@ export const cli = async (args) => {
|
|
|
217
454
|
},
|
|
218
455
|
})
|
|
219
456
|
.command({
|
|
220
|
-
command: "add <name> <address>
|
|
457
|
+
command: "add <name> <address>",
|
|
221
458
|
describe: "Add remote",
|
|
222
459
|
builder: (yargs) => {
|
|
223
460
|
yargs
|
|
224
|
-
.positional("address", {
|
|
225
|
-
type: "string",
|
|
226
|
-
describe: "Remote name",
|
|
227
|
-
demandOption: true,
|
|
228
|
-
})
|
|
229
461
|
.positional("name", {
|
|
230
462
|
type: "string",
|
|
231
463
|
describe: "Remote address",
|
|
232
464
|
demandOption: true,
|
|
233
465
|
})
|
|
234
|
-
.positional("
|
|
466
|
+
.positional("address", {
|
|
235
467
|
type: "string",
|
|
236
|
-
describe: "
|
|
468
|
+
describe: "Remote name",
|
|
237
469
|
demandOption: true,
|
|
470
|
+
})
|
|
471
|
+
.option("group", {
|
|
472
|
+
describe: "Group name",
|
|
473
|
+
type: "string",
|
|
474
|
+
alias: "g",
|
|
475
|
+
default: DEFAULT_REMOTE_GROUP,
|
|
238
476
|
})
|
|
239
477
|
.option("directory", {
|
|
240
478
|
describe: "Peerbit directory",
|
|
@@ -249,7 +487,9 @@ export const cli = async (args) => {
|
|
|
249
487
|
if (args.name === "localhost") {
|
|
250
488
|
throw new Error("Remote can not be named 'localhost'");
|
|
251
489
|
}
|
|
252
|
-
const api = await
|
|
490
|
+
const api = await createClient(await getKeypair(args.directory), {
|
|
491
|
+
address: args.address,
|
|
492
|
+
});
|
|
253
493
|
try {
|
|
254
494
|
await api.program.list();
|
|
255
495
|
}
|
|
@@ -260,7 +500,11 @@ export const cli = async (args) => {
|
|
|
260
500
|
fs.mkdirSync(args.directory, { recursive: true });
|
|
261
501
|
}
|
|
262
502
|
const remotes = new Remotes(getRemotesPath(args.directory));
|
|
263
|
-
remotes.add(
|
|
503
|
+
remotes.add({
|
|
504
|
+
name: args.name,
|
|
505
|
+
address: args.address,
|
|
506
|
+
group: args.group,
|
|
507
|
+
});
|
|
264
508
|
},
|
|
265
509
|
})
|
|
266
510
|
.command({
|
|
@@ -303,6 +547,18 @@ export const cli = async (args) => {
|
|
|
303
547
|
default: "localhost",
|
|
304
548
|
demandOption: false,
|
|
305
549
|
array: true,
|
|
550
|
+
})
|
|
551
|
+
.option("all", {
|
|
552
|
+
type: "boolean",
|
|
553
|
+
describe: "Connect to all nodes",
|
|
554
|
+
default: false,
|
|
555
|
+
})
|
|
556
|
+
.option("group", {
|
|
557
|
+
type: "string",
|
|
558
|
+
describe: "Remote group name",
|
|
559
|
+
alias: "g",
|
|
560
|
+
default: [],
|
|
561
|
+
array: true,
|
|
306
562
|
})
|
|
307
563
|
.option("directory", {
|
|
308
564
|
describe: "Peerbit directory",
|
|
@@ -314,19 +570,24 @@ export const cli = async (args) => {
|
|
|
314
570
|
return yargs;
|
|
315
571
|
},
|
|
316
572
|
handler: async (connectArgs) => {
|
|
317
|
-
const
|
|
573
|
+
const remotes = new Remotes(getRemotesPath(connectArgs.directory));
|
|
574
|
+
let names = connectArgs.name;
|
|
575
|
+
if (names.length === 0 ||
|
|
576
|
+
connectArgs.all ||
|
|
577
|
+
connectArgs.group.length > 0) {
|
|
578
|
+
names = (await remotes.all()).map((x) => x.name);
|
|
579
|
+
}
|
|
318
580
|
const apis = [];
|
|
319
|
-
|
|
581
|
+
const config = await import("./config.js");
|
|
582
|
+
const keypair = await config.getKeypair(connectArgs.directory);
|
|
583
|
+
const selectedRemotes = [];
|
|
320
584
|
if (names.length > 0) {
|
|
321
|
-
const
|
|
322
|
-
for (const name of names) {
|
|
585
|
+
for (const [ix, name] of names.entries()) {
|
|
323
586
|
if (name === "localhost") {
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
apis.push({
|
|
327
|
-
log: (string) => console.log("localhost: " + string),
|
|
587
|
+
selectedRemotes.push({
|
|
588
|
+
address: "http://localhost:" + LOCAL_API_PORT,
|
|
328
589
|
name: "localhost",
|
|
329
|
-
|
|
590
|
+
group: DEFAULT_REMOTE_GROUP,
|
|
330
591
|
});
|
|
331
592
|
}
|
|
332
593
|
else {
|
|
@@ -334,311 +595,365 @@ export const cli = async (args) => {
|
|
|
334
595
|
if (!remote) {
|
|
335
596
|
throw new Error("Missing remote with name: " + name);
|
|
336
597
|
}
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
598
|
+
if (connectArgs.group.length > 0 &&
|
|
599
|
+
!connectArgs.group.includes(remote.group)) {
|
|
600
|
+
continue;
|
|
340
601
|
}
|
|
341
|
-
|
|
342
|
-
logFn = (string) => console.log(string);
|
|
343
|
-
}
|
|
344
|
-
apis.push({
|
|
345
|
-
log: logFn,
|
|
346
|
-
name,
|
|
347
|
-
api: await client(remote.password, remote.address),
|
|
348
|
-
});
|
|
602
|
+
selectedRemotes.push(remote);
|
|
349
603
|
}
|
|
350
604
|
}
|
|
351
605
|
}
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
606
|
+
const maxNameLength = selectedRemotes
|
|
607
|
+
.map((x) => x.name.length)
|
|
608
|
+
.reduce((prev, c, i) => {
|
|
609
|
+
return Math.max(prev, c);
|
|
610
|
+
}, 0);
|
|
611
|
+
if (selectedRemotes.length === 0) {
|
|
612
|
+
console.log(chalk.red("No remotes matched your connection condition"));
|
|
613
|
+
}
|
|
614
|
+
else {
|
|
615
|
+
console.log(`Connected to (${selectedRemotes.length}):`);
|
|
616
|
+
for (const [ix, remote] of selectedRemotes.entries()) {
|
|
617
|
+
const chalkBg = chalk.bgHex(colors[ix]);
|
|
618
|
+
console.log(chalkBg(remote.name));
|
|
619
|
+
const logFn = (string) => console.log(padString(chalkBg(remote.name), maxNameLength, " ", remote.name.length) +
|
|
620
|
+
": " +
|
|
621
|
+
string);
|
|
622
|
+
apis.push({
|
|
623
|
+
log: logFn,
|
|
624
|
+
name: remote.name,
|
|
625
|
+
api: await createClient(keypair, remote),
|
|
626
|
+
});
|
|
356
627
|
}
|
|
357
|
-
|
|
358
|
-
|
|
628
|
+
// try if authenticated
|
|
629
|
+
for (const api of apis) {
|
|
630
|
+
try {
|
|
631
|
+
await api.api.program.list();
|
|
632
|
+
}
|
|
633
|
+
catch (error) {
|
|
634
|
+
throw new Error(`Failed to connect to '${api.name}': ${error?.toString()}`);
|
|
635
|
+
}
|
|
359
636
|
}
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
637
|
+
const rl = readline.createInterface({
|
|
638
|
+
input: process.stdin,
|
|
639
|
+
output: process.stdout,
|
|
640
|
+
terminal: true,
|
|
641
|
+
historySize: 100,
|
|
642
|
+
});
|
|
643
|
+
console.log("Write 'help' to show commands.\n");
|
|
644
|
+
rl.prompt(false);
|
|
645
|
+
const capi = () => yargs
|
|
646
|
+
.default()
|
|
647
|
+
.command("peer", "Peer info", (yargs) => {
|
|
648
|
+
yargs
|
|
649
|
+
.command({
|
|
650
|
+
command: "id",
|
|
651
|
+
describe: "Get peer id",
|
|
652
|
+
handler: async (args) => {
|
|
653
|
+
for (const api of apis) {
|
|
654
|
+
api.log((await api.api.peer.id.get()).toString());
|
|
655
|
+
}
|
|
656
|
+
},
|
|
657
|
+
})
|
|
658
|
+
.command({
|
|
659
|
+
command: "address",
|
|
660
|
+
describe: "Get addresses",
|
|
661
|
+
handler: async (args) => {
|
|
662
|
+
for (const api of apis) {
|
|
663
|
+
(await api.api.peer.addresses.get()).forEach((x) => api.log(x.toString()));
|
|
664
|
+
}
|
|
665
|
+
},
|
|
666
|
+
})
|
|
667
|
+
.strict()
|
|
668
|
+
.demandCommand();
|
|
669
|
+
return yargs;
|
|
373
670
|
})
|
|
374
|
-
.command({
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
671
|
+
.command("access", "Modify access control for this node", (yargs) => {
|
|
672
|
+
yargs
|
|
673
|
+
.command({
|
|
674
|
+
command: "grant <peer-id>",
|
|
675
|
+
describe: "Give a peer-id admin capabilities",
|
|
676
|
+
builder: (yargs) => {
|
|
677
|
+
yargs.positional("peer-id", {
|
|
678
|
+
describe: "Peer id",
|
|
679
|
+
type: "string",
|
|
680
|
+
demandOption: true,
|
|
681
|
+
});
|
|
682
|
+
return yargs;
|
|
683
|
+
},
|
|
684
|
+
handler: async (args) => {
|
|
685
|
+
const peerId = peerIdFromString(args["peer-id"]);
|
|
686
|
+
for (const api of apis) {
|
|
687
|
+
await api.api.access.allow(peerId);
|
|
688
|
+
}
|
|
689
|
+
},
|
|
690
|
+
})
|
|
691
|
+
.command({
|
|
692
|
+
command: "deny <peer-id>",
|
|
693
|
+
describe: "Remove admin capabilities from peer-id",
|
|
694
|
+
builder: (yargs) => {
|
|
695
|
+
yargs.positional("peer-id", {
|
|
696
|
+
describe: "Peer id",
|
|
697
|
+
demandOption: true,
|
|
698
|
+
});
|
|
699
|
+
return yargs;
|
|
700
|
+
},
|
|
701
|
+
handler: async (args) => {
|
|
702
|
+
const peerId = peerIdFromString(args["peer-id"]);
|
|
703
|
+
for (const api of apis) {
|
|
704
|
+
await api.api.access.deny(peerId);
|
|
705
|
+
}
|
|
706
|
+
},
|
|
707
|
+
})
|
|
708
|
+
.strict()
|
|
709
|
+
.demandCommand();
|
|
382
710
|
})
|
|
383
|
-
.
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
},
|
|
711
|
+
.command("network", "Manage network", (yargs) => {
|
|
712
|
+
yargs
|
|
713
|
+
.command({
|
|
714
|
+
command: "bootstrap",
|
|
715
|
+
describe: "Connect to bootstrap nodes",
|
|
716
|
+
handler: async () => {
|
|
717
|
+
for (const api of apis) {
|
|
718
|
+
await api.api.network.bootstrap();
|
|
719
|
+
}
|
|
720
|
+
},
|
|
721
|
+
})
|
|
722
|
+
.strict()
|
|
723
|
+
.demandCommand();
|
|
397
724
|
})
|
|
398
|
-
.
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
console.log(
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
725
|
+
.command("topic", "Manage topics the node is listening to", (yargs) => {
|
|
726
|
+
yargs
|
|
727
|
+
.command({
|
|
728
|
+
command: "list",
|
|
729
|
+
aliases: "ls",
|
|
730
|
+
describe: "List all topics",
|
|
731
|
+
builder: (yargs) => {
|
|
732
|
+
yargs.option("replicate", {
|
|
733
|
+
type: "boolean",
|
|
734
|
+
describe: "Replicate data on this topic",
|
|
735
|
+
aliases: "r",
|
|
736
|
+
default: false,
|
|
737
|
+
});
|
|
738
|
+
return yargs;
|
|
739
|
+
},
|
|
740
|
+
handler: async (args) => {
|
|
741
|
+
/* const c = await client();
|
|
742
|
+
const topics = await c.topics.get(args.replicate);
|
|
743
|
+
if (topics?.length > 0) {
|
|
744
|
+
console.log("Topic (" + topics.length + "):");
|
|
745
|
+
for (const t of topics) {
|
|
746
|
+
console.log(t);
|
|
747
|
+
}
|
|
748
|
+
} else {
|
|
749
|
+
console.log("Not subscribed to any topics");
|
|
750
|
+
} */
|
|
751
|
+
console.error("Not implemented");
|
|
752
|
+
},
|
|
753
|
+
})
|
|
754
|
+
.strict()
|
|
755
|
+
.demandCommand();
|
|
756
|
+
return yargs;
|
|
429
757
|
})
|
|
430
|
-
.
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
758
|
+
.command("program", "Manage programs", (yargs) => {
|
|
759
|
+
yargs
|
|
760
|
+
.command({
|
|
761
|
+
command: "status <address>",
|
|
762
|
+
describe: "Is a program open",
|
|
763
|
+
builder: (yargs) => {
|
|
764
|
+
yargs.positional("address", {
|
|
765
|
+
type: "string",
|
|
766
|
+
describe: "Program address",
|
|
767
|
+
demandOption: true,
|
|
768
|
+
});
|
|
769
|
+
return yargs;
|
|
770
|
+
},
|
|
771
|
+
handler: async (args) => {
|
|
772
|
+
for (const api of apis) {
|
|
773
|
+
const program = await api.api.program.has(args.address);
|
|
774
|
+
if (!program) {
|
|
775
|
+
api.log(chalk.red("Closed"));
|
|
776
|
+
}
|
|
777
|
+
else {
|
|
778
|
+
api.log(chalk.green("Open"));
|
|
779
|
+
}
|
|
452
780
|
}
|
|
453
|
-
|
|
454
|
-
|
|
781
|
+
},
|
|
782
|
+
})
|
|
783
|
+
.command({
|
|
784
|
+
command: "drop <address>",
|
|
785
|
+
describe: "Drop a program",
|
|
786
|
+
builder: (yargs) => {
|
|
787
|
+
yargs.positional("address", {
|
|
788
|
+
type: "string",
|
|
789
|
+
describe: "Program address",
|
|
790
|
+
demandOption: true,
|
|
791
|
+
});
|
|
792
|
+
return yargs;
|
|
793
|
+
},
|
|
794
|
+
handler: async (args) => {
|
|
795
|
+
for (const api of apis) {
|
|
796
|
+
try {
|
|
797
|
+
await api.api.program.drop(args.address);
|
|
798
|
+
}
|
|
799
|
+
catch (error) {
|
|
800
|
+
api.log(chalk.red(`Failed to drop ${args.address}: ${error.toString()}`));
|
|
801
|
+
}
|
|
455
802
|
}
|
|
456
|
-
}
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
try {
|
|
473
|
-
await api.api.program.drop(args.address);
|
|
803
|
+
},
|
|
804
|
+
})
|
|
805
|
+
.command({
|
|
806
|
+
command: "close <address>",
|
|
807
|
+
describe: "Close a program",
|
|
808
|
+
builder: (yargs) => {
|
|
809
|
+
yargs.positional("address", {
|
|
810
|
+
type: "string",
|
|
811
|
+
describe: "Program address",
|
|
812
|
+
demandOption: true,
|
|
813
|
+
});
|
|
814
|
+
return yargs;
|
|
815
|
+
},
|
|
816
|
+
handler: async (args) => {
|
|
817
|
+
for (const api of apis) {
|
|
818
|
+
await api.api.program.close(args.address);
|
|
474
819
|
}
|
|
475
|
-
|
|
476
|
-
|
|
820
|
+
},
|
|
821
|
+
})
|
|
822
|
+
.command({
|
|
823
|
+
command: "list",
|
|
824
|
+
describe: "List all running programs",
|
|
825
|
+
aliases: "ls",
|
|
826
|
+
handler: async (args) => {
|
|
827
|
+
for (const api of apis) {
|
|
828
|
+
const list = await api.api.program.list();
|
|
829
|
+
api.log(`Running programs (${list.length}):`);
|
|
830
|
+
list.forEach((p) => {
|
|
831
|
+
api.log(chalk.green(p));
|
|
832
|
+
});
|
|
477
833
|
}
|
|
478
|
-
}
|
|
479
|
-
}
|
|
834
|
+
},
|
|
835
|
+
})
|
|
836
|
+
.command({
|
|
837
|
+
command: "open [program]",
|
|
838
|
+
describe: "Open program",
|
|
839
|
+
builder: (yargs) => {
|
|
840
|
+
yargs.positional("program", {
|
|
841
|
+
type: "string",
|
|
842
|
+
describe: "Identifier",
|
|
843
|
+
demandOption: true,
|
|
844
|
+
});
|
|
845
|
+
yargs.option("base64", {
|
|
846
|
+
type: "string",
|
|
847
|
+
describe: "Base64 encoded serialized",
|
|
848
|
+
aliases: "b",
|
|
849
|
+
});
|
|
850
|
+
yargs.option("variant", {
|
|
851
|
+
type: "string",
|
|
852
|
+
describe: "Variant name",
|
|
853
|
+
aliases: "v",
|
|
854
|
+
});
|
|
855
|
+
return yargs;
|
|
856
|
+
},
|
|
857
|
+
handler: async (args) => {
|
|
858
|
+
if (!args.base64 && !args.variant) {
|
|
859
|
+
throw new Error("Either base64 or variant argument needs to be provided");
|
|
860
|
+
}
|
|
861
|
+
let startArg;
|
|
862
|
+
if (args.base64) {
|
|
863
|
+
startArg = {
|
|
864
|
+
base64: args.base64,
|
|
865
|
+
};
|
|
866
|
+
}
|
|
867
|
+
else {
|
|
868
|
+
startArg = {
|
|
869
|
+
variant: args.variant,
|
|
870
|
+
};
|
|
871
|
+
}
|
|
872
|
+
for (const api of apis) {
|
|
873
|
+
const address = await api.api.program.open(startArg);
|
|
874
|
+
api.log("Started program with address: ");
|
|
875
|
+
api.log(chalk.green(address.toString()));
|
|
876
|
+
}
|
|
877
|
+
},
|
|
878
|
+
})
|
|
879
|
+
.strict()
|
|
880
|
+
.demandCommand();
|
|
881
|
+
return yargs;
|
|
480
882
|
})
|
|
481
883
|
.command({
|
|
482
|
-
command: "
|
|
483
|
-
describe: "
|
|
884
|
+
command: "install <package-spec>",
|
|
885
|
+
describe: "install and import a dependency",
|
|
484
886
|
builder: (yargs) => {
|
|
485
|
-
yargs.positional("
|
|
887
|
+
yargs.positional("package-spec", {
|
|
486
888
|
type: "string",
|
|
487
|
-
describe: "
|
|
889
|
+
describe: "Installed dependency will be loaded with js import(...)",
|
|
488
890
|
demandOption: true,
|
|
489
891
|
});
|
|
490
892
|
return yargs;
|
|
491
893
|
},
|
|
492
894
|
handler: async (args) => {
|
|
895
|
+
// if ends with .tgz assume it is a file
|
|
896
|
+
let installCommand;
|
|
897
|
+
const packageName = args["package-spec"];
|
|
898
|
+
if (packageName.endsWith(".tgz")) {
|
|
899
|
+
const packagePath = path.isAbsolute(packageName)
|
|
900
|
+
? packageName
|
|
901
|
+
: path.join(process.cwd(), packageName);
|
|
902
|
+
const buffer = fs.readFileSync(packagePath);
|
|
903
|
+
const base64 = toBase64(buffer);
|
|
904
|
+
installCommand = {
|
|
905
|
+
type: "tgz",
|
|
906
|
+
name: await getPackageName(packageName),
|
|
907
|
+
base64,
|
|
908
|
+
};
|
|
909
|
+
}
|
|
910
|
+
else {
|
|
911
|
+
installCommand = { type: "npm", name: packageName };
|
|
912
|
+
}
|
|
493
913
|
for (const api of apis) {
|
|
494
|
-
await api.api.
|
|
914
|
+
const newPrograms = await api.api.dependency.install(installCommand);
|
|
915
|
+
api.log(`New programs available (${newPrograms.length}):`);
|
|
916
|
+
newPrograms.forEach((p) => {
|
|
917
|
+
api.log(chalk.green(p));
|
|
918
|
+
});
|
|
495
919
|
}
|
|
496
920
|
},
|
|
497
921
|
})
|
|
498
922
|
.command({
|
|
499
|
-
command: "
|
|
500
|
-
describe: "
|
|
501
|
-
|
|
502
|
-
handler: async (args) => {
|
|
923
|
+
command: "restart",
|
|
924
|
+
describe: "Restart the server",
|
|
925
|
+
handler: async () => {
|
|
503
926
|
for (const api of apis) {
|
|
504
|
-
|
|
505
|
-
api.log(`Running programs (${list.length}):`);
|
|
506
|
-
list.forEach((p) => {
|
|
507
|
-
api.log(chalk.green(p));
|
|
508
|
-
});
|
|
927
|
+
await api.api.restart();
|
|
509
928
|
}
|
|
510
929
|
},
|
|
511
930
|
})
|
|
512
931
|
.command({
|
|
513
|
-
command: "
|
|
514
|
-
describe: "
|
|
515
|
-
|
|
516
|
-
yargs.positional("program", {
|
|
517
|
-
type: "string",
|
|
518
|
-
describe: "Identifier",
|
|
519
|
-
demandOption: true,
|
|
520
|
-
});
|
|
521
|
-
yargs.option("base64", {
|
|
522
|
-
type: "string",
|
|
523
|
-
describe: "Base64 encoded serialized",
|
|
524
|
-
aliases: "b",
|
|
525
|
-
});
|
|
526
|
-
yargs.option("variant", {
|
|
527
|
-
type: "string",
|
|
528
|
-
describe: "Variant name",
|
|
529
|
-
aliases: "v",
|
|
530
|
-
});
|
|
531
|
-
return yargs;
|
|
532
|
-
},
|
|
533
|
-
handler: async (args) => {
|
|
534
|
-
if (!args.base64 && !args.variant) {
|
|
535
|
-
throw new Error("Either base64 or variant argument needs to be provided");
|
|
536
|
-
}
|
|
537
|
-
let startArg;
|
|
538
|
-
if (args.base64) {
|
|
539
|
-
startArg = {
|
|
540
|
-
base64: args.base64,
|
|
541
|
-
};
|
|
542
|
-
}
|
|
543
|
-
else {
|
|
544
|
-
startArg = {
|
|
545
|
-
variant: args.variant,
|
|
546
|
-
};
|
|
547
|
-
}
|
|
932
|
+
command: "stop",
|
|
933
|
+
describe: "Stop the server",
|
|
934
|
+
handler: async () => {
|
|
548
935
|
for (const api of apis) {
|
|
549
|
-
|
|
550
|
-
api.log("Started program with address: ");
|
|
551
|
-
api.log(chalk.green(address.toString()));
|
|
936
|
+
await api.api.stop();
|
|
552
937
|
}
|
|
553
938
|
},
|
|
554
939
|
})
|
|
940
|
+
.help()
|
|
555
941
|
.strict()
|
|
556
|
-
.
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
type: "string",
|
|
565
|
-
describe: "Installed dependency will be loaded with js import(...)",
|
|
566
|
-
demandOption: true,
|
|
567
|
-
});
|
|
568
|
-
return yargs;
|
|
569
|
-
},
|
|
570
|
-
handler: async (args) => {
|
|
571
|
-
// if ends with .tgz assume it is a file
|
|
572
|
-
let installCommand;
|
|
573
|
-
const packageName = args["package-spec"];
|
|
574
|
-
if (packageName.endsWith(".tgz")) {
|
|
575
|
-
const packagePath = path.isAbsolute(packageName)
|
|
576
|
-
? packageName
|
|
577
|
-
: path.join(process.cwd(), packageName);
|
|
578
|
-
const buffer = fs.readFileSync(packagePath);
|
|
579
|
-
const base64 = toBase64(buffer);
|
|
580
|
-
installCommand = {
|
|
581
|
-
type: "tgz",
|
|
582
|
-
name: await getPackageName(packageName),
|
|
583
|
-
base64,
|
|
584
|
-
};
|
|
942
|
+
.scriptName("")
|
|
943
|
+
.demandCommand()
|
|
944
|
+
.showHelpOnFail(true)
|
|
945
|
+
.exitProcess(false);
|
|
946
|
+
rl.on("line", async (cargs) => {
|
|
947
|
+
const cmds = capi();
|
|
948
|
+
try {
|
|
949
|
+
await cmds.parse(cargs);
|
|
585
950
|
}
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
}
|
|
589
|
-
for (const api of apis) {
|
|
590
|
-
const newPrograms = await api.api.dependency.install(installCommand);
|
|
591
|
-
api.log(`New programs available (${newPrograms.length}):`);
|
|
592
|
-
newPrograms.forEach((p) => {
|
|
593
|
-
api.log(chalk.green(p));
|
|
594
|
-
});
|
|
951
|
+
catch (error) {
|
|
952
|
+
/* console.log(chalk.red("Error parsing command: " + cargs))*/
|
|
595
953
|
}
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
command: "restart",
|
|
600
|
-
describe: "Restart the server",
|
|
601
|
-
handler: async () => {
|
|
602
|
-
for (const api of apis) {
|
|
603
|
-
await api.api.restart();
|
|
604
|
-
}
|
|
605
|
-
},
|
|
606
|
-
})
|
|
607
|
-
.command({
|
|
608
|
-
command: "terminate",
|
|
609
|
-
describe: "Terminate the server",
|
|
610
|
-
handler: async () => {
|
|
611
|
-
for (const api of apis) {
|
|
612
|
-
await api.api.terminate();
|
|
613
|
-
}
|
|
614
|
-
},
|
|
615
|
-
})
|
|
616
|
-
.help()
|
|
617
|
-
.strict()
|
|
618
|
-
.scriptName("")
|
|
619
|
-
.demandCommand()
|
|
620
|
-
.showHelpOnFail(true)
|
|
621
|
-
.exitProcess(false);
|
|
622
|
-
const rl = readline.createInterface({
|
|
623
|
-
input: process.stdin,
|
|
624
|
-
output: process.stdout,
|
|
625
|
-
terminal: true,
|
|
626
|
-
historySize: 100,
|
|
627
|
-
});
|
|
628
|
-
console.log(chalk.green("Connected"));
|
|
629
|
-
console.log("Write 'help' to show commands.\n");
|
|
630
|
-
const first = true;
|
|
631
|
-
rl.prompt(false);
|
|
632
|
-
rl.on("line", async (cargs) => {
|
|
633
|
-
const cmds = capi();
|
|
634
|
-
try {
|
|
635
|
-
await cmds.parse(cargs);
|
|
636
|
-
}
|
|
637
|
-
catch (error) {
|
|
638
|
-
/* console.log(chalk.red("Error parsing command: " + cargs))*/
|
|
639
|
-
}
|
|
640
|
-
rl.prompt(true);
|
|
641
|
-
});
|
|
954
|
+
rl.prompt(true);
|
|
955
|
+
});
|
|
956
|
+
}
|
|
642
957
|
},
|
|
643
958
|
})
|
|
644
959
|
.help()
|