@bitsocial/bitsocial-cli 0.19.46 → 0.19.48
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/README.md +36 -19
- package/dist/cli/commands/community/edit.js +15 -6
- package/dist/cli/commands/community/start.d.ts +3 -0
- package/dist/cli/commands/community/start.js +25 -5
- package/dist/cli/commands/update/install.d.ts +0 -2
- package/dist/cli/commands/update/install.js +21 -67
- package/dist/update/fast-update.d.ts +6 -0
- package/dist/update/fast-update.js +182 -0
- package/dist/update/npm-registry.d.ts +1 -0
- package/dist/update/npm-registry.js +1 -1
- package/oclif.manifest.json +16 -4
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -344,7 +344,7 @@ EXAMPLES
|
|
|
344
344
|
$ bitsocial challenge install ./my-local-challenge
|
|
345
345
|
```
|
|
346
346
|
|
|
347
|
-
_See code: [src/cli/commands/challenge/install.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.
|
|
347
|
+
_See code: [src/cli/commands/challenge/install.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.48/src/cli/commands/challenge/install.ts)_
|
|
348
348
|
|
|
349
349
|
## `bitsocial challenge list`
|
|
350
350
|
|
|
@@ -367,7 +367,7 @@ EXAMPLES
|
|
|
367
367
|
$ bitsocial challenge list -q
|
|
368
368
|
```
|
|
369
369
|
|
|
370
|
-
_See code: [src/cli/commands/challenge/list.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.
|
|
370
|
+
_See code: [src/cli/commands/challenge/list.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.48/src/cli/commands/challenge/list.ts)_
|
|
371
371
|
|
|
372
372
|
## `bitsocial challenge remove NAME`
|
|
373
373
|
|
|
@@ -392,7 +392,7 @@ EXAMPLES
|
|
|
392
392
|
$ bitsocial challenge remove @scope/my-challenge
|
|
393
393
|
```
|
|
394
394
|
|
|
395
|
-
_See code: [src/cli/commands/challenge/remove.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.
|
|
395
|
+
_See code: [src/cli/commands/challenge/remove.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.48/src/cli/commands/challenge/remove.ts)_
|
|
396
396
|
|
|
397
397
|
## `bitsocial community create`
|
|
398
398
|
|
|
@@ -422,7 +422,7 @@ EXAMPLES
|
|
|
422
422
|
$ bitsocial community create --jsonFile ./create-options.json
|
|
423
423
|
```
|
|
424
424
|
|
|
425
|
-
_See code: [src/cli/commands/community/create.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.
|
|
425
|
+
_See code: [src/cli/commands/community/create.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.48/src/cli/commands/community/create.ts)_
|
|
426
426
|
|
|
427
427
|
## `bitsocial community delete ADDRESSES`
|
|
428
428
|
|
|
@@ -447,7 +447,7 @@ EXAMPLES
|
|
|
447
447
|
$ bitsocial community delete 12D3KooWG3XbzoVyAE6Y9vHZKF64Yuuu4TjdgQKedk14iYmTEPWu
|
|
448
448
|
```
|
|
449
449
|
|
|
450
|
-
_See code: [src/cli/commands/community/delete.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.
|
|
450
|
+
_See code: [src/cli/commands/community/delete.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.48/src/cli/commands/community/delete.ts)_
|
|
451
451
|
|
|
452
452
|
## `bitsocial community edit ADDRESS`
|
|
453
453
|
|
|
@@ -467,10 +467,22 @@ FLAGS
|
|
|
467
467
|
DESCRIPTION
|
|
468
468
|
Edit a community's properties. For a list of properties, visit https://github.com/pkcprotocol/pkc-js
|
|
469
469
|
|
|
470
|
+
Merge behavior with CLI flags:
|
|
471
|
+
- Objects are merged with the community's current state (new keys are added, existing keys are overwritten).
|
|
472
|
+
- Arrays are extended: new values are prepended to the existing array.
|
|
473
|
+
- Setting a value to null removes it (e.g. --roles['mod.bso'] null).
|
|
474
|
+
|
|
475
|
+
Merge behavior with --jsonFile:
|
|
476
|
+
- Objects are merged the same way as CLI flags.
|
|
477
|
+
- Arrays are replaced entirely (RFC 7396 JSON Merge Patch semantics).
|
|
478
|
+
- When both --jsonFile and CLI flags are provided, CLI flags take priority.
|
|
479
|
+
|
|
480
|
+
For modifying complex settings like challenges, consider using a web UI instead: https://bitsocial.net/apps
|
|
481
|
+
|
|
470
482
|
EXAMPLES
|
|
471
|
-
Change the
|
|
483
|
+
Change the name of the community
|
|
472
484
|
|
|
473
|
-
$ bitsocial community edit 12D3KooWG3XbzoVyAE6Y9vHZKF64Yuuu4TjdgQKedk14iYmTEPWu --
|
|
485
|
+
$ bitsocial community edit 12D3KooWG3XbzoVyAE6Y9vHZKF64Yuuu4TjdgQKedk14iYmTEPWu --name newName.bso
|
|
474
486
|
|
|
475
487
|
Add the author address 'esteban.bso' as an admin on the community
|
|
476
488
|
|
|
@@ -505,7 +517,7 @@ EXAMPLES
|
|
|
505
517
|
$ bitsocial community edit bitsocial.bso --jsonFile ./edit-options.json
|
|
506
518
|
```
|
|
507
519
|
|
|
508
|
-
_See code: [src/cli/commands/community/edit.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.
|
|
520
|
+
_See code: [src/cli/commands/community/edit.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.48/src/cli/commands/community/edit.ts)_
|
|
509
521
|
|
|
510
522
|
## `bitsocial community get [ADDRESS]`
|
|
511
523
|
|
|
@@ -536,7 +548,7 @@ EXAMPLES
|
|
|
536
548
|
$ bitsocial community get --publicKey 12D3KooWG3XbzoVyAE6Y9vHZKF64Yuuu4TjdgQKedk14iYmTEPWu
|
|
537
549
|
```
|
|
538
550
|
|
|
539
|
-
_See code: [src/cli/commands/community/get.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.
|
|
551
|
+
_See code: [src/cli/commands/community/get.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.48/src/cli/commands/community/get.ts)_
|
|
540
552
|
|
|
541
553
|
## `bitsocial community list`
|
|
542
554
|
|
|
@@ -559,7 +571,7 @@ EXAMPLES
|
|
|
559
571
|
$ bitsocial community list
|
|
560
572
|
```
|
|
561
573
|
|
|
562
|
-
_See code: [src/cli/commands/community/list.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.
|
|
574
|
+
_See code: [src/cli/commands/community/list.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.48/src/cli/commands/community/list.ts)_
|
|
563
575
|
|
|
564
576
|
## `bitsocial community start ADDRESSES`
|
|
565
577
|
|
|
@@ -567,13 +579,14 @@ Start a community
|
|
|
567
579
|
|
|
568
580
|
```
|
|
569
581
|
USAGE
|
|
570
|
-
$ bitsocial community start ADDRESSES... --pkcRpcUrl <value>
|
|
582
|
+
$ bitsocial community start ADDRESSES... --pkcRpcUrl <value> [--concurrency <value>]
|
|
571
583
|
|
|
572
584
|
ARGUMENTS
|
|
573
585
|
ADDRESSES... Addresses of communities to start. Separated by space
|
|
574
586
|
|
|
575
587
|
FLAGS
|
|
576
|
-
--
|
|
588
|
+
--concurrency=<value> [default: 5] Number of communities to start in parallel
|
|
589
|
+
--pkcRpcUrl=<value> (required) [default: ws://localhost:9138/] URL to PKC RPC
|
|
577
590
|
|
|
578
591
|
DESCRIPTION
|
|
579
592
|
Start a community
|
|
@@ -586,9 +599,13 @@ EXAMPLES
|
|
|
586
599
|
Start all communities in your data path
|
|
587
600
|
|
|
588
601
|
$ bitsocial community start $(bitsocial community list -q)
|
|
602
|
+
|
|
603
|
+
Start communities sequentially (no concurrency)
|
|
604
|
+
|
|
605
|
+
$ bitsocial community start $(bitsocial community list -q) --concurrency 1
|
|
589
606
|
```
|
|
590
607
|
|
|
591
|
-
_See code: [src/cli/commands/community/start.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.
|
|
608
|
+
_See code: [src/cli/commands/community/start.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.48/src/cli/commands/community/start.ts)_
|
|
592
609
|
|
|
593
610
|
## `bitsocial community stop ADDRESSES`
|
|
594
611
|
|
|
@@ -613,7 +630,7 @@ EXAMPLES
|
|
|
613
630
|
$ bitsocial community stop Qmb99crTbSUfKXamXwZBe829Vf6w5w5TktPkb6WstC9RFW
|
|
614
631
|
```
|
|
615
632
|
|
|
616
|
-
_See code: [src/cli/commands/community/stop.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.
|
|
633
|
+
_See code: [src/cli/commands/community/stop.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.48/src/cli/commands/community/stop.ts)_
|
|
617
634
|
|
|
618
635
|
## `bitsocial daemon`
|
|
619
636
|
|
|
@@ -654,7 +671,7 @@ EXAMPLES
|
|
|
654
671
|
$ bitsocial daemon --chainProviderUrls https://mainnet.infura.io/v3/YOUR_KEY
|
|
655
672
|
```
|
|
656
673
|
|
|
657
|
-
_See code: [src/cli/commands/daemon.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.
|
|
674
|
+
_See code: [src/cli/commands/daemon.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.48/src/cli/commands/daemon.ts)_
|
|
658
675
|
|
|
659
676
|
## `bitsocial help [COMMAND]`
|
|
660
677
|
|
|
@@ -720,7 +737,7 @@ EXAMPLES
|
|
|
720
737
|
$ bitsocial logs --stdout -f
|
|
721
738
|
```
|
|
722
739
|
|
|
723
|
-
_See code: [src/cli/commands/logs.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.
|
|
740
|
+
_See code: [src/cli/commands/logs.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.48/src/cli/commands/logs.ts)_
|
|
724
741
|
|
|
725
742
|
## `bitsocial update check`
|
|
726
743
|
|
|
@@ -737,7 +754,7 @@ EXAMPLES
|
|
|
737
754
|
$ bitsocial update check
|
|
738
755
|
```
|
|
739
756
|
|
|
740
|
-
_See code: [src/cli/commands/update/check.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.
|
|
757
|
+
_See code: [src/cli/commands/update/check.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.48/src/cli/commands/update/check.ts)_
|
|
741
758
|
|
|
742
759
|
## `bitsocial update install [VERSION]`
|
|
743
760
|
|
|
@@ -769,7 +786,7 @@ EXAMPLES
|
|
|
769
786
|
$ bitsocial update install --no-restart-daemons
|
|
770
787
|
```
|
|
771
788
|
|
|
772
|
-
_See code: [src/cli/commands/update/install.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.
|
|
789
|
+
_See code: [src/cli/commands/update/install.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.48/src/cli/commands/update/install.ts)_
|
|
773
790
|
|
|
774
791
|
## `bitsocial update versions`
|
|
775
792
|
|
|
@@ -791,7 +808,7 @@ EXAMPLES
|
|
|
791
808
|
$ bitsocial update versions --limit 5
|
|
792
809
|
```
|
|
793
810
|
|
|
794
|
-
_See code: [src/cli/commands/update/versions.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.
|
|
811
|
+
_See code: [src/cli/commands/update/versions.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.48/src/cli/commands/update/versions.ts)_
|
|
795
812
|
<!-- commandsstop -->
|
|
796
813
|
|
|
797
814
|
## Contribution
|
|
@@ -5,7 +5,19 @@ import { BaseCommand } from "../../base-command.js";
|
|
|
5
5
|
import { PKCLogger, mergeDeep, parseJsoncFile, replaceNullWithUndefined } from "../../../util.js";
|
|
6
6
|
import * as remeda from "remeda";
|
|
7
7
|
export default class Edit extends BaseCommand {
|
|
8
|
-
static description =
|
|
8
|
+
static description = `Edit a community's properties. For a list of properties, visit https://github.com/pkcprotocol/pkc-js
|
|
9
|
+
|
|
10
|
+
Merge behavior with CLI flags:
|
|
11
|
+
- Objects are merged with the community's current state (new keys are added, existing keys are overwritten).
|
|
12
|
+
- Arrays are extended: new values are prepended to the existing array.
|
|
13
|
+
- Setting a value to null removes it (e.g. --roles['mod.bso'] null).
|
|
14
|
+
|
|
15
|
+
Merge behavior with --jsonFile:
|
|
16
|
+
- Objects are merged the same way as CLI flags.
|
|
17
|
+
- Arrays are replaced entirely (RFC 7396 JSON Merge Patch semantics).
|
|
18
|
+
- When both --jsonFile and CLI flags are provided, CLI flags take priority.
|
|
19
|
+
|
|
20
|
+
For modifying complex settings like challenges, consider using a web UI instead: https://bitsocial.net/apps`;
|
|
9
21
|
static args = {
|
|
10
22
|
address: Args.string({
|
|
11
23
|
name: "address",
|
|
@@ -21,12 +33,9 @@ export default class Edit extends BaseCommand {
|
|
|
21
33
|
})
|
|
22
34
|
};
|
|
23
35
|
static examples = [
|
|
24
|
-
// TODO update this to change the name instead
|
|
25
|
-
// Also are we testing modifying name properly?
|
|
26
|
-
// in theory user should not modify address, they should modify name
|
|
27
36
|
{
|
|
28
|
-
description: "Change the
|
|
29
|
-
command: "bitsocial community edit 12D3KooWG3XbzoVyAE6Y9vHZKF64Yuuu4TjdgQKedk14iYmTEPWu --
|
|
37
|
+
description: "Change the name of the community",
|
|
38
|
+
command: "bitsocial community edit 12D3KooWG3XbzoVyAE6Y9vHZKF64Yuuu4TjdgQKedk14iYmTEPWu --name newName.bso"
|
|
30
39
|
},
|
|
31
40
|
{
|
|
32
41
|
description: "Add the author address 'esteban.bso' as an admin on the community",
|
|
@@ -5,6 +5,9 @@ export default class Start extends BaseCommand {
|
|
|
5
5
|
static args: {
|
|
6
6
|
addresses: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
|
|
7
7
|
};
|
|
8
|
+
static flags: {
|
|
9
|
+
concurrency: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
|
+
};
|
|
8
11
|
static examples: (string | {
|
|
9
12
|
description: string;
|
|
10
13
|
command: string;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { PKCLogger } from "../../../util.js";
|
|
2
2
|
import { BaseCommand } from "../../base-command.js";
|
|
3
|
-
import { Args } from "@oclif/core";
|
|
3
|
+
import { Args, Flags } from "@oclif/core";
|
|
4
|
+
import pLimit from "p-limit";
|
|
4
5
|
export default class Start extends BaseCommand {
|
|
5
6
|
static description = "Start a community";
|
|
6
7
|
static strict = false; // To allow for variable length arguments
|
|
@@ -11,12 +12,23 @@ export default class Start extends BaseCommand {
|
|
|
11
12
|
description: "Addresses of communities to start. Separated by space"
|
|
12
13
|
})
|
|
13
14
|
};
|
|
15
|
+
static flags = {
|
|
16
|
+
concurrency: Flags.integer({
|
|
17
|
+
description: "Number of communities to start in parallel",
|
|
18
|
+
default: 5,
|
|
19
|
+
min: 0
|
|
20
|
+
})
|
|
21
|
+
};
|
|
14
22
|
static examples = [
|
|
15
23
|
"bitsocial community start plebbit.bso",
|
|
16
24
|
"bitsocial community start 12D3KooWG3XbzoVyAE6Y9vHZKF64Yuuu4TjdgQKedk14iYmTEPWu",
|
|
17
25
|
{
|
|
18
26
|
description: "Start all communities in your data path",
|
|
19
27
|
command: "bitsocial community start $(bitsocial community list -q)"
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
description: "Start communities sequentially (no concurrency)",
|
|
31
|
+
command: "bitsocial community start $(bitsocial community list -q) --concurrency 1"
|
|
20
32
|
}
|
|
21
33
|
];
|
|
22
34
|
async run() {
|
|
@@ -25,8 +37,11 @@ export default class Start extends BaseCommand {
|
|
|
25
37
|
const log = PKCLogger("bitsocial-cli:commands:community:start");
|
|
26
38
|
log(`addresses: `, addresses);
|
|
27
39
|
log(`flags: `, flags);
|
|
40
|
+
const concurrency = Math.max(flags.concurrency, 1);
|
|
41
|
+
const limit = pLimit(concurrency);
|
|
28
42
|
const pkc = await this._connectToPkcRpc(flags.pkcRpcUrl.toString());
|
|
29
|
-
|
|
43
|
+
const errors = [];
|
|
44
|
+
const tasks = addresses.map((address) => limit(async () => {
|
|
30
45
|
try {
|
|
31
46
|
const community = await pkc.createCommunity({ address });
|
|
32
47
|
await community.start();
|
|
@@ -36,11 +51,16 @@ export default class Start extends BaseCommand {
|
|
|
36
51
|
const error = e instanceof Error ? e : new Error(typeof e === "string" ? e : JSON.stringify(e));
|
|
37
52
|
//@ts-expect-error
|
|
38
53
|
error.details = { ...error.details, address };
|
|
54
|
+
errors.push({ address, error });
|
|
55
|
+
}
|
|
56
|
+
}));
|
|
57
|
+
await Promise.all(tasks);
|
|
58
|
+
await pkc.destroy();
|
|
59
|
+
if (errors.length > 0) {
|
|
60
|
+
for (const { error } of errors) {
|
|
39
61
|
console.error(error);
|
|
40
|
-
await pkc.destroy();
|
|
41
|
-
this.exit(1);
|
|
42
62
|
}
|
|
63
|
+
this.exit(1);
|
|
43
64
|
}
|
|
44
|
-
await pkc.destroy();
|
|
45
65
|
}
|
|
46
66
|
}
|
|
@@ -2,13 +2,9 @@ import { Args, Flags, Command } from "@oclif/core";
|
|
|
2
2
|
import { spawn } from "child_process";
|
|
3
3
|
import tcpPortUsed from "tcp-port-used";
|
|
4
4
|
import { fetchLatestVersion, installGlobal } from "../../../update/npm-registry.js";
|
|
5
|
+
import { fastInstallGlobal } from "../../../update/fast-update.js";
|
|
5
6
|
import { compareVersions } from "../../../update/semver.js";
|
|
6
7
|
import { getAliveDaemonStates } from "../../../common-utils/daemon-state.js";
|
|
7
|
-
import PKC from "@pkcprotocol/pkc-js";
|
|
8
|
-
const getPKCConnectOverride = () => {
|
|
9
|
-
const globalWithOverride = globalThis;
|
|
10
|
-
return globalWithOverride.__PKC_RPC_CONNECT_OVERRIDE;
|
|
11
|
-
};
|
|
12
8
|
export default class Install extends Command {
|
|
13
9
|
static description = "Install a specific version of bitsocial from npm";
|
|
14
10
|
static args = {
|
|
@@ -95,11 +91,25 @@ export default class Install extends Command {
|
|
|
95
91
|
return;
|
|
96
92
|
}
|
|
97
93
|
this.log(`Installing bitsocial-cli@${targetVersion}...`);
|
|
98
|
-
|
|
99
|
-
|
|
94
|
+
let installed = false;
|
|
95
|
+
if (!flags.force) {
|
|
96
|
+
try {
|
|
97
|
+
installed = await fastInstallGlobal(targetVersion, this.config.root, (msg) => this.log(msg));
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
installed = false;
|
|
101
|
+
}
|
|
100
102
|
}
|
|
101
|
-
|
|
102
|
-
|
|
103
|
+
if (!installed) {
|
|
104
|
+
if (!flags.force) {
|
|
105
|
+
this.log("Falling back to full install...");
|
|
106
|
+
}
|
|
107
|
+
try {
|
|
108
|
+
await installGlobal(targetVersion);
|
|
109
|
+
}
|
|
110
|
+
catch (err) {
|
|
111
|
+
this.error(`Update failed: ${err.message}`, { exit: 1 });
|
|
112
|
+
}
|
|
103
113
|
}
|
|
104
114
|
this.log(`Installed bitsocial v${targetVersion} (was v${current}).`);
|
|
105
115
|
// Restart daemons with the new binary
|
|
@@ -127,68 +137,12 @@ export default class Install extends Command {
|
|
|
127
137
|
const started = await tcpPortUsed.waitUntilUsed(port, 500, 30000).then(() => true).catch(() => false);
|
|
128
138
|
if (started) {
|
|
129
139
|
this.log(` Daemon started (port ${port}).`);
|
|
130
|
-
await this._reportCommunityStatus(d.pkcRpcUrl);
|
|
131
140
|
}
|
|
132
141
|
else {
|
|
133
142
|
this.warn(` Daemon may not have started — port ${port} not responding after 30s. Check logs with: bitsocial logs`);
|
|
134
143
|
}
|
|
135
144
|
}
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
const connectOverride = getPKCConnectOverride();
|
|
139
|
-
if (connectOverride) {
|
|
140
|
-
return connectOverride(pkcRpcUrl);
|
|
141
|
-
}
|
|
142
|
-
const pkc = await PKC({ pkcRpcClientsOptions: [pkcRpcUrl] });
|
|
143
|
-
const errors = [];
|
|
144
|
-
pkc.on("error", (err) => {
|
|
145
|
-
errors.push(err);
|
|
146
|
-
});
|
|
147
|
-
await new Promise((resolve) => {
|
|
148
|
-
const timeout = setTimeout(() => {
|
|
149
|
-
pkc.removeListener("communitieschange", handler);
|
|
150
|
-
resolve();
|
|
151
|
-
}, 20000);
|
|
152
|
-
const handler = () => {
|
|
153
|
-
if (pkc.communities.length > 0) {
|
|
154
|
-
pkc.removeListener("communitieschange", handler);
|
|
155
|
-
clearTimeout(timeout);
|
|
156
|
-
resolve();
|
|
157
|
-
}
|
|
158
|
-
};
|
|
159
|
-
pkc.on("communitieschange", handler);
|
|
160
|
-
});
|
|
161
|
-
return pkc;
|
|
162
|
-
}
|
|
163
|
-
async _reportCommunityStatus(pkcRpcUrl) {
|
|
164
|
-
let pkc;
|
|
165
|
-
try {
|
|
166
|
-
pkc = await this._connectToRpc(pkcRpcUrl);
|
|
167
|
-
const communities = pkc.communities;
|
|
168
|
-
if (communities.length === 0)
|
|
169
|
-
return;
|
|
170
|
-
const statuses = await Promise.all(communities.map(async (address) => {
|
|
171
|
-
const community = await pkc.createCommunity({ address });
|
|
172
|
-
return community.started;
|
|
173
|
-
}));
|
|
174
|
-
const startedCount = statuses.filter(Boolean).length;
|
|
175
|
-
const total = communities.length;
|
|
176
|
-
if (startedCount === total) {
|
|
177
|
-
this.log(` ${startedCount} ${startedCount === 1 ? "community" : "communities"} started.`);
|
|
178
|
-
}
|
|
179
|
-
else if (startedCount > 0) {
|
|
180
|
-
this.log(` ${startedCount} of ${total} communities started (remaining still loading).`);
|
|
181
|
-
}
|
|
182
|
-
else {
|
|
183
|
-
this.log(` ${total} ${total === 1 ? "community" : "communities"} in data path (still loading). Check with: bitsocial community list`);
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
catch {
|
|
187
|
-
this.warn("Could not check community status.");
|
|
188
|
-
}
|
|
189
|
-
finally {
|
|
190
|
-
if (pkc)
|
|
191
|
-
await pkc.destroy().catch(() => { });
|
|
192
|
-
}
|
|
145
|
+
this.log("Check community status with: bitsocial community list");
|
|
146
|
+
this.log("Check logs with: bitsocial logs");
|
|
193
147
|
}
|
|
194
148
|
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Attempt a fast update by downloading only the tarball and reusing node_modules/
|
|
3
|
+
* and dist/webuis/ from the existing install. Falls back (returns false) if
|
|
4
|
+
* dependencies changed or any step fails.
|
|
5
|
+
*/
|
|
6
|
+
export declare function fastInstallGlobal(version: string, installRoot: string, log: (msg: string) => void): Promise<boolean>;
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import { createHash } from "node:crypto";
|
|
3
|
+
import { createWriteStream } from "node:fs";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { Readable } from "node:stream";
|
|
6
|
+
import { finished as streamFinished } from "node:stream/promises";
|
|
7
|
+
import decompress from "decompress";
|
|
8
|
+
import { runNpmPack, ensureNpmAvailable } from "../challenge-packages/challenge-utils.js";
|
|
9
|
+
import { PACKAGE_NAME } from "./npm-registry.js";
|
|
10
|
+
/**
|
|
11
|
+
* Attempt a fast update by downloading only the tarball and reusing node_modules/
|
|
12
|
+
* and dist/webuis/ from the existing install. Falls back (returns false) if
|
|
13
|
+
* dependencies changed or any step fails.
|
|
14
|
+
*/
|
|
15
|
+
export async function fastInstallGlobal(version, installRoot, log) {
|
|
16
|
+
const stagingDir = installRoot + ".__fast_update_staging";
|
|
17
|
+
const backupDir = installRoot + ".__fast_update_backup";
|
|
18
|
+
// Phase: cleanup leftovers from any interrupted previous fast-update
|
|
19
|
+
await fs.rm(stagingDir, { recursive: true, force: true });
|
|
20
|
+
await fs.rm(backupDir, { recursive: true, force: true });
|
|
21
|
+
let nodeModulesMoved = false;
|
|
22
|
+
try {
|
|
23
|
+
// Phase: download tarball
|
|
24
|
+
await ensureNpmAvailable();
|
|
25
|
+
await fs.mkdir(stagingDir, { recursive: true });
|
|
26
|
+
log("Trying fast update...");
|
|
27
|
+
const tgzPath = await runNpmPack(`${PACKAGE_NAME}@${version}`, stagingDir);
|
|
28
|
+
// Phase: extract (strip: 1 removes the "package/" prefix in npm tarballs)
|
|
29
|
+
await decompress(tgzPath, stagingDir, { strip: 1 });
|
|
30
|
+
await fs.rm(tgzPath);
|
|
31
|
+
// Phase: compare dependencies
|
|
32
|
+
const oldPkg = JSON.parse(await fs.readFile(path.join(installRoot, "package.json"), "utf-8"));
|
|
33
|
+
const newPkg = JSON.parse(await fs.readFile(path.join(stagingDir, "package.json"), "utf-8"));
|
|
34
|
+
if (JSON.stringify(oldPkg.dependencies) !== JSON.stringify(newPkg.dependencies)) {
|
|
35
|
+
log("Dependencies changed, falling back to full install.");
|
|
36
|
+
await fs.rm(stagingDir, { recursive: true, force: true });
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
// Phase: reuse node_modules from old install (O(1) rename, same filesystem)
|
|
40
|
+
try {
|
|
41
|
+
await fs.rename(path.join(installRoot, "node_modules"), path.join(stagingDir, "node_modules"));
|
|
42
|
+
nodeModulesMoved = true;
|
|
43
|
+
}
|
|
44
|
+
catch (err) {
|
|
45
|
+
if (err.code === "EXDEV") {
|
|
46
|
+
log("Cross-device rename, falling back to full install.");
|
|
47
|
+
await fs.rm(stagingDir, { recursive: true, force: true });
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
throw err;
|
|
51
|
+
}
|
|
52
|
+
// Phase: reuse unchanged webuis, download only changed ones
|
|
53
|
+
const oldWebuis = oldPkg.webuis ?? [];
|
|
54
|
+
const newWebuis = newPkg.webuis ?? [];
|
|
55
|
+
const oldWebuisByUrl = new Map(oldWebuis.map((w) => [w.url, w.sha256OfHtmlZip]));
|
|
56
|
+
// Move the entire dist/webuis/ directory from old install to staging
|
|
57
|
+
const oldWebuisDir = path.join(installRoot, "dist", "webuis");
|
|
58
|
+
const newWebuisDir = path.join(stagingDir, "dist", "webuis");
|
|
59
|
+
let webuisDirMoved = false;
|
|
60
|
+
try {
|
|
61
|
+
await fs.access(oldWebuisDir);
|
|
62
|
+
await fs.rename(oldWebuisDir, newWebuisDir);
|
|
63
|
+
webuisDirMoved = true;
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
// webuis dir missing in old install — will create fresh
|
|
67
|
+
}
|
|
68
|
+
// Identify which webui entries changed
|
|
69
|
+
const changedWebuis = newWebuis.filter((w) => oldWebuisByUrl.get(w.url) !== w.sha256OfHtmlZip);
|
|
70
|
+
if (changedWebuis.length > 0 && webuisDirMoved) {
|
|
71
|
+
// Delete subdirectories for changed webuis so they get re-downloaded.
|
|
72
|
+
// We don't know exact dir names, but we can match by repo name from the URL.
|
|
73
|
+
const existingDirs = await fs.readdir(newWebuisDir, { withFileTypes: true })
|
|
74
|
+
.catch(() => []);
|
|
75
|
+
for (const changed of changedWebuis) {
|
|
76
|
+
const repoName = extractRepoName(changed.url);
|
|
77
|
+
if (!repoName)
|
|
78
|
+
continue;
|
|
79
|
+
for (const entry of existingDirs) {
|
|
80
|
+
if (entry.isDirectory() && entry.name.toLowerCase().includes(repoName.toLowerCase())) {
|
|
81
|
+
await fs.rm(path.join(newWebuisDir, entry.name), { recursive: true, force: true });
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
// Phase: atomic swap
|
|
87
|
+
await fs.rename(installRoot, backupDir);
|
|
88
|
+
await fs.rename(stagingDir, installRoot);
|
|
89
|
+
nodeModulesMoved = false; // now part of installRoot, no rollback needed
|
|
90
|
+
// Phase: download changed/missing webuis
|
|
91
|
+
if (changedWebuis.length > 0 || !webuisDirMoved) {
|
|
92
|
+
const webuisDir = path.join(installRoot, "dist", "webuis");
|
|
93
|
+
await fs.mkdir(webuisDir, { recursive: true });
|
|
94
|
+
const toDownload = !webuisDirMoved ? newWebuis : changedWebuis;
|
|
95
|
+
if (toDownload.length > 0) {
|
|
96
|
+
log(`Downloading ${toDownload.length} changed web UI(s)...`);
|
|
97
|
+
for (const entry of toDownload) {
|
|
98
|
+
try {
|
|
99
|
+
await downloadWebui(entry, webuisDir, log);
|
|
100
|
+
}
|
|
101
|
+
catch (err) {
|
|
102
|
+
log(`Warning: failed to download ${entry.url}: ${err.message}`);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
// Phase: cleanup backup
|
|
108
|
+
await fs.rm(backupDir, { recursive: true, force: true });
|
|
109
|
+
return true;
|
|
110
|
+
}
|
|
111
|
+
catch (err) {
|
|
112
|
+
// Rollback: restore node_modules if we moved it out
|
|
113
|
+
if (nodeModulesMoved) {
|
|
114
|
+
try {
|
|
115
|
+
await fs.rename(path.join(stagingDir, "node_modules"), path.join(installRoot, "node_modules"));
|
|
116
|
+
}
|
|
117
|
+
catch {
|
|
118
|
+
// best-effort
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
// If installRoot was renamed to backup but staging didn't land, restore it
|
|
122
|
+
try {
|
|
123
|
+
await fs.access(installRoot);
|
|
124
|
+
}
|
|
125
|
+
catch {
|
|
126
|
+
try {
|
|
127
|
+
await fs.rename(backupDir, installRoot);
|
|
128
|
+
}
|
|
129
|
+
catch {
|
|
130
|
+
// best-effort
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
await fs.rm(stagingDir, { recursive: true, force: true });
|
|
134
|
+
log(`Fast update failed: ${err.message}`);
|
|
135
|
+
return false;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
/** Extract repo name from a GitHub release URL (e.g. "seedit" from ".../plebbit/seedit/releases/tag/v0.5.10") */
|
|
139
|
+
function extractRepoName(url) {
|
|
140
|
+
const match = url.match(/github\.com\/[^/]+\/([^/]+)\/releases\/tag\//);
|
|
141
|
+
return match?.[1];
|
|
142
|
+
}
|
|
143
|
+
/** Download a single webui entry — mirrors the logic in bin/postinstall.js */
|
|
144
|
+
async function downloadWebui(entry, webuisDir, log) {
|
|
145
|
+
const match = entry.url.match(/github\.com\/([^/]+\/[^/]+)\/releases\/tag\/(.+)$/);
|
|
146
|
+
if (!match)
|
|
147
|
+
throw new Error(`Could not parse GitHub release URL: ${entry.url}`);
|
|
148
|
+
const [, ownerRepo, tag] = match;
|
|
149
|
+
const githubToken = process.env["GITHUB_TOKEN"];
|
|
150
|
+
const headers = {};
|
|
151
|
+
if (githubToken)
|
|
152
|
+
headers["authorization"] = `Bearer ${githubToken}`;
|
|
153
|
+
const releaseReq = await fetch(`https://api.github.com/repos/${ownerRepo}/releases/tags/${tag}`, { headers });
|
|
154
|
+
if (!releaseReq.ok)
|
|
155
|
+
throw new Error(`Failed to fetch release ${ownerRepo}@${tag}, status ${releaseReq.status}`);
|
|
156
|
+
const release = await releaseReq.json();
|
|
157
|
+
const htmlZipAsset = release.assets.find((asset) => asset.name.includes("html"));
|
|
158
|
+
if (!htmlZipAsset)
|
|
159
|
+
throw new Error(`No HTML zip asset in ${ownerRepo}@${tag}`);
|
|
160
|
+
const zipfilePath = path.join(webuisDir, htmlZipAsset.name);
|
|
161
|
+
const downloadReq = await fetch(htmlZipAsset["browser_download_url"], { headers });
|
|
162
|
+
if (!downloadReq.ok || !downloadReq.body)
|
|
163
|
+
throw new Error(`Failed to download ${htmlZipAsset.name}, status ${downloadReq.status}`);
|
|
164
|
+
const writer = createWriteStream(zipfilePath);
|
|
165
|
+
await streamFinished(Readable.fromWeb(downloadReq.body).pipe(writer));
|
|
166
|
+
writer.close();
|
|
167
|
+
// Verify SHA-256 checksum
|
|
168
|
+
const fileBuffer = await fs.readFile(zipfilePath);
|
|
169
|
+
const actualHash = createHash("sha256").update(fileBuffer).digest("hex");
|
|
170
|
+
if (actualHash !== entry.sha256OfHtmlZip) {
|
|
171
|
+
await fs.rm(zipfilePath);
|
|
172
|
+
throw new Error(`SHA-256 mismatch for ${htmlZipAsset.name}! Expected: ${entry.sha256OfHtmlZip}, Actual: ${actualHash}`);
|
|
173
|
+
}
|
|
174
|
+
await decompress(zipfilePath, webuisDir);
|
|
175
|
+
await fs.rm(zipfilePath);
|
|
176
|
+
// Rename index.html to prevent access to unconfigured version
|
|
177
|
+
const extractedDirName = htmlZipAsset.name.replace(".zip", "");
|
|
178
|
+
const indexPath = path.join(webuisDir, extractedDirName, "index.html");
|
|
179
|
+
const backupPath = path.join(webuisDir, extractedDirName, "index_backup_no_rpc.html");
|
|
180
|
+
await fs.rename(indexPath, backupPath);
|
|
181
|
+
log(`Downloaded ${ownerRepo}@${tag}`);
|
|
182
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { spawn } from "child_process";
|
|
2
2
|
import { getNpmCliPath, getNpmEnv, ensureNpmAvailable } from "../challenge-packages/challenge-utils.js";
|
|
3
|
-
const PACKAGE_NAME = "@bitsocial/bitsocial-cli";
|
|
3
|
+
export const PACKAGE_NAME = "@bitsocial/bitsocial-cli";
|
|
4
4
|
function runNpmView(args) {
|
|
5
5
|
return new Promise(async (resolve, reject) => {
|
|
6
6
|
const npmCliPath = await getNpmCliPath();
|
package/oclif.manifest.json
CHANGED
|
@@ -388,11 +388,11 @@
|
|
|
388
388
|
"required": true
|
|
389
389
|
}
|
|
390
390
|
},
|
|
391
|
-
"description": "Edit a community's properties. For a list of properties, visit https://github.com/pkcprotocol/pkc-js",
|
|
391
|
+
"description": "Edit a community's properties. For a list of properties, visit https://github.com/pkcprotocol/pkc-js\n\nMerge behavior with CLI flags:\n - Objects are merged with the community's current state (new keys are added, existing keys are overwritten).\n - Arrays are extended: new values are prepended to the existing array.\n - Setting a value to null removes it (e.g. --roles['mod.bso'] null).\n\nMerge behavior with --jsonFile:\n - Objects are merged the same way as CLI flags.\n - Arrays are replaced entirely (RFC 7396 JSON Merge Patch semantics).\n - When both --jsonFile and CLI flags are provided, CLI flags take priority.\n\nFor modifying complex settings like challenges, consider using a web UI instead: https://bitsocial.net/apps",
|
|
392
392
|
"examples": [
|
|
393
393
|
{
|
|
394
|
-
"description": "Change the
|
|
395
|
-
"command": "bitsocial community edit 12D3KooWG3XbzoVyAE6Y9vHZKF64Yuuu4TjdgQKedk14iYmTEPWu --
|
|
394
|
+
"description": "Change the name of the community",
|
|
395
|
+
"command": "bitsocial community edit 12D3KooWG3XbzoVyAE6Y9vHZKF64Yuuu4TjdgQKedk14iYmTEPWu --name newName.bso"
|
|
396
396
|
},
|
|
397
397
|
{
|
|
398
398
|
"description": "Add the author address 'esteban.bso' as an admin on the community",
|
|
@@ -576,6 +576,10 @@
|
|
|
576
576
|
{
|
|
577
577
|
"description": "Start all communities in your data path",
|
|
578
578
|
"command": "bitsocial community start $(bitsocial community list -q)"
|
|
579
|
+
},
|
|
580
|
+
{
|
|
581
|
+
"description": "Start communities sequentially (no concurrency)",
|
|
582
|
+
"command": "bitsocial community start $(bitsocial community list -q) --concurrency 1"
|
|
579
583
|
}
|
|
580
584
|
],
|
|
581
585
|
"flags": {
|
|
@@ -587,6 +591,14 @@
|
|
|
587
591
|
"hasDynamicHelp": false,
|
|
588
592
|
"multiple": false,
|
|
589
593
|
"type": "option"
|
|
594
|
+
},
|
|
595
|
+
"concurrency": {
|
|
596
|
+
"description": "Number of communities to start in parallel",
|
|
597
|
+
"name": "concurrency",
|
|
598
|
+
"default": 5,
|
|
599
|
+
"hasDynamicHelp": false,
|
|
600
|
+
"multiple": false,
|
|
601
|
+
"type": "option"
|
|
590
602
|
}
|
|
591
603
|
},
|
|
592
604
|
"hasDynamicHelp": false,
|
|
@@ -758,5 +770,5 @@
|
|
|
758
770
|
]
|
|
759
771
|
}
|
|
760
772
|
},
|
|
761
|
-
"version": "0.19.
|
|
773
|
+
"version": "0.19.48"
|
|
762
774
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bitsocial/bitsocial-cli",
|
|
3
|
-
"version": "0.19.
|
|
3
|
+
"version": "0.19.48",
|
|
4
4
|
"description": "Command line interface to Bitsocial API",
|
|
5
5
|
"types": "./dist/index.d.ts",
|
|
6
6
|
"homepage": "https://github.com/bitsocialnet/bitsocial-cli",
|
|
@@ -118,13 +118,14 @@
|
|
|
118
118
|
"@oclif/plugin-help": "6.2.36",
|
|
119
119
|
"@oclif/plugin-not-found": "3.2.73",
|
|
120
120
|
"@oclif/table": "0.5.1",
|
|
121
|
-
"@pkcprotocol/pkc-js": "0.0.
|
|
121
|
+
"@pkcprotocol/pkc-js": "0.0.19",
|
|
122
122
|
"dataobject-parser": "1.2.22",
|
|
123
123
|
"decompress": "4.2.1",
|
|
124
124
|
"env-paths": "2.2.1",
|
|
125
125
|
"exit-hook": "4.0.0",
|
|
126
126
|
"express": "4.19.2",
|
|
127
127
|
"kubo": "0.40.1",
|
|
128
|
+
"p-limit": "7.3.0",
|
|
128
129
|
"strip-json-comments": "5.0.3",
|
|
129
130
|
"tcp-port-used": "1.0.2",
|
|
130
131
|
"tslib": "2.6.2",
|