@bitsocial/bitsocial-cli 0.19.50 → 0.19.52
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 +15 -15
- package/dist/cli/commands/daemon.d.ts +22 -0
- package/dist/cli/commands/daemon.js +66 -36
- package/dist/common-utils/daemon-file-logger.d.ts +23 -0
- package/dist/common-utils/daemon-file-logger.js +112 -0
- package/dist/common-utils/resolvers.d.ts +1 -1
- package/dist/common-utils/resolvers.js +2 -2
- package/oclif.manifest.json +1 -1
- package/package.json +6 -5
package/README.md
CHANGED
|
@@ -344,7 +344,7 @@ EXAMPLES
|
|
|
344
344
|
$ bitsocial challenge install ./my-local-challenge
|
|
345
345
|
```
|
|
346
346
|
|
|
347
|
-
_See code: [src/cli/commands/challenge/install.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.
|
|
347
|
+
_See code: [src/cli/commands/challenge/install.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.52/src/cli/commands/challenge/install.ts)_
|
|
348
348
|
|
|
349
349
|
## `bitsocial challenge list`
|
|
350
350
|
|
|
@@ -367,7 +367,7 @@ EXAMPLES
|
|
|
367
367
|
$ bitsocial challenge list -q
|
|
368
368
|
```
|
|
369
369
|
|
|
370
|
-
_See code: [src/cli/commands/challenge/list.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.
|
|
370
|
+
_See code: [src/cli/commands/challenge/list.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.52/src/cli/commands/challenge/list.ts)_
|
|
371
371
|
|
|
372
372
|
## `bitsocial challenge remove NAME`
|
|
373
373
|
|
|
@@ -392,7 +392,7 @@ EXAMPLES
|
|
|
392
392
|
$ bitsocial challenge remove @scope/my-challenge
|
|
393
393
|
```
|
|
394
394
|
|
|
395
|
-
_See code: [src/cli/commands/challenge/remove.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.
|
|
395
|
+
_See code: [src/cli/commands/challenge/remove.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.52/src/cli/commands/challenge/remove.ts)_
|
|
396
396
|
|
|
397
397
|
## `bitsocial community create`
|
|
398
398
|
|
|
@@ -422,7 +422,7 @@ EXAMPLES
|
|
|
422
422
|
$ bitsocial community create --jsonFile ./create-options.json
|
|
423
423
|
```
|
|
424
424
|
|
|
425
|
-
_See code: [src/cli/commands/community/create.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.
|
|
425
|
+
_See code: [src/cli/commands/community/create.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.52/src/cli/commands/community/create.ts)_
|
|
426
426
|
|
|
427
427
|
## `bitsocial community delete ADDRESSES`
|
|
428
428
|
|
|
@@ -447,7 +447,7 @@ EXAMPLES
|
|
|
447
447
|
$ bitsocial community delete 12D3KooWG3XbzoVyAE6Y9vHZKF64Yuuu4TjdgQKedk14iYmTEPWu
|
|
448
448
|
```
|
|
449
449
|
|
|
450
|
-
_See code: [src/cli/commands/community/delete.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.
|
|
450
|
+
_See code: [src/cli/commands/community/delete.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.52/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.
|
|
520
|
+
_See code: [src/cli/commands/community/edit.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.52/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.
|
|
551
|
+
_See code: [src/cli/commands/community/get.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.52/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.
|
|
574
|
+
_See code: [src/cli/commands/community/list.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.52/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.
|
|
608
|
+
_See code: [src/cli/commands/community/start.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.52/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.
|
|
633
|
+
_See code: [src/cli/commands/community/stop.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.52/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.
|
|
674
|
+
_See code: [src/cli/commands/daemon.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.52/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.
|
|
740
|
+
_See code: [src/cli/commands/logs.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.52/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.
|
|
757
|
+
_See code: [src/cli/commands/update/check.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.52/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.
|
|
789
|
+
_See code: [src/cli/commands/update/install.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.52/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.
|
|
811
|
+
_See code: [src/cli/commands/update/versions.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.52/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
|
|
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
|
-
|
|
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
|
-
//
|
|
129
|
-
//
|
|
130
|
-
//
|
|
131
|
-
|
|
132
|
-
writeTimestampedLine(
|
|
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", () =>
|
|
154
|
-
|
|
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
|
|
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
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
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[]
|
|
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
|
|
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
|
|
12
|
+
return resolverProviders.map((provider) => new BsoResolver({ key: `bso-${provider}`, provider }));
|
|
13
13
|
}
|
package/oclif.manifest.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bitsocial/bitsocial-cli",
|
|
3
|
-
"version": "0.19.
|
|
3
|
+
"version": "0.19.52",
|
|
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.
|
|
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.
|
|
122
|
+
"@pkcprotocol/pkc-js": "0.0.25",
|
|
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.
|
|
146
|
-
"sha256OfHtmlZip": "
|
|
146
|
+
"url": "https://github.com/bitsocialnet/5chan/releases/tag/v0.8.3",
|
|
147
|
+
"sha256OfHtmlZip": "37081cf4511ca3bd553955500ec62227355ef925a46b81dc8cebc265362cf77f"
|
|
147
148
|
}
|
|
148
149
|
]
|
|
149
150
|
}
|