@bitsocial/bitsocial-cli 0.19.45 → 0.19.47

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 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.45/src/cli/commands/challenge/install.ts)_
347
+ _See code: [src/cli/commands/challenge/install.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.47/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.45/src/cli/commands/challenge/list.ts)_
370
+ _See code: [src/cli/commands/challenge/list.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.47/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.45/src/cli/commands/challenge/remove.ts)_
395
+ _See code: [src/cli/commands/challenge/remove.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.47/src/cli/commands/challenge/remove.ts)_
396
396
 
397
397
  ## `bitsocial community create`
398
398
 
@@ -400,12 +400,13 @@ Create a community with specific properties. A newly created community will be s
400
400
 
401
401
  ```
402
402
  USAGE
403
- $ bitsocial community create --pkcRpcUrl <value> [--privateKeyPath <value>]
403
+ $ bitsocial community create --pkcRpcUrl <value> [--privateKeyPath <value>] [-f <value>]
404
404
 
405
405
  FLAGS
406
- --pkcRpcUrl=<value> (required) [default: ws://localhost:9138/] URL to PKC RPC
407
- --privateKeyPath=<value> Private key (PEM) of the community signer that will be used to determine address (if address
408
- is not a domain). If it's not provided then PKC will generate a private key
406
+ -f, --jsonFile=<value> Path to a JSON/JSONC file containing create options (supports comments)
407
+ --pkcRpcUrl=<value> (required) [default: ws://localhost:9138/] URL to PKC RPC
408
+ --privateKeyPath=<value> Private key (PEM) of the community signer that will be used to determine address (if
409
+ address is not a domain). If it's not provided then PKC will generate a private key
409
410
 
410
411
  DESCRIPTION
411
412
  Create a community with specific properties. A newly created community will be started after creation and be able to
@@ -415,9 +416,13 @@ EXAMPLES
415
416
  Create a community with title 'Hello Plebs' and description 'Welcome'
416
417
 
417
418
  $ bitsocial community create --title 'Hello Plebs' --description 'Welcome'
419
+
420
+ Create a community using options from a JSON/JSONC file
421
+
422
+ $ bitsocial community create --jsonFile ./create-options.json
418
423
  ```
419
424
 
420
- _See code: [src/cli/commands/community/create.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.45/src/cli/commands/community/create.ts)_
425
+ _See code: [src/cli/commands/community/create.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.47/src/cli/commands/community/create.ts)_
421
426
 
422
427
  ## `bitsocial community delete ADDRESSES`
423
428
 
@@ -442,7 +447,7 @@ EXAMPLES
442
447
  $ bitsocial community delete 12D3KooWG3XbzoVyAE6Y9vHZKF64Yuuu4TjdgQKedk14iYmTEPWu
443
448
  ```
444
449
 
445
- _See code: [src/cli/commands/community/delete.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.45/src/cli/commands/community/delete.ts)_
450
+ _See code: [src/cli/commands/community/delete.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.47/src/cli/commands/community/delete.ts)_
446
451
 
447
452
  ## `bitsocial community edit ADDRESS`
448
453
 
@@ -456,16 +461,28 @@ ARGUMENTS
456
461
  ADDRESS Address of the community to edit. It could be the name domain, or a public key
457
462
 
458
463
  FLAGS
459
- -f, --jsonFile=<value> Path to a JSON file containing edit options
464
+ -f, --jsonFile=<value> Path to a JSON/JSONC file containing edit options (supports comments)
460
465
  --pkcRpcUrl=<value> (required) [default: ws://localhost:9138/] URL to PKC RPC
461
466
 
462
467
  DESCRIPTION
463
468
  Edit a community's properties. For a list of properties, visit https://github.com/pkcprotocol/pkc-js
464
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
+
465
482
  EXAMPLES
466
- Change the address of the community to a new domain address
483
+ Change the name of the community
467
484
 
468
- $ bitsocial community edit 12D3KooWG3XbzoVyAE6Y9vHZKF64Yuuu4TjdgQKedk14iYmTEPWu --address newAddress.bso
485
+ $ bitsocial community edit 12D3KooWG3XbzoVyAE6Y9vHZKF64Yuuu4TjdgQKedk14iYmTEPWu --name newName.bso
469
486
 
470
487
  Add the author address 'esteban.bso' as an admin on the community
471
488
 
@@ -495,12 +512,12 @@ EXAMPLES
495
512
 
496
513
  $ bitsocial community edit bitsocial.bso --settings.fetchThumbnailUrls=false
497
514
 
498
- Edit a community using options from a JSON file
515
+ Edit a community using options from a JSON/JSONC file
499
516
 
500
517
  $ bitsocial community edit bitsocial.bso --jsonFile ./edit-options.json
501
518
  ```
