@cogcoin/client 0.5.14 → 1.0.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.
Files changed (172) hide show
  1. package/README.md +80 -25
  2. package/dist/app-paths.d.ts +5 -6
  3. package/dist/app-paths.js +8 -16
  4. package/dist/art/balance.txt +10 -0
  5. package/dist/art/welcome.txt +16 -0
  6. package/dist/bitcoind/bootstrap/controller.d.ts +1 -0
  7. package/dist/bitcoind/bootstrap/controller.js +53 -1
  8. package/dist/bitcoind/client/follow-block-times.d.ts +1 -0
  9. package/dist/bitcoind/client/follow-block-times.js +1 -1
  10. package/dist/bitcoind/client/internal-types.d.ts +7 -3
  11. package/dist/bitcoind/client/managed-client.d.ts +4 -2
  12. package/dist/bitcoind/client/managed-client.js +14 -0
  13. package/dist/bitcoind/client/sync-engine.js +72 -11
  14. package/dist/bitcoind/hash-order.d.ts +4 -0
  15. package/dist/bitcoind/hash-order.js +13 -0
  16. package/dist/bitcoind/indexer-daemon-main.js +11 -3
  17. package/dist/bitcoind/normalize.js +3 -2
  18. package/dist/bitcoind/processing-start-height.d.ts +5 -0
  19. package/dist/bitcoind/processing-start-height.js +7 -0
  20. package/dist/bitcoind/progress/constants.d.ts +4 -0
  21. package/dist/bitcoind/progress/constants.js +4 -0
  22. package/dist/bitcoind/progress/controller.d.ts +2 -1
  23. package/dist/bitcoind/progress/controller.js +3 -3
  24. package/dist/bitcoind/progress/follow-scene.d.ts +6 -2
  25. package/dist/bitcoind/progress/follow-scene.js +29 -6
  26. package/dist/bitcoind/progress/formatting.d.ts +1 -0
  27. package/dist/bitcoind/progress/formatting.js +6 -0
  28. package/dist/bitcoind/progress/train-scene.js +37 -18
  29. package/dist/bitcoind/progress/tty-renderer.d.ts +6 -1
  30. package/dist/bitcoind/progress/tty-renderer.js +8 -4
  31. package/dist/bitcoind/rpc.d.ts +2 -1
  32. package/dist/bitcoind/rpc.js +3 -0
  33. package/dist/bitcoind/types.d.ts +16 -0
  34. package/dist/bytes.d.ts +1 -0
  35. package/dist/bytes.js +3 -0
  36. package/dist/cli/art.d.ts +2 -0
  37. package/dist/cli/art.js +37 -0
  38. package/dist/cli/commands/client-admin.d.ts +2 -0
  39. package/dist/cli/commands/client-admin.js +91 -0
  40. package/dist/cli/commands/follow.js +0 -2
  41. package/dist/cli/commands/mining-admin.js +6 -47
  42. package/dist/cli/commands/mining-read.js +11 -50
  43. package/dist/cli/commands/mining-runtime.js +38 -3
  44. package/dist/cli/commands/service-runtime.js +0 -2
  45. package/dist/cli/commands/status.js +8 -2
  46. package/dist/cli/commands/sync.js +51 -4
  47. package/dist/cli/commands/wallet-admin.js +142 -136
  48. package/dist/cli/commands/wallet-mutation.js +91 -79
  49. package/dist/cli/commands/wallet-read.js +15 -18
  50. package/dist/cli/context.js +4 -14
  51. package/dist/cli/mining-format.d.ts +0 -1
  52. package/dist/cli/mining-format.js +5 -37
  53. package/dist/cli/mining-json.d.ts +0 -18
  54. package/dist/cli/mining-json.js +0 -35
  55. package/dist/cli/mutation-command-groups.d.ts +1 -2
  56. package/dist/cli/mutation-command-groups.js +0 -5
  57. package/dist/cli/mutation-json.d.ts +24 -145
  58. package/dist/cli/mutation-json.js +30 -136
  59. package/dist/cli/mutation-resolved-json.d.ts +0 -7
  60. package/dist/cli/mutation-resolved-json.js +4 -10
  61. package/dist/cli/mutation-success.d.ts +2 -0
  62. package/dist/cli/mutation-success.js +11 -1
  63. package/dist/cli/mutation-text-format.js +1 -3
  64. package/dist/cli/output.d.ts +1 -1
  65. package/dist/cli/output.js +254 -231
  66. package/dist/cli/parse.d.ts +1 -1
  67. package/dist/cli/parse.js +93 -122
  68. package/dist/cli/preview-json.d.ts +17 -120
  69. package/dist/cli/preview-json.js +14 -97
  70. package/dist/cli/prompt.js +8 -13
  71. package/dist/cli/read-json.d.ts +15 -37
  72. package/dist/cli/read-json.js +44 -140
  73. package/dist/cli/runner.js +10 -13
  74. package/dist/cli/types.d.ts +8 -17
  75. package/dist/cli/types.js +0 -2
  76. package/dist/cli/wallet-format.d.ts +1 -0
  77. package/dist/cli/wallet-format.js +205 -144
  78. package/dist/cli/workflow-hints.d.ts +3 -3
  79. package/dist/cli/workflow-hints.js +11 -8
  80. package/dist/client/default-client.d.ts +3 -1
  81. package/dist/client/default-client.js +45 -2
  82. package/dist/client/factory.js +1 -1
  83. package/dist/client/initialization.js +23 -0
  84. package/dist/client/persistence.js +5 -5
  85. package/dist/client/store-adapter.js +1 -0
  86. package/dist/sqlite/checkpoints.d.ts +1 -0
  87. package/dist/sqlite/checkpoints.js +7 -0
  88. package/dist/sqlite/store.js +14 -1
  89. package/dist/types.d.ts +1 -0
  90. package/dist/wallet/coin-control.d.ts +41 -11
  91. package/dist/wallet/coin-control.js +100 -357
  92. package/dist/wallet/descriptor-normalization.d.ts +1 -3
  93. package/dist/wallet/descriptor-normalization.js +0 -16
  94. package/dist/wallet/lifecycle.d.ts +7 -99
  95. package/dist/wallet/lifecycle.js +513 -968
  96. package/dist/wallet/managed-core-wallet.d.ts +13 -0
  97. package/dist/wallet/managed-core-wallet.js +20 -0
  98. package/dist/wallet/mining/constants.d.ts +5 -12
  99. package/dist/wallet/mining/constants.js +5 -12
  100. package/dist/wallet/mining/control.d.ts +1 -13
  101. package/dist/wallet/mining/control.js +45 -349
  102. package/dist/wallet/mining/index.d.ts +3 -4
  103. package/dist/wallet/mining/index.js +1 -2
  104. package/dist/wallet/mining/runner.d.ts +179 -6
  105. package/dist/wallet/mining/runner.js +891 -501
  106. package/dist/wallet/mining/runtime-artifacts.js +23 -3
  107. package/dist/wallet/mining/sentence-protocol.d.ts +44 -0
  108. package/dist/wallet/mining/sentence-protocol.js +123 -0
  109. package/dist/wallet/mining/sentences.d.ts +4 -8
  110. package/dist/wallet/mining/sentences.js +3 -52
  111. package/dist/wallet/mining/state.d.ts +11 -6
  112. package/dist/wallet/mining/state.js +7 -6
  113. package/dist/wallet/mining/types.d.ts +2 -30
  114. package/dist/wallet/mining/visualizer.d.ts +31 -3
  115. package/dist/wallet/mining/visualizer.js +135 -13
  116. package/dist/wallet/read/context.d.ts +0 -2
  117. package/dist/wallet/read/context.js +119 -140
  118. package/dist/wallet/read/filter.js +2 -11
  119. package/dist/wallet/read/index.d.ts +1 -1
  120. package/dist/wallet/read/project.js +24 -77
  121. package/dist/wallet/read/types.d.ts +10 -25
  122. package/dist/wallet/reset.d.ts +0 -1
  123. package/dist/wallet/reset.js +60 -138
  124. package/dist/wallet/root-resolution.d.ts +1 -5
  125. package/dist/wallet/root-resolution.js +0 -18
  126. package/dist/wallet/runtime.d.ts +0 -6
  127. package/dist/wallet/runtime.js +0 -8
  128. package/dist/wallet/state/client-password-agent.js +208 -0
  129. package/dist/wallet/state/client-password.d.ts +65 -0
  130. package/dist/wallet/state/client-password.js +952 -0
  131. package/dist/wallet/state/crypto.d.ts +1 -20
  132. package/dist/wallet/state/crypto.js +0 -63
  133. package/dist/wallet/state/provider.d.ts +23 -11
  134. package/dist/wallet/state/provider.js +248 -290
  135. package/dist/wallet/state/storage.d.ts +2 -2
  136. package/dist/wallet/state/storage.js +48 -16
  137. package/dist/wallet/tx/anchor.d.ts +3 -28
  138. package/dist/wallet/tx/anchor.js +349 -1240
  139. package/dist/wallet/tx/bitcoin-transfer.d.ts +35 -0
  140. package/dist/wallet/tx/bitcoin-transfer.js +200 -0
  141. package/dist/wallet/tx/cog.d.ts +5 -1
  142. package/dist/wallet/tx/cog.js +149 -185
  143. package/dist/wallet/tx/common.d.ts +74 -10
  144. package/dist/wallet/tx/common.js +315 -138
  145. package/dist/wallet/tx/domain-admin.d.ts +3 -1
  146. package/dist/wallet/tx/domain-admin.js +61 -99
  147. package/dist/wallet/tx/domain-market.d.ts +5 -1
  148. package/dist/wallet/tx/domain-market.js +221 -228
  149. package/dist/wallet/tx/field.d.ts +4 -10
  150. package/dist/wallet/tx/field.js +84 -914
  151. package/dist/wallet/tx/identity-selector.d.ts +9 -3
  152. package/dist/wallet/tx/identity-selector.js +17 -35
  153. package/dist/wallet/tx/index.d.ts +3 -1
  154. package/dist/wallet/tx/index.js +2 -1
  155. package/dist/wallet/tx/register.d.ts +3 -1
  156. package/dist/wallet/tx/register.js +62 -220
  157. package/dist/wallet/tx/reputation.d.ts +3 -1
  158. package/dist/wallet/tx/reputation.js +58 -95
  159. package/dist/wallet/types.d.ts +8 -122
  160. package/package.json +5 -5
  161. package/dist/wallet/archive.d.ts +0 -4
  162. package/dist/wallet/archive.js +0 -41
  163. package/dist/wallet/mining/hook-protocol.d.ts +0 -47
  164. package/dist/wallet/mining/hook-protocol.js +0 -161
  165. package/dist/wallet/mining/hook-runner.js +0 -52
  166. package/dist/wallet/mining/hooks.d.ts +0 -38
  167. package/dist/wallet/mining/hooks.js +0 -520
  168. package/dist/wallet/state/explicit-lock.d.ts +0 -4
  169. package/dist/wallet/state/explicit-lock.js +0 -19
  170. package/dist/wallet/state/session.d.ts +0 -12
  171. package/dist/wallet/state/session.js +0 -23
  172. /package/dist/wallet/{mining/hook-runner.d.ts → state/client-password-agent.d.ts} +0 -0
