@cogcoin/client 0.5.3 → 0.5.5

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 (70) hide show
  1. package/README.md +11 -3
  2. package/dist/app-paths.d.ts +2 -0
  3. package/dist/app-paths.js +4 -0
  4. package/dist/art/wallet.txt +10 -0
  5. package/dist/bitcoind/bootstrap/chunk-manifest.d.ts +14 -0
  6. package/dist/bitcoind/bootstrap/chunk-manifest.js +85 -0
  7. package/dist/bitcoind/bootstrap/chunk-recovery.d.ts +4 -0
  8. package/dist/bitcoind/bootstrap/chunk-recovery.js +122 -0
  9. package/dist/bitcoind/bootstrap/constants.d.ts +3 -1
  10. package/dist/bitcoind/bootstrap/constants.js +3 -1
  11. package/dist/bitcoind/bootstrap/controller.d.ts +6 -1
  12. package/dist/bitcoind/bootstrap/controller.js +14 -7
  13. package/dist/bitcoind/bootstrap/default-snapshot-chunk-manifest.d.ts +2 -0
  14. package/dist/bitcoind/bootstrap/default-snapshot-chunk-manifest.js +2309 -0
  15. package/dist/bitcoind/bootstrap/download.js +177 -83
  16. package/dist/bitcoind/bootstrap/headers.d.ts +4 -2
  17. package/dist/bitcoind/bootstrap/headers.js +29 -4
  18. package/dist/bitcoind/bootstrap/state.d.ts +11 -1
  19. package/dist/bitcoind/bootstrap/state.js +50 -23
  20. package/dist/bitcoind/bootstrap/types.d.ts +12 -1
  21. package/dist/bitcoind/client/internal-types.d.ts +1 -0
  22. package/dist/bitcoind/client/managed-client.js +27 -13
  23. package/dist/bitcoind/client/sync-engine.js +42 -5
  24. package/dist/bitcoind/errors.js +9 -0
  25. package/dist/bitcoind/indexer-daemon.d.ts +9 -0
  26. package/dist/bitcoind/indexer-daemon.js +51 -14
  27. package/dist/bitcoind/service.d.ts +9 -0
  28. package/dist/bitcoind/service.js +65 -24
  29. package/dist/bitcoind/testing.d.ts +2 -2
  30. package/dist/bitcoind/testing.js +2 -2
  31. package/dist/bitcoind/types.d.ts +9 -0
  32. package/dist/cli/commands/service-runtime.d.ts +2 -0
  33. package/dist/cli/commands/service-runtime.js +432 -0
  34. package/dist/cli/commands/wallet-admin.js +227 -132
  35. package/dist/cli/commands/wallet-mutation.js +597 -580
  36. package/dist/cli/context.js +23 -1
  37. package/dist/cli/mutation-json.d.ts +17 -1
  38. package/dist/cli/mutation-json.js +42 -0
  39. package/dist/cli/output.js +113 -2
  40. package/dist/cli/parse.d.ts +1 -1
  41. package/dist/cli/parse.js +65 -0
  42. package/dist/cli/preview-json.d.ts +19 -1
  43. package/dist/cli/preview-json.js +31 -0
  44. package/dist/cli/prompt.js +40 -12
  45. package/dist/cli/runner.js +12 -0
  46. package/dist/cli/signals.d.ts +1 -0
  47. package/dist/cli/signals.js +44 -0
  48. package/dist/cli/types.d.ts +24 -2
  49. package/dist/cli/types.js +6 -0
  50. package/dist/cli/wallet-format.js +3 -0
  51. package/dist/cli/workflow-hints.d.ts +1 -0
  52. package/dist/cli/workflow-hints.js +3 -0
  53. package/dist/wallet/fs/lock.d.ts +2 -0
  54. package/dist/wallet/fs/lock.js +32 -0
  55. package/dist/wallet/lifecycle.d.ts +19 -1
  56. package/dist/wallet/lifecycle.js +315 -8
  57. package/dist/wallet/material.d.ts +2 -0
  58. package/dist/wallet/material.js +8 -1
  59. package/dist/wallet/mnemonic-art.d.ts +2 -0
  60. package/dist/wallet/mnemonic-art.js +54 -0
  61. package/dist/wallet/reset.d.ts +61 -0
  62. package/dist/wallet/reset.js +781 -0
  63. package/dist/wallet/runtime.d.ts +2 -0
  64. package/dist/wallet/runtime.js +2 -0
  65. package/dist/wallet/state/pending-init.d.ts +24 -0
  66. package/dist/wallet/state/pending-init.js +59 -0
  67. package/dist/wallet/state/provider.d.ts +1 -0
  68. package/dist/wallet/state/provider.js +7 -1
  69. package/dist/wallet/types.d.ts +8 -0
  70. package/package.json +6 -4
