@cogcoin/client 0.5.6 → 0.5.7

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 (69) hide show
  1. package/README.md +11 -2
  2. package/dist/bitcoind/bootstrap/getblock-archive.d.ts +39 -0
  3. package/dist/bitcoind/bootstrap/getblock-archive.js +548 -0
  4. package/dist/bitcoind/bootstrap.d.ts +1 -0
  5. package/dist/bitcoind/bootstrap.js +1 -0
  6. package/dist/bitcoind/client/factory.js +84 -30
  7. package/dist/bitcoind/client/managed-client.js +2 -1
  8. package/dist/bitcoind/client/sync-engine.js +7 -0
  9. package/dist/bitcoind/errors.js +18 -0
  10. package/dist/bitcoind/indexer-daemon-main.js +78 -0
  11. package/dist/bitcoind/indexer-daemon.d.ts +3 -1
  12. package/dist/bitcoind/indexer-daemon.js +13 -6
  13. package/dist/bitcoind/node.js +2 -0
  14. package/dist/bitcoind/progress/constants.d.ts +1 -0
  15. package/dist/bitcoind/progress/constants.js +1 -0
  16. package/dist/bitcoind/progress/controller.d.ts +22 -0
  17. package/dist/bitcoind/progress/controller.js +48 -23
  18. package/dist/bitcoind/progress/formatting.js +25 -0
  19. package/dist/bitcoind/progress/render-policy.d.ts +35 -0
  20. package/dist/bitcoind/progress/render-policy.js +81 -0
  21. package/dist/bitcoind/service-paths.js +2 -6
  22. package/dist/bitcoind/service.d.ts +5 -1
  23. package/dist/bitcoind/service.js +93 -54
  24. package/dist/bitcoind/testing.d.ts +1 -1
  25. package/dist/bitcoind/testing.js +1 -1
  26. package/dist/bitcoind/types.d.ts +35 -1
  27. package/dist/cli/commands/follow.js +2 -0
  28. package/dist/cli/commands/getblock-archive-restart.d.ts +5 -0
  29. package/dist/cli/commands/getblock-archive-restart.js +15 -0
  30. package/dist/cli/commands/mining-admin.js +4 -0
  31. package/dist/cli/commands/mining-read.js +8 -5
  32. package/dist/cli/commands/mining-runtime.js +4 -0
  33. package/dist/cli/commands/status.js +2 -0
  34. package/dist/cli/commands/sync.js +2 -0
  35. package/dist/cli/commands/wallet-admin.js +29 -3
  36. package/dist/cli/commands/wallet-mutation.js +57 -4
  37. package/dist/cli/commands/wallet-read.js +2 -0
  38. package/dist/cli/context.js +5 -3
  39. package/dist/cli/mutation-command-groups.d.ts +2 -1
  40. package/dist/cli/mutation-command-groups.js +5 -0
  41. package/dist/cli/mutation-json.d.ts +18 -2
  42. package/dist/cli/mutation-json.js +47 -0
  43. package/dist/cli/mutation-success.d.ts +1 -0
  44. package/dist/cli/mutation-success.js +2 -2
  45. package/dist/cli/output.js +84 -1
  46. package/dist/cli/parse.d.ts +1 -1
  47. package/dist/cli/parse.js +127 -3
  48. package/dist/cli/preview-json.d.ts +10 -1
  49. package/dist/cli/preview-json.js +30 -0
  50. package/dist/cli/prompt.js +1 -1
  51. package/dist/cli/runner.js +3 -0
  52. package/dist/cli/types.d.ts +11 -4
  53. package/dist/cli/wallet-format.js +6 -0
  54. package/dist/wallet/lifecycle.d.ts +15 -1
  55. package/dist/wallet/lifecycle.js +147 -83
  56. package/dist/wallet/mining/visualizer.d.ts +11 -6
  57. package/dist/wallet/mining/visualizer.js +32 -15
  58. package/dist/wallet/reset.js +39 -27
  59. package/dist/wallet/runtime.d.ts +12 -1
  60. package/dist/wallet/runtime.js +53 -11
  61. package/dist/wallet/state/provider.d.ts +1 -0
  62. package/dist/wallet/state/provider.js +119 -3
  63. package/dist/wallet/state/seed-index.d.ts +43 -0
  64. package/dist/wallet/state/seed-index.js +151 -0
  65. package/dist/wallet/tx/anchor.d.ts +22 -0
  66. package/dist/wallet/tx/anchor.js +215 -8
  67. package/dist/wallet/tx/index.d.ts +1 -1
  68. package/dist/wallet/tx/index.js +1 -1
  69. package/package.json +1 -1
