@bitsocial/bitsocial-cli 0.19.41 → 0.19.43

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
@@ -32,7 +32,7 @@ Bitsocial is p2p and decentralized social media protocol built completely with I
32
32
 
33
33
  ## Install
34
34
 
35
- Requires [Node.js 22](https://nodejs.org/) or later.
35
+ Requires Node.js 22 or later. We recommend using [nvm](https://github.com/nvm-sh/nvm) to install and manage Node.js versions.
36
36
 
37
37
  ```sh-session
38
38
  npm install -g @bitsocial/bitsocial-cli
@@ -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.41/src/cli/commands/challenge/install.ts)_
347
+ _See code: [src/cli/commands/challenge/install.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.43/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.41/src/cli/commands/challenge/list.ts)_
370
+ _See code: [src/cli/commands/challenge/list.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.43/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.41/src/cli/commands/challenge/remove.ts)_
395
+ _See code: [src/cli/commands/challenge/remove.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.43/src/cli/commands/challenge/remove.ts)_
396
396
 
397
397
  ## `bitsocial community create`
398
398
 
@@ -417,7 +417,7 @@ EXAMPLES
417
417
  $ bitsocial community create --title 'Hello Plebs' --description 'Welcome'
418
418
  ```
419
419
 
420
- _See code: [src/cli/commands/community/create.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.41/src/cli/commands/community/create.ts)_
420
+ _See code: [src/cli/commands/community/create.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.43/src/cli/commands/community/create.ts)_
421
421
 
422
422
  ## `bitsocial community delete ADDRESSES`
423
423
 
@@ -442,7 +442,7 @@ EXAMPLES
442
442
  $ bitsocial community delete 12D3KooWG3XbzoVyAE6Y9vHZKF64Yuuu4TjdgQKedk14iYmTEPWu
443
443
  ```
444
444
 
445
- _See code: [src/cli/commands/community/delete.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.41/src/cli/commands/community/delete.ts)_
445
+ _See code: [src/cli/commands/community/delete.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.43/src/cli/commands/community/delete.ts)_
446
446
 
447
447
  ## `bitsocial community edit ADDRESS`
448
448
 
@@ -494,7 +494,7 @@ EXAMPLES
494
494
  $ bitsocial community edit plebbit.bso --settings.fetchThumbnailUrls=false
495
495
  ```
496
496
 
497
- _See code: [src/cli/commands/community/edit.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.41/src/cli/commands/community/edit.ts)_
497
+ _See code: [src/cli/commands/community/edit.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.43/src/cli/commands/community/edit.ts)_
498
498
 
499
499
  ## `bitsocial community get [ADDRESS]`
500
500
 
@@ -525,7 +525,7 @@ EXAMPLES
525
525
  $ bitsocial community get --publicKey 12D3KooWG3XbzoVyAE6Y9vHZKF64Yuuu4TjdgQKedk14iYmTEPWu
526
526
  ```
527
527
 
528
- _See code: [src/cli/commands/community/get.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.41/src/cli/commands/community/get.ts)_
528
+ _See code: [src/cli/commands/community/get.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.43/src/cli/commands/community/get.ts)_
529
529
 
530
530
  ## `bitsocial community list`
531
531
 
@@ -548,7 +548,7 @@ EXAMPLES
548
548
  $ bitsocial community list
549
549
  ```
550
550
 
551
- _See code: [src/cli/commands/community/list.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.41/src/cli/commands/community/list.ts)_
551
+ _See code: [src/cli/commands/community/list.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.43/src/cli/commands/community/list.ts)_
552
552
 
553
553
  ## `bitsocial community start ADDRESSES`
554
554
 
@@ -577,7 +577,7 @@ EXAMPLES
577
577
  $ bitsocial community start $(bitsocial community list -q)
578
578
  ```
579
579
 
580
- _See code: [src/cli/commands/community/start.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.41/src/cli/commands/community/start.ts)_
580
+ _See code: [src/cli/commands/community/start.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.43/src/cli/commands/community/start.ts)_
581
581
 
582
582
  ## `bitsocial community stop ADDRESSES`
583
583
 
@@ -602,7 +602,7 @@ EXAMPLES
602
602
  $ bitsocial community stop Qmb99crTbSUfKXamXwZBe829Vf6w5w5TktPkb6WstC9RFW
603
603
  ```
604
604
 
605
- _See code: [src/cli/commands/community/stop.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.41/src/cli/commands/community/stop.ts)_
605
+ _See code: [src/cli/commands/community/stop.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.43/src/cli/commands/community/stop.ts)_
606
606
 
607
607
  ## `bitsocial daemon`
608
608
 
@@ -613,9 +613,10 @@ USAGE
613
613
  $ bitsocial daemon --pkcRpcUrl <value> --logPath <value> [--chainProviderUrls <value>...]
614
614
 
615
615
  FLAGS
616
- --chainProviderUrls=<value>... [default: viem,https://ethrpc.xyz] Ethereum RPC URL(s) for .bso/.eth name resolution.
617
- Can be specified multiple times. Defaults to viem public transport and
618
- https://ethrpc.xyz
616
+ --chainProviderUrls=<value>... [default:
617
+ https://eth.drpc.org,https://ethereum.publicnode.com,https://ethereum-rpc.publicnode.c
618
+ om,https://rpc.mevblocker.io,https://1rpc.io/eth,https://eth-pokt.nodies.app] RPC
619
+ URL(s) for .bso name resolution. Can be specified multiple times.
619
620
  --logPath=<value> (required) [default: /home/runner/.local/state/bitsocial] Specify a directory which
620
621
  will be used to store logs
621
622
  --pkcRpcUrl=<value> (required) [default: ws://localhost:9138/] Specify PKC RPC URL to listen on
@@ -640,11 +641,9 @@ EXAMPLES
640
641
  $ bitsocial daemon --pkcOptions.kuboRpcClientsOptions[0] https://remoteipfsnode.com
641
642
 
642
643
  $ bitsocial daemon --chainProviderUrls https://mainnet.infura.io/v3/YOUR_KEY
643
-
644
- $ bitsocial daemon --chainProviderUrls viem --chainProviderUrls https://mainnet.infura.io/v3/YOUR_KEY
645
644
  ```
646
645
 
647
- _See code: [src/cli/commands/daemon.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.41/src/cli/commands/daemon.ts)_
646
+ _See code: [src/cli/commands/daemon.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.43/src/cli/commands/daemon.ts)_
648
647
 
649
648
  ## `bitsocial help [COMMAND]`
650
649
 
@@ -701,7 +700,7 @@ EXAMPLES
701
700
  $ bitsocial logs --since 1h -f
702
701
  ```
703
702
 
704
- _See code: [src/cli/commands/logs.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.41/src/cli/commands/logs.ts)_
703
+ _See code: [src/cli/commands/logs.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.43/src/cli/commands/logs.ts)_
705
704
 
706
705
  ## `bitsocial update check`
707
706
 
@@ -718,7 +717,7 @@ EXAMPLES
718
717
  $ bitsocial update check
719
718
  ```
720
719
 
721
- _See code: [src/cli/commands/update/check.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.41/src/cli/commands/update/check.ts)_
720
+ _See code: [src/cli/commands/update/check.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.43/src/cli/commands/update/check.ts)_
722
721
 
723
722
  ## `bitsocial update install [VERSION]`
724
723
 
@@ -726,13 +725,14 @@ Install a specific version of bitsocial from npm
726
725
 
727
726
  ```
728
727
  USAGE
729
- $ bitsocial update install [VERSION] [--force]
728
+ $ bitsocial update install [VERSION] [--force] [--restart-daemons]
730
729
 
731
730
  ARGUMENTS
732
731
  [VERSION] [default: latest] Version to install (e.g. "0.19.40" or "latest")
733
732
 
734
733
  FLAGS
735
- --force Reinstall even if already on the requested version
734
+ --force Reinstall even if already on the requested version
735
+ --[no-]restart-daemons Stop all running daemons, update, and restart them with the same settings
736
736
 
737
737
  DESCRIPTION
738
738
  Install a specific version of bitsocial from npm
@@ -745,9 +745,11 @@ EXAMPLES
745
745
  $ bitsocial update install 0.19.40
746
746
 
747
747
  $ bitsocial update install --force
748
+
749
+ $ bitsocial update install --no-restart-daemons
748
750
  ```
749
751
 
750
- _See code: [src/cli/commands/update/install.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.41/src/cli/commands/update/install.ts)_
752
+ _See code: [src/cli/commands/update/install.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.43/src/cli/commands/update/install.ts)_
751
753
 
752
754
  ## `bitsocial update versions`
753
755
 
@@ -769,7 +771,7 @@ EXAMPLES
769
771
  $ bitsocial update versions --limit 5
770
772
  ```
771
773
 
772
- _See code: [src/cli/commands/update/versions.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.41/src/cli/commands/update/versions.ts)_
774
+ _See code: [src/cli/commands/update/versions.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.43/src/cli/commands/update/versions.ts)_
773
775
  <!-- commandsstop -->
774
776
 
775
777
  ## Contribution
@@ -0,0 +1 @@
1
+ export declare function printBanner(): void;
@@ -0,0 +1,94 @@
1
+ // ASCII banner, edited as two parallel grids.
2
+ //
3
+ // SHAPE holds the raw glyphs (braille art + figlet text). Leave these alone
4
+ // unless you want to change the art itself.
5
+ //
6
+ // COLORS is the paint map. Each character corresponds 1:1 to the character
7
+ // at the same position in SHAPE:
8
+ // B = blue (#1a4fd0) — the sphere
9
+ // S = silver (#e5e7eb) — the rings and the "Bitsocial" text
10
+ // . = no color (pass the glyph through as-is; use this for spaces)
11
+ //
12
+ // To retouch the art, find a glyph in SHAPE, then flip the character at the
13
+ // same column in the matching COLORS row. A common case: a ring cell came out
14
+ // blue because the sphere mask had more dots there — change its 'B' to 'S'.
15
+ //
16
+ // Both grids MUST have the same number of rows. Each row in COLORS must be at
17
+ // least as wide as the corresponding SHAPE row (extra chars are ignored).
18
+ // Palette sourced from bitsocialnet/bitsocial-web/about/tailwind.config.ts.
19
+ const SHAPE = [
20
+ " ⢀⣴⣿⣿⣦⡀ ",
21
+ " ⣾⣿⠁⠈⣿⣷⡀ ",
22
+ " ⢸⣿⡇ ⢸⣿⣇ ",
23
+ " ⢀⣀⣼⣿⣷⣶⣶⣶⣿⣿⣄⡀ ",
24
+ " ⢀⣠⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⣤⡀ ",
25
+ " ⣴⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣦ 888888b. d8b 888 d8b 888",
26
+ " ⢀⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⡀ 888 \"88b Y8P 888 Y8P 888",
27
+ " ⣀⣤⣤⣶⣶⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⣶⣶⣦⣤⣀ 888 .88P 888 888",
28
+ "⣰⣿⡿⠛⠉⠉ ⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿ ⠉⠉⠛⠟⣿⣦ 8888888K. 888 888888 .d8888b .d88b. .d8888b 888 8888b. 888",
29
+ "⠻⣷⣦⣤⣀⣀ ⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿ ⢀⣀⣀⣴⣿⠟ 888 \"Y88b 888 888 88K d88\"\"88b d88P\" 888 \"88b 888",
30
+ " ⠉⠛⠻⠿⠿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠿⠿⠟⠛⠉ 888 888 888 888 \"Y8888b. 888 888 888 888 .d888888 888",
31
+ " ⠈⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿ 888 d88P 888 Y88b. X88 Y88..88P Y88b. 888 888 888 888",
32
+ " ⠻⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠟ 8888888P\" 888 \"Y888 88888P' \"Y88P\" \"Y8888P 888 \"Y888888 888",
33
+ " ⠈⠻⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⠟⠁ ",
34
+ " ⠉⠛⢿⣿⣿⣿⣿⣿⣿⣿⠛⠉ ",
35
+ " ⢸⣿⡆ ⣿⡿ ",
36
+ " ⠈⢿⣷⡀⣸⣿⠃ ",
37
+ " ⠈⠿⣿⡿⠃ "
38
+ ];
39
+ const COLORS = [
40
+ "................SSSSSS.......................................................................................",
41
+ "................SSSSSSS......................................................................................",
42
+ "...............SSSSSSSS......................................................................................",
43
+ ".............BBBBBBBBSSBB....................................................................................",
44
+ "..........BBBBBBBBBBBSSBBBBB.................................................................................",
45
+ ".........BBBBBBBBBBBBSSBBBBBB...............SSSSSSSS...SSS.SSS...............................SSS..........SSS",
46
+ ".......BBBBBBBBBBBBBBSSBBBBBBBB.............SSS..SSSS..SSS.SSS...............................SSS..........SSS",
47
+ "..SSSSSBBBBBBBBBBBBBBSSBBBBBBBBSSSSSSS......SSS..SSSS......SSS............................................SSS",
48
+ "SSSSSS.BBBBBBBBBBBBBBSSBBBBBBBBSSSSSSS......SSSSSSSSS..SSS.SSSSSS.SSSSSSS...SSSSSS...SSSSSSS.SSS..SSSSSS..SSS",
49
+ "SSSSSS.BBBBBBBBBBBBBBSSBBBBBBBBSSSSSSS......SSS..SSSSS.SSS.SSS....SSS......SSSSSSSS.SSSSS....SSS.....SSSS.SSS",
50
+ "..SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS......SSS....SSS.SSS.SSS....SSSSSSSS.SSS..SSS.SSS......SSS.SSSSSSSS.SSS",
51
+ ".......BBBBBBBBBBBBBBSSBBBBBBBBBBBBBBB......SSS...SSSS.SSS.SSSSS.......SSS.SSSSSSSS.SSSSS....SSS.SSS..SSS.SSS",
52
+ ".........BBBBBBBBBBBBSSBBBBBBBBB............SSSSSSSSS..SSS..SSSSS..SSSSSSS..SSSSSS...SSSSSSS.SSS.SSSSSSSS.SSS",
53
+ "..........BBBBBBBBBBBSSBBBBBBB...............................................................................",
54
+ ".............BBBBBBBBSSBBBB..................................................................................",
55
+ "...............SSSSSSSSS.....................................................................................",
56
+ "...............SSSSSSSS......................................................................................",
57
+ "................SSSSSS......................................................................................."
58
+ ];
59
+ const BLUE = "\x1b[38;2;26;79;208m";
60
+ const SILVER = "\x1b[38;2;229;231;235m";
61
+ const RESET = "\x1b[0m";
62
+ function paint(shape, colors) {
63
+ let out = "";
64
+ let current = ".";
65
+ for (let i = 0; i < shape.length; i++) {
66
+ const glyph = shape[i];
67
+ const want = colors[i] ?? ".";
68
+ if (want !== current) {
69
+ if (current !== ".")
70
+ out += RESET;
71
+ if (want === "B")
72
+ out += BLUE;
73
+ else if (want === "S")
74
+ out += SILVER;
75
+ current = want;
76
+ }
77
+ out += glyph;
78
+ }
79
+ if (current !== ".")
80
+ out += RESET;
81
+ return out;
82
+ }
83
+ function supportsColor() {
84
+ if (process.env["NO_COLOR"])
85
+ return false;
86
+ if (process.env["FORCE_COLOR"])
87
+ return true;
88
+ return Boolean(process.stdout.isTTY);
89
+ }
90
+ export function printBanner() {
91
+ const useColor = supportsColor();
92
+ const lines = SHAPE.map((row, i) => (useColor ? paint(row, COLORS[i] ?? "") : row));
93
+ process.stdout.write(lines.join("\n") + "\n\n");
94
+ }
@@ -5,9 +5,11 @@ import path from "path";
5
5
  import tcpPortUsed from "tcp-port-used";
6
6
  import { getLanIpV4Address, PKCLogger, setupDebugLogger, loadKuboConfigFile, parseMultiAddrKuboRpcToUrl, parseMultiAddrIpfsGatewayToUrl } from "../../util.js";
7
7
  import { startDaemonServer } from "../../webui/daemon-server.js";
8
+ import { printBanner } from "../ascii-banner.js";
8
9
  import { loadChallengesIntoPKC } from "../../challenge-packages/challenge-utils.js";
9
10
  import { migrateDataDirectory } from "../../common-utils/data-migration.js";
10
- import { createBsoResolvers } from "../../common-utils/resolvers.js";
11
+ import { createBsoResolvers, DEFAULT_PROVIDERS } from "../../common-utils/resolvers.js";
12
+ import { pruneStaleStates, writeDaemonState, deleteDaemonState } from "../../common-utils/daemon-state.js";
11
13
  import fs from "fs";
12
14
  import fsPromise from "fs/promises";
13
15
  /** Replace wildcard bind addresses with loopback for connectivity checks (macOS rejects connect to 0.0.0.0 with EINVAL) */
@@ -47,9 +49,9 @@ export default class Daemon extends Command {
47
49
  default: defaults.PKC_LOG_PATH
48
50
  }),
49
51
  chainProviderUrls: Flags.string({
50
- description: 'Ethereum RPC URL(s) for .bso/.eth name resolution. Can be specified multiple times. Defaults to viem public transport and https://ethrpc.xyz',
52
+ description: "RPC URL(s) for .bso name resolution. Can be specified multiple times.",
51
53
  multiple: true,
52
- default: ["viem", "https://ethrpc.xyz"]
54
+ default: DEFAULT_PROVIDERS
53
55
  })
54
56
  };
55
57
  static examples = [
@@ -58,7 +60,6 @@ export default class Daemon extends Command {
58
60
  "bitsocial daemon --pkcOptions.dataPath /tmp/bitsocial-datapath/",
59
61
  "bitsocial daemon --pkcOptions.kuboRpcClientsOptions[0] https://remoteipfsnode.com",
60
62
  "bitsocial daemon --chainProviderUrls https://mainnet.infura.io/v3/YOUR_KEY",
61
- "bitsocial daemon --chainProviderUrls viem --chainProviderUrls https://mainnet.infura.io/v3/YOUR_KEY"
62
63
  ];
63
64
  _setupLogger(Logger) {
64
65
  setupDebugLogger(Logger, { enableDefaultNamespace: true });
@@ -83,7 +84,11 @@ export default class Daemon extends Command {
83
84
  deletedLogFile = logFileToDelete;
84
85
  await fsPromise.rm(path.join(logPath, logFileToDelete));
85
86
  }
86
- return { logFilePath: path.join(logPath, `bitsocial_cli_daemon_${new Date().toISOString().replace(/:/g, "-")}.log`), deletedLogFile, logfilesCapacity };
87
+ return {
88
+ logFilePath: path.join(logPath, `bitsocial_cli_daemon_${new Date().toISOString().replace(/:/g, "-")}.log`),
89
+ deletedLogFile,
90
+ logfilesCapacity
91
+ };
87
92
  }
88
93
  async _pipeDebugLogsToLogFile(logPath, Logger) {
89
94
  const { logFilePath, deletedLogFile, logfilesCapacity } = await this._getNewLogfileByEvacuatingOldLogsIfNeeded(logPath);
@@ -149,6 +154,7 @@ export default class Daemon extends Command {
149
154
  return { logFilePath, stdoutWrite };
150
155
  }
151
156
  async run() {
157
+ printBanner();
152
158
  // Non-blocking update check — fire-and-forget, won't delay startup
153
159
  import("../../update/npm-registry.js")
154
160
  .then(({ fetchLatestVersion }) => fetchLatestVersion().then(async (latest) => {
@@ -200,11 +206,24 @@ export default class Daemon extends Command {
200
206
  defaultPkcOptions.kuboRpcClientsOptions = [kuboRpcEndpoint.toString()];
201
207
  const mergedPkcOptions = { ...defaultPkcOptions, ...pkcOptionsFromFlag };
202
208
  log("Merged pkc options that will be used for this node", mergedPkcOptions);
209
+ const { nameResolvers: _nr, ...printablePkcOptions } = mergedPkcOptions;
210
+ console.log("PKC options:", JSON.stringify(printablePkcOptions, null, 2));
203
211
  // Migrate data directory before creating PKC instance
204
212
  migrateDataDirectory(mergedPkcOptions.dataPath);
213
+ // Prune stale daemon state files (dead PIDs from crashed daemons)
214
+ await pruneStaleStates();
215
+ // Persist this daemon's PID and startup args so `bitsocial update install --restart-daemons` can stop and restart it
216
+ const daemonArgv = process.argv.slice(process.argv.indexOf("daemon") + 1);
217
+ await writeDaemonState({
218
+ pid: process.pid,
219
+ startedAt: new Date().toISOString(),
220
+ argv: daemonArgv,
221
+ pkcRpcUrl: pkcRpcUrl.toString()
222
+ });
205
223
  // Create BSO name resolvers for .bso/.eth domain resolution
206
224
  const bsoResolvers = createBsoResolvers(flags.chainProviderUrls, mergedPkcOptions.dataPath);
207
225
  mergedPkcOptions.nameResolvers = [...(mergedPkcOptions.nameResolvers || []), ...bsoResolvers];
226
+ console.log(".bso name resolvers:", bsoResolvers.map((r) => r.provider).join(", "));
208
227
  let mainProcessExited = false;
209
228
  let pendingKuboStart;
210
229
  // Kubo Node may fail randomly, we need to set a listener so when it exits because of an error we restart it
@@ -421,6 +440,8 @@ export default class Daemon extends Command {
421
440
  console.log("\nShutting down Bitsocial daemon, it may take a few seconds to shut down all communities and the IPFS node...");
422
441
  log("Received signal to exit, shutting down both kubo and pkc rpc. Please wait, it may take a few seconds");
423
442
  mainProcessExited = true;
443
+ // Remove daemon state file so update install knows we're gone
444
+ await deleteDaemonState(process.pid).catch(() => { });
424
445
  // Start killing Kubo immediately, in parallel with daemon server destroy.
425
446
  // This way Kubo receives SIGINT right away, even if daemonServer.destroy() hangs.
426
447
  const kuboKillPromise = killKuboProcess();
@@ -6,7 +6,9 @@ export default class Install extends Command {
6
6
  };
7
7
  static flags: {
8
8
  force: import("@oclif/core/interfaces").BooleanFlag<boolean>;
9
+ "restart-daemons": import("@oclif/core/interfaces").BooleanFlag<boolean>;
9
10
  };
10
11
  static examples: string[];
11
12
  run(): Promise<void>;
13
+ private _restartDaemons;
12
14
  }
@@ -1,8 +1,9 @@
1
1
  import { Args, Flags, Command } from "@oclif/core";
2
+ import { spawn } from "child_process";
2
3
  import tcpPortUsed from "tcp-port-used";
3
- import defaults from "../../../common-utils/defaults.js";
4
4
  import { fetchLatestVersion, installGlobal } from "../../../update/npm-registry.js";
5
5
  import { compareVersions } from "../../../update/semver.js";
6
+ import { getAliveDaemonStates } from "../../../common-utils/daemon-state.js";
6
7
  export default class Install extends Command {
7
8
  static description = "Install a specific version of bitsocial from npm";
8
9
  static args = {
@@ -16,21 +17,54 @@ export default class Install extends Command {
16
17
  force: Flags.boolean({
17
18
  description: "Reinstall even if already on the requested version",
18
19
  default: false
20
+ }),
21
+ "restart-daemons": Flags.boolean({
22
+ description: "Stop all running daemons, update, and restart them with the same settings",
23
+ default: true,
24
+ allowNo: true
19
25
  })
20
26
  };
21
27
  static examples = [
22
28
  "bitsocial update install",
23
29
  "bitsocial update install latest",
24
30
  "bitsocial update install 0.19.40",
25
- "bitsocial update install --force"
31
+ "bitsocial update install --force",
32
+ "bitsocial update install --no-restart-daemons"
26
33
  ];
27
34
  async run() {
28
35
  const { args, flags } = await this.parse(Install);
29
- // Check if daemon is running refuse to update while it's active
30
- const rpcPort = Number(defaults.PKC_RPC_URL.port);
31
- const daemonRunning = await tcpPortUsed.check(rpcPort, "127.0.0.1").catch(() => false);
32
- if (daemonRunning) {
33
- this.error(`Daemon is running on port ${rpcPort}. Stop it first with Ctrl-C, then run 'bitsocial update install'.`, { exit: 1 });
36
+ // Check for running daemons via state files
37
+ const aliveDaemons = await getAliveDaemonStates();
38
+ if (aliveDaemons.length > 0) {
39
+ if (!flags["restart-daemons"]) {
40
+ this.error(`${aliveDaemons.length} daemon(s) running. Stop them first, then retry.`, { exit: 1 });
41
+ }
42
+ // Stop all running daemons
43
+ for (const d of aliveDaemons) {
44
+ this.log(`Stopping daemon (PID ${d.pid})...`);
45
+ try {
46
+ process.kill(d.pid, "SIGINT");
47
+ }
48
+ catch (e) {
49
+ if (e.code === "ESRCH") {
50
+ this.log(` PID ${d.pid} already exited.`);
51
+ continue;
52
+ }
53
+ throw e;
54
+ }
55
+ }
56
+ // Wait for all daemon ports to be free
57
+ for (const d of aliveDaemons) {
58
+ const url = new URL(d.pkcRpcUrl);
59
+ const port = Number(url.port);
60
+ const host = url.hostname;
61
+ this.log(`Waiting for port ${port} to be free...`);
62
+ const freed = await tcpPortUsed.waitUntilFree(port, 500, 30000).then(() => true).catch(() => false);
63
+ if (!freed) {
64
+ this.error(`Daemon (PID ${d.pid}) did not shut down within 30 seconds on port ${port}.`, { exit: 1 });
65
+ }
66
+ }
67
+ this.log("All daemons stopped.");
34
68
  }
35
69
  // Resolve the target version
36
70
  let targetVersion;
@@ -49,6 +83,10 @@ export default class Install extends Command {
49
83
  // Skip if already on this version (unless --force)
50
84
  if (compareVersions(current, targetVersion) === 0 && !flags.force) {
51
85
  this.log(`Already on v${current}. Use --force to reinstall.`);
86
+ if (aliveDaemons.length > 0 && flags["restart-daemons"]) {
87
+ // We stopped daemons but don't need to update — restart them
88
+ await this._restartDaemons(aliveDaemons);
89
+ }
52
90
  return;
53
91
  }
54
92
  this.log(`Installing bitsocial-cli@${targetVersion}...`);
@@ -59,5 +97,35 @@ export default class Install extends Command {
59
97
  this.error(`Update failed: ${err.message}`, { exit: 1 });
60
98
  }
61
99
  this.log(`Installed bitsocial v${targetVersion} (was v${current}).`);
100
+ // Restart daemons with the new binary
101
+ if (aliveDaemons.length > 0 && flags["restart-daemons"]) {
102
+ await this._restartDaemons(aliveDaemons);
103
+ }
104
+ }
105
+ async _restartDaemons(daemons) {
106
+ this.log(`Restarting ${daemons.length} daemon(s)...`);
107
+ for (const d of daemons) {
108
+ const argStr = d.argv.length > 0 ? d.argv.join(" ") : "(defaults)";
109
+ this.log(` Starting daemon with args: ${argStr}`);
110
+ const child = spawn("bitsocial", ["daemon", ...d.argv], {
111
+ detached: true,
112
+ stdio: "ignore"
113
+ });
114
+ child.unref();
115
+ if (!child.pid) {
116
+ this.warn(`Failed to spawn daemon for args: ${argStr}`);
117
+ continue;
118
+ }
119
+ // Wait briefly for the daemon's RPC port to come up
120
+ const url = new URL(d.pkcRpcUrl);
121
+ const port = Number(url.port);
122
+ const started = await tcpPortUsed.waitUntilUsed(port, 500, 30000).then(() => true).catch(() => false);
123
+ if (started) {
124
+ this.log(` Daemon started (port ${port}).`);
125
+ }
126
+ else {
127
+ this.warn(` Daemon may not have started — port ${port} not responding after 30s. Check logs with: bitsocial logs`);
128
+ }
129
+ }
62
130
  }
63
131
  }
@@ -0,0 +1,3 @@
1
+ import { Hook } from "@oclif/core";
2
+ declare const hook: Hook<"init">;
3
+ export default hook;
@@ -0,0 +1,15 @@
1
+ import { printBanner } from "../../ascii-banner.js";
2
+ const hook = async function (opts) {
3
+ // Skip when --version is being handled by version-hook
4
+ if (process.argv.includes("--version"))
5
+ return;
6
+ // Show banner on bare `bitsocial` invocation (no command, no help flag).
7
+ // oclif sets opts.id to undefined when no command is matched; argv length > 2
8
+ // means the user passed flags/args and expects normal command output.
9
+ const bareInvocation = process.argv.length <= 2;
10
+ const helpInvocation = !opts.id && (process.argv.includes("--help") || process.argv.includes("-h") || process.argv.includes("help"));
11
+ if (bareInvocation || helpInvocation) {
12
+ printBanner();
13
+ }
14
+ };
15
+ export default hook;
@@ -0,0 +1,16 @@
1
+ export interface DaemonState {
2
+ pid: number;
3
+ startedAt: string;
4
+ argv: string[];
5
+ pkcRpcUrl: string;
6
+ }
7
+ /** Write a daemon state file atomically (write to .tmp then rename). */
8
+ export declare function writeDaemonState(state: DaemonState): Promise<void>;
9
+ /** Read all state files from the daemon states directory. */
10
+ export declare function readAllDaemonStates(): Promise<DaemonState[]>;
11
+ /** Delete a specific daemon's state file. Ignores ENOENT. */
12
+ export declare function deleteDaemonState(pid: number): Promise<void>;
13
+ /** Delete state files for dead PIDs from disk. */
14
+ export declare function pruneStaleStates(): Promise<void>;
15
+ /** Read all states, delete stale files (dead PIDs) from disk, return only alive ones. */
16
+ export declare function getAliveDaemonStates(): Promise<DaemonState[]>;
@@ -0,0 +1,85 @@
1
+ import defaults from "./defaults.js";
2
+ import path from "path";
3
+ import fs from "fs/promises";
4
+ const DAEMON_STATES_DIR = path.join(defaults.PKC_DATA_PATH, ".daemon_states");
5
+ function stateFilePath(pid) {
6
+ return path.join(DAEMON_STATES_DIR, `${pid}-daemon.state`);
7
+ }
8
+ /** Write a daemon state file atomically (write to .tmp then rename). */
9
+ export async function writeDaemonState(state) {
10
+ await fs.mkdir(DAEMON_STATES_DIR, { recursive: true });
11
+ const dest = stateFilePath(state.pid);
12
+ const tmp = dest + ".tmp";
13
+ await fs.writeFile(tmp, JSON.stringify(state, null, 2));
14
+ await fs.rename(tmp, dest);
15
+ }
16
+ /** Read all state files from the daemon states directory. */
17
+ export async function readAllDaemonStates() {
18
+ let entries;
19
+ try {
20
+ entries = await fs.readdir(DAEMON_STATES_DIR);
21
+ }
22
+ catch (e) {
23
+ if (e.code === "ENOENT")
24
+ return [];
25
+ throw e;
26
+ }
27
+ const states = [];
28
+ for (const entry of entries) {
29
+ if (!entry.endsWith("-daemon.state"))
30
+ continue;
31
+ try {
32
+ const content = await fs.readFile(path.join(DAEMON_STATES_DIR, entry), "utf-8");
33
+ states.push(JSON.parse(content));
34
+ }
35
+ catch {
36
+ // Corrupted or partially written — skip
37
+ }
38
+ }
39
+ return states;
40
+ }
41
+ /** Delete a specific daemon's state file. Ignores ENOENT. */
42
+ export async function deleteDaemonState(pid) {
43
+ try {
44
+ await fs.unlink(stateFilePath(pid));
45
+ }
46
+ catch (e) {
47
+ if (e.code !== "ENOENT")
48
+ throw e;
49
+ }
50
+ }
51
+ /** Check whether a PID is alive. */
52
+ function isPidAlive(pid) {
53
+ try {
54
+ process.kill(pid, 0);
55
+ return true;
56
+ }
57
+ catch (e) {
58
+ if (e.code === "EPERM")
59
+ return true; // alive but owned by another user
60
+ return false; // ESRCH — no such process
61
+ }
62
+ }
63
+ /** Delete state files for dead PIDs from disk. */
64
+ export async function pruneStaleStates() {
65
+ const states = await readAllDaemonStates();
66
+ for (const state of states) {
67
+ if (!isPidAlive(state.pid)) {
68
+ await deleteDaemonState(state.pid);
69
+ }
70
+ }
71
+ }
72
+ /** Read all states, delete stale files (dead PIDs) from disk, return only alive ones. */
73
+ export async function getAliveDaemonStates() {
74
+ const states = await readAllDaemonStates();
75
+ const alive = [];
76
+ for (const state of states) {
77
+ if (isPidAlive(state.pid)) {
78
+ alive.push(state);
79
+ }
80
+ else {
81
+ await deleteDaemonState(state.pid);
82
+ }
83
+ }
84
+ return alive;
85
+ }
@@ -1,2 +1,3 @@
1
1
  import { BsoResolver } from "@bitsocial/bso-resolver";
2
+ export declare const DEFAULT_PROVIDERS: string[];
2
3
  export declare function createBsoResolvers(providers?: string[], dataPath?: string): BsoResolver[];
@@ -1,5 +1,12 @@
1
1
  import { BsoResolver } from "@bitsocial/bso-resolver";
2
- const DEFAULT_PROVIDERS = ["viem", "https://ethrpc.xyz"];
2
+ export const DEFAULT_PROVIDERS = [
3
+ "https://eth.drpc.org",
4
+ "https://ethereum.publicnode.com",
5
+ "https://ethereum-rpc.publicnode.com",
6
+ "https://rpc.mevblocker.io",
7
+ "https://1rpc.io/eth",
8
+ "https://eth-pokt.nodies.app"
9
+ ];
3
10
  export function createBsoResolvers(providers, dataPath) {
4
11
  const resolverProviders = providers && providers.length > 0 ? providers : DEFAULT_PROVIDERS;
5
12
  return resolverProviders.map((provider) => new BsoResolver({ key: `bso-${provider}`, provider, dataPath }));
@@ -9,8 +9,7 @@
9
9
  "bitsocial daemon --pkcRpcUrl ws://localhost:53812",
10
10
  "bitsocial daemon --pkcOptions.dataPath /tmp/bitsocial-datapath/",
11
11
  "bitsocial daemon --pkcOptions.kuboRpcClientsOptions[0] https://remoteipfsnode.com",
12
- "bitsocial daemon --chainProviderUrls https://mainnet.infura.io/v3/YOUR_KEY",
13
- "bitsocial daemon --chainProviderUrls viem --chainProviderUrls https://mainnet.infura.io/v3/YOUR_KEY"
12
+ "bitsocial daemon --chainProviderUrls https://mainnet.infura.io/v3/YOUR_KEY"
14
13
  ],
15
14
  "flags": {
16
15
  "pkcRpcUrl": {
@@ -32,11 +31,15 @@
32
31
  "type": "option"
33
32
  },
34
33
  "chainProviderUrls": {
35
- "description": "Ethereum RPC URL(s) for .bso/.eth name resolution. Can be specified multiple times. Defaults to viem public transport and https://ethrpc.xyz",
34
+ "description": "RPC URL(s) for .bso name resolution. Can be specified multiple times.",
36
35
  "name": "chainProviderUrls",
37
36
  "default": [
38
- "viem",
39
- "https://ethrpc.xyz"
37
+ "https://eth.drpc.org",
38
+ "https://ethereum.publicnode.com",
39
+ "https://ethereum-rpc.publicnode.com",
40
+ "https://rpc.mevblocker.io",
41
+ "https://1rpc.io/eth",
42
+ "https://eth-pokt.nodies.app"
40
43
  ],
41
44
  "hasDynamicHelp": false,
42
45
  "multiple": true,
@@ -640,7 +643,8 @@
640
643
  "bitsocial update install",
641
644
  "bitsocial update install latest",
642
645
  "bitsocial update install 0.19.40",
643
- "bitsocial update install --force"
646
+ "bitsocial update install --force",
647
+ "bitsocial update install --no-restart-daemons"
644
648
  ],
645
649
  "flags": {
646
650
  "force": {
@@ -648,6 +652,12 @@
648
652
  "name": "force",
649
653
  "allowNo": false,
650
654
  "type": "boolean"
655
+ },
656
+ "restart-daemons": {
657
+ "description": "Stop all running daemons, update, and restart them with the same settings",
658
+ "name": "restart-daemons",
659
+ "allowNo": true,
660
+ "type": "boolean"
651
661
  }
652
662
  },
653
663
  "hasDynamicHelp": false,
@@ -703,5 +713,5 @@
703
713
  ]
704
714
  }
705
715
  },
706
- "version": "0.19.41"
716
+ "version": "0.19.43"
707
717
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bitsocial/bitsocial-cli",
3
- "version": "0.19.41",
3
+ "version": "0.19.43",
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",
@@ -62,7 +62,8 @@
62
62
  },
63
63
  "hooks": {
64
64
  "init": [
65
- "./dist/cli/hooks/init/version-hook"
65
+ "./dist/cli/hooks/init/version-hook",
66
+ "./dist/cli/hooks/init/banner-hook"
66
67
  ],
67
68
  "prerun": [
68
69
  "./dist/cli/hooks/prerun/parse-dynamic-flags-hook"
@@ -110,13 +111,13 @@
110
111
  "wait-on": "6.0.1"
111
112
  },
112
113
  "dependencies": {
113
- "@bitsocial/bso-resolver": "0.0.4",
114
+ "@bitsocial/bso-resolver": "0.0.5",
114
115
  "@multiformats/multiaddr": "13.0.1",
115
116
  "@oclif/core": "4.8.0",
116
117
  "@oclif/plugin-help": "6.2.36",
117
118
  "@oclif/plugin-not-found": "3.2.73",
118
119
  "@oclif/table": "0.5.1",
119
- "@pkcprotocol/pkc-js": "0.0.16",
120
+ "@pkcprotocol/pkc-js": "0.0.17",
120
121
  "dataobject-parser": "1.2.22",
121
122
  "decompress": "4.2.1",
122
123
  "env-paths": "2.2.1",