@@ -8,6 +8,7 @@ import { formatBuyBuyerSummary, formatBuySellerSummary, formatBuySettlementSumma
8
8
  import { createTerminalPrompter } from "../prompt.js";
9
9
  import { writeHandledCliError } from "../output.js";
10
10
  import { getAnchorNextSteps, getRegisterNextSteps, } from "../workflow-hints.js";
11
+ import { createOwnedLockCleanupSignalWatcher, waitForCompletionOrStop, } from "../signals.js";
11
12
  function createFieldValueSource(parsed) {
12
13
  if (parsed.endpointText !== null) {
13
14
  return { kind: "text", value: parsed.endpointText };
@@ -30,645 +31,658 @@ function createCommandPrompter(parsed, context) {
30
31
  : context.createPrompter();
31
32
  }
32
33
  export async function runWalletMutationCommand(parsed, context) {
34
+ const runtimePaths = context.resolveWalletRuntimePaths();
35
+ const stopWatcher = createOwnedLockCleanupSignalWatcher(context.signalSource, context.forceExit, [
36
+ runtimePaths.walletControlLockPath,
37
+ runtimePaths.miningControlLockPath,
38
+ runtimePaths.bitcoindLockPath,
39
+ runtimePaths.indexerDaemonLockPath,
40
+ ]);
33
41
  try {
34
- if (!isWalletMutationCommand(parsed.command)) {
35
- writeLine(context.stderr, `wallet mutation command not implemented: ${parsed.command}`);
36
- return 1;
37
- }
38
- const dataDir = parsed.dataDir ?? context.resolveDefaultBitcoindDataDir();
39
- const dbPath = parsed.dbPath ?? context.resolveDefaultClientDatabasePath();
40
- const prompter = createCommandPrompter(parsed, context);
41
- if (isAnchorMutationCommand(parsed.command)) {
42
- const result = await context.anchorDomain({
43
- domainName: parsed.args[0],
44
- foundingMessageText: parsed.anchorMessage,
45
- dataDir,
46
- databasePath: dbPath,
47
- provider: context.walletSecretProvider,
48
- prompter,
49
- });
50
- const nextSteps = getAnchorNextSteps(result.domainName);
51
- return writeMutationCommandSuccess(parsed, context, {
52
- data: buildAnchorMutationData(result, {
53
- foundingMessageText: parsed.anchorMessage,
54
- }),
55
- previewData: buildAnchorPreviewData(result, {
42
+ const outcome = await waitForCompletionOrStop((async () => {
43
+ if (!isWalletMutationCommand(parsed.command)) {
44
+ writeLine(context.stderr, `wallet mutation command not implemented: ${parsed.command}`);
45
+ return 1;
46
+ }
47
+ const dataDir = parsed.dataDir ?? context.resolveDefaultBitcoindDataDir();
48
+ const dbPath = parsed.dbPath ?? context.resolveDefaultClientDatabasePath();
49
+ const prompter = createCommandPrompter(parsed, context);
50
+ if (isAnchorMutationCommand(parsed.command)) {
51
+ const result = await context.anchorDomain({
52
+ domainName: parsed.args[0],
56
53
  foundingMessageText: parsed.anchorMessage,
57
- }),
58
- reusedExisting: result.reusedExisting,
59
- reusedMessage: "The existing anchor family was reconciled instead of creating a duplicate.",
60
- nextSteps: workflowMutationNextSteps(nextSteps),
61
- text: {
62
- heading: "Anchor family submitted.",
63
- fields: [
64
- { label: "Domain", value: result.domainName },
65
- { label: "Dedicated index", value: String(result.dedicatedIndex) },
66
- { label: "Status", value: result.status },
67
- { label: "Tx1", value: result.tx1Txid },
68
- { label: "Tx2", value: result.tx2Txid },
69
- ],
70
- },
71
- });
72
- }
73
- if (isRegisterMutationCommand(parsed.command)) {
74
- const result = await context.registerDomain({
75
- domainName: parsed.args[0],
76
- dataDir,
77
- databasePath: dbPath,
78
- forceRace: parsed.forceRace,
79
- fromIdentity: parsed.fromIdentity,
80
- provider: context.walletSecretProvider,
81
- prompter,
82
- assumeYes: parsed.assumeYes,
83
- });
84
- const nextSteps = getRegisterNextSteps(result.domainName, result.registerKind);
85
- return writeMutationCommandSuccess(parsed, context, {
86
- data: buildRegisterMutationData(result, {
87
- forceRace: parsed.forceRace,
88
- fromIdentity: parsed.fromIdentity,
89
- }),
90
- previewData: buildRegisterPreviewData(result, {
54
+ dataDir,
55
+ databasePath: dbPath,
56
+ provider: context.walletSecretProvider,
57
+ prompter,
58
+ });
59
+ const nextSteps = getAnchorNextSteps(result.domainName);
60
+ return writeMutationCommandSuccess(parsed, context, {
61
+ data: buildAnchorMutationData(result, {
62
+ foundingMessageText: parsed.anchorMessage,
63
+ }),
64
+ previewData: buildAnchorPreviewData(result, {
65
+ foundingMessageText: parsed.anchorMessage,
66
+ }),
67
+ reusedExisting: result.reusedExisting,
68
+ reusedMessage: "The existing anchor family was reconciled instead of creating a duplicate.",
69
+ nextSteps: workflowMutationNextSteps(nextSteps),
70
+ text: {
71
+ heading: "Anchor family submitted.",
72
+ fields: [
73
+ { label: "Domain", value: result.domainName },
74
+ { label: "Dedicated index", value: String(result.dedicatedIndex) },
75
+ { label: "Status", value: result.status },
76
+ { label: "Tx1", value: result.tx1Txid },
77
+ { label: "Tx2", value: result.tx2Txid },
78
+ ],
79
+ },
80
+ });
81
+ }
82
+ if (isRegisterMutationCommand(parsed.command)) {
83
+ const result = await context.registerDomain({
84
+ domainName: parsed.args[0],
85
+ dataDir,
86
+ databasePath: dbPath,
91
87
  forceRace: parsed.forceRace,
92
88
  fromIdentity: parsed.fromIdentity,
93
- }),
94
- reusedExisting: result.reusedExisting,
95
- reusedMessage: "The existing pending registration was reconciled instead of creating a duplicate.",
96
- nextSteps: workflowMutationNextSteps(nextSteps),
97
- text: {
98
- heading: "Registration submitted.",
99
- fields: [
100
- { label: "Domain", value: result.domainName },
101
- { label: "Path", value: result.resolved.path },
102
- { label: "Parent", value: result.resolved.parentDomainName ?? "", when: result.resolved.parentDomainName !== null },
103
- { label: "Sender", value: formatRegisterSenderSummary(result) },
104
- { label: "Economic effect", value: formatRegisterEconomicEffect(result) },
105
- { label: "Status", value: result.status },
106
- { label: "Txid", value: result.txid },
107
- ],
108
- },
109
- });
110
- }
111
- if (isTransferMutationCommand(parsed.command)) {
112
- const result = await context.transferDomain({
113
- domainName: parsed.args[0],
114
- target: parsed.transferTarget,
115
- dataDir,
116
- databasePath: dbPath,
117
- provider: context.walletSecretProvider,
118
- prompter,
119
- assumeYes: parsed.assumeYes,
120
- });
121
- return writeMutationCommandSuccess(parsed, context, {
122
- data: buildDomainMarketMutationData(result, {
123
- commandKind: "transfer",
124
- }),
125
- previewData: buildDomainMarketPreviewData(result, {
126
- commandKind: "transfer",
127
- }),
128
- reusedExisting: result.reusedExisting,
129
- reusedMessage: "The existing pending transfer was reconciled instead of creating a duplicate.",
130
- nextSteps: commandMutationNextSteps(`cogcoin show ${result.domainName}`),
131
- text: {
132
- heading: "Transfer submitted.",
133
- fields: [
134
- { label: "Domain", value: result.domainName },
135
- { label: "Sender", value: formatDomainMarketSenderSummary(result) },
136
- { label: "Recipient", value: formatDomainMarketRecipientSummary(result) },
137
- { label: "Economic effect", value: formatDomainMarketEconomicEffect(result) },
138
- { label: "Status", value: result.status },
139
- { label: "Txid", value: result.txid },
140
- ],
141
- },
142
- });
143
- }
144
- if (isSellOrUnsellMutationCommand(parsed.command)) {
145
- const listedPriceCogtoshi = isUnsellMutationCommand(parsed.command)
146
- ? 0n
147
- : parseCogAmountToCogtoshi(parsed.args[1]);
148
- const result = await context.sellDomain({
149
- domainName: parsed.args[0],
150
- listedPriceCogtoshi,
151
- dataDir,
152
- databasePath: dbPath,
153
- provider: context.walletSecretProvider,
154
- prompter,
155
- assumeYes: parsed.assumeYes,
156
- });
157
- return writeMutationCommandSuccess(parsed, context, {
158
- data: buildDomainMarketMutationData(result, {
159
- commandKind: result.listedPriceCogtoshi === 0n ? "unsell" : "sell",
160
- }),
161
- previewData: buildDomainMarketPreviewData(result, {
162
- commandKind: result.listedPriceCogtoshi === 0n ? "unsell" : "sell",
163
- }),
164
- reusedExisting: result.reusedExisting,
165
- reusedMessage: "The existing pending listing mutation was reconciled instead of creating a duplicate.",
166
- nextSteps: commandMutationNextSteps(`cogcoin show ${result.domainName}`),
167
- text: {
168
- heading: result.listedPriceCogtoshi === 0n ? "Listing cancellation submitted." : "Listing submitted.",
169
- fields: [
170
- { label: "Domain", value: result.domainName },
171
- { label: "Sender", value: formatDomainMarketSenderSummary(result) },
172
- { label: "Price", value: `${result.listedPriceCogtoshi?.toString() ?? "0"} cogtoshi` },
173
- { label: "Economic effect", value: formatDomainMarketEconomicEffect(result) },
174
- { label: "Status", value: result.status },
175
- { label: "Txid", value: result.txid },
176
- ],
177
- },
178
- });
179
- }
180
- if (parsed.command === "domain-endpoint-set" || parsed.command === "domain-endpoint-clear") {
181
- const result = parsed.command === "domain-endpoint-set"
182
- ? await context.setDomainEndpoint({
89
+ provider: context.walletSecretProvider,
90
+ prompter,
91
+ assumeYes: parsed.assumeYes,
92
+ });
93
+ const nextSteps = getRegisterNextSteps(result.domainName, result.registerKind);
94
+ return writeMutationCommandSuccess(parsed, context, {
95
+ data: buildRegisterMutationData(result, {
96
+ forceRace: parsed.forceRace,
97
+ fromIdentity: parsed.fromIdentity,
98
+ }),
99
+ previewData: buildRegisterPreviewData(result, {
100
+ forceRace: parsed.forceRace,
101
+ fromIdentity: parsed.fromIdentity,
102
+ }),
103
+ reusedExisting: result.reusedExisting,
104
+ reusedMessage: "The existing pending registration was reconciled instead of creating a duplicate.",
105
+ nextSteps: workflowMutationNextSteps(nextSteps),
106
+ text: {
107
+ heading: "Registration submitted.",
108
+ fields: [
109
+ { label: "Domain", value: result.domainName },
110
+ { label: "Path", value: result.resolved.path },
111
+ { label: "Parent", value: result.resolved.parentDomainName ?? "", when: result.resolved.parentDomainName !== null },
112
+ { label: "Sender", value: formatRegisterSenderSummary(result) },
113
+ { label: "Economic effect", value: formatRegisterEconomicEffect(result) },
114
+ { label: "Status", value: result.status },
115
+ { label: "Txid", value: result.txid },
116
+ ],
117
+ },
118
+ });
119
+ }
120
+ if (isTransferMutationCommand(parsed.command)) {
121
+ const result = await context.transferDomain({
183
122
  domainName: parsed.args[0],
184
- source: parsed.endpointText !== null
185
- ? { kind: "text", value: parsed.endpointText }
186
- : parsed.endpointJson !== null
187
- ? { kind: "json", value: parsed.endpointJson }
188
- : { kind: "bytes", value: parsed.endpointBytes },
123
+ target: parsed.transferTarget,
189
124
  dataDir,
190
125
  databasePath: dbPath,
191
126
  provider: context.walletSecretProvider,
192
127
  prompter,
193
128
  assumeYes: parsed.assumeYes,
194
- })
195
- : await context.clearDomainEndpoint({
129
+ });
130
+ return writeMutationCommandSuccess(parsed, context, {
131
+ data: buildDomainMarketMutationData(result, {
132
+ commandKind: "transfer",
133
+ }),
134
+ previewData: buildDomainMarketPreviewData(result, {
135
+ commandKind: "transfer",
136
+ }),
137
+ reusedExisting: result.reusedExisting,
138
+ reusedMessage: "The existing pending transfer was reconciled instead of creating a duplicate.",
139
+ nextSteps: commandMutationNextSteps(`cogcoin show ${result.domainName}`),
140
+ text: {
141
+ heading: "Transfer submitted.",
142
+ fields: [
143
+ { label: "Domain", value: result.domainName },
144
+ { label: "Sender", value: formatDomainMarketSenderSummary(result) },
145
+ { label: "Recipient", value: formatDomainMarketRecipientSummary(result) },
146
+ { label: "Economic effect", value: formatDomainMarketEconomicEffect(result) },
147
+ { label: "Status", value: result.status },
148
+ { label: "Txid", value: result.txid },
149
+ ],
150
+ },
151
+ });
152
+ }
153
+ if (isSellOrUnsellMutationCommand(parsed.command)) {
154
+ const listedPriceCogtoshi = isUnsellMutationCommand(parsed.command)
155
+ ? 0n
156
+ : parseCogAmountToCogtoshi(parsed.args[1]);
157
+ const result = await context.sellDomain({
196
158
  domainName: parsed.args[0],
159
+ listedPriceCogtoshi,
197
160
  dataDir,
198
161
  databasePath: dbPath,
199
162
  provider: context.walletSecretProvider,
200
163
  prompter,
201
164
  assumeYes: parsed.assumeYes,
202
165
  });
203
- return writeMutationCommandSuccess(parsed, context, {
204
- data: buildDomainAdminMutationData(result, {
205
- commandKind: parsed.command,
206
- }),
207
- previewData: buildDomainAdminPreviewData(result, {
208
- commandKind: parsed.command,
209
- }),
210
- reusedExisting: result.reusedExisting,
211
- reusedMessage: "The existing pending endpoint mutation was reconciled instead of creating a duplicate.",
212
- nextSteps: commandMutationNextSteps(`cogcoin show ${result.domainName}`),
213
- text: {
214
- heading: parsed.command === "domain-endpoint-set" ? "Endpoint update submitted." : "Endpoint clear submitted.",
215
- fields: [
216
- { label: "Domain", value: result.domainName },
217
- { label: "Sender", value: formatDomainAdminSenderSummary(result) },
218
- { label: "Payload", value: formatDomainAdminPayloadSummary(result) },
219
- { label: "Effect", value: formatDomainAdminEffect(result) },
220
- { label: "Status", value: result.status },
221
- { label: "Txid", value: result.txid },
222
- ],
223
- },
224
- });
225
- }
226
- if (parsed.command === "domain-delegate-set" || parsed.command === "domain-delegate-clear") {
227
- const result = parsed.command === "domain-delegate-set"
228
- ? await context.setDomainDelegate({
166
+ return writeMutationCommandSuccess(parsed, context, {
167
+ data: buildDomainMarketMutationData(result, {
168
+ commandKind: result.listedPriceCogtoshi === 0n ? "unsell" : "sell",
169
+ }),
170
+ previewData: buildDomainMarketPreviewData(result, {
171
+ commandKind: result.listedPriceCogtoshi === 0n ? "unsell" : "sell",
172
+ }),
173
+ reusedExisting: result.reusedExisting,
174
+ reusedMessage: "The existing pending listing mutation was reconciled instead of creating a duplicate.",
175
+ nextSteps: commandMutationNextSteps(`cogcoin show ${result.domainName}`),
176
+ text: {
177
+ heading: result.listedPriceCogtoshi === 0n ? "Listing cancellation submitted." : "Listing submitted.",
178
+ fields: [
179
+ { label: "Domain", value: result.domainName },
180
+ { label: "Sender", value: formatDomainMarketSenderSummary(result) },
181
+ { label: "Price", value: `${result.listedPriceCogtoshi?.toString() ?? "0"} cogtoshi` },
182
+ { label: "Economic effect", value: formatDomainMarketEconomicEffect(result) },
183
+ { label: "Status", value: result.status },
184
+ { label: "Txid", value: result.txid },
185
+ ],
186
+ },
187
+ });
188
+ }
189
+ if (parsed.command === "domain-endpoint-set" || parsed.command === "domain-endpoint-clear") {
190
+ const result = parsed.command === "domain-endpoint-set"
191
+ ? await context.setDomainEndpoint({
192
+ domainName: parsed.args[0],
193
+ source: parsed.endpointText !== null
194
+ ? { kind: "text", value: parsed.endpointText }
195
+ : parsed.endpointJson !== null
196
+ ? { kind: "json", value: parsed.endpointJson }
197
+ : { kind: "bytes", value: parsed.endpointBytes },
198
+ dataDir,
199
+ databasePath: dbPath,
200
+ provider: context.walletSecretProvider,
201
+ prompter,
202
+ assumeYes: parsed.assumeYes,
203
+ })
204
+ : await context.clearDomainEndpoint({
205
+ domainName: parsed.args[0],
206
+ dataDir,
207
+ databasePath: dbPath,
208
+ provider: context.walletSecretProvider,
209
+ prompter,
210
+ assumeYes: parsed.assumeYes,
211
+ });
212
+ return writeMutationCommandSuccess(parsed, context, {
213
+ data: buildDomainAdminMutationData(result, {
214
+ commandKind: parsed.command,
215
+ }),
216
+ previewData: buildDomainAdminPreviewData(result, {
217
+ commandKind: parsed.command,
218
+ }),
219
+ reusedExisting: result.reusedExisting,
220
+ reusedMessage: "The existing pending endpoint mutation was reconciled instead of creating a duplicate.",
221
+ nextSteps: commandMutationNextSteps(`cogcoin show ${result.domainName}`),
222
+ text: {
223
+ heading: parsed.command === "domain-endpoint-set" ? "Endpoint update submitted." : "Endpoint clear submitted.",
224
+ fields: [
225
+ { label: "Domain", value: result.domainName },
226
+ { label: "Sender", value: formatDomainAdminSenderSummary(result) },
227
+ { label: "Payload", value: formatDomainAdminPayloadSummary(result) },
228
+ { label: "Effect", value: formatDomainAdminEffect(result) },
229
+ { label: "Status", value: result.status },
230
+ { label: "Txid", value: result.txid },
231
+ ],
232
+ },
233
+ });
234
+ }
235
+ if (parsed.command === "domain-delegate-set" || parsed.command === "domain-delegate-clear") {
236
+ const result = parsed.command === "domain-delegate-set"
237
+ ? await context.setDomainDelegate({
238
+ domainName: parsed.args[0],
239
+ target: parsed.args[1],
240
+ dataDir,
241
+ databasePath: dbPath,
242
+ provider: context.walletSecretProvider,
243
+ prompter,
244
+ assumeYes: parsed.assumeYes,
245
+ })
246
+ : await context.clearDomainDelegate({
247
+ domainName: parsed.args[0],
248
+ dataDir,
249
+ databasePath: dbPath,
250
+ provider: context.walletSecretProvider,
251
+ prompter,
252
+ assumeYes: parsed.assumeYes,
253
+ });
254
+ return writeMutationCommandSuccess(parsed, context, {
255
+ data: buildDomainAdminMutationData(result, {
256
+ commandKind: parsed.command,
257
+ }),
258
+ previewData: buildDomainAdminPreviewData(result, {
259
+ commandKind: parsed.command,
260
+ }),
261
+ reusedExisting: result.reusedExisting,
262
+ reusedMessage: "The existing pending delegate mutation was reconciled instead of creating a duplicate.",
263
+ nextSteps: commandMutationNextSteps(`cogcoin show ${result.domainName}`),
264
+ text: {
265
+ heading: parsed.command === "domain-delegate-set" ? "Delegate update submitted." : "Delegate clear submitted.",
266
+ fields: [
267
+ { label: "Domain", value: result.domainName },
268
+ { label: "Sender", value: formatDomainAdminSenderSummary(result) },
269
+ { label: "Target", value: formatDomainAdminTargetSummary(result) },
270
+ { label: "Effect", value: formatDomainAdminEffect(result) },
271
+ { label: "Status", value: result.status },
272
+ { label: "Txid", value: result.txid },
273
+ ],
274
+ },
275
+ });
276
+ }
277
+ if (parsed.command === "domain-miner-set" || parsed.command === "domain-miner-clear") {
278
+ const result = parsed.command === "domain-miner-set"
279
+ ? await context.setDomainMiner({
280
+ domainName: parsed.args[0],
281
+ target: parsed.args[1],
282
+ dataDir,
283
+ databasePath: dbPath,
284
+ provider: context.walletSecretProvider,
285
+ prompter,
286
+ assumeYes: parsed.assumeYes,
287
+ })
288
+ : await context.clearDomainMiner({
289
+ domainName: parsed.args[0],
290
+ dataDir,
291
+ databasePath: dbPath,
292
+ provider: context.walletSecretProvider,
293
+ prompter,
294
+ assumeYes: parsed.assumeYes,
295
+ });
296
+ return writeMutationCommandSuccess(parsed, context, {
297
+ data: buildDomainAdminMutationData(result, {
298
+ commandKind: parsed.command,
299
+ }),
300
+ previewData: buildDomainAdminPreviewData(result, {
301
+ commandKind: parsed.command,
302
+ }),
303
+ reusedExisting: result.reusedExisting,
304
+ reusedMessage: "The existing pending miner mutation was reconciled instead of creating a duplicate.",
305
+ nextSteps: commandMutationNextSteps(`cogcoin show ${result.domainName}`),
306
+ text: {
307
+ heading: parsed.command === "domain-miner-set" ? "Miner update submitted." : "Miner clear submitted.",
308
+ fields: [
309
+ { label: "Domain", value: result.domainName },
310
+ { label: "Sender", value: formatDomainAdminSenderSummary(result) },
311
+ { label: "Target", value: formatDomainAdminTargetSummary(result) },
312
+ { label: "Effect", value: formatDomainAdminEffect(result) },
313
+ { label: "Status", value: result.status },
314
+ { label: "Txid", value: result.txid },
315
+ ],
316
+ },
317
+ });
318
+ }
319
+ if (parsed.command === "domain-canonical") {
320
+ const result = await context.setDomainCanonical({
229
321
  domainName: parsed.args[0],
230
- target: parsed.args[1],
231
322
  dataDir,
232
323
  databasePath: dbPath,
233
324
  provider: context.walletSecretProvider,
234
325
  prompter,
235
326
  assumeYes: parsed.assumeYes,
236
- })
237
- : await context.clearDomainDelegate({
327
+ });
328
+ return writeMutationCommandSuccess(parsed, context, {
329
+ data: buildDomainAdminMutationData(result, {
330
+ commandKind: "domain-canonical",
331
+ }),
332
+ previewData: buildDomainAdminPreviewData(result, {
333
+ commandKind: "domain-canonical",
334
+ }),
335
+ reusedExisting: result.reusedExisting,
336
+ reusedMessage: "The existing pending canonical mutation was reconciled instead of creating a duplicate.",
337
+ nextSteps: commandMutationNextSteps(`cogcoin show ${result.domainName}`),
338
+ text: {
339
+ heading: "Canonical update submitted.",
340
+ fields: [
341
+ { label: "Domain", value: result.domainName },
342
+ { label: "Sender", value: formatDomainAdminSenderSummary(result) },
343
+ { label: "Effect", value: formatDomainAdminEffect(result) },
344
+ { label: "Status", value: result.status },
345
+ { label: "Txid", value: result.txid },
346
+ ],
347
+ },
348
+ });
349
+ }
350
+ if (parsed.command === "field-create") {
351
+ const result = await context.createField({
238
352
  domainName: parsed.args[0],
353
+ fieldName: parsed.args[1],
354
+ permanent: parsed.fieldPermanent,
355
+ source: (parsed.endpointText !== null
356
+ || parsed.endpointJson !== null
357
+ || parsed.endpointBytes !== null
358
+ || parsed.fieldFormat !== null)
359
+ ? createFieldValueSource(parsed)
360
+ : null,
239
361
  dataDir,
240
362
  databasePath: dbPath,
241
363
  provider: context.walletSecretProvider,
242
364
  prompter,
243
365
  assumeYes: parsed.assumeYes,
244
366
  });
245
- return writeMutationCommandSuccess(parsed, context, {
246
- data: buildDomainAdminMutationData(result, {
247
- commandKind: parsed.command,
248
- }),
249
- previewData: buildDomainAdminPreviewData(result, {
250
- commandKind: parsed.command,
251
- }),
252
- reusedExisting: result.reusedExisting,
253
- reusedMessage: "The existing pending delegate mutation was reconciled instead of creating a duplicate.",
254
- nextSteps: commandMutationNextSteps(`cogcoin show ${result.domainName}`),
255
- text: {
256
- heading: parsed.command === "domain-delegate-set" ? "Delegate update submitted." : "Delegate clear submitted.",
257
- fields: [
258
- { label: "Domain", value: result.domainName },
259
- { label: "Sender", value: formatDomainAdminSenderSummary(result) },
260
- { label: "Target", value: formatDomainAdminTargetSummary(result) },
261
- { label: "Effect", value: formatDomainAdminEffect(result) },
262
- { label: "Status", value: result.status },
263
- { label: "Txid", value: result.txid },
264
- ],
265
- },
266
- });
267
- }
268
- if (parsed.command === "domain-miner-set" || parsed.command === "domain-miner-clear") {
269
- const result = parsed.command === "domain-miner-set"
270
- ? await context.setDomainMiner({
367
+ return writeMutationCommandSuccess(parsed, context, {
368
+ data: buildFieldMutationData(result),
369
+ previewData: buildFieldPreviewData(result),
370
+ reusedExisting: result.reusedExisting,
371
+ reusedMessage: result.family
372
+ ? "The existing pending field family was reconciled instead of creating a duplicate."
373
+ : "The existing pending field creation was reconciled instead of creating a duplicate.",
374
+ nextSteps: commandMutationNextSteps(`cogcoin field show ${result.domainName} ${result.fieldName}`),
375
+ text: {
376
+ heading: result.family ? "Field create+write family submitted." : "Field creation submitted.",
377
+ fields: [
378
+ { label: "Domain", value: result.domainName },
379
+ { label: "Field", value: result.fieldName },
380
+ { label: "Sender", value: formatFieldSenderSummary(result) },
381
+ { label: "Path", value: formatFieldPath(result) },
382
+ { label: "Value", value: formatFieldValueSummary(result), when: result.resolved?.value !== null && result.resolved?.value !== undefined },
383
+ { label: "Effect", value: formatFieldEffect(result) },
384
+ { label: "Status", value: result.status },
385
+ { label: "Tx1", value: result.tx1Txid ?? "unknown", when: result.family },
386
+ { label: "Tx2", value: result.tx2Txid ?? "unknown", when: result.family },
387
+ { label: "Txid", value: result.txid, when: !result.family },
388
+ ],
389
+ },
390
+ });
391
+ }
392
+ if (parsed.command === "field-set") {
393
+ const result = await context.setField({
271
394
  domainName: parsed.args[0],
272
- target: parsed.args[1],
395
+ fieldName: parsed.args[1],
396
+ source: createFieldValueSource(parsed),
273
397
  dataDir,
274
398
  databasePath: dbPath,
275
399
  provider: context.walletSecretProvider,
276
400
  prompter,
277
401
  assumeYes: parsed.assumeYes,
278
- })
279
- : await context.clearDomainMiner({
402
+ });
403
+ return writeMutationCommandSuccess(parsed, context, {
404
+ data: buildFieldMutationData(result),
405
+ previewData: buildFieldPreviewData(result),
406
+ reusedExisting: result.reusedExisting,
407
+ reusedMessage: "The existing pending field update was reconciled instead of creating a duplicate.",
408
+ nextSteps: commandMutationNextSteps(`cogcoin field show ${result.domainName} ${result.fieldName}`),
409
+ text: {
410
+ heading: "Field update submitted.",
411
+ fields: [
412
+ { label: "Domain", value: result.domainName },
413
+ { label: "Field", value: result.fieldName },
414
+ { label: "Sender", value: formatFieldSenderSummary(result) },
415
+ { label: "Value", value: formatFieldValueSummary(result) },
416
+ { label: "Effect", value: formatFieldEffect(result) },
417
+ { label: "Status", value: result.status },
418
+ { label: "Txid", value: result.txid },
419
+ ],
420
+ },
421
+ });
422
+ }
423
+ if (parsed.command === "field-clear") {
424
+ const result = await context.clearField({
280
425
  domainName: parsed.args[0],
426
+ fieldName: parsed.args[1],
281
427
  dataDir,
282
428
  databasePath: dbPath,
283
429
  provider: context.walletSecretProvider,
284
430
  prompter,
285
431
  assumeYes: parsed.assumeYes,
286
432
  });
287
- return writeMutationCommandSuccess(parsed, context, {
288
- data: buildDomainAdminMutationData(result, {
289
- commandKind: parsed.command,
290
- }),
291
- previewData: buildDomainAdminPreviewData(result, {
292
- commandKind: parsed.command,
293
- }),
294
- reusedExisting: result.reusedExisting,
295
- reusedMessage: "The existing pending miner mutation was reconciled instead of creating a duplicate.",
296
- nextSteps: commandMutationNextSteps(`cogcoin show ${result.domainName}`),
297
- text: {
298
- heading: parsed.command === "domain-miner-set" ? "Miner update submitted." : "Miner clear submitted.",
299
- fields: [
300
- { label: "Domain", value: result.domainName },
301
- { label: "Sender", value: formatDomainAdminSenderSummary(result) },
302
- { label: "Target", value: formatDomainAdminTargetSummary(result) },
303
- { label: "Effect", value: formatDomainAdminEffect(result) },
304
- { label: "Status", value: result.status },
305
- { label: "Txid", value: result.txid },
306
- ],
307
- },
308
- });
309
- }
310
- if (parsed.command === "domain-canonical") {
311
- const result = await context.setDomainCanonical({
312
- domainName: parsed.args[0],
313
- dataDir,
314
- databasePath: dbPath,
315
- provider: context.walletSecretProvider,
316
- prompter,
317
- assumeYes: parsed.assumeYes,
318
- });
319
- return writeMutationCommandSuccess(parsed, context, {
320
- data: buildDomainAdminMutationData(result, {
321
- commandKind: "domain-canonical",
322
- }),
323
- previewData: buildDomainAdminPreviewData(result, {
324
- commandKind: "domain-canonical",
325
- }),
326
- reusedExisting: result.reusedExisting,
327
- reusedMessage: "The existing pending canonical mutation was reconciled instead of creating a duplicate.",
328
- nextSteps: commandMutationNextSteps(`cogcoin show ${result.domainName}`),
329
- text: {
330
- heading: "Canonical update submitted.",
331
- fields: [
332
- { label: "Domain", value: result.domainName },
333
- { label: "Sender", value: formatDomainAdminSenderSummary(result) },
334
- { label: "Effect", value: formatDomainAdminEffect(result) },
335
- { label: "Status", value: result.status },
336
- { label: "Txid", value: result.txid },
337
- ],
338
- },
339
- });
340
- }
341
- if (parsed.command === "field-create") {
342
- const result = await context.createField({
343
- domainName: parsed.args[0],
344
- fieldName: parsed.args[1],
345
- permanent: parsed.fieldPermanent,
346
- source: (parsed.endpointText !== null
347
- || parsed.endpointJson !== null
348
- || parsed.endpointBytes !== null
349
- || parsed.fieldFormat !== null)
350
- ? createFieldValueSource(parsed)
351
- : null,
352
- dataDir,
353
- databasePath: dbPath,
354
- provider: context.walletSecretProvider,
355
- prompter,
356
- assumeYes: parsed.assumeYes,
357
- });
358
- return writeMutationCommandSuccess(parsed, context, {
359
- data: buildFieldMutationData(result),
360
- previewData: buildFieldPreviewData(result),
361
- reusedExisting: result.reusedExisting,
362
- reusedMessage: result.family
363
- ? "The existing pending field family was reconciled instead of creating a duplicate."
364
- : "The existing pending field creation was reconciled instead of creating a duplicate.",
365
- nextSteps: commandMutationNextSteps(`cogcoin field show ${result.domainName} ${result.fieldName}`),
366
- text: {
367
- heading: result.family ? "Field create+write family submitted." : "Field creation submitted.",
368
- fields: [
369
- { label: "Domain", value: result.domainName },
370
- { label: "Field", value: result.fieldName },
371
- { label: "Sender", value: formatFieldSenderSummary(result) },
372
- { label: "Path", value: formatFieldPath(result) },
373
- { label: "Value", value: formatFieldValueSummary(result), when: result.resolved?.value !== null && result.resolved?.value !== undefined },
374
- { label: "Effect", value: formatFieldEffect(result) },
375
- { label: "Status", value: result.status },
376
- { label: "Tx1", value: result.tx1Txid ?? "unknown", when: result.family },
377
- { label: "Tx2", value: result.tx2Txid ?? "unknown", when: result.family },
378
- { label: "Txid", value: result.txid, when: !result.family },
379
- ],
380
- },
381
- });
382
- }
383
- if (parsed.command === "field-set") {
384
- const result = await context.setField({
385
- domainName: parsed.args[0],
386
- fieldName: parsed.args[1],
387
- source: createFieldValueSource(parsed),
388
- dataDir,
389
- databasePath: dbPath,
390
- provider: context.walletSecretProvider,
391
- prompter,
392
- assumeYes: parsed.assumeYes,
393
- });
394
- return writeMutationCommandSuccess(parsed, context, {
395
- data: buildFieldMutationData(result),
396
- previewData: buildFieldPreviewData(result),
397
- reusedExisting: result.reusedExisting,
398
- reusedMessage: "The existing pending field update was reconciled instead of creating a duplicate.",
399
- nextSteps: commandMutationNextSteps(`cogcoin field show ${result.domainName} ${result.fieldName}`),
400
- text: {
401
- heading: "Field update submitted.",
402
- fields: [
403
- { label: "Domain", value: result.domainName },
404
- { label: "Field", value: result.fieldName },
405
- { label: "Sender", value: formatFieldSenderSummary(result) },
406
- { label: "Value", value: formatFieldValueSummary(result) },
407
- { label: "Effect", value: formatFieldEffect(result) },
408
- { label: "Status", value: result.status },
409
- { label: "Txid", value: result.txid },
410
- ],
411
- },
412
- });
413
- }
414
- if (parsed.command === "field-clear") {
415
- const result = await context.clearField({
416
- domainName: parsed.args[0],
417
- fieldName: parsed.args[1],
418
- dataDir,
419
- databasePath: dbPath,
420
- provider: context.walletSecretProvider,
421
- prompter,
422
- assumeYes: parsed.assumeYes,
423
- });
424
- return writeMutationCommandSuccess(parsed, context, {
425
- data: buildFieldMutationData(result),
426
- previewData: buildFieldPreviewData(result),
427
- reusedExisting: result.reusedExisting,
428
- reusedMessage: "The existing pending field clear was reconciled instead of creating a duplicate.",
429
- nextSteps: commandMutationNextSteps(`cogcoin field show ${result.domainName} ${result.fieldName}`),
430
- text: {
431
- heading: "Field clear submitted.",
432
- fields: [
433
- { label: "Domain", value: result.domainName },
434
- { label: "Field", value: result.fieldName },
435
- { label: "Sender", value: formatFieldSenderSummary(result) },
436
- { label: "Effect", value: formatFieldEffect(result) },
437
- { label: "Status", value: result.status },
438
- { label: "Txid", value: result.txid },
439
- ],
440
- },
441
- });
442
- }
443
- if (isSendMutationCommand(parsed.command)) {
444
- const result = await context.sendCog({
445
- amountCogtoshi: parseCogAmountToCogtoshi(parsed.args[0]),
446
- target: parsed.transferTarget,
447
- fromIdentity: parsed.fromIdentity,
448
- dataDir,
449
- databasePath: dbPath,
450
- provider: context.walletSecretProvider,
451
- prompter,
452
- assumeYes: parsed.assumeYes,
453
- });
454
- return writeMutationCommandSuccess(parsed, context, {
455
- data: buildCogMutationData(result, {
456
- commandKind: "send",
457
- fromIdentity: parsed.fromIdentity,
458
- }),
459
- previewData: buildCogPreviewData(result, {
460
- commandKind: "send",
461
- fromIdentity: parsed.fromIdentity,
462
- }),
463
- reusedExisting: result.reusedExisting,
464
- reusedMessage: "The existing pending COG transfer was reconciled instead of creating a duplicate.",
465
- nextSteps: commandMutationNextSteps("cogcoin balance"),
466
- text: {
467
- heading: "COG transfer submitted.",
468
- fields: [
469
- { label: "Sender", value: formatCogSenderSummary(result) },
470
- { label: "Amount", value: `${result.amountCogtoshi?.toString() ?? "unknown"} cogtoshi` },
471
- { label: "Recipient", value: result.recipientScriptPubKeyHex === null || result.recipientScriptPubKeyHex === undefined ? "unknown" : `spk:${result.recipientScriptPubKeyHex}` },
472
- { label: "Status", value: result.status },
473
- { label: "Txid", value: result.txid },
474
- ],
475
- },
476
- });
477
- }
478
- if (parsed.command === "cog-lock") {
479
- const result = await context.lockCogToDomain({
480
- amountCogtoshi: parseCogAmountToCogtoshi(parsed.args[0]),
481
- recipientDomainName: parsed.lockRecipientDomain,
482
- fromIdentity: parsed.fromIdentity,
483
- timeoutBlocksOrDuration: parsed.unlockFor,
484
- timeoutHeight: parsed.untilHeight === null ? null : Number.parseInt(parsed.untilHeight, 10),
485
- conditionHex: parsed.conditionHex,
486
- dataDir,
487
- databasePath: dbPath,
488
- provider: context.walletSecretProvider,
489
- prompter,
490
- assumeYes: parsed.assumeYes,
491
- });
492
- return writeMutationCommandSuccess(parsed, context, {
493
- data: buildCogMutationData(result, {
494
- commandKind: "cog-lock",
433
+ return writeMutationCommandSuccess(parsed, context, {
434
+ data: buildFieldMutationData(result),
435
+ previewData: buildFieldPreviewData(result),
436
+ reusedExisting: result.reusedExisting,
437
+ reusedMessage: "The existing pending field clear was reconciled instead of creating a duplicate.",
438
+ nextSteps: commandMutationNextSteps(`cogcoin field show ${result.domainName} ${result.fieldName}`),
439
+ text: {
440
+ heading: "Field clear submitted.",
441
+ fields: [
442
+ { label: "Domain", value: result.domainName },
443
+ { label: "Field", value: result.fieldName },
444
+ { label: "Sender", value: formatFieldSenderSummary(result) },
445
+ { label: "Effect", value: formatFieldEffect(result) },
446
+ { label: "Status", value: result.status },
447
+ { label: "Txid", value: result.txid },
448
+ ],
449
+ },
450
+ });
451
+ }
452
+ if (isSendMutationCommand(parsed.command)) {
453
+ const result = await context.sendCog({
454
+ amountCogtoshi: parseCogAmountToCogtoshi(parsed.args[0]),
455
+ target: parsed.transferTarget,
495
456
  fromIdentity: parsed.fromIdentity,
496
- timeoutBlocksOrDuration: parsed.unlockFor,
497
- timeoutHeight: parsed.untilHeight,
498
- conditionHex: parsed.conditionHex,
499
- }),
500
- previewData: buildCogPreviewData(result, {
501
- commandKind: "cog-lock",
457
+ dataDir,
458
+ databasePath: dbPath,
459
+ provider: context.walletSecretProvider,
460
+ prompter,
461
+ assumeYes: parsed.assumeYes,
462
+ });
463
+ return writeMutationCommandSuccess(parsed, context, {
464
+ data: buildCogMutationData(result, {
465
+ commandKind: "send",
466
+ fromIdentity: parsed.fromIdentity,
467
+ }),
468
+ previewData: buildCogPreviewData(result, {
469
+ commandKind: "send",
470
+ fromIdentity: parsed.fromIdentity,
471
+ }),
472
+ reusedExisting: result.reusedExisting,
473
+ reusedMessage: "The existing pending COG transfer was reconciled instead of creating a duplicate.",
474
+ nextSteps: commandMutationNextSteps("cogcoin balance"),
475
+ text: {
476
+ heading: "COG transfer submitted.",
477
+ fields: [
478
+ { label: "Sender", value: formatCogSenderSummary(result) },
479
+ { label: "Amount", value: `${result.amountCogtoshi?.toString() ?? "unknown"} cogtoshi` },
480
+ { label: "Recipient", value: result.recipientScriptPubKeyHex === null || result.recipientScriptPubKeyHex === undefined ? "unknown" : `spk:${result.recipientScriptPubKeyHex}` },
481
+ { label: "Status", value: result.status },
482
+ { label: "Txid", value: result.txid },
483
+ ],
484
+ },
485
+ });
486
+ }
487
+ if (parsed.command === "cog-lock") {
488
+ const result = await context.lockCogToDomain({
489
+ amountCogtoshi: parseCogAmountToCogtoshi(parsed.args[0]),
490
+ recipientDomainName: parsed.lockRecipientDomain,
502
491
  fromIdentity: parsed.fromIdentity,
503
492
  timeoutBlocksOrDuration: parsed.unlockFor,
504
- timeoutHeight: parsed.untilHeight,
493
+ timeoutHeight: parsed.untilHeight === null ? null : Number.parseInt(parsed.untilHeight, 10),
505
494
  conditionHex: parsed.conditionHex,
506
- }),
507
- reusedExisting: result.reusedExisting,
508
- reusedMessage: "The existing pending lock was reconciled instead of creating a duplicate.",
509
- nextSteps: commandMutationNextSteps("cogcoin locks"),
510
- text: {
511
- heading: "COG lock submitted.",
512
- fields: [
513
- { label: "Sender", value: formatCogSenderSummary(result) },
514
- { label: "Amount", value: `${result.amountCogtoshi?.toString() ?? "unknown"} cogtoshi` },
515
- { label: "Recipient domain", value: result.recipientDomainName ?? "unknown" },
516
- { label: "Status", value: result.status },
517
- { label: "Txid", value: result.txid },
518
- ],
519
- },
520
- });
521
- }
522
- if (isClaimMutationCommand(parsed.command)) {
523
- const result = await context.claimCogLock({
524
- lockId: Number.parseInt(parsed.args[0], 10),
525
- preimageHex: parsed.preimageHex,
526
- dataDir,
527
- databasePath: dbPath,
528
- provider: context.walletSecretProvider,
529
- prompter,
530
- });
531
- return writeMutationCommandSuccess(parsed, context, {
532
- data: buildCogMutationData(result, {
533
- commandKind: "claim",
534
- fromIdentity: null,
535
- }),
536
- previewData: buildCogPreviewData(result, {
537
- commandKind: "claim",
538
- fromIdentity: null,
539
- }),
540
- reusedExisting: result.reusedExisting,
541
- reusedMessage: "The existing pending claim was reconciled instead of creating a duplicate.",
542
- nextSteps: commandMutationNextSteps("cogcoin locks --claimable"),
543
- text: {
544
- heading: "Lock claim submitted.",
545
- fields: [
546
- { label: "Lock", value: String(result.lockId ?? "unknown") },
547
- { label: "Path", value: formatCogClaimPath(result) },
548
- { label: "Sender", value: formatCogSenderSummary(result) },
549
- { label: "Amount", value: `${result.amountCogtoshi?.toString() ?? "unknown"} cogtoshi` },
550
- { label: "Status", value: result.status },
551
- { label: "Txid", value: result.txid },
552
- ],
553
- },
554
- });
555
- }
556
- if (isReclaimMutationCommand(parsed.command)) {
557
- const result = await context.reclaimCogLock({
558
- lockId: Number.parseInt(parsed.args[0], 10),
559
- dataDir,
560
- databasePath: dbPath,
561
- provider: context.walletSecretProvider,
562
- prompter,
563
- });
564
- return writeMutationCommandSuccess(parsed, context, {
565
- data: buildCogMutationData(result, {
566
- commandKind: "reclaim",
567
- fromIdentity: null,
568
- }),
569
- previewData: buildCogPreviewData(result, {
570
- commandKind: "reclaim",
571
- fromIdentity: null,
572
- }),
573
- reusedExisting: result.reusedExisting,
574
- reusedMessage: "The existing pending reclaim was reconciled instead of creating a duplicate.",
575
- nextSteps: commandMutationNextSteps("cogcoin locks --reclaimable"),
576
- text: {
577
- heading: "Lock reclaim submitted.",
578
- fields: [
579
- { label: "Lock", value: String(result.lockId ?? "unknown") },
580
- { label: "Path", value: formatCogClaimPath(result) },
581
- { label: "Sender", value: formatCogSenderSummary(result) },
582
- { label: "Amount", value: `${result.amountCogtoshi?.toString() ?? "unknown"} cogtoshi` },
583
- { label: "Status", value: result.status },
584
- { label: "Txid", value: result.txid },
585
- ],
586
- },
587
- });
588
- }
589
- if (isReputationMutationCommand(parsed.command)) {
590
- const result = parsed.command === "rep-give"
591
- ? await context.giveReputation({
592
- sourceDomainName: parsed.args[0],
593
- targetDomainName: parsed.args[1],
594
- amountCogtoshi: parseCogAmountToCogtoshi(parsed.args[2]),
595
- reviewText: parsed.reviewText,
596
495
  dataDir,
597
496
  databasePath: dbPath,
598
497
  provider: context.walletSecretProvider,
599
498
  prompter,
600
499
  assumeYes: parsed.assumeYes,
601
- })
602
- : await context.revokeReputation({
603
- sourceDomainName: parsed.args[0],
604
- targetDomainName: parsed.args[1],
605
- amountCogtoshi: parseCogAmountToCogtoshi(parsed.args[2]),
606
- reviewText: parsed.reviewText,
500
+ });
501
+ return writeMutationCommandSuccess(parsed, context, {
502
+ data: buildCogMutationData(result, {
503
+ commandKind: "cog-lock",
504
+ fromIdentity: parsed.fromIdentity,
505
+ timeoutBlocksOrDuration: parsed.unlockFor,
506
+ timeoutHeight: parsed.untilHeight,
507
+ conditionHex: parsed.conditionHex,
508
+ }),
509
+ previewData: buildCogPreviewData(result, {
510
+ commandKind: "cog-lock",
511
+ fromIdentity: parsed.fromIdentity,
512
+ timeoutBlocksOrDuration: parsed.unlockFor,
513
+ timeoutHeight: parsed.untilHeight,
514
+ conditionHex: parsed.conditionHex,
515
+ }),
516
+ reusedExisting: result.reusedExisting,
517
+ reusedMessage: "The existing pending lock was reconciled instead of creating a duplicate.",
518
+ nextSteps: commandMutationNextSteps("cogcoin locks"),
519
+ text: {
520
+ heading: "COG lock submitted.",
521
+ fields: [
522
+ { label: "Sender", value: formatCogSenderSummary(result) },
523
+ { label: "Amount", value: `${result.amountCogtoshi?.toString() ?? "unknown"} cogtoshi` },
524
+ { label: "Recipient domain", value: result.recipientDomainName ?? "unknown" },
525
+ { label: "Status", value: result.status },
526
+ { label: "Txid", value: result.txid },
527
+ ],
528
+ },
529
+ });
530
+ }
531
+ if (isClaimMutationCommand(parsed.command)) {
532
+ const result = await context.claimCogLock({
533
+ lockId: Number.parseInt(parsed.args[0], 10),
534
+ preimageHex: parsed.preimageHex,
607
535
  dataDir,
608
536
  databasePath: dbPath,
609
537
  provider: context.walletSecretProvider,
610
538
  prompter,
611
- assumeYes: parsed.assumeYes,
612
539
  });
613
- return writeMutationCommandSuccess(parsed, context, {
614
- data: buildReputationMutationData(result),
615
- previewData: buildReputationPreviewData(result),
616
- reusedExisting: result.reusedExisting,
617
- reusedMessage: "The existing pending reputation mutation was reconciled instead of creating a duplicate.",
618
- nextSteps: commandMutationNextSteps(`cogcoin show ${result.targetDomainName}`),
619
- text: {
620
- heading: parsed.command === "rep-give" ? "Reputation support submitted." : "Reputation revoke submitted.",
621
- fields: [
622
- { label: "Source domain", value: result.sourceDomainName },
623
- { label: "Target domain", value: result.targetDomainName },
624
- { label: "Sender", value: formatReputationSenderSummary(result) },
625
- { label: "Amount", value: `${result.amountCogtoshi.toString()} cogtoshi` },
626
- { label: "Review", value: formatReputationReviewSummary(result) },
627
- { label: "Effect", value: formatReputationEffect(result) },
628
- { label: "Status", value: result.status },
629
- { label: "Txid", value: result.txid },
630
- ],
631
- },
632
- });
633
- }
634
- if (isBuyMutationCommand(parsed.command)) {
635
- const result = await context.buyDomain({
636
- domainName: parsed.args[0],
637
- fromIdentity: parsed.fromIdentity,
638
- dataDir,
639
- databasePath: dbPath,
640
- provider: context.walletSecretProvider,
641
- prompter,
642
- assumeYes: parsed.assumeYes,
643
- });
644
- return writeMutationCommandSuccess(parsed, context, {
645
- data: buildDomainMarketMutationData(result, {
646
- commandKind: "buy",
647
- fromIdentity: parsed.fromIdentity,
648
- }),
649
- previewData: buildDomainMarketPreviewData(result, {
650
- commandKind: "buy",
540
+ return writeMutationCommandSuccess(parsed, context, {
541
+ data: buildCogMutationData(result, {
542
+ commandKind: "claim",
543
+ fromIdentity: null,
544
+ }),
545
+ previewData: buildCogPreviewData(result, {
546
+ commandKind: "claim",
547
+ fromIdentity: null,
548
+ }),
549
+ reusedExisting: result.reusedExisting,
550
+ reusedMessage: "The existing pending claim was reconciled instead of creating a duplicate.",
551
+ nextSteps: commandMutationNextSteps("cogcoin locks --claimable"),
552
+ text: {
553
+ heading: "Lock claim submitted.",
554
+ fields: [
555
+ { label: "Lock", value: String(result.lockId ?? "unknown") },
556
+ { label: "Path", value: formatCogClaimPath(result) },
557
+ { label: "Sender", value: formatCogSenderSummary(result) },
558
+ { label: "Amount", value: `${result.amountCogtoshi?.toString() ?? "unknown"} cogtoshi` },
559
+ { label: "Status", value: result.status },
560
+ { label: "Txid", value: result.txid },
561
+ ],
562
+ },
563
+ });
564
+ }
565
+ if (isReclaimMutationCommand(parsed.command)) {
566
+ const result = await context.reclaimCogLock({
567
+ lockId: Number.parseInt(parsed.args[0], 10),
568
+ dataDir,
569
+ databasePath: dbPath,
570
+ provider: context.walletSecretProvider,
571
+ prompter,
572
+ });
573
+ return writeMutationCommandSuccess(parsed, context, {
574
+ data: buildCogMutationData(result, {
575
+ commandKind: "reclaim",
576
+ fromIdentity: null,
577
+ }),
578
+ previewData: buildCogPreviewData(result, {
579
+ commandKind: "reclaim",
580
+ fromIdentity: null,
581
+ }),
582
+ reusedExisting: result.reusedExisting,
583
+ reusedMessage: "The existing pending reclaim was reconciled instead of creating a duplicate.",
584
+ nextSteps: commandMutationNextSteps("cogcoin locks --reclaimable"),
585
+ text: {
586
+ heading: "Lock reclaim submitted.",
587
+ fields: [
588
+ { label: "Lock", value: String(result.lockId ?? "unknown") },
589
+ { label: "Path", value: formatCogClaimPath(result) },
590
+ { label: "Sender", value: formatCogSenderSummary(result) },
591
+ { label: "Amount", value: `${result.amountCogtoshi?.toString() ?? "unknown"} cogtoshi` },
592
+ { label: "Status", value: result.status },
593
+ { label: "Txid", value: result.txid },
594
+ ],
595
+ },
596
+ });
597
+ }
598
+ if (isReputationMutationCommand(parsed.command)) {
599
+ const result = parsed.command === "rep-give"
600
+ ? await context.giveReputation({
601
+ sourceDomainName: parsed.args[0],
602
+ targetDomainName: parsed.args[1],
603
+ amountCogtoshi: parseCogAmountToCogtoshi(parsed.args[2]),
604
+ reviewText: parsed.reviewText,
605
+ dataDir,
606
+ databasePath: dbPath,
607
+ provider: context.walletSecretProvider,
608
+ prompter,
609
+ assumeYes: parsed.assumeYes,
610
+ })
611
+ : await context.revokeReputation({
612
+ sourceDomainName: parsed.args[0],
613
+ targetDomainName: parsed.args[1],
614
+ amountCogtoshi: parseCogAmountToCogtoshi(parsed.args[2]),
615
+ reviewText: parsed.reviewText,
616
+ dataDir,
617
+ databasePath: dbPath,
618
+ provider: context.walletSecretProvider,
619
+ prompter,
620
+ assumeYes: parsed.assumeYes,
621
+ });
622
+ return writeMutationCommandSuccess(parsed, context, {
623
+ data: buildReputationMutationData(result),
624
+ previewData: buildReputationPreviewData(result),
625
+ reusedExisting: result.reusedExisting,
626
+ reusedMessage: "The existing pending reputation mutation was reconciled instead of creating a duplicate.",
627
+ nextSteps: commandMutationNextSteps(`cogcoin show ${result.targetDomainName}`),
628
+ text: {
629
+ heading: parsed.command === "rep-give" ? "Reputation support submitted." : "Reputation revoke submitted.",
630
+ fields: [
631
+ { label: "Source domain", value: result.sourceDomainName },
632
+ { label: "Target domain", value: result.targetDomainName },
633
+ { label: "Sender", value: formatReputationSenderSummary(result) },
634
+ { label: "Amount", value: `${result.amountCogtoshi.toString()} cogtoshi` },
635
+ { label: "Review", value: formatReputationReviewSummary(result) },
636
+ { label: "Effect", value: formatReputationEffect(result) },
637
+ { label: "Status", value: result.status },
638
+ { label: "Txid", value: result.txid },
639
+ ],
640
+ },
641
+ });
642
+ }
643
+ if (isBuyMutationCommand(parsed.command)) {
644
+ const result = await context.buyDomain({
645
+ domainName: parsed.args[0],
651
646
  fromIdentity: parsed.fromIdentity,
652
- }),
653
- reusedExisting: result.reusedExisting,
654
- reusedMessage: "The existing pending purchase was reconciled instead of creating a duplicate.",
655
- nextSteps: commandMutationNextSteps(`cogcoin show ${result.domainName}`),
656
- text: {
657
- heading: "Purchase submitted.",
658
- fields: [
659
- { label: "Domain", value: result.domainName },
660
- { label: "Buyer", value: formatBuyBuyerSummary(result) },
661
- { label: "Seller", value: formatBuySellerSummary(result) },
662
- { label: "Price", value: `${result.listedPriceCogtoshi?.toString() ?? "unknown"} cogtoshi` },
663
- { label: "Settlement", value: formatBuySettlementSummary() },
664
- { label: "Status", value: result.status },
665
- { label: "Txid", value: result.txid },
666
- ],
667
- },
668
- });
647
+ dataDir,
648
+ databasePath: dbPath,
649
+ provider: context.walletSecretProvider,
650
+ prompter,
651
+ assumeYes: parsed.assumeYes,
652
+ });
653
+ return writeMutationCommandSuccess(parsed, context, {
654
+ data: buildDomainMarketMutationData(result, {
655
+ commandKind: "buy",
656
+ fromIdentity: parsed.fromIdentity,
657
+ }),
658
+ previewData: buildDomainMarketPreviewData(result, {
659
+ commandKind: "buy",
660
+ fromIdentity: parsed.fromIdentity,
661
+ }),
662
+ reusedExisting: result.reusedExisting,
663
+ reusedMessage: "The existing pending purchase was reconciled instead of creating a duplicate.",
664
+ nextSteps: commandMutationNextSteps(`cogcoin show ${result.domainName}`),
665
+ text: {
666
+ heading: "Purchase submitted.",
667
+ fields: [
668
+ { label: "Domain", value: result.domainName },
669
+ { label: "Buyer", value: formatBuyBuyerSummary(result) },
670
+ { label: "Seller", value: formatBuySellerSummary(result) },
671
+ { label: "Price", value: `${result.listedPriceCogtoshi?.toString() ?? "unknown"} cogtoshi` },
672
+ { label: "Settlement", value: formatBuySettlementSummary() },
673
+ { label: "Status", value: result.status },
674
+ { label: "Txid", value: result.txid },
675
+ ],
676
+ },
677
+ });
678
+ }
679
+ writeLine(context.stderr, `wallet mutation command not implemented: ${parsed.command}`);
680
+ return 1;
681
+ })(), stopWatcher);
682
+ if (outcome.kind === "stopped") {
683
+ return outcome.code;
669
684
  }
670
- writeLine(context.stderr, `wallet mutation command not implemented: ${parsed.command}`);
671
- return 1;
685
+ return outcome.value;
672
686
  }
673
687
  catch (error) {
674
688
  return writeHandledCliError({
@@ -678,4 +692,7 @@ export async function runWalletMutationCommand(parsed, context) {
678
692
  error,
679
693
  });
680
694
  }
695
+ finally {
696
+ stopWatcher.cleanup();
697
+ }
681
698
  }