@@ -1,21 +1,63 @@
1
+ import { join } from "node:path";
1
2
  import { resolveCogcoinPathsForTesting } from "../app-paths.js";
3
+ function resolveSeedLayout(sharedStateRoot, sharedRuntimeRoot, seedName) {
4
+ if (seedName === "main") {
5
+ return {
6
+ seedKind: "main",
7
+ walletStateRoot: sharedStateRoot,
8
+ walletRuntimeRoot: sharedRuntimeRoot,
9
+ };
10
+ }
11
+ return {
12
+ seedKind: "imported",
13
+ walletStateRoot: join(sharedStateRoot, "seeds", seedName),
14
+ walletRuntimeRoot: join(sharedRuntimeRoot, "seeds", seedName),
15
+ };
16
+ }
17
+ export function deriveWalletRuntimePathsForSeed(basePaths, seedName) {
18
+ const resolvedSeedName = seedName ?? "main";
19
+ const seedLayout = resolveSeedLayout(basePaths.stateRoot, basePaths.runtimeRoot, resolvedSeedName);
20
+ return {
21
+ ...basePaths,
22
+ walletRuntimeRoot: seedLayout.walletRuntimeRoot,
23
+ walletStateRoot: seedLayout.walletStateRoot,
24
+ selectedSeedName: resolvedSeedName,
25
+ selectedSeedKind: seedLayout.seedKind,
26
+ walletStateDirectory: seedLayout.walletStateRoot,
27
+ walletStatePath: join(seedLayout.walletStateRoot, "wallet-state.enc"),
28
+ walletStateBackupPath: join(seedLayout.walletStateRoot, "wallet-state.enc.bak"),
29
+ walletInitPendingPath: join(seedLayout.walletStateRoot, "wallet-init-pending.enc"),
30
+ walletInitPendingBackupPath: join(seedLayout.walletStateRoot, "wallet-init-pending.enc.bak"),
31
+ walletUnlockSessionPath: join(seedLayout.walletRuntimeRoot, "wallet-unlock-session.enc"),
32
+ walletExplicitLockPath: join(seedLayout.walletRuntimeRoot, "wallet-explicit-lock.json"),
33
+ miningRoot: join(seedLayout.walletRuntimeRoot, "mining"),
34
+ miningStatusPath: join(seedLayout.walletRuntimeRoot, "mining", "status.json"),
35
+ miningEventsPath: join(seedLayout.walletRuntimeRoot, "mining", "events.jsonl"),
36
+ };
37
+ }
2
38
  export function resolveWalletRuntimePathsForTesting(resolution = {}) {
3
39
  const paths = resolveCogcoinPathsForTesting(resolution);
4
- return {
40
+ return deriveWalletRuntimePathsForSeed({
5
41
  dataRoot: paths.dataRoot,
6
42
  clientDataDir: paths.clientDataDir,
7
43
  clientConfigPath: paths.clientConfigPath,
8
44
  runtimeRoot: paths.runtimeRoot,
45
+ walletRuntimeRoot: paths.runtimeRoot,
9
46
  hooksRoot: paths.hooksRoot,
10
47
  stateRoot: paths.stateRoot,
48
+ walletStateRoot: paths.stateRoot,
49
+ seedRegistryPath: join(paths.stateRoot, "seed-index.json"),
50
+ selectedSeedName: "main",
51
+ selectedSeedKind: "main",
11
52
  bitcoinDataDir: paths.bitcoinDataDir,
12
53
  indexerRoot: paths.indexerRoot,
13
- walletStatePath: paths.walletStatePath,
14
- walletStateBackupPath: paths.walletStateBackupPath,
15
- walletInitPendingPath: paths.walletInitPendingPath,
16
- walletInitPendingBackupPath: paths.walletInitPendingBackupPath,
17
- walletUnlockSessionPath: paths.walletUnlockSessionPath,
18
- walletExplicitLockPath: paths.walletExplicitLockPath,
54
+ walletStateDirectory: paths.stateRoot,
55
+ walletStatePath: join(paths.stateRoot, "wallet-state.enc"),
56
+ walletStateBackupPath: join(paths.stateRoot, "wallet-state.enc.bak"),
57
+ walletInitPendingPath: join(paths.stateRoot, "wallet-init-pending.enc"),
58
+ walletInitPendingBackupPath: join(paths.stateRoot, "wallet-init-pending.enc.bak"),
59
+ walletUnlockSessionPath: join(paths.runtimeRoot, "wallet-unlock-session.enc"),
60
+ walletExplicitLockPath: join(paths.runtimeRoot, "wallet-explicit-lock.json"),
19
61
  walletControlLockPath: paths.walletControlLockPath,
20
62
  bitcoindLockPath: paths.bitcoindLockPath,
21
63
  bitcoindStatusPath: paths.bitcoindStatusPath,
@@ -24,9 +66,9 @@ export function resolveWalletRuntimePathsForTesting(resolution = {}) {
24
66
  hooksMiningDir: paths.hooksMiningDir,
25
67
  hooksMiningEntrypointPath: paths.hooksMiningEntrypointPath,
26
68
  hooksMiningPackageJsonPath: paths.hooksMiningPackageJsonPath,
27
- miningRoot: paths.miningRoot,
28
- miningStatusPath: paths.miningStatusPath,
29
- miningEventsPath: paths.miningEventsPath,
69
+ miningRoot: join(paths.runtimeRoot, "mining"),
70
+ miningStatusPath: join(paths.runtimeRoot, "mining", "status.json"),
71
+ miningEventsPath: join(paths.runtimeRoot, "mining", "events.jsonl"),
30
72
  miningControlLockPath: paths.miningControlLockPath,
31
- };
73
+ }, resolution.seedName);
32
74
  }
@@ -11,6 +11,7 @@ export type LinuxSecretToolRunner = (args: readonly string[], options?: LinuxSec
11
11
  export interface DefaultWalletSecretProviderFactoryOptions {
12
12
  platform?: NodeJS.Platform;
13
13
  linuxSecretToolRunner?: LinuxSecretToolRunner;
14
+ stateRoot?: string;
14
15
  }
15
16
  export interface WalletSecretReference {
16
17
  kind: string;
@@ -1,6 +1,6 @@
1
1
  import { createHash, randomUUID } from "node:crypto";
2
2
  import { execFile, spawn } from "node:child_process";
3
- import { mkdir, readFile, rm } from "node:fs/promises";
3
+ import { access, constants, mkdir, readFile, rm } from "node:fs/promises";
4
4
  import { dirname, join } from "node:path";
5
5
  import { promisify } from "node:util";
6
6
  import { resolveWalletRuntimePathsForTesting } from "../runtime.js";
@@ -42,6 +42,15 @@ function createLinuxSecretToolAttributes(keyId) {
42
42
  function createLinuxSecretToolError(message, cause) {
43
43
  return cause === undefined ? new Error(message) : new Error(message, { cause });
44
44
  }
45
+ function isWalletSecretProviderMessage(error, message) {
46
+ return error instanceof Error && error.message === message;
47
+ }
48
+ function sanitizeSecretKeyId(keyId) {
49
+ return keyId.replace(/[^a-zA-Z0-9._-]+/g, "-");
50
+ }
51
+ function resolveSecretDirectoryPath(options) {
52
+ return join(options.stateRoot ?? resolveWalletRuntimePathsForTesting().stateRoot, "secrets");
53
+ }
45
54
  function isLinuxSecretServiceUnavailableMessage(stderr) {
46
55
  const normalized = stderr.trim().toLowerCase();
47
56
  if (normalized.length === 0) {
@@ -233,6 +242,110 @@ class LinuxSecretToolWalletSecretProvider {
233
242
  }
234
243
  }
235
244
  }
245
+ class LinuxLocalFileWalletSecretProvider {
246
+ kind = "linux-local-file";
247
+ #directoryPath;
248
+ constructor(directoryPath) {
249
+ this.#directoryPath = directoryPath;
250
+ }
251
+ #resolveSecretPath(keyId) {
252
+ return join(this.#directoryPath, `${sanitizeSecretKeyId(keyId)}.secret`);
253
+ }
254
+ async hasSecret(keyId) {
255
+ try {
256
+ await access(this.#resolveSecretPath(keyId), constants.F_OK);
257
+ return true;
258
+ }
259
+ catch {
260
+ return false;
261
+ }
262
+ }
263
+ async loadSecret(keyId) {
264
+ try {
265
+ const encoded = await readFile(this.#resolveSecretPath(keyId), "utf8");
266
+ return base64ToBytes(encoded.trim());
267
+ }
268
+ catch (error) {
269
+ if (error instanceof Error && "code" in error && error.code === "ENOENT") {
270
+ throw new Error(`wallet_secret_missing_${keyId}`);
271
+ }
272
+ throw createLinuxSecretToolError("wallet_secret_provider_linux_runtime_error", error);
273
+ }
274
+ }
275
+ async storeSecret(keyId, secret) {
276
+ try {
277
+ await mkdir(this.#directoryPath, { recursive: true, mode: 0o700 });
278
+ await writeFileAtomic(this.#resolveSecretPath(keyId), `${bytesToBase64(secret)}\n`, { mode: 0o600 });
279
+ }
280
+ catch (error) {
281
+ throw createLinuxSecretToolError("wallet_secret_provider_linux_runtime_error", error);
282
+ }
283
+ }
284
+ async deleteSecret(keyId) {
285
+ await rm(this.#resolveSecretPath(keyId), { force: true }).catch(() => undefined);
286
+ }
287
+ }
288
+ class LinuxFallbackWalletSecretProvider {
289
+ kind = "linux-secret-service-fallback";
290
+ #secretService;
291
+ #fileProvider;
292
+ constructor(options) {
293
+ this.#secretService = options.secretService;
294
+ this.#fileProvider = options.fileProvider;
295
+ }
296
+ async #loadFromFileOrRethrow(keyId, error) {
297
+ try {
298
+ return await this.#fileProvider.loadSecret(keyId);
299
+ }
300
+ catch (fileError) {
301
+ if (isWalletSecretProviderMessage(fileError, `wallet_secret_missing_${keyId}`)
302
+ && (error.message === "wallet_secret_provider_linux_secret_tool_missing"
303
+ || error.message === "wallet_secret_provider_linux_secret_service_unavailable")) {
304
+ throw error;
305
+ }
306
+ throw fileError;
307
+ }
308
+ }
309
+ async loadSecret(keyId) {
310
+ try {
311
+ return await this.#secretService.loadSecret(keyId);
312
+ }
313
+ catch (error) {
314
+ if (!(error instanceof Error)) {
315
+ throw error;
316
+ }
317
+ if (error.message === "wallet_secret_provider_linux_secret_tool_missing"
318
+ || error.message === "wallet_secret_provider_linux_secret_service_unavailable"
319
+ || error.message === `wallet_secret_missing_${keyId}`) {
320
+ return await this.#loadFromFileOrRethrow(keyId, error);
321
+ }
322
+ if (error.message === "wallet_secret_provider_linux_runtime_error"
323
+ && await this.#fileProvider.hasSecret(keyId)) {
324
+ return await this.#fileProvider.loadSecret(keyId);
325
+ }
326
+ throw error;
327
+ }
328
+ }
329
+ async storeSecret(keyId, secret) {
330
+ try {
331
+ await this.#secretService.storeSecret(keyId, secret);
332
+ }
333
+ catch (error) {
334
+ if (isWalletSecretProviderMessage(error, "wallet_secret_provider_linux_secret_tool_missing")
335
+ || isWalletSecretProviderMessage(error, "wallet_secret_provider_linux_secret_service_unavailable")) {
336
+ await this.#fileProvider.storeSecret(keyId, secret);
337
+ return;
338
+ }
339
+ throw error;
340
+ }
341
+ }
342
+ async deleteSecret(keyId) {
343
+ await Promise.allSettled([
344
+ this.#secretService.deleteSecret(keyId),
345
+ this.#fileProvider.deleteSecret(keyId),
346
+ ]);
347
+ }
348
+ }
236
349
  class WindowsDpapiWalletSecretProvider {
237
350
  kind = "windows-dpapi";
238
351
  #directoryPath;
@@ -276,10 +389,13 @@ function createWalletSecretProviderForPlatform(platform, options = {}) {
276
389
  return new MacOsKeychainWalletSecretProvider();
277
390
  }
278
391
  if (platform === "win32") {
279
- return new WindowsDpapiWalletSecretProvider(join(resolveWalletRuntimePathsForTesting().stateRoot, "secrets"));
392
+ return new WindowsDpapiWalletSecretProvider(resolveSecretDirectoryPath(options));
280
393
  }
281
394
  if (platform === "linux") {
282
- return new LinuxSecretToolWalletSecretProvider(options.linuxSecretToolRunner);
395
+ return new LinuxFallbackWalletSecretProvider({
396
+ secretService: new LinuxSecretToolWalletSecretProvider(options.linuxSecretToolRunner),
397
+ fileProvider: new LinuxLocalFileWalletSecretProvider(resolveSecretDirectoryPath(options)),
398
+ });
283
399
  }
284
400
  throw new Error(`wallet_secret_provider_unsupported_${platform}`);
285
401
  }
@@ -0,0 +1,43 @@
1
+ import type { WalletSeedKind, WalletRuntimePaths } from "../runtime.js";
2
+ import { type RawWalletStateEnvelope } from "./storage.js";
3
+ export interface WalletSeedRecord {
4
+ name: string;
5
+ kind: WalletSeedKind;
6
+ walletRootId: string;
7
+ createdAtUnixMs: number;
8
+ restoredAtUnixMs: number | null;
9
+ }
10
+ export interface WalletSeedIndexV1 {
11
+ schemaVersion: 1;
12
+ lastWrittenAtUnixMs: number;
13
+ seeds: WalletSeedRecord[];
14
+ }
15
+ export declare function normalizeWalletSeedName(name: string): string;
16
+ export declare function isValidWalletSeedName(name: string): boolean;
17
+ export declare function assertValidImportedWalletSeedName(name: string): string;
18
+ export declare function findWalletSeedRecord(index: WalletSeedIndexV1, seedName: string): WalletSeedRecord | null;
19
+ export declare function loadWalletSeedIndex(options: {
20
+ paths: Pick<WalletRuntimePaths, "seedRegistryPath" | "walletStatePath" | "walletStateBackupPath">;
21
+ nowUnixMs?: number;
22
+ loadRawWalletStateEnvelope?: (paths: {
23
+ primaryPath: string;
24
+ backupPath: string;
25
+ }) => Promise<RawWalletStateEnvelope | null>;
26
+ }): Promise<WalletSeedIndexV1>;
27
+ export declare function saveWalletSeedIndex(paths: Pick<WalletRuntimePaths, "seedRegistryPath">, index: WalletSeedIndexV1): Promise<void>;
28
+ export declare function ensureMainWalletSeedIndexRecord(options: {
29
+ paths: Pick<WalletRuntimePaths, "seedRegistryPath" | "walletStatePath" | "walletStateBackupPath">;
30
+ walletRootId: string;
31
+ nowUnixMs?: number;
32
+ }): Promise<WalletSeedIndexV1>;
33
+ export declare function addImportedWalletSeedRecord(options: {
34
+ paths: Pick<WalletRuntimePaths, "seedRegistryPath" | "walletStatePath" | "walletStateBackupPath">;
35
+ seedName: string;
36
+ walletRootId: string;
37
+ nowUnixMs?: number;
38
+ }): Promise<WalletSeedIndexV1>;
39
+ export declare function removeWalletSeedRecord(options: {
40
+ paths: Pick<WalletRuntimePaths, "seedRegistryPath" | "walletStatePath" | "walletStateBackupPath">;
41
+ seedName: string;
42
+ nowUnixMs?: number;
43
+ }): Promise<WalletSeedIndexV1>;
@@ -0,0 +1,151 @@
1
+ import { mkdir, readFile } from "node:fs/promises";
2
+ import { dirname } from "node:path";
3
+ import { writeFileAtomic } from "../fs/atomic.js";
4
+ import { extractWalletRootIdHintFromWalletStateEnvelope, loadRawWalletStateEnvelope, } from "./storage.js";
5
+ function createEmptySeedIndex(nowUnixMs) {
6
+ return {
7
+ schemaVersion: 1,
8
+ lastWrittenAtUnixMs: nowUnixMs,
9
+ seeds: [],
10
+ };
11
+ }
12
+ function sortSeedRecords(seeds) {
13
+ return [...seeds].sort((left, right) => left.name.localeCompare(right.name));
14
+ }
15
+ async function readSeedIndexFile(path) {
16
+ try {
17
+ const parsed = JSON.parse(await readFile(path, "utf8"));
18
+ if (parsed?.schemaVersion !== 1 || !Array.isArray(parsed.seeds)) {
19
+ throw new Error("wallet_seed_index_invalid");
20
+ }
21
+ return {
22
+ schemaVersion: 1,
23
+ lastWrittenAtUnixMs: typeof parsed.lastWrittenAtUnixMs === "number" ? parsed.lastWrittenAtUnixMs : 0,
24
+ seeds: sortSeedRecords(parsed.seeds),
25
+ };
26
+ }
27
+ catch (error) {
28
+ if (error instanceof Error && "code" in error && error.code === "ENOENT") {
29
+ return null;
30
+ }
31
+ throw error;
32
+ }
33
+ }
34
+ export function normalizeWalletSeedName(name) {
35
+ return name.trim().toLowerCase();
36
+ }
37
+ export function isValidWalletSeedName(name) {
38
+ return /^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(name);
39
+ }
40
+ export function assertValidImportedWalletSeedName(name) {
41
+ const normalized = normalizeWalletSeedName(name);
42
+ if (!isValidWalletSeedName(normalized)) {
43
+ throw new Error("wallet_seed_name_invalid");
44
+ }
45
+ if (normalized === "main") {
46
+ throw new Error("wallet_seed_name_reserved");
47
+ }
48
+ return normalized;
49
+ }
50
+ export function findWalletSeedRecord(index, seedName) {
51
+ const normalized = normalizeWalletSeedName(seedName);
52
+ return index.seeds.find((seed) => seed.name === normalized) ?? null;
53
+ }
54
+ export async function loadWalletSeedIndex(options) {
55
+ const nowUnixMs = options.nowUnixMs ?? Date.now();
56
+ const stored = await readSeedIndexFile(options.paths.seedRegistryPath);
57
+ if (stored !== null) {
58
+ return stored;
59
+ }
60
+ const rawEnvelope = await (options.loadRawWalletStateEnvelope ?? loadRawWalletStateEnvelope)({
61
+ primaryPath: options.paths.walletStatePath,
62
+ backupPath: options.paths.walletStateBackupPath,
63
+ }).catch(() => null);
64
+ const mainWalletRootId = extractWalletRootIdHintFromWalletStateEnvelope(rawEnvelope?.envelope ?? null);
65
+ if (mainWalletRootId === null) {
66
+ return createEmptySeedIndex(nowUnixMs);
67
+ }
68
+ return {
69
+ schemaVersion: 1,
70
+ lastWrittenAtUnixMs: nowUnixMs,
71
+ seeds: [{
72
+ name: "main",
73
+ kind: "main",
74
+ walletRootId: mainWalletRootId,
75
+ createdAtUnixMs: nowUnixMs,
76
+ restoredAtUnixMs: null,
77
+ }],
78
+ };
79
+ }
80
+ export async function saveWalletSeedIndex(paths, index) {
81
+ await mkdir(dirname(paths.seedRegistryPath), { recursive: true });
82
+ await writeFileAtomic(paths.seedRegistryPath, `${JSON.stringify({
83
+ ...index,
84
+ seeds: sortSeedRecords(index.seeds),
85
+ }, null, 2)}\n`, { mode: 0o600 });
86
+ }
87
+ export async function ensureMainWalletSeedIndexRecord(options) {
88
+ const nowUnixMs = options.nowUnixMs ?? Date.now();
89
+ const index = await loadWalletSeedIndex({
90
+ paths: options.paths,
91
+ nowUnixMs,
92
+ });
93
+ const existing = findWalletSeedRecord(index, "main");
94
+ const seeds = index.seeds.filter((seed) => seed.name !== "main");
95
+ seeds.push({
96
+ name: "main",
97
+ kind: "main",
98
+ walletRootId: options.walletRootId,
99
+ createdAtUnixMs: existing?.createdAtUnixMs ?? nowUnixMs,
100
+ restoredAtUnixMs: existing?.restoredAtUnixMs ?? null,
101
+ });
102
+ const nextIndex = {
103
+ schemaVersion: 1,
104
+ lastWrittenAtUnixMs: nowUnixMs,
105
+ seeds: sortSeedRecords(seeds),
106
+ };
107
+ await saveWalletSeedIndex(options.paths, nextIndex);
108
+ return nextIndex;
109
+ }
110
+ export async function addImportedWalletSeedRecord(options) {
111
+ const nowUnixMs = options.nowUnixMs ?? Date.now();
112
+ const seedName = assertValidImportedWalletSeedName(options.seedName);
113
+ const index = await loadWalletSeedIndex({
114
+ paths: options.paths,
115
+ nowUnixMs,
116
+ });
117
+ if (findWalletSeedRecord(index, seedName) !== null) {
118
+ throw new Error("wallet_seed_name_exists");
119
+ }
120
+ const nextIndex = {
121
+ schemaVersion: 1,
122
+ lastWrittenAtUnixMs: nowUnixMs,
123
+ seeds: sortSeedRecords([
124
+ ...index.seeds,
125
+ {
126
+ name: seedName,
127
+ kind: "imported",
128
+ walletRootId: options.walletRootId,
129
+ createdAtUnixMs: nowUnixMs,
130
+ restoredAtUnixMs: nowUnixMs,
131
+ },
132
+ ]),
133
+ };
134
+ await saveWalletSeedIndex(options.paths, nextIndex);
135
+ return nextIndex;
136
+ }
137
+ export async function removeWalletSeedRecord(options) {
138
+ const nowUnixMs = options.nowUnixMs ?? Date.now();
139
+ const seedName = normalizeWalletSeedName(options.seedName);
140
+ const index = await loadWalletSeedIndex({
141
+ paths: options.paths,
142
+ nowUnixMs,
143
+ });
144
+ const nextIndex = {
145
+ schemaVersion: 1,
146
+ lastWrittenAtUnixMs: nowUnixMs,
147
+ seeds: sortSeedRecords(index.seeds.filter((seed) => seed.name !== seedName)),
148
+ };
149
+ await saveWalletSeedIndex(options.paths, nextIndex);
150
+ return nextIndex;
151
+ }
@@ -4,6 +4,7 @@ import type { RpcTransaction } from "../../bitcoind/types.js";
4
4
  import type { WalletPrompter } from "../lifecycle.js";
5
5
  import { type WalletRuntimePaths } from "../runtime.js";
6
6
  import { type WalletSecretProvider } from "../state/provider.js";
7
+ import type { ProactiveFamilyStateRecord } from "../types.js";
7
8
  import { openWalletReadContext } from "../read/index.js";
8
9
  import { type WalletMutationRpcClient } from "./common.js";
9
10
  interface WalletAnchorRpcClient extends WalletMutationRpcClient {
@@ -36,5 +37,26 @@ export interface AnchorDomainResult {
36
37
  status: "live" | "confirmed";
37
38
  reusedExisting: boolean;
38
39
  }
40
+ export interface ClearPendingAnchorOptions {
41
+ domainName: string;
42
+ dataDir: string;
43
+ databasePath: string;
44
+ provider?: WalletSecretProvider;
45
+ prompter: WalletPrompter;
46
+ assumeYes?: boolean;
47
+ nowUnixMs?: number;
48
+ paths?: WalletRuntimePaths;
49
+ openReadContext?: typeof openWalletReadContext;
50
+ attachService?: typeof attachOrStartManagedBitcoindService;
51
+ rpcFactory?: (config: Parameters<typeof createRpcClient>[0]) => WalletAnchorRpcClient;
52
+ }
53
+ export interface ClearPendingAnchorResult {
54
+ domainName: string;
55
+ cleared: boolean;
56
+ previousFamilyStatus: ProactiveFamilyStateRecord["status"] | null;
57
+ previousFamilyStep: ProactiveFamilyStateRecord["currentStep"] | null;
58
+ releasedDedicatedIndex: number | null;
59
+ }
39
60
  export declare function anchorDomain(options: AnchorDomainOptions): Promise<AnchorDomainResult>;
61
+ export declare function clearPendingAnchor(options: ClearPendingAnchorOptions): Promise<ClearPendingAnchorResult>;
40
62
  export {};