@cogcoin/client 1.0.0 → 1.0.1
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/dist/cli/commands/mining-runtime.js +114 -12
- package/dist/cli/commands/sync.js +1 -91
- package/dist/cli/context.js +2 -1
- package/dist/cli/sync-progress.d.ts +6 -0
- package/dist/cli/sync-progress.js +91 -0
- package/dist/cli/types.d.ts +2 -1
- package/dist/cli/wallet-format.js +3 -0
- package/dist/wallet/mining/index.d.ts +1 -1
- package/dist/wallet/mining/index.js +1 -1
- package/dist/wallet/mining/runner.d.ts +7 -0
- package/dist/wallet/mining/runner.js +15 -11
- package/package.json +1 -1
|
@@ -1,28 +1,109 @@
|
|
|
1
1
|
import { dirname } from "node:path";
|
|
2
|
+
import { FileLockBusyError, acquireFileLock } from "../../wallet/fs/lock.js";
|
|
3
|
+
import { resolveWalletRootIdFromLocalArtifacts } from "../../wallet/root-resolution.js";
|
|
4
|
+
import { withInteractiveWalletSecretProvider } from "../../wallet/state/provider.js";
|
|
2
5
|
import { buildMineStartData, buildMineStopData, } from "../mining-json.js";
|
|
3
6
|
import { buildMineStartPreviewData, buildMineStopPreviewData, } from "../preview-json.js";
|
|
4
|
-
import { writeLine } from "../io.js";
|
|
7
|
+
import { usesTtyProgress, writeLine } from "../io.js";
|
|
5
8
|
import { createTerminalPrompter } from "../prompt.js";
|
|
6
9
|
import { createPreviewSuccessEnvelope, createMutationSuccessEnvelope, describeCanonicalCommand, resolvePreviewJsonSchema, resolveStableMiningControlJsonSchema, writeHandledCliError, writeJsonValue, } from "../output.js";
|
|
7
10
|
import { formatNextStepLines, getMineStopNextSteps, } from "../workflow-hints.js";
|
|
8
|
-
import {
|
|
11
|
+
import { createStopSignalWatcher, waitForCompletionOrStop } from "../signals.js";
|
|
12
|
+
import { createSyncProgressReporter } from "../sync-progress.js";
|
|
9
13
|
function createCommandPrompter(parsed, context) {
|
|
10
14
|
return parsed.outputMode !== "text"
|
|
11
15
|
? createTerminalPrompter(context.stdin, context.stderr)
|
|
12
16
|
: context.createPrompter();
|
|
13
17
|
}
|
|
14
|
-
async function
|
|
15
|
-
|
|
18
|
+
async function ensureMiningProviderSetup(options) {
|
|
19
|
+
const setupReady = await options.context.ensureBuiltInMiningSetupIfNeeded({
|
|
20
|
+
provider: options.provider,
|
|
21
|
+
prompter: options.prompter,
|
|
22
|
+
paths: options.runtimePaths,
|
|
23
|
+
});
|
|
24
|
+
if (!setupReady) {
|
|
25
|
+
throw new Error("Built-in mining provider is not configured. Run `cogcoin mine setup`.");
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
async function syncManagedMiningReadiness(options) {
|
|
29
|
+
const ttyProgressActive = usesTtyProgress(options.parsed.progressOutput, options.context.stderr);
|
|
30
|
+
let controlLock = null;
|
|
31
|
+
let store = null;
|
|
32
|
+
let storeOwned = true;
|
|
33
|
+
let client = null;
|
|
34
|
+
let clientClosed = false;
|
|
16
35
|
try {
|
|
17
|
-
|
|
18
|
-
dataDir: options.dataDir,
|
|
19
|
-
databasePath: options.databasePath,
|
|
20
|
-
secretProvider: options.provider,
|
|
36
|
+
const walletRoot = await resolveWalletRootIdFromLocalArtifacts({
|
|
21
37
|
paths: options.runtimePaths,
|
|
38
|
+
provider: options.provider,
|
|
39
|
+
loadRawWalletStateEnvelope: options.context.loadRawWalletStateEnvelope,
|
|
22
40
|
});
|
|
41
|
+
try {
|
|
42
|
+
controlLock = await acquireFileLock(options.runtimePaths.walletControlLockPath, {
|
|
43
|
+
purpose: "managed-sync",
|
|
44
|
+
walletRootId: walletRoot.walletRootId,
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
catch (error) {
|
|
48
|
+
if (error instanceof FileLockBusyError) {
|
|
49
|
+
throw new Error("wallet_control_lock_busy");
|
|
50
|
+
}
|
|
51
|
+
throw error;
|
|
52
|
+
}
|
|
53
|
+
await options.context.ensureDirectory(dirname(options.databasePath));
|
|
54
|
+
store = await options.context.openSqliteStore({ filename: options.databasePath });
|
|
55
|
+
client = await options.context.openManagedBitcoindClient({
|
|
56
|
+
store,
|
|
57
|
+
databasePath: options.databasePath,
|
|
58
|
+
dataDir: options.dataDir,
|
|
59
|
+
walletRootId: walletRoot.walletRootId,
|
|
60
|
+
progressOutput: options.parsed.progressOutput,
|
|
61
|
+
onProgress: ttyProgressActive ? undefined : createSyncProgressReporter({
|
|
62
|
+
progressOutput: options.parsed.progressOutput,
|
|
63
|
+
write: (line) => {
|
|
64
|
+
writeLine(options.context.stderr, line);
|
|
65
|
+
},
|
|
66
|
+
}),
|
|
67
|
+
});
|
|
68
|
+
storeOwned = false;
|
|
69
|
+
const stopWatcher = createStopSignalWatcher(options.context.signalSource, options.context.stderr, client, options.context.forceExit, [options.runtimePaths.walletControlLockPath]);
|
|
70
|
+
try {
|
|
71
|
+
const syncOutcome = await waitForCompletionOrStop(client.syncToTip(), stopWatcher);
|
|
72
|
+
if (syncOutcome.kind === "stopped") {
|
|
73
|
+
return syncOutcome.code;
|
|
74
|
+
}
|
|
75
|
+
const result = syncOutcome.value;
|
|
76
|
+
if (result.endingHeight !== null && result.endingHeight === result.bestHeight) {
|
|
77
|
+
stopWatcher.cleanup();
|
|
78
|
+
const detachPromise = typeof client.detachToBackgroundFollow === "function"
|
|
79
|
+
? client.detachToBackgroundFollow()
|
|
80
|
+
: Promise.resolve();
|
|
81
|
+
try {
|
|
82
|
+
await detachPromise;
|
|
83
|
+
await client.close();
|
|
84
|
+
clientClosed = true;
|
|
85
|
+
writeLine(options.context.stderr, "Detached cleanly; background indexer follow resumed.");
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
writeLine(options.context.stderr, "Detach failed before background indexer follow was confirmed.");
|
|
90
|
+
return 1;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
throw new Error("Managed sync did not reach the current Bitcoin tip.");
|
|
94
|
+
}
|
|
95
|
+
finally {
|
|
96
|
+
stopWatcher.cleanup();
|
|
97
|
+
if (!clientClosed) {
|
|
98
|
+
await client.close();
|
|
99
|
+
}
|
|
100
|
+
}
|
|
23
101
|
}
|
|
24
102
|
finally {
|
|
25
|
-
|
|
103
|
+
if (storeOwned && store !== null) {
|
|
104
|
+
await store.close().catch(() => undefined);
|
|
105
|
+
}
|
|
106
|
+
await controlLock?.release().catch(() => undefined);
|
|
26
107
|
}
|
|
27
108
|
}
|
|
28
109
|
export async function runMiningRuntimeCommand(parsed, context) {
|
|
@@ -30,17 +111,26 @@ export async function runMiningRuntimeCommand(parsed, context) {
|
|
|
30
111
|
const dbPath = parsed.dbPath ?? context.resolveDefaultClientDatabasePath();
|
|
31
112
|
const dataDir = parsed.dataDir ?? context.resolveDefaultBitcoindDataDir();
|
|
32
113
|
const runtimePaths = context.resolveWalletRuntimePaths(parsed.seedName);
|
|
33
|
-
await context.ensureDirectory(dirname(dbPath));
|
|
34
114
|
if (parsed.command === "mine") {
|
|
35
115
|
const prompter = context.createPrompter();
|
|
36
116
|
const provider = withInteractiveWalletSecretProvider(context.walletSecretProvider, prompter);
|
|
37
|
-
await
|
|
117
|
+
await ensureMiningProviderSetup({
|
|
118
|
+
context,
|
|
119
|
+
provider,
|
|
120
|
+
prompter,
|
|
121
|
+
runtimePaths,
|
|
122
|
+
});
|
|
123
|
+
const preflightCode = await syncManagedMiningReadiness({
|
|
124
|
+
parsed,
|
|
38
125
|
context,
|
|
39
126
|
dataDir,
|
|
40
127
|
databasePath: dbPath,
|
|
41
128
|
provider,
|
|
42
129
|
runtimePaths,
|
|
43
130
|
});
|
|
131
|
+
if (preflightCode !== null) {
|
|
132
|
+
return preflightCode;
|
|
133
|
+
}
|
|
44
134
|
const abortController = new AbortController();
|
|
45
135
|
const onStop = () => {
|
|
46
136
|
abortController.abort();
|
|
@@ -57,6 +147,7 @@ export async function runMiningRuntimeCommand(parsed, context) {
|
|
|
57
147
|
stdout: context.stdout,
|
|
58
148
|
stderr: context.stderr,
|
|
59
149
|
progressOutput: parsed.progressOutput,
|
|
150
|
+
builtInSetupEnsured: true,
|
|
60
151
|
paths: runtimePaths,
|
|
61
152
|
});
|
|
62
153
|
}
|
|
@@ -69,18 +160,29 @@ export async function runMiningRuntimeCommand(parsed, context) {
|
|
|
69
160
|
if (parsed.command === "mine-start") {
|
|
70
161
|
const prompter = createCommandPrompter(parsed, context);
|
|
71
162
|
const provider = withInteractiveWalletSecretProvider(context.walletSecretProvider, prompter);
|
|
72
|
-
await
|
|
163
|
+
await ensureMiningProviderSetup({
|
|
164
|
+
context,
|
|
165
|
+
provider,
|
|
166
|
+
prompter,
|
|
167
|
+
runtimePaths,
|
|
168
|
+
});
|
|
169
|
+
const preflightCode = await syncManagedMiningReadiness({
|
|
170
|
+
parsed,
|
|
73
171
|
context,
|
|
74
172
|
dataDir,
|
|
75
173
|
databasePath: dbPath,
|
|
76
174
|
provider,
|
|
77
175
|
runtimePaths,
|
|
78
176
|
});
|
|
177
|
+
if (preflightCode !== null) {
|
|
178
|
+
return preflightCode;
|
|
179
|
+
}
|
|
79
180
|
const result = await context.startBackgroundMining({
|
|
80
181
|
dataDir,
|
|
81
182
|
databasePath: dbPath,
|
|
82
183
|
provider,
|
|
83
184
|
prompter,
|
|
185
|
+
builtInSetupEnsured: true,
|
|
84
186
|
paths: runtimePaths,
|
|
85
187
|
});
|
|
86
188
|
if (parsed.outputMode === "preview-json") {
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { dirname } from "node:path";
|
|
2
2
|
import { formatManagedSyncErrorMessage } from "../../bitcoind/errors.js";
|
|
3
|
-
import { formatBytes, formatDuration } from "../../bitcoind/progress/formatting.js";
|
|
4
3
|
import { FileLockBusyError, acquireFileLock } from "../../wallet/fs/lock.js";
|
|
5
4
|
import { resolveWalletRootIdFromLocalArtifacts } from "../../wallet/root-resolution.js";
|
|
6
5
|
import { withInteractiveWalletSecretProvider } from "../../wallet/state/provider.js";
|
|
@@ -8,8 +7,8 @@ import { usesTtyProgress, writeLine } from "../io.js";
|
|
|
8
7
|
import { classifyCliError, formatCliTextError } from "../output.js";
|
|
9
8
|
import { createTerminalPrompter } from "../prompt.js";
|
|
10
9
|
import { createStopSignalWatcher, waitForCompletionOrStop } from "../signals.js";
|
|
10
|
+
import { createSyncProgressReporter } from "../sync-progress.js";
|
|
11
11
|
import { formatBalanceReport } from "../wallet-format.js";
|
|
12
|
-
const SYNC_PROGRESS_LOG_INTERVAL_MS = 5_000;
|
|
13
12
|
async function writePostSyncBalanceReport(options) {
|
|
14
13
|
const provider = withInteractiveWalletSecretProvider(options.context.walletSecretProvider, options.context.createPrompter?.() ?? createTerminalPrompter(options.context.stdin, options.context.stdout));
|
|
15
14
|
const readContext = await options.context.openWalletReadContext({
|
|
@@ -26,95 +25,6 @@ async function writePostSyncBalanceReport(options) {
|
|
|
26
25
|
await readContext.close().catch(() => undefined);
|
|
27
26
|
}
|
|
28
27
|
}
|
|
29
|
-
function createSyncProgressReporter(options) {
|
|
30
|
-
let lastPhase = null;
|
|
31
|
-
let lastMessage = "";
|
|
32
|
-
let lastDownloadPrintedAt = 0;
|
|
33
|
-
let lastDownloadBytes = null;
|
|
34
|
-
let lastImportPrintedAt = 0;
|
|
35
|
-
let lastImportBlocks = null;
|
|
36
|
-
const infoEnabled = options.progressOutput !== "none";
|
|
37
|
-
function shouldPrintEntryMessage(message, phase) {
|
|
38
|
-
if (message === "Waiting to start managed sync." || message === "Sync complete.") {
|
|
39
|
-
return false;
|
|
40
|
-
}
|
|
41
|
-
if (message.startsWith("Warning:")) {
|
|
42
|
-
return true;
|
|
43
|
-
}
|
|
44
|
-
if (!infoEnabled) {
|
|
45
|
-
return false;
|
|
46
|
-
}
|
|
47
|
-
if (phase === "getblock_archive_download" || phase === "getblock_archive_import") {
|
|
48
|
-
return true;
|
|
49
|
-
}
|
|
50
|
-
return phase === "snapshot_download"
|
|
51
|
-
|| phase === "wait_headers_for_snapshot"
|
|
52
|
-
|| phase === "load_snapshot"
|
|
53
|
-
|| phase === "bitcoin_sync"
|
|
54
|
-
|| phase === "cogcoin_sync"
|
|
55
|
-
|| message.includes("Getblock manifest")
|
|
56
|
-
|| message.startsWith("Fetching Getblock manifest.")
|
|
57
|
-
|| message.startsWith("Refreshing Getblock manifest.")
|
|
58
|
-
|| message.startsWith("Using Getblock range ");
|
|
59
|
-
}
|
|
60
|
-
function formatDownloadLine(label, event) {
|
|
61
|
-
const current = event.progress.downloadedBytes ?? 0;
|
|
62
|
-
const total = event.progress.totalBytes ?? 0;
|
|
63
|
-
const percent = event.progress.percent ?? (total > 0 ? (current / total) * 100 : 0);
|
|
64
|
-
const speed = event.progress.bytesPerSecond === null ? "--" : `${formatBytes(event.progress.bytesPerSecond)}/s`;
|
|
65
|
-
return `${label}: ${percent.toFixed(2)}% (${formatBytes(current)} / ${formatBytes(total)}, ${speed}, ETA ${formatDuration(event.progress.etaSeconds)})`;
|
|
66
|
-
}
|
|
67
|
-
return (event) => {
|
|
68
|
-
const message = event.progress.message.trim();
|
|
69
|
-
const phaseChanged = event.phase !== lastPhase;
|
|
70
|
-
const messageChanged = message !== lastMessage;
|
|
71
|
-
if ((phaseChanged || messageChanged) && shouldPrintEntryMessage(message, event.phase)) {
|
|
72
|
-
options.write(message);
|
|
73
|
-
}
|
|
74
|
-
if (infoEnabled && event.phase === "getblock_archive_download") {
|
|
75
|
-
const now = Date.now();
|
|
76
|
-
const currentBytes = event.progress.downloadedBytes ?? 0;
|
|
77
|
-
const isComplete = (event.progress.percent ?? 0) >= 100;
|
|
78
|
-
const shouldPrintMilestone = phaseChanged
|
|
79
|
-
|| lastDownloadBytes !== currentBytes && (isComplete
|
|
80
|
-
|| now - lastDownloadPrintedAt >= SYNC_PROGRESS_LOG_INTERVAL_MS);
|
|
81
|
-
if (shouldPrintMilestone) {
|
|
82
|
-
options.write(formatDownloadLine("Getblock download", event));
|
|
83
|
-
lastDownloadPrintedAt = now;
|
|
84
|
-
lastDownloadBytes = currentBytes;
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
else if (infoEnabled && event.phase === "snapshot_download") {
|
|
88
|
-
const now = Date.now();
|
|
89
|
-
const currentBytes = event.progress.downloadedBytes ?? 0;
|
|
90
|
-
const isComplete = (event.progress.percent ?? 0) >= 100;
|
|
91
|
-
const shouldPrintMilestone = phaseChanged
|
|
92
|
-
|| lastDownloadBytes !== currentBytes && (isComplete
|
|
93
|
-
|| now - lastDownloadPrintedAt >= SYNC_PROGRESS_LOG_INTERVAL_MS);
|
|
94
|
-
if (shouldPrintMilestone) {
|
|
95
|
-
options.write(formatDownloadLine("Snapshot download", event));
|
|
96
|
-
lastDownloadPrintedAt = now;
|
|
97
|
-
lastDownloadBytes = currentBytes;
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
else if (infoEnabled && event.phase === "getblock_archive_import") {
|
|
101
|
-
const now = Date.now();
|
|
102
|
-
const currentBlocks = event.progress.blocks ?? 0;
|
|
103
|
-
const targetBlocks = event.progress.targetHeight ?? currentBlocks;
|
|
104
|
-
const isComplete = currentBlocks >= targetBlocks;
|
|
105
|
-
const shouldPrintMilestone = phaseChanged
|
|
106
|
-
|| lastImportBlocks !== currentBlocks && (isComplete
|
|
107
|
-
|| now - lastImportPrintedAt >= SYNC_PROGRESS_LOG_INTERVAL_MS);
|
|
108
|
-
if (shouldPrintMilestone) {
|
|
109
|
-
options.write(`Getblock import: Bitcoin ${currentBlocks.toLocaleString()} / ${targetBlocks.toLocaleString()}`);
|
|
110
|
-
lastImportPrintedAt = now;
|
|
111
|
-
lastImportBlocks = currentBlocks;
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
lastPhase = event.phase;
|
|
115
|
-
lastMessage = message;
|
|
116
|
-
};
|
|
117
|
-
}
|
|
118
28
|
export async function runSyncCommand(parsed, context) {
|
|
119
29
|
const dbPath = parsed.dbPath ?? context.resolveDefaultClientDatabasePath();
|
|
120
30
|
const dataDir = parsed.dataDir ?? context.resolveDefaultBitcoindDataDir();
|
package/dist/cli/context.js
CHANGED
|
@@ -10,7 +10,7 @@ import { initializeWallet, deleteImportedWalletSeed, previewResetWallet, repairW
|
|
|
10
10
|
import { resolveWalletRuntimePathsForTesting } from "../wallet/runtime.js";
|
|
11
11
|
import { openWalletReadContext } from "../wallet/read/index.js";
|
|
12
12
|
import { loadRawWalletStateEnvelope, loadWalletState } from "../wallet/state/storage.js";
|
|
13
|
-
import { followMiningLog, inspectMiningControlPlane, readMiningLog, runForegroundMining, setupBuiltInMining, startBackgroundMining, stopBackgroundMining, } from "../wallet/mining/index.js";
|
|
13
|
+
import { ensureBuiltInMiningSetupIfNeeded, followMiningLog, inspectMiningControlPlane, readMiningLog, runForegroundMining, setupBuiltInMining, startBackgroundMining, stopBackgroundMining, } from "../wallet/mining/index.js";
|
|
14
14
|
import { createLazyDefaultWalletSecretProvider } from "../wallet/state/provider.js";
|
|
15
15
|
import { 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";
|
|
16
16
|
import { createTerminalPrompter } from "./prompt.js";
|
|
@@ -80,6 +80,7 @@ export function createDefaultContext(overrides = {}) {
|
|
|
80
80
|
giveReputation: overrides.giveReputation ?? giveReputation,
|
|
81
81
|
revokeReputation: overrides.revokeReputation ?? revokeReputation,
|
|
82
82
|
inspectMiningControlPlane: overrides.inspectMiningControlPlane ?? inspectMiningControlPlane,
|
|
83
|
+
ensureBuiltInMiningSetupIfNeeded: overrides.ensureBuiltInMiningSetupIfNeeded ?? ensureBuiltInMiningSetupIfNeeded,
|
|
83
84
|
runForegroundMining: overrides.runForegroundMining ?? runForegroundMining,
|
|
84
85
|
startBackgroundMining: overrides.startBackgroundMining ?? startBackgroundMining,
|
|
85
86
|
stopBackgroundMining: overrides.stopBackgroundMining ?? stopBackgroundMining,
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { ManagedBitcoindProgressEvent } from "../bitcoind/types.js";
|
|
2
|
+
import type { ParsedCliArgs } from "./types.js";
|
|
3
|
+
export declare function createSyncProgressReporter(options: {
|
|
4
|
+
progressOutput: ParsedCliArgs["progressOutput"];
|
|
5
|
+
write: (line: string) => void;
|
|
6
|
+
}): (event: ManagedBitcoindProgressEvent) => void;
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { formatBytes, formatDuration } from "../bitcoind/progress/formatting.js";
|
|
2
|
+
const SYNC_PROGRESS_LOG_INTERVAL_MS = 5_000;
|
|
3
|
+
export function createSyncProgressReporter(options) {
|
|
4
|
+
let lastPhase = null;
|
|
5
|
+
let lastMessage = "";
|
|
6
|
+
let lastDownloadPrintedAt = 0;
|
|
7
|
+
let lastDownloadBytes = null;
|
|
8
|
+
let lastImportPrintedAt = 0;
|
|
9
|
+
let lastImportBlocks = null;
|
|
10
|
+
const infoEnabled = options.progressOutput !== "none";
|
|
11
|
+
function shouldPrintEntryMessage(message, phase) {
|
|
12
|
+
if (message === "Waiting to start managed sync." || message === "Sync complete.") {
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
if (message.startsWith("Warning:")) {
|
|
16
|
+
return true;
|
|
17
|
+
}
|
|
18
|
+
if (!infoEnabled) {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
if (phase === "getblock_archive_download" || phase === "getblock_archive_import") {
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
24
|
+
return phase === "snapshot_download"
|
|
25
|
+
|| phase === "wait_headers_for_snapshot"
|
|
26
|
+
|| phase === "load_snapshot"
|
|
27
|
+
|| phase === "bitcoin_sync"
|
|
28
|
+
|| phase === "cogcoin_sync"
|
|
29
|
+
|| message.includes("Getblock manifest")
|
|
30
|
+
|| message.startsWith("Fetching Getblock manifest.")
|
|
31
|
+
|| message.startsWith("Refreshing Getblock manifest.")
|
|
32
|
+
|| message.startsWith("Using Getblock range ");
|
|
33
|
+
}
|
|
34
|
+
function formatDownloadLine(label, event) {
|
|
35
|
+
const current = event.progress.downloadedBytes ?? 0;
|
|
36
|
+
const total = event.progress.totalBytes ?? 0;
|
|
37
|
+
const percent = event.progress.percent ?? (total > 0 ? (current / total) * 100 : 0);
|
|
38
|
+
const speed = event.progress.bytesPerSecond === null ? "--" : `${formatBytes(event.progress.bytesPerSecond)}/s`;
|
|
39
|
+
return `${label}: ${percent.toFixed(2)}% (${formatBytes(current)} / ${formatBytes(total)}, ${speed}, ETA ${formatDuration(event.progress.etaSeconds)})`;
|
|
40
|
+
}
|
|
41
|
+
return (event) => {
|
|
42
|
+
const message = event.progress.message.trim();
|
|
43
|
+
const phaseChanged = event.phase !== lastPhase;
|
|
44
|
+
const messageChanged = message !== lastMessage;
|
|
45
|
+
if ((phaseChanged || messageChanged) && shouldPrintEntryMessage(message, event.phase)) {
|
|
46
|
+
options.write(message);
|
|
47
|
+
}
|
|
48
|
+
if (infoEnabled && event.phase === "getblock_archive_download") {
|
|
49
|
+
const now = Date.now();
|
|
50
|
+
const currentBytes = event.progress.downloadedBytes ?? 0;
|
|
51
|
+
const isComplete = (event.progress.percent ?? 0) >= 100;
|
|
52
|
+
const shouldPrintMilestone = phaseChanged
|
|
53
|
+
|| lastDownloadBytes !== currentBytes && (isComplete
|
|
54
|
+
|| now - lastDownloadPrintedAt >= SYNC_PROGRESS_LOG_INTERVAL_MS);
|
|
55
|
+
if (shouldPrintMilestone) {
|
|
56
|
+
options.write(formatDownloadLine("Getblock download", event));
|
|
57
|
+
lastDownloadPrintedAt = now;
|
|
58
|
+
lastDownloadBytes = currentBytes;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
else if (infoEnabled && event.phase === "snapshot_download") {
|
|
62
|
+
const now = Date.now();
|
|
63
|
+
const currentBytes = event.progress.downloadedBytes ?? 0;
|
|
64
|
+
const isComplete = (event.progress.percent ?? 0) >= 100;
|
|
65
|
+
const shouldPrintMilestone = phaseChanged
|
|
66
|
+
|| lastDownloadBytes !== currentBytes && (isComplete
|
|
67
|
+
|| now - lastDownloadPrintedAt >= SYNC_PROGRESS_LOG_INTERVAL_MS);
|
|
68
|
+
if (shouldPrintMilestone) {
|
|
69
|
+
options.write(formatDownloadLine("Snapshot download", event));
|
|
70
|
+
lastDownloadPrintedAt = now;
|
|
71
|
+
lastDownloadBytes = currentBytes;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
else if (infoEnabled && event.phase === "getblock_archive_import") {
|
|
75
|
+
const now = Date.now();
|
|
76
|
+
const currentBlocks = event.progress.blocks ?? 0;
|
|
77
|
+
const targetBlocks = event.progress.targetHeight ?? currentBlocks;
|
|
78
|
+
const isComplete = currentBlocks >= targetBlocks;
|
|
79
|
+
const shouldPrintMilestone = phaseChanged
|
|
80
|
+
|| lastImportBlocks !== currentBlocks && (isComplete
|
|
81
|
+
|| now - lastImportPrintedAt >= SYNC_PROGRESS_LOG_INTERVAL_MS);
|
|
82
|
+
if (shouldPrintMilestone) {
|
|
83
|
+
options.write(`Getblock import: Bitcoin ${currentBlocks.toLocaleString()} / ${targetBlocks.toLocaleString()}`);
|
|
84
|
+
lastImportPrintedAt = now;
|
|
85
|
+
lastImportBlocks = currentBlocks;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
lastPhase = event.phase;
|
|
89
|
+
lastMessage = message;
|
|
90
|
+
};
|
|
91
|
+
}
|
package/dist/cli/types.d.ts
CHANGED
|
@@ -10,7 +10,7 @@ import type { WalletPrompter, initializeWallet, deleteImportedWalletSeed, previe
|
|
|
10
10
|
import type { openWalletReadContext } from "../wallet/read/index.js";
|
|
11
11
|
import { loadRawWalletStateEnvelope, loadWalletState } from "../wallet/state/storage.js";
|
|
12
12
|
import type { WalletSecretProvider } from "../wallet/state/provider.js";
|
|
13
|
-
import type { followMiningLog, inspectMiningControlPlane, readMiningLog, runForegroundMining, setupBuiltInMining, startBackgroundMining, stopBackgroundMining } from "../wallet/mining/index.js";
|
|
13
|
+
import type { ensureBuiltInMiningSetupIfNeeded, followMiningLog, inspectMiningControlPlane, readMiningLog, runForegroundMining, setupBuiltInMining, startBackgroundMining, stopBackgroundMining } from "../wallet/mining/index.js";
|
|
14
14
|
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
15
|
export type ProgressOutput = "auto" | "tty" | "none";
|
|
16
16
|
export type OutputMode = "text" | "json" | "preview-json";
|
|
@@ -145,6 +145,7 @@ export interface CliRunnerContext {
|
|
|
145
145
|
giveReputation?: typeof giveReputation;
|
|
146
146
|
revokeReputation?: typeof revokeReputation;
|
|
147
147
|
inspectMiningControlPlane?: typeof inspectMiningControlPlane;
|
|
148
|
+
ensureBuiltInMiningSetupIfNeeded?: typeof ensureBuiltInMiningSetupIfNeeded;
|
|
148
149
|
runForegroundMining?: typeof runForegroundMining;
|
|
149
150
|
startBackgroundMining?: typeof startBackgroundMining;
|
|
150
151
|
stopBackgroundMining?: typeof stopBackgroundMining;
|
|
@@ -558,6 +558,9 @@ function getBalanceNextSteps(context) {
|
|
|
558
558
|
return [`Transfer BTC to ${context.model.walletAddress ?? "this wallet address"} so your anchored root domain can keep mining.`];
|
|
559
559
|
}
|
|
560
560
|
if (context.fundingSpendableSats !== null && context.fundingSpendableSats > BALANCE_MINING_THRESHOLD_SATS) {
|
|
561
|
+
if (context.mining?.provider.status === "missing") {
|
|
562
|
+
return ["Run `cogcoin mine setup` to configure your mining provider."];
|
|
563
|
+
}
|
|
561
564
|
return ["Run `cogcoin mine` to start mining with your anchored root domain."];
|
|
562
565
|
}
|
|
563
566
|
return [];
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export { isMiningGenerationAbortRequested, markMiningGenerationActive, markMiningGenerationInactive, readMiningGenerationActivity, readMiningPreemptionRequest, requestMiningGenerationPreemption, } from "./coordination.js";
|
|
2
2
|
export { followMiningLog, inspectMiningControlPlane, readMiningLog, refreshMiningRuntimeStatus, setupBuiltInMining, } from "./control.js";
|
|
3
|
-
export { runBackgroundMiningWorker, runForegroundMining, startBackgroundMining, stopBackgroundMining, type MiningStartResult, } from "./runner.js";
|
|
3
|
+
export { ensureBuiltInMiningSetupIfNeeded, runBackgroundMiningWorker, runForegroundMining, startBackgroundMining, stopBackgroundMining, type MiningStartResult, } from "./runner.js";
|
|
4
4
|
export { appendMiningEvent, loadMiningRuntimeStatus, readMiningEvents, resolveRotatedMiningEventsPath, saveMiningRuntimeStatus, } from "./runtime-artifacts.js";
|
|
5
5
|
export type { MiningSentenceCandidateV1, MiningSentenceGenerationRequestV1, MiningSentenceGenerationResponseV1, } from "./sentence-protocol.js";
|
|
6
6
|
export { loadClientConfig, saveBuiltInMiningProviderConfig, saveClientConfig, } from "./config.js";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { isMiningGenerationAbortRequested, markMiningGenerationActive, markMiningGenerationInactive, readMiningGenerationActivity, readMiningPreemptionRequest, requestMiningGenerationPreemption, } from "./coordination.js";
|
|
2
2
|
export { followMiningLog, inspectMiningControlPlane, readMiningLog, refreshMiningRuntimeStatus, setupBuiltInMining, } from "./control.js";
|
|
3
|
-
export { runBackgroundMiningWorker, runForegroundMining, startBackgroundMining, stopBackgroundMining, } from "./runner.js";
|
|
3
|
+
export { ensureBuiltInMiningSetupIfNeeded, runBackgroundMiningWorker, runForegroundMining, startBackgroundMining, stopBackgroundMining, } from "./runner.js";
|
|
4
4
|
export { appendMiningEvent, loadMiningRuntimeStatus, readMiningEvents, resolveRotatedMiningEventsPath, saveMiningRuntimeStatus, } from "./runtime-artifacts.js";
|
|
5
5
|
export { loadClientConfig, saveBuiltInMiningProviderConfig, saveClientConfig, } from "./config.js";
|
|
@@ -137,6 +137,7 @@ export interface RunForegroundMiningOptions extends RunnerDependencies {
|
|
|
137
137
|
databasePath: string;
|
|
138
138
|
provider?: WalletSecretProvider;
|
|
139
139
|
prompter: WalletPrompter;
|
|
140
|
+
builtInSetupEnsured?: boolean;
|
|
140
141
|
stdout?: {
|
|
141
142
|
write(chunk: string): void;
|
|
142
143
|
};
|
|
@@ -154,6 +155,7 @@ export interface StartBackgroundMiningOptions extends RunnerDependencies {
|
|
|
154
155
|
databasePath: string;
|
|
155
156
|
provider?: WalletSecretProvider;
|
|
156
157
|
prompter: WalletPrompter;
|
|
158
|
+
builtInSetupEnsured?: boolean;
|
|
157
159
|
paths?: WalletRuntimePaths;
|
|
158
160
|
}
|
|
159
161
|
export interface StopBackgroundMiningOptions extends RunnerDependencies {
|
|
@@ -276,6 +278,11 @@ export declare function publishCandidateForTesting(options: {
|
|
|
276
278
|
publishAttempt?: typeof publishCandidateOnce;
|
|
277
279
|
appendEventFn?: typeof appendEvent;
|
|
278
280
|
}): Promise<MiningPublishOutcome>;
|
|
281
|
+
export declare function ensureBuiltInMiningSetupIfNeeded(options: {
|
|
282
|
+
provider: WalletSecretProvider;
|
|
283
|
+
prompter: WalletPrompter;
|
|
284
|
+
paths: WalletRuntimePaths;
|
|
285
|
+
}): Promise<boolean>;
|
|
279
286
|
export declare function runForegroundMining(options: RunForegroundMiningOptions): Promise<void>;
|
|
280
287
|
export declare function startBackgroundMining(options: StartBackgroundMiningOptions): Promise<MiningStartResult>;
|
|
281
288
|
export declare function stopBackgroundMining(options: StopBackgroundMiningOptions): Promise<MiningRuntimeStatusV1 | null>;
|
|
@@ -1811,7 +1811,7 @@ async function publishCandidate(options) {
|
|
|
1811
1811
|
export async function publishCandidateForTesting(options) {
|
|
1812
1812
|
return await publishCandidate(options);
|
|
1813
1813
|
}
|
|
1814
|
-
async function
|
|
1814
|
+
export async function ensureBuiltInMiningSetupIfNeeded(options) {
|
|
1815
1815
|
const config = await loadClientConfig({
|
|
1816
1816
|
path: options.paths.clientConfigPath,
|
|
1817
1817
|
provider: options.provider,
|
|
@@ -2732,11 +2732,13 @@ export async function runForegroundMining(options) {
|
|
|
2732
2732
|
if (existing?.runMode === "background") {
|
|
2733
2733
|
throw new Error("Background mining is already active. Run `cogcoin mine stop` first.");
|
|
2734
2734
|
}
|
|
2735
|
-
const setupReady =
|
|
2736
|
-
|
|
2737
|
-
|
|
2738
|
-
|
|
2739
|
-
|
|
2735
|
+
const setupReady = options.builtInSetupEnsured === true
|
|
2736
|
+
? true
|
|
2737
|
+
: await ensureBuiltInMiningSetupIfNeeded({
|
|
2738
|
+
provider,
|
|
2739
|
+
prompter: options.prompter,
|
|
2740
|
+
paths,
|
|
2741
|
+
});
|
|
2740
2742
|
if (!setupReady) {
|
|
2741
2743
|
throw new Error("Built-in mining provider is not configured. Run `cogcoin mine setup`.");
|
|
2742
2744
|
}
|
|
@@ -2801,11 +2803,13 @@ export async function startBackgroundMining(options) {
|
|
|
2801
2803
|
if (existing?.runMode === "foreground") {
|
|
2802
2804
|
throw new Error("Foreground mining is already active. Interrupt that process directly.");
|
|
2803
2805
|
}
|
|
2804
|
-
const setupReady =
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
|
|
2806
|
+
const setupReady = options.builtInSetupEnsured === true
|
|
2807
|
+
? true
|
|
2808
|
+
: await ensureBuiltInMiningSetupIfNeeded({
|
|
2809
|
+
provider,
|
|
2810
|
+
prompter: options.prompter,
|
|
2811
|
+
paths,
|
|
2812
|
+
});
|
|
2809
2813
|
if (!setupReady) {
|
|
2810
2814
|
throw new Error("Built-in mining provider is not configured. Run `cogcoin mine setup`.");
|
|
2811
2815
|
}
|
package/package.json
CHANGED