@bitsocial/bitsocial-cli 0.19.51 → 0.19.53

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.51/src/cli/commands/challenge/install.ts)_
347
+ _See code: [src/cli/commands/challenge/install.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.53/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.51/src/cli/commands/challenge/list.ts)_
370
+ _See code: [src/cli/commands/challenge/list.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.53/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.51/src/cli/commands/challenge/remove.ts)_
395
+ _See code: [src/cli/commands/challenge/remove.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.53/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.51/src/cli/commands/community/create.ts)_
425
+ _See code: [src/cli/commands/community/create.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.53/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.51/src/cli/commands/community/delete.ts)_
450
+ _See code: [src/cli/commands/community/delete.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.53/src/cli/commands/community/delete.ts)_
451
451
 
452
452
  ## `bitsocial community edit ADDRESS`
453
453
 
@@ -517,7 +517,7 @@ EXAMPLES
517
517
  $ bitsocial community edit bitsocial.bso --jsonFile ./edit-options.json
518
518
  ```
519
519
 
520
- _See code: [src/cli/commands/community/edit.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.51/src/cli/commands/community/edit.ts)_
520
+ _See code: [src/cli/commands/community/edit.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.53/src/cli/commands/community/edit.ts)_
521
521
 
522
522
  ## `bitsocial community get [ADDRESS]`
523
523
 
@@ -548,7 +548,7 @@ EXAMPLES
548
548
  $ bitsocial community get --publicKey 12D3KooWG3XbzoVyAE6Y9vHZKF64Yuuu4TjdgQKedk14iYmTEPWu
549
549
  ```
550
550
 
551
- _See code: [src/cli/commands/community/get.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.51/src/cli/commands/community/get.ts)_
551
+ _See code: [src/cli/commands/community/get.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.53/src/cli/commands/community/get.ts)_
552
552
 
553
553
  ## `bitsocial community list`
554
554
 
@@ -571,7 +571,7 @@ EXAMPLES
571
571
  $ bitsocial community list
572
572
  ```
573
573
 
574
- _See code: [src/cli/commands/community/list.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.51/src/cli/commands/community/list.ts)_
574
+ _See code: [src/cli/commands/community/list.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.53/src/cli/commands/community/list.ts)_
575
575
 
576
576
  ## `bitsocial community start ADDRESSES`
577
577
 
@@ -605,7 +605,7 @@ EXAMPLES
605
605
  $ bitsocial community start $(bitsocial community list -q) --concurrency 1
606
606
  ```
607
607
 
608
- _See code: [src/cli/commands/community/start.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.51/src/cli/commands/community/start.ts)_
608
+ _See code: [src/cli/commands/community/start.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.53/src/cli/commands/community/start.ts)_
609
609
 
610
610
  ## `bitsocial community stop ADDRESSES`
611
611
 
@@ -630,7 +630,7 @@ EXAMPLES
630
630
  $ bitsocial community stop Qmb99crTbSUfKXamXwZBe829Vf6w5w5TktPkb6WstC9RFW
631
631
  ```
632
632
 
633
- _See code: [src/cli/commands/community/stop.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.51/src/cli/commands/community/stop.ts)_
633
+ _See code: [src/cli/commands/community/stop.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.53/src/cli/commands/community/stop.ts)_
634
634
 
635
635
  ## `bitsocial daemon`
636
636
 
@@ -671,7 +671,7 @@ EXAMPLES
671
671
  $ bitsocial daemon --chainProviderUrls https://mainnet.infura.io/v3/YOUR_KEY
672
672
  ```
673
673
 
674
- _See code: [src/cli/commands/daemon.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.51/src/cli/commands/daemon.ts)_
674
+ _See code: [src/cli/commands/daemon.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.53/src/cli/commands/daemon.ts)_
675
675
 
676
676
  ## `bitsocial help [COMMAND]`
677
677
 
@@ -737,7 +737,7 @@ EXAMPLES
737
737
  $ bitsocial logs --stdout -f
738
738
  ```
739
739
 
740
- _See code: [src/cli/commands/logs.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.51/src/cli/commands/logs.ts)_
740
+ _See code: [src/cli/commands/logs.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.53/src/cli/commands/logs.ts)_
741
741
 
742
742
  ## `bitsocial update check`
743
743
 
@@ -754,7 +754,7 @@ EXAMPLES
754
754
  $ bitsocial update check
755
755
  ```
756
756
 
