@cogcoin/client 1.0.1 → 1.1.0
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 +4 -2
- package/dist/bitcoind/client/factory.d.ts +0 -8
- package/dist/bitcoind/client/factory.js +1 -59
- package/dist/bitcoind/client/managed-client.d.ts +1 -3
- package/dist/bitcoind/client/managed-client.js +3 -47
- package/dist/bitcoind/indexer-daemon-main.js +173 -28
- package/dist/bitcoind/indexer-daemon.d.ts +14 -3
- package/dist/bitcoind/indexer-daemon.js +145 -29
- package/dist/bitcoind/indexer-monitor.d.ts +12 -0
- package/dist/bitcoind/indexer-monitor.js +89 -0
- package/dist/bitcoind/progress/follow-scene.d.ts +7 -1
- package/dist/bitcoind/progress/follow-scene.js +87 -4
- package/dist/bitcoind/progress/tty-renderer.d.ts +2 -0
- package/dist/bitcoind/progress/tty-renderer.js +2 -0
- package/dist/bitcoind/retryable-rpc.js +3 -0
- package/dist/bitcoind/service.d.ts +1 -0
- package/dist/bitcoind/service.js +31 -9
- package/dist/bitcoind/testing.d.ts +0 -1
- package/dist/bitcoind/testing.js +0 -1
- package/dist/bitcoind/types.d.ts +5 -2
- package/dist/cli/commands/follow.js +44 -49
- package/dist/cli/commands/mining-admin.js +65 -2
- package/dist/cli/commands/mining-read.js +43 -3
- package/dist/cli/commands/mining-runtime.js +91 -73
- package/dist/cli/commands/service-runtime.js +42 -2
- package/dist/cli/commands/status.js +3 -1
- package/dist/cli/commands/sync.js +50 -90
- package/dist/cli/commands/update.d.ts +2 -0
- package/dist/cli/commands/update.js +101 -0
- package/dist/cli/commands/wallet-admin.js +21 -3
- package/dist/cli/commands/wallet-read.js +2 -0
- package/dist/cli/context.js +36 -1
- package/dist/cli/managed-indexer-observer.d.ts +33 -0
- package/dist/cli/managed-indexer-observer.js +163 -0
- package/dist/cli/mining-format.d.ts +3 -1
- package/dist/cli/mining-format.js +63 -0
- package/dist/cli/mining-json.d.ts +11 -1
- package/dist/cli/mining-json.js +15 -0
- package/dist/cli/output.js +74 -2
- package/dist/cli/parse.d.ts +1 -1
- package/dist/cli/parse.js +28 -0
- package/dist/cli/prompt.js +109 -0
- package/dist/cli/read-json.d.ts +26 -1
- package/dist/cli/read-json.js +48 -0
- package/dist/cli/runner.js +8 -2
- package/dist/cli/signals.d.ts +12 -0
- package/dist/cli/signals.js +31 -13
- package/dist/cli/types.d.ts +13 -4
- package/dist/cli/update-notifier.js +7 -222
- package/dist/cli/update-service.d.ts +34 -0
- package/dist/cli/update-service.js +152 -0
- package/dist/client/initialization.js +5 -0
- package/dist/semver.d.ts +12 -0
- package/dist/semver.js +68 -0
- package/dist/wallet/lifecycle.d.ts +10 -0
- package/dist/wallet/mining/config.js +64 -3
- package/dist/wallet/mining/control.d.ts +5 -1
- package/dist/wallet/mining/control.js +269 -26
- package/dist/wallet/mining/domain-prompts.d.ts +17 -0
- package/dist/wallet/mining/domain-prompts.js +130 -0
- package/dist/wallet/mining/index.d.ts +2 -1
- package/dist/wallet/mining/index.js +1 -0
- package/dist/wallet/mining/provider-model.d.ts +30 -0
- package/dist/wallet/mining/provider-model.js +134 -0
- package/dist/wallet/mining/runner.d.ts +156 -5
- package/dist/wallet/mining/runner.js +1019 -399
- package/dist/wallet/mining/runtime-artifacts.js +1 -0
- package/dist/wallet/mining/sentence-protocol.d.ts +1 -0
- package/dist/wallet/mining/sentences.d.ts +2 -2
- package/dist/wallet/mining/sentences.js +32 -6
- package/dist/wallet/mining/types.d.ts +35 -1
- package/dist/wallet/mining/visualizer.d.ts +3 -0
- package/dist/wallet/mining/visualizer.js +132 -15
- package/dist/wallet/read/context.d.ts +1 -0
- package/dist/wallet/read/context.js +15 -7
- package/dist/wallet/state/client-password-agent.js +4 -1
- package/dist/wallet/state/client-password.js +15 -8
- package/dist/wallet/tx/common.js +1 -1
- package/package.json +3 -2
package/dist/cli/read-json.js
CHANGED
|
@@ -288,6 +288,18 @@ function buildMiningStatusData(mining) {
|
|
|
288
288
|
phase: mining.runtime.currentPhase,
|
|
289
289
|
lastSuspendDetectedAtUnixMs: mining.runtime.lastSuspendDetectedAtUnixMs,
|
|
290
290
|
pauseReason: mining.runtime.pauseReason,
|
|
291
|
+
provider: {
|
|
292
|
+
configured: mining.provider.configured,
|
|
293
|
+
kind: mining.provider.provider,
|
|
294
|
+
modelId: mining.provider.modelId,
|
|
295
|
+
effectiveModel: mining.provider.effectiveModel,
|
|
296
|
+
modelOverride: mining.provider.modelOverride,
|
|
297
|
+
modelSelectionSource: mining.provider.modelSelectionSource,
|
|
298
|
+
usingDefaultModel: mining.provider.usingDefaultModel,
|
|
299
|
+
extraPromptConfigured: mining.provider.extraPromptConfigured,
|
|
300
|
+
estimatedDailyCostUsd: mining.provider.estimatedDailyCostUsd,
|
|
301
|
+
estimatedDailyCostDisplay: mining.provider.estimatedDailyCostDisplay,
|
|
302
|
+
},
|
|
291
303
|
providerState: mining.runtime.providerState,
|
|
292
304
|
tipsAligned: mining.runtime.tipsAligned,
|
|
293
305
|
sameDomainCompetitorSuppressed: mining.runtime.sameDomainCompetitorSuppressed,
|
|
@@ -393,6 +405,14 @@ export function buildMineStatusJson(mining) {
|
|
|
393
405
|
if (mining.runtime.miningState === "repair-required") {
|
|
394
406
|
nextSteps.push("Run `cogcoin repair` before mining again.");
|
|
395
407
|
}
|
|
408
|
+
else if (mining.runtime.providerState === "not-found") {
|
|
409
|
+
nextSteps.push(mining.provider.usingDefaultModel === false
|
|
410
|
+
? "Run `cogcoin mine setup` and clear or correct the provider model."
|
|
411
|
+
: "Run `cogcoin mine setup` and choose a valid provider model.");
|
|
412
|
+
}
|
|
413
|
+
else if (mining.runtime.currentPublishDecision === "publish-paused-insufficient-funds") {
|
|
414
|
+
nextSteps.push("Wait for enough safe BTC funding to become spendable for the next publish; mining resumes automatically.");
|
|
415
|
+
}
|
|
396
416
|
else if (mining.runtime.pauseReason === "zero-reward") {
|
|
397
417
|
nextSteps.push("Wait for the next positive-reward target height; mining resumes automatically.");
|
|
398
418
|
}
|
|
@@ -472,6 +492,34 @@ export function buildMineLogJson(events, page, rotation) {
|
|
|
472
492
|
},
|
|
473
493
|
};
|
|
474
494
|
}
|
|
495
|
+
export function buildMinePromptListJson(result) {
|
|
496
|
+
const explanations = [];
|
|
497
|
+
const nextSteps = [];
|
|
498
|
+
if (result.prompts.length === 0) {
|
|
499
|
+
explanations.push("No mineable root domains or stored per-domain mining prompts are configured.");
|
|
500
|
+
nextSteps.push("Run `cogcoin domains --mineable` to see eligible mining domains.");
|
|
501
|
+
}
|
|
502
|
+
else {
|
|
503
|
+
const nextDomainPrompt = result.prompts.find((entry) => entry.mineable && entry.prompt === null);
|
|
504
|
+
if (nextDomainPrompt !== undefined) {
|
|
505
|
+
nextSteps.push(`cogcoin mine prompt ${nextDomainPrompt.domain.name}`);
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
return {
|
|
509
|
+
warnings: [],
|
|
510
|
+
explanations,
|
|
511
|
+
nextSteps,
|
|
512
|
+
data: {
|
|
513
|
+
fallbackPromptConfigured: result.fallbackPromptConfigured,
|
|
514
|
+
prompts: result.prompts.map((entry) => ({
|
|
515
|
+
domain: entry.domain,
|
|
516
|
+
mineable: entry.mineable,
|
|
517
|
+
prompt: entry.prompt,
|
|
518
|
+
effectivePromptSource: entry.effectivePromptSource,
|
|
519
|
+
})),
|
|
520
|
+
},
|
|
521
|
+
};
|
|
522
|
+
}
|
|
475
523
|
export function buildBalanceJson(context) {
|
|
476
524
|
const messages = createBaseMessages(context);
|
|
477
525
|
const total = walletCogBalance(context);
|
package/dist/cli/runner.js
CHANGED
|
@@ -10,6 +10,7 @@ import { runMiningRuntimeCommand } from "./commands/mining-runtime.js";
|
|
|
10
10
|
import { runServiceRuntimeCommand } from "./commands/service-runtime.js";
|
|
11
11
|
import { runStatusCommand } from "./commands/status.js";
|
|
12
12
|
import { runSyncCommand } from "./commands/sync.js";
|
|
13
|
+
import { runUpdateCommand } from "./commands/update.js";
|
|
13
14
|
import { runWalletAdminCommand } from "./commands/wallet-admin.js";
|
|
14
15
|
import { runWalletMutationCommand } from "./commands/wallet-mutation.js";
|
|
15
16
|
import { runWalletReadCommand } from "./commands/wallet-read.js";
|
|
@@ -52,6 +53,9 @@ export async function runCli(argv, contextOverrides = {}) {
|
|
|
52
53
|
}
|
|
53
54
|
await maybeNotifyAboutCliUpdate(parsed, context);
|
|
54
55
|
try {
|
|
56
|
+
if (parsed.command === "update") {
|
|
57
|
+
return runUpdateCommand(parsed, context);
|
|
58
|
+
}
|
|
55
59
|
if (commandUsesExistingWalletSeed(parsed)) {
|
|
56
60
|
const mainPaths = context.resolveWalletRuntimePaths("main");
|
|
57
61
|
const seedIndex = await loadWalletSeedIndex({
|
|
@@ -88,7 +92,8 @@ export async function runCli(argv, contextOverrides = {}) {
|
|
|
88
92
|
|| parsed.command === "mine-stop") {
|
|
89
93
|
return runMiningRuntimeCommand(parsed, context);
|
|
90
94
|
}
|
|
91
|
-
if (parsed.command === "mine-setup"
|
|
95
|
+
if (parsed.command === "mine-setup"
|
|
96
|
+
|| parsed.command === "mine-prompt") {
|
|
92
97
|
return runMiningAdminCommand(parsed, context);
|
|
93
98
|
}
|
|
94
99
|
if (parsed.command === "init"
|
|
@@ -135,7 +140,8 @@ export async function runCli(argv, contextOverrides = {}) {
|
|
|
135
140
|
|| parsed.command === "rep-revoke") {
|
|
136
141
|
return runWalletMutationCommand(parsed, context);
|
|
137
142
|
}
|
|
138
|
-
if (parsed.command === "mine-
|
|
143
|
+
if (parsed.command === "mine-prompt-list"
|
|
144
|
+
|| parsed.command === "mine-status"
|
|
139
145
|
|| parsed.command === "mine-log") {
|
|
140
146
|
return runMiningReadCommand(parsed, context);
|
|
141
147
|
}
|
package/dist/cli/signals.d.ts
CHANGED
|
@@ -1,4 +1,16 @@
|
|
|
1
1
|
import type { InterruptibleOutcome, ManagedClientLike, SignalSource, StopSignalWatcher, WritableLike } from "./types.js";
|
|
2
|
+
export declare function createCloseSignalWatcher(options: {
|
|
3
|
+
signalSource: SignalSource;
|
|
4
|
+
stderr: WritableLike;
|
|
5
|
+
closeable: {
|
|
6
|
+
close(): Promise<void>;
|
|
7
|
+
};
|
|
8
|
+
forceExit: (code: number) => never | void;
|
|
9
|
+
lockPaths?: readonly string[];
|
|
10
|
+
firstMessage?: string | null;
|
|
11
|
+
successMessage?: string | null;
|
|
12
|
+
failureMessage?: string | null;
|
|
13
|
+
}): StopSignalWatcher;
|
|
2
14
|
export declare function createStopSignalWatcher(signalSource: SignalSource, stderr: WritableLike, client: ManagedClientLike, forceExit: (code: number) => never | void, lockPaths?: readonly string[]): StopSignalWatcher;
|
|
3
15
|
export declare function createOwnedLockCleanupSignalWatcher(signalSource: SignalSource, forceExit: (code: number) => never | void, lockPaths: readonly string[]): StopSignalWatcher;
|
|
4
16
|
export declare function waitForCompletionOrStop<T>(promise: Promise<T>, stopWatcher: StopSignalWatcher): Promise<InterruptibleOutcome<T>>;
|
package/dist/cli/signals.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { writeLine } from "./io.js";
|
|
2
2
|
import { clearLockIfOwnedByCurrentProcess } from "../wallet/fs/lock.js";
|
|
3
|
-
export function
|
|
3
|
+
export function createCloseSignalWatcher(options) {
|
|
4
4
|
let closing = false;
|
|
5
5
|
let resolved = false;
|
|
6
6
|
let onSignal = () => { };
|
|
7
7
|
const cleanup = () => {
|
|
8
|
-
signalSource.off("SIGINT", onSignal);
|
|
9
|
-
signalSource.off("SIGTERM", onSignal);
|
|
8
|
+
options.signalSource.off("SIGINT", onSignal);
|
|
9
|
+
options.signalSource.off("SIGTERM", onSignal);
|
|
10
10
|
};
|
|
11
11
|
const promise = new Promise((resolve) => {
|
|
12
12
|
const settle = (code) => {
|
|
@@ -18,26 +18,32 @@ export function createStopSignalWatcher(signalSource, stderr, client, forceExit,
|
|
|
18
18
|
resolve(code);
|
|
19
19
|
};
|
|
20
20
|
const releaseOwnedLocks = async () => {
|
|
21
|
-
await Promise.allSettled([...new Set(lockPaths)].map(async (lockPath) => {
|
|
21
|
+
await Promise.allSettled([...new Set(options.lockPaths ?? [])].map(async (lockPath) => {
|
|
22
22
|
await clearLockIfOwnedByCurrentProcess(lockPath);
|
|
23
23
|
}));
|
|
24
24
|
};
|
|
25
25
|
const onFirstSignal = () => {
|
|
26
26
|
closing = true;
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
if (options.firstMessage !== null && options.firstMessage !== undefined) {
|
|
28
|
+
writeLine(options.stderr, options.firstMessage);
|
|
29
|
+
}
|
|
30
|
+
void options.closeable.close().then(async () => {
|
|
29
31
|
await releaseOwnedLocks();
|
|
30
32
|
if (resolved) {
|
|
31
33
|
return;
|
|
32
34
|
}
|
|
33
|
-
|
|
35
|
+
if (options.successMessage !== null && options.successMessage !== undefined) {
|
|
36
|
+
writeLine(options.stderr, options.successMessage);
|
|
37
|
+
}
|
|
34
38
|
settle(0);
|
|
35
39
|
}, async () => {
|
|
36
40
|
await releaseOwnedLocks();
|
|
37
41
|
if (resolved) {
|
|
38
42
|
return;
|
|
39
43
|
}
|
|
40
|
-
|
|
44
|
+
if (options.failureMessage !== null && options.failureMessage !== undefined) {
|
|
45
|
+
writeLine(options.stderr, options.failureMessage);
|
|
46
|
+
}
|
|
41
47
|
settle(1);
|
|
42
48
|
});
|
|
43
49
|
};
|
|
@@ -47,23 +53,35 @@ export function createStopSignalWatcher(signalSource, stderr, client, forceExit,
|
|
|
47
53
|
return;
|
|
48
54
|
}
|
|
49
55
|
settle(130);
|
|
50
|
-
if (lockPaths.length === 0) {
|
|
51
|
-
forceExit(130);
|
|
56
|
+
if ((options.lockPaths ?? []).length === 0) {
|
|
57
|
+
options.forceExit(130);
|
|
52
58
|
return;
|
|
53
59
|
}
|
|
54
60
|
void releaseOwnedLocks().finally(() => {
|
|
55
|
-
forceExit(130);
|
|
61
|
+
options.forceExit(130);
|
|
56
62
|
});
|
|
57
63
|
};
|
|
58
64
|
});
|
|
59
|
-
signalSource.on("SIGINT", onSignal);
|
|
60
|
-
signalSource.on("SIGTERM", onSignal);
|
|
65
|
+
options.signalSource.on("SIGINT", onSignal);
|
|
66
|
+
options.signalSource.on("SIGTERM", onSignal);
|
|
61
67
|
return {
|
|
62
68
|
cleanup,
|
|
63
69
|
isStopping: () => closing,
|
|
64
70
|
promise,
|
|
65
71
|
};
|
|
66
72
|
}
|
|
73
|
+
export function createStopSignalWatcher(signalSource, stderr, client, forceExit, lockPaths = []) {
|
|
74
|
+
return createCloseSignalWatcher({
|
|
75
|
+
signalSource,
|
|
76
|
+
stderr,
|
|
77
|
+
closeable: client,
|
|
78
|
+
forceExit,
|
|
79
|
+
lockPaths,
|
|
80
|
+
firstMessage: "Detaching from managed Cogcoin client and resuming background indexer follow...",
|
|
81
|
+
successMessage: "Detached cleanly; background indexer follow resumed.",
|
|
82
|
+
failureMessage: "Detach failed before background indexer follow was confirmed.",
|
|
83
|
+
});
|
|
84
|
+
}
|
|
67
85
|
export function createOwnedLockCleanupSignalWatcher(signalSource, forceExit, lockPaths) {
|
|
68
86
|
let stopping = false;
|
|
69
87
|
let resolved = false;
|
package/dist/cli/types.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { inspectPassiveClientStatus } from "../passive-status.js";
|
|
2
|
+
import type { ManagedIndexerMonitor, openManagedIndexerMonitor } from "../bitcoind/indexer-monitor.js";
|
|
2
3
|
import { createRpcClient } from "../bitcoind/node.js";
|
|
3
4
|
import type { ManagedBitcoindProgressEvent } from "../bitcoind/types.js";
|
|
4
5
|
import { attachOrStartIndexerDaemon, probeIndexerDaemon, readObservedIndexerDaemonStatus, stopIndexerDaemonService } from "../bitcoind/indexer-daemon.js";
|
|
@@ -10,11 +11,11 @@ import type { WalletPrompter, initializeWallet, deleteImportedWalletSeed, previe
|
|
|
10
11
|
import type { openWalletReadContext } from "../wallet/read/index.js";
|
|
11
12
|
import { loadRawWalletStateEnvelope, loadWalletState } from "../wallet/state/storage.js";
|
|
12
13
|
import type { WalletSecretProvider } from "../wallet/state/provider.js";
|
|
13
|
-
import type { ensureBuiltInMiningSetupIfNeeded, followMiningLog, inspectMiningControlPlane, readMiningLog, runForegroundMining, setupBuiltInMining, startBackgroundMining, stopBackgroundMining } from "../wallet/mining/index.js";
|
|
14
|
+
import type { ensureBuiltInMiningSetupIfNeeded, followMiningLog, inspectMiningControlPlane, inspectMiningDomainPromptState, readMiningLog, runForegroundMining, setupBuiltInMining, startBackgroundMining, stopBackgroundMining, updateMiningDomainPrompt } from "../wallet/mining/index.js";
|
|
14
15
|
import type { anchorDomain, transferBitcoin, buyDomain, claimCogLock, clearDomainDelegate, clearDomainEndpoint, clearDomainMiner, clearField, createField, giveReputation, lockCogToDomain, registerDomain, reclaimCogLock, revokeReputation, sendCog, setField, setDomainCanonical, setDomainDelegate, setDomainEndpoint, setDomainMiner, sellDomain, transferDomain } from "../wallet/tx/index.js";
|
|
15
16
|
export type ProgressOutput = "auto" | "tty" | "none";
|
|
16
17
|
export type OutputMode = "text" | "json" | "preview-json";
|
|
17
|
-
export type CommandName = "init" | "restore" | "reset" | "repair" | "sync" | "status" | "client-lock" | "client-change-password" | "client-unlock" | "follow" | "bitcoin-start" | "bitcoin-stop" | "bitcoin-status" | "bitcoin-transfer" | "indexer-start" | "indexer-stop" | "indexer-status" | "anchor" | "domain-anchor" | "register" | "domain-register" | "transfer" | "domain-transfer" | "sell" | "domain-sell" | "unsell" | "domain-unsell" | "buy" | "domain-buy" | "domain-endpoint-set" | "domain-endpoint-clear" | "domain-delegate-set" | "domain-delegate-clear" | "domain-miner-set" | "domain-miner-clear" | "domain-canonical" | "field-list" | "field-show" | "field-create" | "field-set" | "field-clear" | "send" | "claim" | "reclaim" | "cog-send" | "cog-claim" | "cog-reclaim" | "cog-lock" | "rep-give" | "rep-revoke" | "cog-balance" | "cog-locks" | "mine" | "mine-start" | "mine-stop" | "mine-setup" | "mine-status" | "mine-log" | "wallet-init" | "wallet-delete" | "wallet-restore" | "wallet-show-mnemonic" | "wallet-status" | "wallet-address" | "wallet-ids" | "address" | "ids" | "balance" | "locks" | "domain-list" | "domains" | "domain-show" | "show" | "fields" | "field";
|
|
18
|
+
export type CommandName = "init" | "restore" | "reset" | "repair" | "update" | "sync" | "status" | "client-lock" | "client-change-password" | "client-unlock" | "follow" | "bitcoin-start" | "bitcoin-stop" | "bitcoin-status" | "bitcoin-transfer" | "indexer-start" | "indexer-stop" | "indexer-status" | "anchor" | "domain-anchor" | "register" | "domain-register" | "transfer" | "domain-transfer" | "sell" | "domain-sell" | "unsell" | "domain-unsell" | "buy" | "domain-buy" | "domain-endpoint-set" | "domain-endpoint-clear" | "domain-delegate-set" | "domain-delegate-clear" | "domain-miner-set" | "domain-miner-clear" | "domain-canonical" | "field-list" | "field-show" | "field-create" | "field-set" | "field-clear" | "send" | "claim" | "reclaim" | "cog-send" | "cog-claim" | "cog-reclaim" | "cog-lock" | "rep-give" | "rep-revoke" | "cog-balance" | "cog-locks" | "mine" | "mine-start" | "mine-stop" | "mine-setup" | "mine-prompt" | "mine-prompt-list" | "mine-status" | "mine-log" | "wallet-init" | "wallet-delete" | "wallet-restore" | "wallet-show-mnemonic" | "wallet-status" | "wallet-address" | "wallet-ids" | "address" | "ids" | "balance" | "locks" | "domain-list" | "domains" | "domain-show" | "show" | "fields" | "field";
|
|
18
19
|
export interface WritableLike {
|
|
19
20
|
isTTY?: boolean;
|
|
20
21
|
write(chunk: string): void;
|
|
@@ -71,7 +72,6 @@ export interface ManagedClientLike {
|
|
|
71
72
|
bestHeight: number;
|
|
72
73
|
}>;
|
|
73
74
|
playSyncCompletionScene?(): Promise<void>;
|
|
74
|
-
detachToBackgroundFollow?(): Promise<void>;
|
|
75
75
|
startFollowingTip(): Promise<void>;
|
|
76
76
|
getNodeStatus(): Promise<{
|
|
77
77
|
indexedTip: {
|
|
@@ -83,6 +83,8 @@ export interface ManagedClientLike {
|
|
|
83
83
|
}>;
|
|
84
84
|
close(): Promise<void>;
|
|
85
85
|
}
|
|
86
|
+
export interface ManagedIndexerMonitorLike extends ManagedIndexerMonitor {
|
|
87
|
+
}
|
|
86
88
|
export interface CliRunnerContext {
|
|
87
89
|
stdout?: WritableLike;
|
|
88
90
|
stderr?: WritableLike;
|
|
@@ -92,10 +94,14 @@ export interface CliRunnerContext {
|
|
|
92
94
|
signalSource?: SignalSource;
|
|
93
95
|
forceExit?: (code: number) => never | void;
|
|
94
96
|
fetchImpl?: typeof fetch;
|
|
97
|
+
runGlobalClientUpdateInstall?: (options: {
|
|
98
|
+
stdout: WritableLike;
|
|
99
|
+
stderr: WritableLike;
|
|
100
|
+
env: NodeJS.ProcessEnv;
|
|
101
|
+
}) => Promise<void>;
|
|
95
102
|
openSqliteStore?: typeof openSqliteStore;
|
|
96
103
|
openManagedBitcoindClient?: (options: {
|
|
97
104
|
store: ClientStoreAdapter;
|
|
98
|
-
databasePath?: string;
|
|
99
105
|
dataDir?: string;
|
|
100
106
|
walletRootId?: string;
|
|
101
107
|
progressOutput?: ProgressOutput;
|
|
@@ -105,6 +111,7 @@ export interface CliRunnerContext {
|
|
|
105
111
|
nextArchiveEndHeight: number;
|
|
106
112
|
}) => Promise<boolean>;
|
|
107
113
|
}) => Promise<ManagedClientLike>;
|
|
114
|
+
openManagedIndexerMonitor?: typeof openManagedIndexerMonitor;
|
|
108
115
|
attachManagedBitcoindService?: typeof attachOrStartManagedBitcoindService;
|
|
109
116
|
probeManagedBitcoindService?: typeof probeManagedBitcoindService;
|
|
110
117
|
stopManagedBitcoindService?: typeof stopManagedBitcoindService;
|
|
@@ -145,11 +152,13 @@ export interface CliRunnerContext {
|
|
|
145
152
|
giveReputation?: typeof giveReputation;
|
|
146
153
|
revokeReputation?: typeof revokeReputation;
|
|
147
154
|
inspectMiningControlPlane?: typeof inspectMiningControlPlane;
|
|
155
|
+
inspectMiningDomainPromptState?: typeof inspectMiningDomainPromptState;
|
|
148
156
|
ensureBuiltInMiningSetupIfNeeded?: typeof ensureBuiltInMiningSetupIfNeeded;
|
|
149
157
|
runForegroundMining?: typeof runForegroundMining;
|
|
150
158
|
startBackgroundMining?: typeof startBackgroundMining;
|
|
151
159
|
stopBackgroundMining?: typeof stopBackgroundMining;
|
|
152
160
|
setupBuiltInMining?: typeof setupBuiltInMining;
|
|
161
|
+
updateMiningDomainPrompt?: typeof updateMiningDomainPrompt;
|
|
153
162
|
readMiningLog?: typeof readMiningLog;
|
|
154
163
|
followMiningLog?: typeof followMiningLog;
|
|
155
164
|
repairWallet?: typeof repairWallet;
|
|
@@ -1,143 +1,14 @@
|
|
|
1
|
-
import { readFile } from "node:fs/promises";
|
|
2
|
-
import { writeJsonFileAtomic } from "../wallet/fs/atomic.js";
|
|
3
1
|
import { writeLine } from "./io.js";
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
const UPDATE_CHECK_URL = "https://registry.npmjs.org/@cogcoin/client/latest";
|
|
8
|
-
function createEmptyUpdateCheckCache() {
|
|
9
|
-
return {
|
|
10
|
-
schemaVersion: UPDATE_CHECK_CACHE_SCHEMA_VERSION,
|
|
11
|
-
lastCheckedAtUnixMs: 0,
|
|
12
|
-
latestVersion: null,
|
|
13
|
-
lastNotifiedCurrentVersion: null,
|
|
14
|
-
lastNotifiedLatestVersion: null,
|
|
15
|
-
lastNotifiedAtUnixMs: null,
|
|
16
|
-
};
|
|
17
|
-
}
|
|
18
|
-
function parseSemver(version) {
|
|
19
|
-
const match = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/.exec(version.trim());
|
|
20
|
-
if (match === null) {
|
|
21
|
-
return null;
|
|
22
|
-
}
|
|
23
|
-
const prerelease = match[4] === undefined
|
|
24
|
-
? []
|
|
25
|
-
: match[4].split(".").map((raw) => ({
|
|
26
|
-
raw,
|
|
27
|
-
numeric: /^(0|[1-9]\d*)$/.test(raw),
|
|
28
|
-
numericValue: /^(0|[1-9]\d*)$/.test(raw) ? Number(raw) : null,
|
|
29
|
-
}));
|
|
30
|
-
return {
|
|
31
|
-
major: Number(match[1]),
|
|
32
|
-
minor: Number(match[2]),
|
|
33
|
-
patch: Number(match[3]),
|
|
34
|
-
prerelease,
|
|
35
|
-
};
|
|
36
|
-
}
|
|
37
|
-
function compareSemver(left, right) {
|
|
38
|
-
const leftParsed = parseSemver(left);
|
|
39
|
-
const rightParsed = parseSemver(right);
|
|
40
|
-
if (leftParsed === null || rightParsed === null) {
|
|
41
|
-
return null;
|
|
42
|
-
}
|
|
43
|
-
if (leftParsed.major !== rightParsed.major) {
|
|
44
|
-
return leftParsed.major > rightParsed.major ? 1 : -1;
|
|
45
|
-
}
|
|
46
|
-
if (leftParsed.minor !== rightParsed.minor) {
|
|
47
|
-
return leftParsed.minor > rightParsed.minor ? 1 : -1;
|
|
48
|
-
}
|
|
49
|
-
if (leftParsed.patch !== rightParsed.patch) {
|
|
50
|
-
return leftParsed.patch > rightParsed.patch ? 1 : -1;
|
|
51
|
-
}
|
|
52
|
-
if (leftParsed.prerelease.length === 0 && rightParsed.prerelease.length === 0) {
|
|
53
|
-
return 0;
|
|
54
|
-
}
|
|
55
|
-
if (leftParsed.prerelease.length === 0) {
|
|
56
|
-
return 1;
|
|
57
|
-
}
|
|
58
|
-
if (rightParsed.prerelease.length === 0) {
|
|
59
|
-
return -1;
|
|
60
|
-
}
|
|
61
|
-
const maxLength = Math.max(leftParsed.prerelease.length, rightParsed.prerelease.length);
|
|
62
|
-
for (let index = 0; index < maxLength; index += 1) {
|
|
63
|
-
const leftIdentifier = leftParsed.prerelease[index];
|
|
64
|
-
const rightIdentifier = rightParsed.prerelease[index];
|
|
65
|
-
if (leftIdentifier === undefined) {
|
|
66
|
-
return -1;
|
|
67
|
-
}
|
|
68
|
-
if (rightIdentifier === undefined) {
|
|
69
|
-
return 1;
|
|
70
|
-
}
|
|
71
|
-
if (leftIdentifier.numeric && rightIdentifier.numeric) {
|
|
72
|
-
if (leftIdentifier.numericValue !== rightIdentifier.numericValue) {
|
|
73
|
-
return leftIdentifier.numericValue > rightIdentifier.numericValue ? 1 : -1;
|
|
74
|
-
}
|
|
75
|
-
continue;
|
|
76
|
-
}
|
|
77
|
-
if (leftIdentifier.numeric !== rightIdentifier.numeric) {
|
|
78
|
-
return leftIdentifier.numeric ? -1 : 1;
|
|
79
|
-
}
|
|
80
|
-
if (leftIdentifier.raw !== rightIdentifier.raw) {
|
|
81
|
-
return leftIdentifier.raw > rightIdentifier.raw ? 1 : -1;
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
return 0;
|
|
85
|
-
}
|
|
86
|
-
function isUpdateCheckDisabled(env) {
|
|
87
|
-
const raw = env.COGCOIN_DISABLE_UPDATE_CHECK;
|
|
88
|
-
if (raw === undefined) {
|
|
2
|
+
import { PASSIVE_UPDATE_CHECK_TIMEOUT_MS, UPDATE_CHECK_MAX_AGE_MS, applyUpdateCheckResult, compareSemver, createEmptyUpdateCheckCache, fetchLatestPublishedVersion, isUpdateCheckDisabled, loadUpdateCheckCache, persistUpdateCheckCache, recordUpdateNotification, shouldRefreshUpdateCheck, } from "./update-service.js";
|
|
3
|
+
function isEligibleForUpdateNotification(parsed, context) {
|
|
4
|
+
if (parsed.command === "update") {
|
|
89
5
|
return false;
|
|
90
6
|
}
|
|
91
|
-
const normalized = raw.trim().toLowerCase();
|
|
92
|
-
return normalized === "1" || normalized === "true" || normalized === "yes";
|
|
93
|
-
}
|
|
94
|
-
function isEligibleForUpdateNotification(parsed, context) {
|
|
95
7
|
if (parsed.outputMode !== "text" || parsed.help || parsed.version) {
|
|
96
8
|
return false;
|
|
97
9
|
}
|
|
98
10
|
return context.stdout.isTTY === true || context.stderr.isTTY === true;
|
|
99
11
|
}
|
|
100
|
-
function normalizeUpdateCheckCache(parsed) {
|
|
101
|
-
if (typeof parsed !== "object" || parsed === null) {
|
|
102
|
-
return null;
|
|
103
|
-
}
|
|
104
|
-
const candidate = parsed;
|
|
105
|
-
if (candidate.schemaVersion !== UPDATE_CHECK_CACHE_SCHEMA_VERSION) {
|
|
106
|
-
return null;
|
|
107
|
-
}
|
|
108
|
-
return {
|
|
109
|
-
schemaVersion: UPDATE_CHECK_CACHE_SCHEMA_VERSION,
|
|
110
|
-
lastCheckedAtUnixMs: typeof candidate.lastCheckedAtUnixMs === "number" ? candidate.lastCheckedAtUnixMs : 0,
|
|
111
|
-
latestVersion: typeof candidate.latestVersion === "string" ? candidate.latestVersion : null,
|
|
112
|
-
lastNotifiedCurrentVersion: typeof candidate.lastNotifiedCurrentVersion === "string"
|
|
113
|
-
? candidate.lastNotifiedCurrentVersion
|
|
114
|
-
: null,
|
|
115
|
-
lastNotifiedLatestVersion: typeof candidate.lastNotifiedLatestVersion === "string"
|
|
116
|
-
? candidate.lastNotifiedLatestVersion
|
|
117
|
-
: null,
|
|
118
|
-
lastNotifiedAtUnixMs: typeof candidate.lastNotifiedAtUnixMs === "number"
|
|
119
|
-
? candidate.lastNotifiedAtUnixMs
|
|
120
|
-
: null,
|
|
121
|
-
lastCheckErrorKind: typeof candidate.lastCheckErrorKind === "string"
|
|
122
|
-
? candidate.lastCheckErrorKind
|
|
123
|
-
: undefined,
|
|
124
|
-
};
|
|
125
|
-
}
|
|
126
|
-
async function loadUpdateCheckCache(cachePath) {
|
|
127
|
-
try {
|
|
128
|
-
const raw = await readFile(cachePath, "utf8");
|
|
129
|
-
return normalizeUpdateCheckCache(JSON.parse(raw));
|
|
130
|
-
}
|
|
131
|
-
catch (error) {
|
|
132
|
-
if (error instanceof Error && "code" in error && error.code === "ENOENT") {
|
|
133
|
-
return null;
|
|
134
|
-
}
|
|
135
|
-
return null;
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
function shouldRefreshUpdateCheck(cache, now) {
|
|
139
|
-
return now - cache.lastCheckedAtUnixMs >= UPDATE_CHECK_MAX_AGE_MS;
|
|
140
|
-
}
|
|
141
12
|
function shouldNotifyForVersionPair(cache, currentVersion, latestVersion, now) {
|
|
142
13
|
const versionComparison = compareSemver(latestVersion, currentVersion);
|
|
143
14
|
if (versionComparison === null || versionComparison <= 0) {
|
|
@@ -152,117 +23,31 @@ function shouldNotifyForVersionPair(cache, currentVersion, latestVersion, now) {
|
|
|
152
23
|
}
|
|
153
24
|
return now - cache.lastNotifiedAtUnixMs >= UPDATE_CHECK_MAX_AGE_MS;
|
|
154
25
|
}
|
|
155
|
-
function recordUpdateNotification(cache, currentVersion, latestVersion, now) {
|
|
156
|
-
return {
|
|
157
|
-
...cache,
|
|
158
|
-
lastNotifiedCurrentVersion: currentVersion,
|
|
159
|
-
lastNotifiedLatestVersion: latestVersion,
|
|
160
|
-
lastNotifiedAtUnixMs: now,
|
|
161
|
-
};
|
|
162
|
-
}
|
|
163
|
-
async function fetchLatestPublishedVersion(fetchImpl) {
|
|
164
|
-
const controller = new AbortController();
|
|
165
|
-
const timer = setTimeout(() => {
|
|
166
|
-
controller.abort();
|
|
167
|
-
}, UPDATE_CHECK_TIMEOUT_MS);
|
|
168
|
-
try {
|
|
169
|
-
const response = await fetchImpl(UPDATE_CHECK_URL, {
|
|
170
|
-
headers: {
|
|
171
|
-
accept: "application/json",
|
|
172
|
-
},
|
|
173
|
-
signal: controller.signal,
|
|
174
|
-
});
|
|
175
|
-
if (!response.ok) {
|
|
176
|
-
return {
|
|
177
|
-
kind: "failure",
|
|
178
|
-
errorKind: `http_${response.status}`,
|
|
179
|
-
};
|
|
180
|
-
}
|
|
181
|
-
let payload;
|
|
182
|
-
try {
|
|
183
|
-
payload = await response.json();
|
|
184
|
-
}
|
|
185
|
-
catch {
|
|
186
|
-
return {
|
|
187
|
-
kind: "failure",
|
|
188
|
-
errorKind: "invalid_json",
|
|
189
|
-
};
|
|
190
|
-
}
|
|
191
|
-
const latestVersion = typeof payload.version === "string"
|
|
192
|
-
? payload.version
|
|
193
|
-
: null;
|
|
194
|
-
if (latestVersion === null) {
|
|
195
|
-
return {
|
|
196
|
-
kind: "failure",
|
|
197
|
-
errorKind: "invalid_payload",
|
|
198
|
-
};
|
|
199
|
-
}
|
|
200
|
-
if (parseSemver(latestVersion) === null) {
|
|
201
|
-
return {
|
|
202
|
-
kind: "failure",
|
|
203
|
-
errorKind: "invalid_semver",
|
|
204
|
-
};
|
|
205
|
-
}
|
|
206
|
-
return {
|
|
207
|
-
kind: "success",
|
|
208
|
-
latestVersion,
|
|
209
|
-
};
|
|
210
|
-
}
|
|
211
|
-
catch (error) {
|
|
212
|
-
if (error instanceof Error && error.name === "AbortError") {
|
|
213
|
-
return {
|
|
214
|
-
kind: "failure",
|
|
215
|
-
errorKind: "timeout",
|
|
216
|
-
};
|
|
217
|
-
}
|
|
218
|
-
return {
|
|
219
|
-
kind: "failure",
|
|
220
|
-
errorKind: "network",
|
|
221
|
-
};
|
|
222
|
-
}
|
|
223
|
-
finally {
|
|
224
|
-
clearTimeout(timer);
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
26
|
function writeUpdateNotice(context, currentVersion, latestVersion) {
|
|
228
27
|
writeLine(context.stderr, `Update available: Cogcoin ${currentVersion} -> ${latestVersion}`);
|
|
229
28
|
writeLine(context.stderr, "Run: npm install -g @cogcoin/client");
|
|
230
29
|
}
|
|
231
|
-
async function persistUpdateCheckCache(cachePath, cache) {
|
|
232
|
-
await writeJsonFileAtomic(cachePath, cache);
|
|
233
|
-
}
|
|
234
30
|
export async function maybeNotifyAboutCliUpdate(parsed, context) {
|
|
235
31
|
try {
|
|
236
32
|
if (!isEligibleForUpdateNotification(parsed, context) || isUpdateCheckDisabled(context.env)) {
|
|
237
33
|
return;
|
|
238
34
|
}
|
|
239
35
|
const currentVersion = await context.readPackageVersion();
|
|
240
|
-
if (parseSemver(currentVersion) === null) {
|
|
241
|
-
return;
|
|
242
|
-
}
|
|
243
36
|
const cachePath = context.resolveUpdateCheckStatePath();
|
|
244
37
|
const now = context.now();
|
|
245
38
|
let cache = await loadUpdateCheckCache(cachePath) ?? createEmptyUpdateCheckCache();
|
|
246
39
|
let cacheChanged = false;
|
|
247
40
|
if (shouldRefreshUpdateCheck(cache, now)) {
|
|
248
|
-
const updateResult = await fetchLatestPublishedVersion(context.fetchImpl
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
latestVersion: updateResult.kind === "success"
|
|
253
|
-
? updateResult.latestVersion
|
|
254
|
-
: cache.latestVersion,
|
|
255
|
-
lastCheckErrorKind: updateResult.kind === "success"
|
|
256
|
-
? undefined
|
|
257
|
-
: updateResult.errorKind,
|
|
258
|
-
};
|
|
41
|
+
const updateResult = await fetchLatestPublishedVersion(context.fetchImpl, {
|
|
42
|
+
timeoutMs: PASSIVE_UPDATE_CHECK_TIMEOUT_MS,
|
|
43
|
+
});
|
|
44
|
+
cache = applyUpdateCheckResult(cache, updateResult, now);
|
|
259
45
|
cacheChanged = true;
|
|
260
46
|
}
|
|
261
47
|
if (cache.latestVersion !== null
|
|
262
48
|
&& shouldNotifyForVersionPair(cache, currentVersion, cache.latestVersion, now)) {
|
|
263
49
|
writeUpdateNotice(context, currentVersion, cache.latestVersion);
|
|
264
50
|
cache = recordUpdateNotification(cache, currentVersion, cache.latestVersion, now);
|
|
265
|
-
cacheChanged = true;
|
|
266
51
|
await persistUpdateCheckCache(cachePath, cache);
|
|
267
52
|
return;
|
|
268
53
|
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { compareSemver, parseSemver, type ParsedSemver } from "../semver.js";
|
|
2
|
+
export declare const UPDATE_CHECK_CACHE_SCHEMA_VERSION = 1;
|
|
3
|
+
export declare const UPDATE_CHECK_MAX_AGE_MS: number;
|
|
4
|
+
export declare const PASSIVE_UPDATE_CHECK_TIMEOUT_MS = 500;
|
|
5
|
+
export declare const EXPLICIT_UPDATE_CHECK_TIMEOUT_MS = 5000;
|
|
6
|
+
export declare const UPDATE_CHECK_URL = "https://registry.npmjs.org/@cogcoin/client/latest";
|
|
7
|
+
export declare const CLI_INSTALL_COMMAND = "npm install -g @cogcoin/client";
|
|
8
|
+
export interface UpdateCheckCache {
|
|
9
|
+
schemaVersion: typeof UPDATE_CHECK_CACHE_SCHEMA_VERSION;
|
|
10
|
+
lastCheckedAtUnixMs: number;
|
|
11
|
+
latestVersion: string | null;
|
|
12
|
+
lastNotifiedCurrentVersion: string | null;
|
|
13
|
+
lastNotifiedLatestVersion: string | null;
|
|
14
|
+
lastNotifiedAtUnixMs: number | null;
|
|
15
|
+
lastCheckErrorKind?: string;
|
|
16
|
+
}
|
|
17
|
+
export type UpdateCheckResult = {
|
|
18
|
+
kind: "success";
|
|
19
|
+
latestVersion: string;
|
|
20
|
+
} | {
|
|
21
|
+
kind: "failure";
|
|
22
|
+
errorKind: string;
|
|
23
|
+
};
|
|
24
|
+
export declare function createEmptyUpdateCheckCache(): UpdateCheckCache;
|
|
25
|
+
export { compareSemver, parseSemver, type ParsedSemver };
|
|
26
|
+
export declare function isUpdateCheckDisabled(env: NodeJS.ProcessEnv): boolean;
|
|
27
|
+
export declare function loadUpdateCheckCache(cachePath: string): Promise<UpdateCheckCache | null>;
|
|
28
|
+
export declare function shouldRefreshUpdateCheck(cache: UpdateCheckCache, now: number): boolean;
|
|
29
|
+
export declare function applyUpdateCheckResult(cache: UpdateCheckCache, result: UpdateCheckResult, now: number): UpdateCheckCache;
|
|
30
|
+
export declare function recordUpdateNotification(cache: UpdateCheckCache, currentVersion: string, latestVersion: string, now: number): UpdateCheckCache;
|
|
31
|
+
export declare function fetchLatestPublishedVersion(fetchImpl: typeof fetch, options?: {
|
|
32
|
+
timeoutMs?: number;
|
|
33
|
+
}): Promise<UpdateCheckResult>;
|
|
34
|
+
export declare function persistUpdateCheckCache(cachePath: string, cache: UpdateCheckCache): Promise<void>;
|