502
519
 
503
- _See code: [src/cli/commands/community/edit.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.45/src/cli/commands/community/edit.ts)_
520
+ _See code: [src/cli/commands/community/edit.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.47/src/cli/commands/community/edit.ts)_
504
521
 
505
522
  ## `bitsocial community get [ADDRESS]`
506
523
 
@@ -531,7 +548,7 @@ EXAMPLES
531
548
  $ bitsocial community get --publicKey 12D3KooWG3XbzoVyAE6Y9vHZKF64Yuuu4TjdgQKedk14iYmTEPWu
532
549
  ```
533
550
 
534
- _See code: [src/cli/commands/community/get.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.45/src/cli/commands/community/get.ts)_
551
+ _See code: [src/cli/commands/community/get.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.47/src/cli/commands/community/get.ts)_
535
552
 
536
553
  ## `bitsocial community list`
537
554
 
@@ -554,7 +571,7 @@ EXAMPLES
554
571
  $ bitsocial community list
555
572
  ```
556
573
 
557
- _See code: [src/cli/commands/community/list.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.45/src/cli/commands/community/list.ts)_
574
+ _See code: [src/cli/commands/community/list.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.47/src/cli/commands/community/list.ts)_
558
575
 
559
576
  ## `bitsocial community start ADDRESSES`
560
577
 
@@ -562,13 +579,14 @@ Start a community
562
579
 
563
580
  ```
564
581
  USAGE
565
- $ bitsocial community start ADDRESSES... --pkcRpcUrl <value>
582
+ $ bitsocial community start ADDRESSES... --pkcRpcUrl <value> [--concurrency <value>]
566
583
 
567
584
  ARGUMENTS
568
585
  ADDRESSES... Addresses of communities to start. Separated by space
569
586
 
570
587
  FLAGS
571
- --pkcRpcUrl=<value> (required) [default: ws://localhost:9138/] URL to PKC RPC
588
+ --concurrency=<value> [default: 5] Number of communities to start in parallel
589
+ --pkcRpcUrl=<value> (required) [default: ws://localhost:9138/] URL to PKC RPC
572
590
 
573
591
  DESCRIPTION
574
592
  Start a community
@@ -581,9 +599,13 @@ EXAMPLES
581
599
  Start all communities in your data path
582
600
 
583
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
584
606
  ```
585
607
 
586
- _See code: [src/cli/commands/community/start.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.45/src/cli/commands/community/start.ts)_
608
+ _See code: [src/cli/commands/community/start.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.47/src/cli/commands/community/start.ts)_
587
609
 
588
610
  ## `bitsocial community stop ADDRESSES`
589
611
 
@@ -608,7 +630,7 @@ EXAMPLES
608
630
  $ bitsocial community stop Qmb99crTbSUfKXamXwZBe829Vf6w5w5TktPkb6WstC9RFW
609
631
  ```
610
632
 
611
- _See code: [src/cli/commands/community/stop.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.45/src/cli/commands/community/stop.ts)_
633
+ _See code: [src/cli/commands/community/stop.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.47/src/cli/commands/community/stop.ts)_
612
634
 
613
635
  ## `bitsocial daemon`
614
636
 
@@ -649,7 +671,7 @@ EXAMPLES
649
671
  $ bitsocial daemon --chainProviderUrls https://mainnet.infura.io/v3/YOUR_KEY
650
672
  ```