757
- _See code: [src/cli/commands/update/check.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.51/src/cli/commands/update/check.ts)_
757
+ _See code: [src/cli/commands/update/check.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.53/src/cli/commands/update/check.ts)_
758
758
 
759
759
  ## `bitsocial update install [VERSION]`
760
760
 
@@ -786,7 +786,7 @@ EXAMPLES
786
786
  $ bitsocial update install --no-restart-daemons
787
787
  ```
788
788
 
789
- _See code: [src/cli/commands/update/install.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.51/src/cli/commands/update/install.ts)_
789
+ _See code: [src/cli/commands/update/install.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.53/src/cli/commands/update/install.ts)_
790
790
 
791
791
  ## `bitsocial update versions`
792
792
 
@@ -808,7 +808,7 @@ EXAMPLES
808
808
  $ bitsocial update versions --limit 5
809
809
  ```
810
810
 
811
- _See code: [src/cli/commands/update/versions.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.51/src/cli/commands/update/versions.ts)_
811
+ _See code: [src/cli/commands/update/versions.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.53/src/cli/commands/update/versions.ts)_
812
812
  <!-- commandsstop -->
813
813
 
814
814
  ## Contribution
@@ -1,4 +1,26 @@
1
1
  import { Command } from "@oclif/core";
2
+ export interface KeepKuboUpTickDeps {
3
+ pkcRpcUrl: URL;
4
+ tcpPortUsedCheck: (port: number, host: string) => Promise<boolean>;
5
+ pkcOptionsFromFlag: {
6
+ kuboRpcClientsOptions?: unknown;
7
+ } | undefined;
8
+ usingDifferentProcessRpc: boolean;
9
+ hasKuboProcess: boolean;
10
+ hasPendingKuboStart: boolean;
11
+ keepKuboUp: () => Promise<void>;
12
+ createOrConnectRpc: () => Promise<void>;
13
+ onError: (message: string) => void;
14
+ }
15
+ /**
16
+ * Runs one tick of the keepKuboUp interval. Exported so it can be unit-tested.
17
+ *
18
+ * Both `tcpPortUsedCheck` and the downstream `keepKuboUp`/`createOrConnectRpc` calls
19
+ * are wrapped in try/catch — a transient ETIMEDOUT from the port check (or any other
20
+ * error from this tick) must not propagate to the setInterval callback, which would
21
+ * become an unhandledRejection (issue #37 bug 3).
22
+ */
23
+ export declare function runKeepKuboUpTick(deps: KeepKuboUpTickDeps): Promise<void>;
2
24
  export default class Daemon extends Command {
3
25
  static description: string;
4
26
  static flags: {
@@ -10,6 +10,7 @@ import { loadChallengesIntoPKC } from "../../challenge-packages/challenge-utils.
10
10
  import { migrateDataDirectory } from "../../common-utils/data-migration.js";
11
11
  import { createBsoResolvers, DEFAULT_PROVIDERS } from "../../common-utils/resolvers.js";
12
12
  import { pruneStaleStates, writeDaemonState, deleteDaemonState } from "../../common-utils/daemon-state.js";
13
+ import { createDaemonFileLogger } from "../../common-utils/daemon-file-logger.js";
13
14
  import fs from "fs";
14
15
  import fsPromise from "fs/promises";
15
16
  /** Replace wildcard bind addresses with loopback for connectivity checks (macOS rejects connect to 0.0.0.0 with EINVAL) */
@@ -32,6 +33,36 @@ const defaultPkcOptions = {
32
33
  dataPath: defaults.PKC_DATA_PATH,
33
34
  httpRoutersOptions: defaults.HTTP_TRACKERS
34
35
  };
36
+ /**
37
+ * Runs one tick of the keepKuboUp interval. Exported so it can be unit-tested.
38
+ *
39
+ * Both `tcpPortUsedCheck` and the downstream `keepKuboUp`/`createOrConnectRpc` calls
40
+ * are wrapped in try/catch — a transient ETIMEDOUT from the port check (or any other
41
+ * error from this tick) must not propagate to the setInterval callback, which would
42
+ * become an unhandledRejection (issue #37 bug 3).
43
+ */
44
+ export async function runKeepKuboUpTick(deps) {
45
+ let isRpcPortTaken = false;
46
+ try {
47
+ isRpcPortTaken = await deps.tcpPortUsedCheck(Number(deps.pkcRpcUrl.port), deps.pkcRpcUrl.hostname);
48
+ if (!deps.pkcOptionsFromFlag?.kuboRpcClientsOptions && !isRpcPortTaken && !deps.usingDifferentProcessRpc)
49
+ await deps.keepKuboUp();
50
+ else if (deps.pkcOptionsFromFlag?.kuboRpcClientsOptions && !deps.usingDifferentProcessRpc)
51
+ await deps.keepKuboUp();
52
+ // Retry if kubo died and onKuboExit's restart attempt failed (e.g. transient port conflict)
53
+ else if (!deps.hasKuboProcess && !deps.hasPendingKuboStart && !deps.usingDifferentProcessRpc)
54
+ await deps.keepKuboUp();
55
+ }
56
+ catch (error) {
57
+ deps.onError(`keepKuboUp tick error (will retry): ${error instanceof Error ? error.message : String(error)}`);
58
+ }
59
+ try {
60
+ await deps.createOrConnectRpc();
61
+ }
62
+ catch (error) {
63
+ deps.onError(`createOrConnectRpc tick error (will retry): ${error instanceof Error ? error.message : String(error)}`);
64
+ }
65
+ }
35
66
  export default class Daemon extends Command {
36
67
  static description = `Run a network-connected Bitsocial node. Once the daemon is running you can create and start your communities and receive publications from users. The daemon will also serve web ui on http that can be accessed through a browser on any machine. Within the web ui users are able to browse, create and manage their communities fully P2P.
37
68
  Options can be passed to the RPC's instance through flag --pkcOptions.optionName. For a list of pkc options (https://github.com/pkcprotocol/pkc-js?tab=readme-ov-file#pkcoptions)
@@ -92,20 +123,9 @@ export default class Daemon extends Command {
92
123
  }
93
124
  async _pipeDebugLogsToLogFile(logPath, Logger) {
94
125
  const { logFilePath, deletedLogFile, logfilesCapacity } = await this._getNewLogfileByEvacuatingOldLogsIfNeeded(logPath);
95
- const logFile = fs.createWriteStream(logFilePath, { flags: "a" });
126
+ const fileLogger = createDaemonFileLogger({ logFilePath });
96
127
  const stdoutWrite = process.stdout.write.bind(process.stdout);
97
128
  const stderrWrite = process.stderr.write.bind(process.stderr);
98
- const isLogFileOverLimit = () => logFile.bytesWritten > 20000000; // 20mb
99
- const writeTimestampedLine = (text, stream) => {
100
- if (isLogFileOverLimit())
101
- return;
102
- if (!text || text.trim().length === 0)
103
- return;
104
- const timestamp = `[${new Date().toISOString()}] [${stream}] `;
105
- const lines = text.split("\n");
106
- const timestamped = lines.map((line, i) => (i === 0 ? timestamp + line : line)).join("\n");
107
- logFile.write(timestamped);
108
- };
109
129
  // Redirect debug library output directly to the log file
110
130
  // instead of stderr, so only real errors appear in the terminal
111
131
  const require = createRequire(import.meta.url);
@@ -115,21 +135,30 @@ export default class Daemon extends Command {
115
135
  debugModule.inspectOpts.colors = true;
116
136
  debugModule.inspectOpts.hideDate = true;
117
137
  debugModule.log = (...args) => {
118
- writeTimestampedLine(formatWithOptions({ depth: Logger.inspectOpts?.depth || 10, colors: true }, ...args).trimStart() + EOL, "stderr");
138
+ const text = formatWithOptions({ depth: Logger.inspectOpts?.depth || 10, colors: true }, ...args).trimStart() + EOL;
139
+ const wrote = fileLogger.writeTimestampedLine(text, "stderr");
140
+ // If the file logger could not accept the write (closed / pending buffer full),
141
+ // fall back to original stderr so debug output is never silently lost
142
+ if (!wrote)
143
+ stderrWrite(text);
119
144
  };
120
145
  const asString = (data) => (typeof data === "string" ? data : Buffer.from(data).toString());
121
146
  process.stdout.write = (...args) => {
122
147
  //@ts-expect-error
123
148
  const res = stdoutWrite(...args);
124
- writeTimestampedLine(asString(args[0]), "stdout");
149
+ fileLogger.writeTimestampedLine(asString(args[0]), "stdout");
125
150
  return res;
126
151
  };
127
152
  process.stderr.write = (...args) => {
128
- // Only write stderr to the log file, not to the terminal.
129
- // Debug output goes to stderr; we want it in logs only.
130
- // Real errors are caught by uncaughtException/unhandledRejection handlers
131
- // which use console.error -> stderr.write -> this override -> log file.
132
- writeTimestampedLine(asString(args[0]).trimStart(), "stderr");
153
+ // Debug output goes to stderr; route it to the log file.
154
+ // If the file logger is unavailable (closed, errored), fall back to original stderr
155
+ // so output is never silently swallowed.
156
+ const text = asString(args[0]);
157
+ const wrote = fileLogger.writeTimestampedLine(text.trimStart(), "stderr");
158
+ if (!wrote) {
159
+ //@ts-expect-error
160
+ return stderrWrite(...args);
161
+ }
133
162
  return true;
134
163
  };
135
164
  const log = Logger("bitsocial-cli:daemon");
@@ -150,8 +179,12 @@ export default class Daemon extends Command {
150
179
  writeErrorToTerminal(err);
151
180
  console.error(err);
152
181
  });
153
- process.on("exit", () => logFile.close());
154
- return { logFilePath, stdoutWrite };
182
+ process.on("exit", () => {
183
+ // close() returns a promise but exit handlers must be synchronous.
184
+ // Best-effort: trigger the close; the underlying writeStream flushes on process exit.
185
+ fileLogger.close().catch(() => { });
186
+ });
187
+ return { logFilePath, stdoutWrite, fileLogger };
155
188
  }
156
189
  async run() {
157
190
  printBanner();
@@ -221,7 +254,7 @@ export default class Daemon extends Command {
221
254
  pkcRpcUrl: pkcRpcUrl.toString()
222
255
  });
223
256
  // Create BSO name resolvers for .bso/.eth domain resolution
224
- const bsoResolvers = createBsoResolvers(flags.chainProviderUrls, mergedPkcOptions.dataPath);
257
+ const bsoResolvers = createBsoResolvers(flags.chainProviderUrls);
225
258
  mergedPkcOptions.nameResolvers = [...(mergedPkcOptions.nameResolvers || []), ...bsoResolvers];
226
259
  let mainProcessExited = false;
227
260
  let pendingKuboStart;
@@ -466,20 +499,17 @@ export default class Daemon extends Command {
466
499
  keepKuboUpInterval = setInterval(async () => {
467
500
  if (mainProcessExited)
468
501
  return;
469
- const isRpcPortTaken = await tcpPortUsed.check(Number(pkcRpcUrl.port), pkcRpcUrl.hostname);
470
- try {
471
- if (!pkcOptionsFromFlag?.kuboRpcClientsOptions && !isRpcPortTaken && !usingDifferentProcessRpc)
472
- await keepKuboUp();
473
- else if (pkcOptionsFromFlag?.kuboRpcClientsOptions && !usingDifferentProcessRpc)
474
- await keepKuboUp();
475
- // Retry if kubo died and onKuboExit's restart attempt failed (e.g. transient port conflict)
476
- else if (!kuboProcess && !pendingKuboStart && !usingDifferentProcessRpc)
477
- await keepKuboUp();
478
- }
479
- catch (error) {
480
- log.trace(`keepKuboUp error (will retry): ${error instanceof Error ? error.message : String(error)}`);
481
- }
482
- await createOrConnectRpc();
502
+ await runKeepKuboUpTick({
503
+ pkcRpcUrl,
504
+ tcpPortUsedCheck: (port, host) => tcpPortUsed.check(port, host),
505
+ pkcOptionsFromFlag,
506
+ usingDifferentProcessRpc,
507
+ hasKuboProcess: !!kuboProcess,
508
+ hasPendingKuboStart: !!pendingKuboStart,
509
+ keepKuboUp,
510
+ createOrConnectRpc,
511
+ onError: (msg) => log.trace(msg)
512
+ });
483
513
  }, 5000);
484
514
  }
485
515
  catch (err) {
@@ -0,0 +1,23 @@
1
+ export interface DaemonFileLoggerOptions {
2
+ logFilePath: string;
3
+ /** Trigger a trim once the current writer's bytesWritten exceeds this. Defaults to 20 MB. */
4
+ maxBytes?: number;
5
+ /** Target file size after trim. Must be < maxBytes. Defaults to 15 MB. */
6
+ trimToBytes?: number;
7
+ /** Bound on how many bytes of writes are buffered while a trim cycle is running. */
8
+ pendingByteCap?: number;
9
+ }
10
+ export interface DaemonFileLogger {
11
+ /**
12
+ * Append a timestamped line to the log file. Returns false when the write was
13
+ * dropped (e.g. the logger is closed or the in-flight pending buffer is full),
14
+ * letting the caller decide whether to fall back to terminal output.
15
+ */
16
+ writeTimestampedLine(text: string, stream: "stdout" | "stderr"): boolean;
17
+ close(): Promise<void>;
18
+ /** Force-run a trim cycle — for tests. */
19
+ _trimNow(): Promise<void>;
20
+ readonly currentPath: string;
21
+ readonly bytesWritten: number;
22
+ }
23
+ export declare function createDaemonFileLogger(options: DaemonFileLoggerOptions): DaemonFileLogger;
@@ -0,0 +1,112 @@
1
+ import fs from "fs";
2
+ import fsPromise from "fs/promises";
3
+ const DEFAULT_MAX = 20_000_000;
4
+ const DEFAULT_TRIM_TO = 15_000_000;
5
+ const DEFAULT_PENDING_CAP = 5_000_000;
6
+ export function createDaemonFileLogger(options) {
7
+ const maxBytes = options.maxBytes ?? DEFAULT_MAX;
8
+ const trimToBytes = options.trimToBytes ?? DEFAULT_TRIM_TO;
9
+ const pendingByteCap = options.pendingByteCap ?? DEFAULT_PENDING_CAP;
10
+ if (trimToBytes >= maxBytes) {
11
+ throw new Error(`trimToBytes (${trimToBytes}) must be less than maxBytes (${maxBytes})`);
12
+ }
13
+ const logFilePath = options.logFilePath;
14
+ let stream = fs.createWriteStream(logFilePath, { flags: "a" });
15
+ stream.on("error", () => { });
16
+ let trimming;
17
+ let pending = [];
18
+ let pendingBytes = 0;
19
+ let closed = false;
20
+ const reopenStream = () => {
21
+ stream.removeAllListeners("error");
22
+ stream = fs.createWriteStream(logFilePath, { flags: "a" });
23
+ stream.on("error", () => { });
24
+ };
25
+ const trim = async () => {
26
+ // End the current stream first so its buffered writes flush to disk before we read the file
27
+ await new Promise((res) => stream.end(() => res()));
28
+ const stat = await fsPromise.stat(logFilePath).catch(() => null);
29
+ if (stat && stat.size > trimToBytes) {
30
+ const fd = await fsPromise.open(logFilePath, "r");
31
+ try {
32
+ const buf = Buffer.alloc(trimToBytes);
33
+ await fd.read(buf, 0, trimToBytes, stat.size - trimToBytes);
34
+ // Skip a partial line at the start so the file always starts on a line boundary
35
+ const firstNewline = buf.indexOf(0x0a);
36
+ const tail = firstNewline >= 0 ? buf.subarray(firstNewline + 1) : buf;
37
+ await fsPromise.writeFile(logFilePath, tail);
38
+ }
39
+ finally {
40
+ await fd.close();
41
+ }
42
+ }
43
+ reopenStream();
44
+ };
45
+ const drainPending = () => {
46
+ if (pending.length === 0)
47
+ return;
48
+ const drained = pending;
49
+ pending = [];
50
+ pendingBytes = 0;
51
+ for (const chunk of drained) {
52
+ stream.write(chunk);
53
+ }
54
+ };
55
+ const scheduleTrim = () => {
56
+ if (trimming)
57
+ return;
58
+ trimming = trim()
59
+ .catch(() => {
60
+ // If trim fails (FS error, etc.) we drop the pending buffer rather than
61
+ // hold memory forever. The next write will hit the same condition and retry.
62
+ pending = [];
63
+ pendingBytes = 0;
64
+ })
65
+ .finally(() => {
66
+ trimming = undefined;
67
+ drainPending();
68
+ });
69
+ };
70
+ const writeTimestampedLine = (text, streamLabel) => {
71
+ if (closed)
72
+ return false;
73
+ if (!text || text.trim().length === 0)
74
+ return false;
75
+ const timestamp = `[${new Date().toISOString()}] [${streamLabel}] `;
76
+ const lines = text.split("\n");
77
+ const timestamped = lines.map((line, i) => (i === 0 ? timestamp + line : line)).join("\n");
78
+ if (trimming) {
79
+ // A trim cycle is in flight — buffer up to pendingByteCap, then drop with false
80
+ // so the caller can fall back to writing to the terminal.
81
+ if (pendingBytes + timestamped.length > pendingByteCap)
82
+ return false;
83
+ pending.push(timestamped);
84
+ pendingBytes += timestamped.length;
85
+ return true;
86
+ }
87
+ stream.write(timestamped);
88
+ if (stream.bytesWritten > maxBytes)
89
+ scheduleTrim();
90
+ return true;
91
+ };
92
+ return {
93
+ writeTimestampedLine,
94
+ async close() {
95
+ closed = true;
96
+ if (trimming)
97
+ await trimming.catch(() => { });
98
+ await new Promise((res) => stream.end(() => res()));
99
+ },
100
+ async _trimNow() {
101
+ scheduleTrim();
102
+ if (trimming)
103
+ await trimming;
104
+ },
105
+ get currentPath() {
106
+ return logFilePath;
107
+ },
108
+ get bytesWritten() {
109
+ return stream.bytesWritten;
110
+ }
111
+ };
112
+ }
@@ -1,3 +1,3 @@
1
1
  import { BsoResolver } from "@bitsocial/bso-resolver";
2
2
  export declare const DEFAULT_PROVIDERS: string[];
3
- export declare function createBsoResolvers(providers?: string[], dataPath?: string): BsoResolver[];
3
+ export declare function createBsoResolvers(providers?: string[]): BsoResolver[];
@@ -7,7 +7,7 @@ export const DEFAULT_PROVIDERS = [
7
7
  "https://1rpc.io/eth",
8
8
  "https://eth-pokt.nodies.app"
9
9
  ];
10
- export function createBsoResolvers(providers, dataPath) {
10
+ export function createBsoResolvers(providers) {
11
11
  const resolverProviders = providers && providers.length > 0 ? providers : DEFAULT_PROVIDERS;
12
- return resolverProviders.map((provider) => new BsoResolver({ key: `bso-${provider}`, provider, dataPath }));
12
+ return resolverProviders.map((provider) => new BsoResolver({ key: `bso-${provider}`, provider }));
13
13
  }
@@ -153,133 +153,6 @@
153
153
  "logs.js"
154
154
  ]
155
155
  },
156
- "challenge:install": {
157
- "aliases": [],
158
- "args": {
159
- "package": {
160
- "description": "Package specifier — anything npm can install (name, name@version, git URL, tarball URL, local path)",
161
- "name": "package",
162
- "required": true
163
- }
164
- },
165
- "description": "Install a challenge package (npm package name, git URL, tarball URL, or local path)",
166
- "examples": [
167
- "bitsocial challenge install @bitsocial/mintpass-challenge",
168
- "bitsocial challenge install @bitsocial/mintpass-challenge@1.0.0",
169
- "bitsocial challenge install github:user/repo",
170
- "bitsocial challenge install https://example.com/my-challenge-1.0.0.tar.gz",
171
- "bitsocial challenge install ./my-local-challenge"
172
- ],
173
- "flags": {
174
- "pkcOptions.dataPath": {
175
- "description": "Data path to install the challenge into",
176
- "name": "pkcOptions.dataPath",
177
- "required": false,
178
- "hasDynamicHelp": false,
179
- "multiple": false,
180
- "type": "option"
181
- }
182
- },
183
- "hasDynamicHelp": false,
184
- "hiddenAliases": [],
185
- "id": "challenge:install",
186
- "pluginAlias": "@bitsocial/bitsocial-cli",
187
- "pluginName": "@bitsocial/bitsocial-cli",
188
- "pluginType": "core",
189
- "strict": true,
190
- "enableJsonFlag": false,
191
- "isESM": true,
192
- "relativePath": [
193
- "dist",
194
- "cli",
195
- "commands",
196
- "challenge",
197
- "install.js"
198
- ]
199
- },
200
- "challenge:list": {
201
- "aliases": [],
202
- "args": {},
203
- "description": "List installed challenge packages",
204
- "examples": [
205
- "bitsocial challenge list",
206
- "bitsocial challenge list -q"
207
- ],
208
- "flags": {
209
- "quiet": {
210
- "char": "q",
211
- "name": "quiet",
212
- "summary": "Only display challenge names",
213
- "allowNo": false,
214
- "type": "boolean"
215
- },
216
- "pkcOptions.dataPath": {
217
- "description": "Data path where challenges are installed",
218
- "name": "pkcOptions.dataPath",
219
- "required": false,
220
- "hasDynamicHelp": false,
221
- "multiple": false,
222
- "type": "option"
223
- }
224
- },
225
- "hasDynamicHelp": false,
226
- "hiddenAliases": [],
227
- "id": "challenge:list",
228
- "pluginAlias": "@bitsocial/bitsocial-cli",
229
- "pluginName": "@bitsocial/bitsocial-cli",
230
- "pluginType": "core",
231
- "strict": true,
232
- "enableJsonFlag": false,
233
- "isESM": true,
234
- "relativePath": [
235
- "dist",
236
- "cli",
237
- "commands",
238
- "challenge",
239
- "list.js"
240
- ]
241
- },
242
- "challenge:remove": {
243
- "aliases": [],
244
- "args": {
245
- "name": {
246
- "description": "The challenge package name (e.g., my-challenge or @scope/my-challenge)",
247
- "name": "name",
248
- "required": true
249
- }
250
- },
251
- "description": "Remove an installed challenge package",
252
- "examples": [
253
- "bitsocial challenge remove my-challenge",
254
- "bitsocial challenge remove @scope/my-challenge"
255
- ],
256
- "flags": {
257
- "pkcOptions.dataPath": {
258
- "description": "Data path where challenges are installed",
259
- "name": "pkcOptions.dataPath",
260
- "required": false,
261
- "hasDynamicHelp": false,
262
- "multiple": false,
263
- "type": "option"
264
- }
265
- },
266
- "hasDynamicHelp": false,
267
- "hiddenAliases": [],
268
- "id": "challenge:remove",
269
- "pluginAlias": "@bitsocial/bitsocial-cli",
270
- "pluginName": "@bitsocial/bitsocial-cli",
271
- "pluginType": "core",
272
- "strict": true,
273
- "enableJsonFlag": false,
274
- "isESM": true,
275
- "relativePath": [
276
- "dist",
277
- "cli",
278
- "commands",
279
- "challenge",
280
- "remove.js"
281
- ]
282
- },
283
156
  "community:create": {
284
157
  "aliases": [],
285
158
  "args": {},
@@ -660,6 +533,133 @@
660
533
  "stop.js"
661
534
  ]
662
535
  },
536
+ "challenge:install": {
537
+ "aliases": [],
538
+ "args": {
539
+ "package": {
540
+ "description": "Package specifier — anything npm can install (name, name@version, git URL, tarball URL, local path)",
541
+ "name": "package",
542
+ "required": true
543
+ }
544
+ },
545
+ "description": "Install a challenge package (npm package name, git URL, tarball URL, or local path)",
546
+ "examples": [
547
+ "bitsocial challenge install @bitsocial/mintpass-challenge",
548
+ "bitsocial challenge install @bitsocial/mintpass-challenge@1.0.0",
549
+ "bitsocial challenge install github:user/repo",
550
+ "bitsocial challenge install https://example.com/my-challenge-1.0.0.tar.gz",
551
+ "bitsocial challenge install ./my-local-challenge"
552
+ ],
553
+ "flags": {
554
+ "pkcOptions.dataPath": {
555
+ "description": "Data path to install the challenge into",
556
+ "name": "pkcOptions.dataPath",
557
+ "required": false,
558
+ "hasDynamicHelp": false,
559
+ "multiple": false,
560
+ "type": "option"
561
+ }
562
+ },
563
+ "hasDynamicHelp": false,
564
+ "hiddenAliases": [],
565
+ "id": "challenge:install",
566
+ "pluginAlias": "@bitsocial/bitsocial-cli",
567
+ "pluginName": "@bitsocial/bitsocial-cli",
568
+ "pluginType": "core",
569
+ "strict": true,
570
+ "enableJsonFlag": false,
571
+ "isESM": true,
572
+ "relativePath": [
573
+ "dist",
574
+ "cli",
575
+ "commands",
576
+ "challenge",
577
+ "install.js"
578
+ ]
579
+ },
580
+ "challenge:list": {
581
+ "aliases": [],
582
+ "args": {},
583
+ "description": "List installed challenge packages",
584
+ "examples": [
585
+ "bitsocial challenge list",
586
+ "bitsocial challenge list -q"
587
+ ],
588
+ "flags": {
589
+ "quiet": {
590
+ "char": "q",
591
+ "name": "quiet",
592
+ "summary": "Only display challenge names",
593
+ "allowNo": false,
594
+ "type": "boolean"
595
+ },
596
+ "pkcOptions.dataPath": {
597
+ "description": "Data path where challenges are installed",
598
+ "name": "pkcOptions.dataPath",
599
+ "required": false,
600
+ "hasDynamicHelp": false,
601
+ "multiple": false,
602
+ "type": "option"
603
+ }
604
+ },
605
+ "hasDynamicHelp": false,
606
+ "hiddenAliases": [],
607
+ "id": "challenge:list",
608
+ "pluginAlias": "@bitsocial/bitsocial-cli",
609
+ "pluginName": "@bitsocial/bitsocial-cli",
610
+ "pluginType": "core",
611
+ "strict": true,
612
+ "enableJsonFlag": false,
613
+ "isESM": true,
614
+ "relativePath": [
615
+ "dist",
616
+ "cli",
617
+ "commands",
618
+ "challenge",
619
+ "list.js"
620
+ ]
621
+ },
622
+ "challenge:remove": {
623
+ "aliases": [],
624
+ "args": {
625
+ "name": {
626
+ "description": "The challenge package name (e.g., my-challenge or @scope/my-challenge)",
627
+ "name": "name",
628
+ "required": true
629
+ }
630
+ },
631
+ "description": "Remove an installed challenge package",
632
+ "examples": [
633
+ "bitsocial challenge remove my-challenge",
634
+ "bitsocial challenge remove @scope/my-challenge"
635
+ ],
636
+ "flags": {
637
+ "pkcOptions.dataPath": {
638
+ "description": "Data path where challenges are installed",
639
+ "name": "pkcOptions.dataPath",
640
+ "required": false,
641
+ "hasDynamicHelp": false,
642
+ "multiple": false,
643
+ "type": "option"
644
+ }
645
+ },
646
+ "hasDynamicHelp": false,
647
+ "hiddenAliases": [],
648
+ "id": "challenge:remove",
649
+ "pluginAlias": "@bitsocial/bitsocial-cli",
650
+ "pluginName": "@bitsocial/bitsocial-cli",
651
+ "pluginType": "core",
652
+ "strict": true,
653
+ "enableJsonFlag": false,
654
+ "isESM": true,
655
+ "relativePath": [
656
+ "dist",
657
+ "cli",
658
+ "commands",
659
+ "challenge",
660
+ "remove.js"
661
+ ]
662
+ },
663
663
  "update:check": {
664
664
  "aliases": [],
665
665
  "args": {},
@@ -770,5 +770,5 @@
770
770
  ]
771
771
  }
772
772
  },
773
- "version": "0.19.51"
773
+ "version": "0.19.53"
774
774
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bitsocial/bitsocial-cli",
3
- "version": "0.19.51",
3
+ "version": "0.19.53",
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",
@@ -93,6 +93,7 @@
93
93
  "@types/express": "4.17.21",
94
94
  "@types/node": "22.19.13",
95
95
  "@types/prettier": "2.7.1",
96
+ "@types/sinon": "17.0.4",
96
97
  "@types/tcp-port-used": "1.0.4",
97
98
  "@types/ws": "8.18.1",
98
99
  "commitizen": "4.2.5",
@@ -112,13 +113,13 @@
112
113
  "wait-on": "6.0.1"
113
114
  },
114
115
  "dependencies": {
115
- "@bitsocial/bso-resolver": "0.0.6",
116
+ "@bitsocial/bso-resolver": "0.0.8",
116
117
  "@multiformats/multiaddr": "13.0.1",
117
118
  "@oclif/core": "4.8.0",
118
119
  "@oclif/plugin-help": "6.2.36",
119
120
  "@oclif/plugin-not-found": "3.2.73",
120
121
  "@oclif/table": "0.5.1",
121
- "@pkcprotocol/pkc-js": "0.0.20",
122
+ "@pkcprotocol/pkc-js": "0.0.28",
122
123
  "dataobject-parser": "1.2.22",
123
124
  "decompress": "4.2.1",
124
125
  "env-paths": "2.2.1",
@@ -142,8 +143,8 @@
142
143
  "sha256OfHtmlZip": "14653b19e88868fda6fef26601f477947e44251cc52bd95d7d6fec158ae67b6f"
143
144
  },
144
145
  {
145
- "url": "https://github.com/bitsocialnet/5chan/releases/tag/v0.8.0",
146
- "sha256OfHtmlZip": "8954fd1965ab386d41ea17858f0b3436b3534eaf8a751936c47061b36314ef8b"
146
+ "url": "https://github.com/bitsocialnet/5chan/releases/tag/v0.8.3",
147
+ "sha256OfHtmlZip": "37081cf4511ca3bd553955500ec62227355ef925a46b81dc8cebc265362cf77f"
147
148
  }
148
149
  ]
149
150
  }