@@ -173,6 +173,7 @@ export interface ManagedBitcoindClient extends Client {
173
173
  syncToTip(): Promise<SyncResult>;
174
174
  startFollowingTip(): Promise<void>;
175
175
  getNodeStatus(): Promise<ManagedBitcoindStatus>;
176
+ detachToBackgroundFollow(): Promise<void>;
176
177
  close(): Promise<void>;
177
178
  }
178
179
  export interface InternalManagedBitcoindOptions extends ManagedBitcoindOptions {
@@ -275,6 +276,7 @@ export interface RpcPrevout {
275
276
  }
276
277
  export interface RpcVin {
277
278
  txid?: string;
279
+ vout?: number;
278
280
  coinbase?: string;
279
281
  prevout?: RpcPrevout;
280
282
  }
@@ -298,6 +300,11 @@ export interface RpcMempoolEntry {
298
300
  ancestorsize?: number;
299
301
  descendantsize?: number;
300
302
  }
303
+ export interface RpcEstimateSmartFeeResult {
304
+ feerate?: number;
305
+ errors?: string[];
306
+ blocks?: number;
307
+ }
301
308
  export interface RpcRawMempoolVerbose {
302
309
  txids: string[];
303
310
  mempool_sequence: string | number;
@@ -310,6 +317,11 @@ export interface RpcWalletTransaction {
310
317
  blockheight?: number;
311
318
  time?: number;
312
319
  timereceived?: number;
320
+ details?: Array<{
321
+ address?: string;
322
+ vout?: number;
323
+ }>;
324
+ decoded?: RpcTransaction;
313
325
  }
314
326
  export interface RpcBlock {
315
327
  hash: string;
@@ -409,6 +421,10 @@ export interface RpcListUnspentEntry {
409
421
  }
410
422
  export interface RpcDecodedPsbt {
411
423
  tx: RpcTransaction;
424
+ inputs?: Array<{
425
+ witness_utxo?: RpcVout;
426
+ non_witness_utxo?: RpcTransaction;
427
+ }>;
412
428
  }
413
429
  export interface RpcFinalizePsbtResult {
414
430
  psbt?: string;
package/dist/bytes.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  export declare function bytesToHex(bytes: Uint8Array): string;
2
2
  export declare function hexToBytes(hex: string): Uint8Array;
3
3
  export declare function cloneBytes(bytes: Uint8Array): Uint8Array;
4
+ export declare function reverseBytes(bytes: Uint8Array): Uint8Array;
4
5
  export declare function encodeText(value: string): Uint8Array;
5
6
  export declare function decodeText(bytes: Uint8Array): string;
6
7
  export declare function encodeNullableText(value: string | null): Uint8Array;
package/dist/bytes.js CHANGED
@@ -9,6 +9,9 @@ export function hexToBytes(hex) {
9
9
  export function cloneBytes(bytes) {
10
10
  return new Uint8Array(bytes);
11
11
  }
12
+ export function reverseBytes(bytes) {
13
+ return Uint8Array.from([...bytes].reverse());
14
+ }
12
15
  export function encodeText(value) {
13
16
  return textEncoder.encode(value);
14
17
  }
@@ -0,0 +1,2 @@
1
+ export declare function loadWelcomeArtText(): string;
2
+ export declare function loadBalanceArtText(): string;
@@ -0,0 +1,37 @@
1
+ import { readFileSync } from "node:fs";
2
+ let welcomeArtCache = null;
3
+ let balanceArtCache = null;
4
+ const FIXED_ART_WIDTH = 80;
5
+ const BALANCE_ART_HEIGHT = 10;
6
+ function normalizeFixedArtText(raw, options) {
7
+ const lines = raw.replaceAll("\r\n", "\n").split("\n");
8
+ if (lines[lines.length - 1] === "") {
9
+ lines.pop();
10
+ }
11
+ if (lines.length !== options.height) {
12
+ throw new Error(`${options.name}_art_height_invalid_${lines.length}`);
13
+ }
14
+ for (const line of lines) {
15
+ if (line.length !== FIXED_ART_WIDTH) {
16
+ throw new Error(`${options.name}_art_width_invalid_${line.length}`);
17
+ }
18
+ }
19
+ return lines.join("\n");
20
+ }
21
+ export function loadWelcomeArtText() {
22
+ if (welcomeArtCache !== null) {
23
+ return welcomeArtCache;
24
+ }
25
+ welcomeArtCache = readFileSync(new URL("../art/welcome.txt", import.meta.url), "utf8");
26
+ return welcomeArtCache;
27
+ }
28
+ export function loadBalanceArtText() {
29
+ if (balanceArtCache !== null) {
30
+ return balanceArtCache;
31
+ }
32
+ balanceArtCache = normalizeFixedArtText(readFileSync(new URL("../art/balance.txt", import.meta.url), "utf8"), {
33
+ name: "balance",
34
+ height: BALANCE_ART_HEIGHT,
35
+ });
36
+ return balanceArtCache;
37
+ }
@@ -0,0 +1,2 @@
1
+ import type { ParsedCliArgs, RequiredCliRunnerContext } from "../types.js";
2
+ export declare function runClientAdminCommand(parsed: ParsedCliArgs, context: RequiredCliRunnerContext): Promise<number>;
@@ -0,0 +1,91 @@
1
+ import { writeLine } from "../io.js";
2
+ import { createTerminalPrompter } from "../prompt.js";
3
+ import { createMutationSuccessEnvelope, describeCanonicalCommand, resolveStableMutationJsonSchema, writeHandledCliError, writeJsonValue, } from "../output.js";
4
+ import { changeClientPassword, lockClientPassword, unlockClientPassword, } from "../../wallet/state/provider.js";
5
+ function createCommandPrompter(parsed, context) {
6
+ return parsed.outputMode !== "text"
7
+ ? createTerminalPrompter(context.stdin, context.stderr)
8
+ : context.createPrompter();
9
+ }
10
+ export async function runClientAdminCommand(parsed, context) {
11
+ try {
12
+ if (parsed.command === "client-lock") {
13
+ const status = await lockClientPassword(context.walletSecretProvider);
14
+ if (parsed.outputMode === "json") {
15
+ writeJsonValue(context.stdout, createMutationSuccessEnvelope(resolveStableMutationJsonSchema(parsed), describeCanonicalCommand(parsed), "locked", {
16
+ resultType: "operation",
17
+ operation: {
18
+ kind: "client-lock",
19
+ locked: true,
20
+ unlockUntilUnixMs: status.unlockUntilUnixMs,
21
+ },
22
+ state: {
23
+ locked: true,
24
+ unlockUntilUnixMs: status.unlockUntilUnixMs,
25
+ },
26
+ }));
27
+ return 0;
28
+ }
29
+ writeLine(context.stdout, "Client locked.");
30
+ return 0;
31
+ }
32
+ if (parsed.command === "client-unlock") {
33
+ const prompter = createCommandPrompter(parsed, context);
34
+ const status = await unlockClientPassword(context.walletSecretProvider, prompter);
35
+ if (parsed.outputMode === "json") {
36
+ writeJsonValue(context.stdout, createMutationSuccessEnvelope(resolveStableMutationJsonSchema(parsed), describeCanonicalCommand(parsed), "unlocked", {
37
+ resultType: "operation",
38
+ operation: {
39
+ kind: "client-unlock",
40
+ unlocked: status.unlocked,
41
+ unlockUntilUnixMs: status.unlockUntilUnixMs,
42
+ },
43
+ state: {
44
+ unlocked: status.unlocked,
45
+ unlockUntilUnixMs: status.unlockUntilUnixMs,
46
+ },
47
+ }));
48
+ return 0;
49
+ }
50
+ writeLine(context.stdout, status.unlockUntilUnixMs === null
51
+ ? "Client unlocked."
52
+ : `Client unlocked until ${new Date(status.unlockUntilUnixMs).toISOString()}.`);
53
+ return 0;
54
+ }
55
+ if (parsed.command === "client-change-password") {
56
+ const prompter = createCommandPrompter(parsed, context);
57
+ const status = await changeClientPassword(context.walletSecretProvider, prompter);
58
+ if (parsed.outputMode === "json") {
59
+ writeJsonValue(context.stdout, createMutationSuccessEnvelope(resolveStableMutationJsonSchema(parsed), describeCanonicalCommand(parsed), "changed", {
60
+ resultType: "operation",
61
+ operation: {
62
+ kind: "client-change-password",
63
+ changed: true,
64
+ unlocked: status.unlocked,
65
+ unlockUntilUnixMs: status.unlockUntilUnixMs,
66
+ },
67
+ state: {
68
+ changed: true,
69
+ unlocked: status.unlocked,
70
+ unlockUntilUnixMs: status.unlockUntilUnixMs,
71
+ },
72
+ }));
73
+ return 0;
74
+ }
75
+ writeLine(context.stdout, status.unlockUntilUnixMs === null
76
+ ? "Client password changed."
77
+ : `Client password changed. Client unlocked until ${new Date(status.unlockUntilUnixMs).toISOString()}.`);
78
+ return 0;
79
+ }
80
+ writeLine(context.stderr, `client admin command not implemented: ${parsed.command}`);
81
+ return 1;
82
+ }
83
+ catch (error) {
84
+ return writeHandledCliError({
85
+ parsed,
86
+ stdout: context.stdout,
87
+ stderr: context.stderr,
88
+ error,
89
+ });
90
+ }
91
+ }
@@ -16,8 +16,6 @@ export async function runFollowCommand(parsed, context) {
16
16
  paths: runtimePaths,
17
17
  provider: context.walletSecretProvider,
18
18
  loadRawWalletStateEnvelope: context.loadRawWalletStateEnvelope,
19
- loadUnlockSession: context.loadUnlockSession,
20
- loadWalletExplicitLock: context.loadWalletExplicitLock,
21
19
  });
22
20
  try {
23
21
  controlLock = await acquireFileLock(runtimePaths.walletControlLockPath, {
@@ -1,9 +1,10 @@
1
- import { buildHooksDisableMiningData, buildHooksEnableMiningData, buildMineSetupData, } from "../mining-json.js";
2
- import { buildHooksPreviewData } from "../preview-json.js";
1
+ import { buildMineSetupData, } from "../mining-json.js";
2
+ import { buildMineSetupPreviewData } from "../preview-json.js";
3
3
  import { writeLine } from "../io.js";
4
4
  import { createTerminalPrompter } from "../prompt.js";
5
5
  import { createPreviewSuccessEnvelope, createMutationSuccessEnvelope, describeCanonicalCommand, resolvePreviewJsonSchema, resolveStableMiningControlJsonSchema, writeHandledCliError, writeJsonValue, } from "../output.js";
6
- import { formatNextStepLines, getHooksEnableMiningNextSteps, getMineSetupNextSteps, } from "../workflow-hints.js";
6
+ import { formatNextStepLines, getMineSetupNextSteps, } from "../workflow-hints.js";
7
+ import { withInteractiveWalletSecretProvider } from "../../wallet/state/provider.js";
7
8
  function createCommandPrompter(parsed, context) {
8
9
  return parsed.outputMode !== "text"
9
10
  ? createTerminalPrompter(context.stdin, context.stderr)
@@ -11,52 +12,10 @@ function createCommandPrompter(parsed, context) {
11
12
  }
12
13
  export async function runMiningAdminCommand(parsed, context) {
13
14
  try {
14
- const provider = context.walletSecretProvider;
15
15
  const runtimePaths = context.resolveWalletRuntimePaths(parsed.seedName);
16
- if (parsed.command === "hooks-mining-enable") {
17
- const prompter = createCommandPrompter(parsed, context);
18
- const view = await context.enableMiningHooks({
19
- provider,
20
- prompter,
21
- paths: runtimePaths,
22
- });
23
- const nextSteps = getHooksEnableMiningNextSteps();
24
- if (parsed.outputMode === "preview-json") {
25
- writeJsonValue(context.stdout, createPreviewSuccessEnvelope(resolvePreviewJsonSchema(parsed), describeCanonicalCommand(parsed), "enabled", buildHooksPreviewData("hooks-enable-mining", view), {
26
- nextSteps,
27
- }));
28
- return 0;
29
- }
30
- if (parsed.outputMode === "json") {
31
- writeJsonValue(context.stdout, createMutationSuccessEnvelope(resolveStableMiningControlJsonSchema(parsed), "cogcoin hooks enable mining", "enabled", buildHooksEnableMiningData(view), {
32
- nextSteps,
33
- }));
34
- return 0;
35
- }
36
- writeLine(context.stdout, "Custom mining hook enabled.");
37
- for (const line of formatNextStepLines(nextSteps)) {
38
- writeLine(context.stdout, line);
39
- }
40
- return 0;
41
- }
42
- if (parsed.command === "hooks-mining-disable") {
43
- const view = await context.disableMiningHooks({
44
- provider,
45
- paths: runtimePaths,
46
- });
47
- if (parsed.outputMode === "preview-json") {
48
- writeJsonValue(context.stdout, createPreviewSuccessEnvelope(resolvePreviewJsonSchema(parsed), describeCanonicalCommand(parsed), "disabled", buildHooksPreviewData("hooks-disable-mining", view)));
49
- return 0;
50
- }
51
- if (parsed.outputMode === "json") {
52
- writeJsonValue(context.stdout, createMutationSuccessEnvelope(resolveStableMiningControlJsonSchema(parsed), "cogcoin hooks disable mining", "disabled", buildHooksDisableMiningData(view)));
53
- return 0;
54
- }
55
- writeLine(context.stdout, "Mining hooks switched back to builtin mode.");
56
- return 0;
57
- }
58
16
  if (parsed.command === "mine-setup") {
59
17
  const prompter = createCommandPrompter(parsed, context);
18
+ const provider = withInteractiveWalletSecretProvider(context.walletSecretProvider, prompter);
60
19
  const view = await context.setupBuiltInMining({
61
20
  provider,
62
21
  prompter,
@@ -64,7 +23,7 @@ export async function runMiningAdminCommand(parsed, context) {
64
23
  });
65
24
  const nextSteps = getMineSetupNextSteps();
66
25
  if (parsed.outputMode === "preview-json") {
67
- writeJsonValue(context.stdout, createPreviewSuccessEnvelope(resolvePreviewJsonSchema(parsed), describeCanonicalCommand(parsed), "configured", buildHooksPreviewData("mine-setup", view), {
26
+ writeJsonValue(context.stdout, createPreviewSuccessEnvelope(resolvePreviewJsonSchema(parsed), describeCanonicalCommand(parsed), "configured", buildMineSetupPreviewData(view), {
68
27
  nextSteps,
69
28
  }));
70
29
  return 0;
@@ -1,10 +1,10 @@
1
1
  import { dirname } from "node:path";
2
2
  import { stat } from "node:fs/promises";
3
- import { formatHooksStatusReport, formatMineStatusReport, formatMiningEventRecord } from "../mining-format.js";
3
+ import { formatMineStatusReport, formatMiningEventRecord } from "../mining-format.js";
4
4
  import { writeLine } from "../io.js";
5
- import { inspectWalletLocalState } from "../../wallet/read/index.js";
6
5
  import { createErrorEnvelope, createSuccessEnvelope, describeCanonicalCommand, normalizeListPage, writeJsonValue, } from "../output.js";
7
- import { buildHooksStatusJson, buildMineLogJson, buildMineStatusJson } from "../read-json.js";
6
+ import { buildMineLogJson, buildMineStatusJson } from "../read-json.js";
7
+ import { withInteractiveWalletSecretProvider } from "../../wallet/state/provider.js";
8
8
  async function readRotationIndices(paths) {
9
9
  const rotation = [];
10
10
  for (let index = 1; index <= 4; index += 1) {
@@ -26,46 +26,6 @@ export async function runMiningReadCommand(parsed, context) {
26
26
  const dataDir = parsed.dataDir ?? context.resolveDefaultBitcoindDataDir();
27
27
  const runtimePaths = context.resolveWalletRuntimePaths(parsed.seedName);
28
28
  await context.ensureDirectory(dirname(dbPath));
29
- if (parsed.command === "hooks-mining-status") {
30
- const localState = await inspectWalletLocalState({
31
- secretProvider: context.walletSecretProvider,
32
- paths: runtimePaths,
33
- });
34
- const view = await context.inspectMiningControlPlane({
35
- provider: context.walletSecretProvider,
36
- localState,
37
- bitcoind: {
38
- health: "unavailable",
39
- status: null,
40
- message: "Managed bitcoind status unavailable during hook inspection.",
41
- },
42
- nodeStatus: null,
43
- nodeHealth: "unavailable",
44
- indexer: {
45
- health: "unavailable",
46
- status: null,
47
- message: null,
48
- snapshotTip: null,
49
- source: "none",
50
- daemonInstanceId: null,
51
- snapshotSeq: null,
52
- openedAtUnixMs: null,
53
- },
54
- verify: parsed.verify,
55
- paths: runtimePaths,
56
- });
57
- if (parsed.outputMode === "json") {
58
- const result = buildHooksStatusJson(view);
59
- writeJsonValue(context.stdout, createSuccessEnvelope("cogcoin/hooks-status/v1", describeCanonicalCommand(parsed), result.data, {
60
- warnings: result.warnings,
61
- explanations: result.explanations,
62
- nextSteps: result.nextSteps,
63
- }));
64
- return 0;
65
- }
66
- writeLine(context.stdout, formatHooksStatusReport(view));
67
- return 0;
68
- }
69
29
  if (parsed.command === "mine-log") {
70
30
  if (!parsed.follow) {
71
31
  const allEvents = await context.readMiningLog({
@@ -128,15 +88,18 @@ export async function runMiningReadCommand(parsed, context) {
128
88
  }
129
89
  return 0;
130
90
  }
91
+ const provider = parsed.outputMode === "text"
92
+ ? withInteractiveWalletSecretProvider(context.walletSecretProvider, context.createPrompter())
93
+ : context.walletSecretProvider;
131
94
  const readContext = await context.openWalletReadContext({
132
95
  dataDir,
133
96
  databasePath: dbPath,
134
- secretProvider: context.walletSecretProvider,
97
+ secretProvider: provider,
135
98
  paths: runtimePaths,
136
99
  });
137
100
  try {
138
101
  const mining = readContext.mining ?? await context.inspectMiningControlPlane({
139
- provider: context.walletSecretProvider,
102
+ provider,
140
103
  localState: readContext.localState,
141
104
  bitcoind: readContext.bitcoind,
142
105
  nodeStatus: readContext.nodeStatus,
@@ -163,11 +126,9 @@ export async function runMiningReadCommand(parsed, context) {
163
126
  catch (error) {
164
127
  const message = error instanceof Error ? error.message : String(error);
165
128
  if (parsed.outputMode === "json") {
166
- writeJsonValue(context.stdout, createErrorEnvelope(parsed.command === "hooks-mining-status"
167
- ? "cogcoin/hooks-status/v1"
168
- : parsed.command === "mine-log"
169
- ? "cogcoin/mine-log/v1"
170
- : "cogcoin/mine-status/v1", describeCanonicalCommand(parsed), message, message));
129
+ writeJsonValue(context.stdout, createErrorEnvelope(parsed.command === "mine-log"
130
+ ? "cogcoin/mine-log/v1"
131
+ : "cogcoin/mine-status/v1", describeCanonicalCommand(parsed), message, message));
171
132
  return 5;
172
133
  }
173
134
  writeLine(context.stderr, message);
@@ -5,19 +5,42 @@ import { writeLine } from "../io.js";
5
5
  import { createTerminalPrompter } from "../prompt.js";
6
6
  import { createPreviewSuccessEnvelope, createMutationSuccessEnvelope, describeCanonicalCommand, resolvePreviewJsonSchema, resolveStableMiningControlJsonSchema, writeHandledCliError, writeJsonValue, } from "../output.js";
7
7
  import { formatNextStepLines, getMineStopNextSteps, } from "../workflow-hints.js";
8
+ import { withInteractiveWalletSecretProvider } from "../../wallet/state/provider.js";
8
9
  function createCommandPrompter(parsed, context) {
9
10
  return parsed.outputMode !== "text"
10
11
  ? createTerminalPrompter(context.stdin, context.stderr)
11
12
  : context.createPrompter();
12
13
  }
14
+ async function prestartManagedMiningServices(options) {
15
+ let readContext = null;
16
+ try {
17
+ readContext = await options.context.openWalletReadContext({
18
+ dataDir: options.dataDir,
19
+ databasePath: options.databasePath,
20
+ secretProvider: options.provider,
21
+ paths: options.runtimePaths,
22
+ });
23
+ }
24
+ finally {
25
+ await readContext?.close();
26
+ }
27
+ }
13
28
  export async function runMiningRuntimeCommand(parsed, context) {
14
29
  try {
15
30
  const dbPath = parsed.dbPath ?? context.resolveDefaultClientDatabasePath();
16
31
  const dataDir = parsed.dataDir ?? context.resolveDefaultBitcoindDataDir();
17
- const provider = context.walletSecretProvider;
18
32
  const runtimePaths = context.resolveWalletRuntimePaths(parsed.seedName);
19
33
  await context.ensureDirectory(dirname(dbPath));
20
34
  if (parsed.command === "mine") {
35
+ const prompter = context.createPrompter();
36
+ const provider = withInteractiveWalletSecretProvider(context.walletSecretProvider, prompter);
37
+ await prestartManagedMiningServices({
38
+ context,
39
+ dataDir,
40
+ databasePath: dbPath,
41
+ provider,
42
+ runtimePaths,
43
+ });
21
44
  const abortController = new AbortController();
22
45
  const onStop = () => {
23
46
  abortController.abort();
@@ -29,7 +52,7 @@ export async function runMiningRuntimeCommand(parsed, context) {
29
52
  dataDir,
30
53
  databasePath: dbPath,
31
54
  provider,
32
- prompter: context.createPrompter(),
55
+ prompter,
33
56
  signal: abortController.signal,
34
57
  stdout: context.stdout,
35
58
  stderr: context.stderr,
@@ -44,11 +67,20 @@ export async function runMiningRuntimeCommand(parsed, context) {
44
67
  return 0;
45
68
  }
46
69
  if (parsed.command === "mine-start") {
70
+ const prompter = createCommandPrompter(parsed, context);
71
+ const provider = withInteractiveWalletSecretProvider(context.walletSecretProvider, prompter);
72
+ await prestartManagedMiningServices({
73
+ context,
74
+ dataDir,
75
+ databasePath: dbPath,
76
+ provider,
77
+ runtimePaths,
78
+ });
47
79
  const result = await context.startBackgroundMining({
48
80
  dataDir,
49
81
  databasePath: dbPath,
50
82
  provider,
51
- prompter: createCommandPrompter(parsed, context),
83
+ prompter,
52
84
  paths: runtimePaths,
53
85
  });
54
86
  if (parsed.outputMode === "preview-json") {
@@ -73,6 +105,9 @@ export async function runMiningRuntimeCommand(parsed, context) {
73
105
  return 0;
74
106
  }
75
107
  if (parsed.command === "mine-stop") {
108
+ const provider = parsed.outputMode === "text"
109
+ ? withInteractiveWalletSecretProvider(context.walletSecretProvider, context.createPrompter())
110
+ : context.walletSecretProvider;
76
111
  const snapshot = await context.stopBackgroundMining({
77
112
  dataDir,
78
113
  databasePath: dbPath,
@@ -38,8 +38,6 @@ async function resolveEffectiveWalletRootId(context) {
38
38
  paths: context.resolveWalletRuntimePaths(),
39
39
  provider: context.walletSecretProvider,
40
40
  loadRawWalletStateEnvelope: context.loadRawWalletStateEnvelope,
41
- loadUnlockSession: context.loadUnlockSession,
42
- loadWalletExplicitLock: context.loadWalletExplicitLock,
43
41
  }).catch(() => ({
44
42
  walletRootId: UNINITIALIZED_WALLET_ROOT_ID,
45
43
  source: "default-uninitialized",
@@ -1,17 +1,22 @@
1
1
  import { dirname } from "node:path";
2
2
  import { buildStatusJson } from "../read-json.js";
3
- import { formatWalletOverviewReport } from "../wallet-format.js";
3
+ import { formatBalanceReport, formatWalletOverviewReport } from "../wallet-format.js";
4
4
  import { writeLine } from "../io.js";
5
+ import { createTerminalPrompter } from "../prompt.js";
5
6
  import { createSuccessEnvelope, describeCanonicalCommand, writeJsonValue } from "../output.js";
7
+ import { withInteractiveWalletSecretProvider } from "../../wallet/state/provider.js";
6
8
  export async function runStatusCommand(parsed, context) {
7
9
  const dbPath = parsed.dbPath ?? context.resolveDefaultClientDatabasePath();
8
10
  const dataDir = parsed.dataDir ?? context.resolveDefaultBitcoindDataDir();
9
11
  const runtimePaths = context.resolveWalletRuntimePaths(parsed.seedName);
10
12
  await context.ensureDirectory(dirname(dbPath));
13
+ const provider = parsed.outputMode === "text"
14
+ ? withInteractiveWalletSecretProvider(context.walletSecretProvider, context.createPrompter?.() ?? createTerminalPrompter(context.stdin, context.stdout))
15
+ : context.walletSecretProvider;
11
16
  const readContext = await context.openWalletReadContext({
12
17
  dataDir,
13
18
  databasePath: dbPath,
14
- secretProvider: context.walletSecretProvider,
19
+ secretProvider: provider,
15
20
  paths: runtimePaths,
16
21
  });
17
22
  try {
@@ -25,6 +30,7 @@ export async function runStatusCommand(parsed, context) {
25
30
  return 0;
26
31
  }
27
32
  writeLine(context.stdout, formatWalletOverviewReport(readContext, await context.readPackageVersion()));
33
+ writeLine(context.stdout, formatBalanceReport(readContext));
28
34
  return 0;
29
35
  }
30
36
  finally {
@@ -3,10 +3,29 @@ import { formatManagedSyncErrorMessage } from "../../bitcoind/errors.js";
3
3
  import { formatBytes, formatDuration } from "../../bitcoind/progress/formatting.js";
4
4
  import { FileLockBusyError, acquireFileLock } from "../../wallet/fs/lock.js";
5
5
  import { resolveWalletRootIdFromLocalArtifacts } from "../../wallet/root-resolution.js";
6
+ import { withInteractiveWalletSecretProvider } from "../../wallet/state/provider.js";
6
7
  import { usesTtyProgress, writeLine } from "../io.js";
7
8
  import { classifyCliError, formatCliTextError } from "../output.js";
9
+ import { createTerminalPrompter } from "../prompt.js";
8
10
  import { createStopSignalWatcher, waitForCompletionOrStop } from "../signals.js";
11
+ import { formatBalanceReport } from "../wallet-format.js";
9
12
  const SYNC_PROGRESS_LOG_INTERVAL_MS = 5_000;
13
+ async function writePostSyncBalanceReport(options) {
14
+ const provider = withInteractiveWalletSecretProvider(options.context.walletSecretProvider, options.context.createPrompter?.() ?? createTerminalPrompter(options.context.stdin, options.context.stdout));
15
+ const readContext = await options.context.openWalletReadContext({
16
+ dataDir: options.dataDir,
17
+ databasePath: options.databasePath,
18
+ secretProvider: provider,
19
+ walletControlLockHeld: true,
20
+ paths: options.runtimePaths,
21
+ });
22
+ try {
23
+ writeLine(options.context.stdout, formatBalanceReport(readContext));
24
+ }
25
+ finally {
26
+ await readContext.close().catch(() => undefined);
27
+ }
28
+ }
10
29
  function createSyncProgressReporter(options) {
11
30
  let lastPhase = null;
12
31
  let lastMessage = "";
@@ -104,13 +123,13 @@ export async function runSyncCommand(parsed, context) {
104
123
  let controlLock = null;
105
124
  let store = null;
106
125
  let storeOwned = true;
126
+ let client = null;
127
+ let clientClosed = false;
107
128
  try {
108
129
  const walletRoot = await resolveWalletRootIdFromLocalArtifacts({
109
130
  paths: runtimePaths,
110
131
  provider: context.walletSecretProvider,
111
132
  loadRawWalletStateEnvelope: context.loadRawWalletStateEnvelope,
112
- loadUnlockSession: context.loadUnlockSession,
113
- loadWalletExplicitLock: context.loadWalletExplicitLock,
114
133
  });
115
134
  try {
116
135
  controlLock = await acquireFileLock(runtimePaths.walletControlLockPath, {
@@ -126,7 +145,7 @@ export async function runSyncCommand(parsed, context) {
126
145
  }
127
146
  await context.ensureDirectory(dirname(dbPath));
128
147
  store = await context.openSqliteStore({ filename: dbPath });
129
- const client = await context.openManagedBitcoindClient({
148
+ client = await context.openManagedBitcoindClient({
130
149
  store,
131
150
  databasePath: dbPath,
132
151
  dataDir,
@@ -147,6 +166,32 @@ export async function runSyncCommand(parsed, context) {
147
166
  return syncOutcome.code;
148
167
  }
149
168
  const result = syncOutcome.value;
169
+ if (result.endingHeight !== null && result.endingHeight === result.bestHeight) {
170
+ stopWatcher.cleanup();
171
+ const detachPromise = typeof client.detachToBackgroundFollow === "function"
172
+ ? client.detachToBackgroundFollow()
173
+ : Promise.resolve();
174
+ if (typeof client.playSyncCompletionScene === "function") {
175
+ await client.playSyncCompletionScene().catch(() => undefined);
176
+ }
177
+ try {
178
+ await detachPromise;
179
+ await client.close();
180
+ clientClosed = true;
181
+ writeLine(context.stderr, "Detached cleanly; background indexer follow resumed.");
182
+ await writePostSyncBalanceReport({
183
+ context,
184
+ dataDir,
185
+ databasePath: dbPath,
186
+ runtimePaths,
187
+ }).catch(() => undefined);
188
+ return 0;
189
+ }
190
+ catch {
191
+ writeLine(context.stderr, "Detach failed before background indexer follow was confirmed.");
192
+ return 1;
193
+ }
194
+ }
150
195
  if (typeof client.playSyncCompletionScene === "function") {
151
196
  const completionOutcome = await waitForCompletionOrStop(client.playSyncCompletionScene().catch(() => undefined), stopWatcher);
152
197
  if (completionOutcome.kind === "stopped") {
@@ -161,7 +206,9 @@ export async function runSyncCommand(parsed, context) {
161
206
  }
162
207
  finally {
163
208
  stopWatcher.cleanup();
164
- await client.close();
209
+ if (!clientClosed) {
210
+ await client.close();
211
+ }
165
212
  }
166
213
  }
167
214
  catch (error) {