651
673
 
652
- _See code: [src/cli/commands/daemon.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.45/src/cli/commands/daemon.ts)_
674
+ _See code: [src/cli/commands/daemon.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.47/src/cli/commands/daemon.ts)_
653
675
 
654
676
  ## `bitsocial help [COMMAND]`
655
677
 
@@ -677,7 +699,8 @@ View the latest BitSocial daemon log file. By default dumps the full log and exi
677
699
 
678
700
  ```
679
701
  USAGE
680
- $ bitsocial logs [-f] [-n <value>] [--since <value>] [--until <value>] [--logPath <value>]
702
+ $ bitsocial logs [-f] [-n <value>] [--since <value>] [--until <value>] [--logPath <value>] [--stdout |
703
+ --stderr]
681
704
 
682
705
  FLAGS
683
706
  -f, --follow Follow log output in real-time (like tail -f)
@@ -685,6 +708,8 @@ FLAGS
685
708
  --logPath=<value> Specify the directory containing log files
686
709
  --since=<value> Show logs since timestamp (ISO 8601, e.g. 2026-01-02T13:23:37Z) or relative time (e.g. 30s,
687
710
  42m, 2h, 1d)
711
+ --stderr Show only stderr log entries (output of pkc-logger library)
712
+ --stdout Show only stdout log entries
688
713
  --until=<value> Show logs before timestamp (ISO 8601, e.g. 2026-01-02T13:23:37Z) or relative time (e.g. 30s,
689
714
  42m, 2h, 1d)
690
715
 
@@ -704,9 +729,15 @@ EXAMPLES
704
729
  $ bitsocial logs --since 2026-01-02T13:23:37Z --until 2026-01-02T14:00:00Z
705
730
 
706
731
  $ bitsocial logs --since 1h -f
732
+
733
+ $ bitsocial logs --stdout
734
+
735
+ $ bitsocial logs --stderr
736
+
737
+ $ bitsocial logs --stdout -f
707
738
  ```
708
739
 
709
- _See code: [src/cli/commands/logs.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.45/src/cli/commands/logs.ts)_
740
+ _See code: [src/cli/commands/logs.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.47/src/cli/commands/logs.ts)_
710
741
 
711
742
  ## `bitsocial update check`
712
743
 
@@ -723,7 +754,7 @@ EXAMPLES
723
754
  $ bitsocial update check
724
755
  ```
725
756
 
726
- _See code: [src/cli/commands/update/check.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.45/src/cli/commands/update/check.ts)_
757
+ _See code: [src/cli/commands/update/check.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.47/src/cli/commands/update/check.ts)_
727
758
 
728
759
  ## `bitsocial update install [VERSION]`
729
760
 
@@ -755,7 +786,7 @@ EXAMPLES
755
786
  $ bitsocial update install --no-restart-daemons
756
787
  ```
757
788
 
758
- _See code: [src/cli/commands/update/install.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.45/src/cli/commands/update/install.ts)_
789
+ _See code: [src/cli/commands/update/install.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.47/src/cli/commands/update/install.ts)_
759
790
 
760
791
  ## `bitsocial update versions`
761
792
 
@@ -777,7 +808,7 @@ EXAMPLES
777
808
  $ bitsocial update versions --limit 5
778
809
  ```
779
810
 
780
- _See code: [src/cli/commands/update/versions.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.45/src/cli/commands/update/versions.ts)_
811
+ _See code: [src/cli/commands/update/versions.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.47/src/cli/commands/update/versions.ts)_
781
812
  <!-- commandsstop -->
782
813
 
783
814
  ## Contribution
@@ -7,6 +7,7 @@ export default class Create extends BaseCommand {
7
7
  }[];
8
8
  static flags: {
9
9
  privateKeyPath: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
10
+ jsonFile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
10
11
  };
11
12
  run(): Promise<void>;
12
13
  }
@@ -3,7 +3,7 @@ import { Flags } from "@oclif/core";
3
3
  import DataObjectParser from "dataobject-parser";
4
4
  import fs from "fs";
5
5
  import { BaseCommand } from "../../base-command.js";
6
- import { PKCLogger } from "../../../util.js";
6
+ import { PKCLogger, mergeDeep, parseJsoncFile } from "../../../util.js";
7
7
  import * as remeda from "remeda";
8
8
  export default class Create extends BaseCommand {
9
9
  static description = "Create a community with specific properties. A newly created community will be started after creation and be able to receive publications. For a list of properties, visit https://github.com/pkcprotocol/pkc-js";
@@ -11,12 +11,21 @@ export default class Create extends BaseCommand {
11
11
  {
12
12
  description: "Create a community with title 'Hello Plebs' and description 'Welcome'",
13
13
  command: "<%= config.bin %> <%= command.id %> --title 'Hello Plebs' --description 'Welcome'"
14
+ },
15
+ {
16
+ description: "Create a community using options from a JSON/JSONC file",
17
+ command: "<%= config.bin %> <%= command.id %> --jsonFile ./create-options.json"
14
18
  }
15
19
  ];
16
20
  static flags = {
17
21
  privateKeyPath: Flags.file({
18
22
  exists: true,
19
23
  description: "Private key (PEM) of the community signer that will be used to determine address (if address is not a domain). If it's not provided then PKC will generate a private key"
24
+ }),
25
+ jsonFile: Flags.file({
26
+ char: "f",
27
+ exists: true,
28
+ description: "Path to a JSON/JSONC file containing create options (supports comments)"
20
29
  })
21
30
  };
22
31
  async run() {
@@ -24,7 +33,34 @@ export default class Create extends BaseCommand {
24
33
  const log = PKCLogger("bitsocial-cli:commands:community:create");
25
34
  log(`flags: `, flags);
26
35
  const pkc = await this._connectToPkcRpc(flags.pkcRpcUrl.toString());
27
- const createOptions = DataObjectParser.transpose(remeda.omit(flags, ["pkcRpcUrl", "privateKeyPath"]))["_data"];
36
+ const cliCreateOptions = DataObjectParser.transpose(remeda.omit(flags, ["pkcRpcUrl", "privateKeyPath", "jsonFile"]))["_data"];
37
+ // Parse JSONC file if provided
38
+ let jsonFileOptions = {};
39
+ if (flags.jsonFile) {
40
+ try {
41
+ jsonFileOptions = await parseJsoncFile(flags.jsonFile);
42
+ log("JSONC file options parsed:", jsonFileOptions);
43
+ }
44
+ catch (e) {
45
+ if (e instanceof Error) {
46
+ await pkc.destroy();
47
+ this.error(e.message);
48
+ }
49
+ throw e;
50
+ }
51
+ }
52
+ // Merge: JSON file options first, then CLI flags override
53
+ let createOptions;
54
+ if (flags.jsonFile && Object.keys(cliCreateOptions).length > 0) {
55
+ createOptions = mergeDeep(jsonFileOptions, cliCreateOptions);
56
+ }
57
+ else if (flags.jsonFile) {
58
+ createOptions = jsonFileOptions;
59
+ }
60
+ else {
61
+ createOptions = cliCreateOptions;
62
+ }
63
+ log("Final create options:", createOptions);
28
64
  if (flags.privateKeyPath)
29
65
  try {
30
66
  //@ts-expect-error
@@ -1,12 +1,23 @@
1
1
  //@ts-expect-error
2
2
  import DataObjectParser from "dataobject-parser";
3
3
  import { Args, Flags } from "@oclif/core";
4
- import fs from "fs";
5
4
  import { BaseCommand } from "../../base-command.js";
6
- import { PKCLogger, mergeDeep } from "../../../util.js";
5
+ import { PKCLogger, mergeDeep, parseJsoncFile, replaceNullWithUndefined } from "../../../util.js";
7
6
  import * as remeda from "remeda";
8
7
  export default class Edit extends BaseCommand {
9
- static description = "Edit a community's properties. For a list of properties, visit https://github.com/pkcprotocol/pkc-js";
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`;
10
21
  static args = {
11
22
  address: Args.string({
12
23
  name: "address",
@@ -18,16 +29,13 @@ export default class Edit extends BaseCommand {
18
29
  jsonFile: Flags.file({
19
30
  char: "f",
20
31
  exists: true,
21
- description: "Path to a JSON file containing edit options"
32
+ description: "Path to a JSON/JSONC file containing edit options (supports comments)"
22
33
  })
23
34
  };
24
35
  static examples = [
25
- // TODO update this to change the name instead
26
- // Also are we testing modifying name properly?
27
- // in theory user should not modify address, they should modify name
28
36
  {
29
- description: "Change the address of the community to a new domain address",
30
- command: "bitsocial community edit 12D3KooWG3XbzoVyAE6Y9vHZKF64Yuuu4TjdgQKedk14iYmTEPWu --address newAddress.bso"
37
+ description: "Change the name of the community",
38
+ command: "bitsocial community edit 12D3KooWG3XbzoVyAE6Y9vHZKF64Yuuu4TjdgQKedk14iYmTEPWu --name newName.bso"
31
39
  },
32
40
  {
33
41
  description: "Add the author address 'esteban.bso' as an admin on the community",
@@ -54,7 +62,7 @@ export default class Edit extends BaseCommand {
54
62
  command: "bitsocial community edit bitsocial.bso --settings.fetchThumbnailUrls=false"
55
63
  },
56
64
  {
57
- description: "Edit a community using options from a JSON file",
65
+ description: "Edit a community using options from a JSON/JSONC file",
58
66
  command: "bitsocial community edit bitsocial.bso --jsonFile ./edit-options.json"
59
67
  }
60
68
  ];
@@ -65,21 +73,16 @@ export default class Edit extends BaseCommand {
65
73
  const pkc = await this._connectToPkcRpc(flags.pkcRpcUrl.toString());
66
74
  const cliEditOptions = DataObjectParser.transpose(remeda.omit(flags, ["pkcRpcUrl", "jsonFile"]))["_data"];
67
75
  log("CLI edit options parsed:", cliEditOptions);
68
- // Parse JSON file if provided
76
+ // Parse JSONC file if provided
69
77
  let jsonFileOptions = {};
70
78
  if (flags.jsonFile) {
71
79
  try {
72
- const fileContent = await fs.promises.readFile(flags.jsonFile, "utf-8");
73
- const parsed = JSON.parse(fileContent);
74
- if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
75
- this.error("JSON file must contain a JSON object (not an array, null, string, or number)");
76
- }
77
- jsonFileOptions = parsed;
78
- log("JSON file options parsed:", jsonFileOptions);
80
+ jsonFileOptions = await parseJsoncFile(flags.jsonFile);
81
+ log("JSONC file options parsed:", jsonFileOptions);
79
82
  }
80
83
  catch (e) {
81
- if (e instanceof SyntaxError) {
82
- this.error(`Invalid JSON in file ${flags.jsonFile}: ${e.message}`);
84
+ if (e instanceof Error) {
85
+ this.error(e.message);
83
86
  }
84
87
  throw e;
85
88
  }
@@ -102,9 +105,11 @@ export default class Edit extends BaseCommand {
102
105
  try {
103
106
  const community = await pkc.createCommunity({ address: args.address });
104
107
  const mergedState = remeda.pick(community, remeda.keys.strict(editOptions));
105
- const finalMergedState = mergeDeep(mergedState, editOptions);
108
+ // JSON file edits use RFC 7396 JSON Merge Patch semantics (arrays replace, objects merge).
109
+ // CLI flag edits use concat semantics (arrays extend with new values).
110
+ const finalMergedState = mergeDeep(mergedState, editOptions, flags.jsonFile ? "replace" : "concat");
106
111
  log("Internal community state after merge:", finalMergedState);
107
- await community.edit(finalMergedState);
112
+ await community.edit(replaceNullWithUndefined(finalMergedState));
108
113
  this.log(community.address);
109
114
  }
110
115
  catch (e) {
@@ -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
- for (const address of addresses) {
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
  }
@@ -96,12 +96,12 @@ export default class Daemon extends Command {
96
96
  const stdoutWrite = process.stdout.write.bind(process.stdout);
97
97
  const stderrWrite = process.stderr.write.bind(process.stderr);
98
98
  const isLogFileOverLimit = () => logFile.bytesWritten > 20000000; // 20mb
99
- const writeTimestampedLine = (text) => {
99
+ const writeTimestampedLine = (text, stream) => {
100
100
  if (isLogFileOverLimit())
101
101
  return;
102
102
  if (!text || text.trim().length === 0)
103
103
  return;
104
- const timestamp = `[${new Date().toISOString()}] `;
104
+ const timestamp = `[${new Date().toISOString()}] [${stream}] `;
105
105
  const lines = text.split("\n");
106
106
  const timestamped = lines.map((line, i) => (i === 0 ? timestamp + line : line)).join("\n");
107
107
  logFile.write(timestamped);
@@ -115,13 +115,13 @@ export default class Daemon extends Command {
115
115
  debugModule.inspectOpts.colors = true;
116
116
  debugModule.inspectOpts.hideDate = true;
117
117
  debugModule.log = (...args) => {
118
- writeTimestampedLine(formatWithOptions({ depth: Logger.inspectOpts?.depth || 10, colors: true }, ...args).trimStart() + EOL);
118
+ writeTimestampedLine(formatWithOptions({ depth: Logger.inspectOpts?.depth || 10, colors: true }, ...args).trimStart() + EOL, "stderr");
119
119
  };
120
120
  const asString = (data) => (typeof data === "string" ? data : Buffer.from(data).toString());
121
121
  process.stdout.write = (...args) => {
122
122
  //@ts-expect-error
123
123
  const res = stdoutWrite(...args);
124
- writeTimestampedLine(asString(args[0]));
124
+ writeTimestampedLine(asString(args[0]), "stdout");
125
125
  return res;
126
126
  };
127
127
  process.stderr.write = (...args) => {
@@ -129,7 +129,7 @@ export default class Daemon extends Command {
129
129
  // Debug output goes to stderr; we want it in logs only.
130
130
  // Real errors are caught by uncaughtException/unhandledRejection handlers
131
131
  // which use console.error -> stderr.write -> this override -> log file.
132
- writeTimestampedLine(asString(args[0]).trimStart());
132
+ writeTimestampedLine(asString(args[0]).trimStart(), "stderr");
133
133
  return true;
134
134
  };
135
135
  const log = Logger("bitsocial-cli:daemon");
@@ -1,6 +1,7 @@
1
1
  import { Command } from "@oclif/core";
2
2
  interface LogEntry {
3
3
  timestamp: Date | null;
4
+ stream: "stdout" | "stderr" | null;
4
5
  lines: string[];
5
6
  }
6
7
  export default class Logs extends Command {
@@ -11,13 +12,17 @@ export default class Logs extends Command {
11
12
  since: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
12
13
  until: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
13
14
  logPath: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
15
+ stdout: import("@oclif/core/interfaces").BooleanFlag<boolean>;
16
+ stderr: import("@oclif/core/interfaces").BooleanFlag<boolean>;
14
17
  };
15
18
  static examples: string[];
16
19
  private _findLatestLogFile;
17
20
  _parseTimestamp(value: string): Date;
18
21
  _extractTimestamp(line: string): Date | null;
22
+ _extractStream(line: string): "stdout" | "stderr" | null;
19
23
  _parseLogEntries(content: string): LogEntry[];
20
24
  _filterEntries(entries: LogEntry[], since?: Date, until?: Date): LogEntry[];
25
+ _filterByStream(entries: LogEntry[], stream: "stdout" | "stderr"): LogEntry[];
21
26
  _tailEntries(entries: LogEntry[], tailValue: string): LogEntry[];
22
27
  run(): Promise<void>;
23
28
  }