@cogcoin/client 1.0.1 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/README.md +4 -2
  2. package/dist/bitcoind/client/factory.d.ts +0 -8
  3. package/dist/bitcoind/client/factory.js +1 -59
  4. package/dist/bitcoind/client/managed-client.d.ts +1 -3
  5. package/dist/bitcoind/client/managed-client.js +3 -47
  6. package/dist/bitcoind/indexer-daemon-main.js +173 -28
  7. package/dist/bitcoind/indexer-daemon.d.ts +14 -3
  8. package/dist/bitcoind/indexer-daemon.js +145 -29
  9. package/dist/bitcoind/indexer-monitor.d.ts +12 -0
  10. package/dist/bitcoind/indexer-monitor.js +89 -0
  11. package/dist/bitcoind/progress/follow-scene.d.ts +7 -1
  12. package/dist/bitcoind/progress/follow-scene.js +87 -4
  13. package/dist/bitcoind/progress/tty-renderer.d.ts +2 -0
  14. package/dist/bitcoind/progress/tty-renderer.js +2 -0
  15. package/dist/bitcoind/retryable-rpc.js +3 -0
  16. package/dist/bitcoind/service.d.ts +1 -0
  17. package/dist/bitcoind/service.js +31 -9
  18. package/dist/bitcoind/testing.d.ts +0 -1
  19. package/dist/bitcoind/testing.js +0 -1
  20. package/dist/bitcoind/types.d.ts +5 -2
  21. package/dist/cli/commands/follow.js +44 -49
  22. package/dist/cli/commands/mining-admin.js +65 -2
  23. package/dist/cli/commands/mining-read.js +43 -3
  24. package/dist/cli/commands/mining-runtime.js +91 -73
  25. package/dist/cli/commands/service-runtime.js +42 -2
  26. package/dist/cli/commands/status.js +3 -1
  27. package/dist/cli/commands/sync.js +50 -90
  28. package/dist/cli/commands/update.d.ts +2 -0
  29. package/dist/cli/commands/update.js +101 -0
  30. package/dist/cli/commands/wallet-admin.js +21 -3
  31. package/dist/cli/commands/wallet-read.js +2 -0
  32. package/dist/cli/context.js +36 -1
  33. package/dist/cli/managed-indexer-observer.d.ts +33 -0
  34. package/dist/cli/managed-indexer-observer.js +163 -0
  35. package/dist/cli/mining-format.d.ts +3 -1
  36. package/dist/cli/mining-format.js +63 -0
  37. package/dist/cli/mining-json.d.ts +11 -1
  38. package/dist/cli/mining-json.js +15 -0
  39. package/dist/cli/output.js +74 -2
  40. package/dist/cli/parse.d.ts +1 -1
  41. package/dist/cli/parse.js +28 -0
  42. package/dist/cli/prompt.js +109 -0
  43. package/dist/cli/read-json.d.ts +26 -1
  44. package/dist/cli/read-json.js +48 -0
  45. package/dist/cli/runner.js +8 -2
  46. package/dist/cli/signals.d.ts +12 -0
  47. package/dist/cli/signals.js +31 -13
  48. package/dist/cli/types.d.ts +13 -4
  49. package/dist/cli/update-notifier.js +7 -222
  50. package/dist/cli/update-service.d.ts +34 -0
  51. package/dist/cli/update-service.js +152 -0
  52. package/dist/client/initialization.js +5 -0
  53. package/dist/semver.d.ts +12 -0
  54. package/dist/semver.js +68 -0
  55. package/dist/wallet/lifecycle.d.ts +10 -0
  56. package/dist/wallet/mining/config.js +64 -3
  57. package/dist/wallet/mining/control.d.ts +5 -1
  58. package/dist/wallet/mining/control.js +269 -26
  59. package/dist/wallet/mining/domain-prompts.d.ts +17 -0
  60. package/dist/wallet/mining/domain-prompts.js +130 -0
  61. package/dist/wallet/mining/index.d.ts +2 -1
  62. package/dist/wallet/mining/index.js +1 -0
  63. package/dist/wallet/mining/provider-model.d.ts +30 -0
  64. package/dist/wallet/mining/provider-model.js +134 -0
  65. package/dist/wallet/mining/runner.d.ts +156 -5
  66. package/dist/wallet/mining/runner.js +1019 -399
  67. package/dist/wallet/mining/runtime-artifacts.js +1 -0
  68. package/dist/wallet/mining/sentence-protocol.d.ts +1 -0
  69. package/dist/wallet/mining/sentences.d.ts +2 -2
  70. package/dist/wallet/mining/sentences.js +32 -6
  71. package/dist/wallet/mining/types.d.ts +35 -1
  72. package/dist/wallet/mining/visualizer.d.ts +3 -0
  73. package/dist/wallet/mining/visualizer.js +132 -15
  74. package/dist/wallet/read/context.d.ts +1 -0
  75. package/dist/wallet/read/context.js +15 -7
  76. package/dist/wallet/state/client-password-agent.js +4 -1
  77. package/dist/wallet/state/client-password.js +15 -8
  78. package/dist/wallet/tx/common.js +1 -1
  79. package/package.json +3 -2
@@ -13,6 +13,23 @@ function formatIndexerTruthSource(source) {
13
13
  return "none";
14
14
  }
15
15
  }
16
+ function formatProviderModel(mining) {
17
+ if (mining.provider.effectiveModel === null || mining.provider.usingDefaultModel === null) {
18
+ return null;
19
+ }
20
+ return `${mining.provider.effectiveModel} (${mining.provider.usingDefaultModel ? "default" : "override"})`;
21
+ }
22
+ function formatProviderModelSource(mining) {
23
+ return mining.provider.modelSelectionSource;
24
+ }
25
+ function resolveProviderNotFoundNextStep(mining) {
26
+ return mining.provider.usingDefaultModel === false
27
+ ? "Next: run `cogcoin mine setup` and clear or correct the provider model."
28
+ : "Next: run `cogcoin mine setup` and choose a valid provider model.";
29
+ }
30
+ function resolveInsufficientFundsNextStep() {
31
+ return "Next: wait for enough safe BTC funding to become spendable for the next publish; mining resumes automatically.";
32
+ }
16
33
  export function formatMiningSummaryLine(mining) {
17
34
  const provider = mining.provider.configured
18
35
  ? `${mining.provider.provider} configured`
@@ -42,6 +59,17 @@ export function formatMineStatusReport(mining) {
42
59
  lines.push(`Last suspend detected: ${formatMaybeIso(mining.runtime.lastSuspendDetectedAtUnixMs)}`);
43
60
  }
44
61
  lines.push(`Provider: ${mining.provider.configured ? `${mining.provider.provider} configured` : mining.provider.status}`);
62
+ const providerModel = formatProviderModel(mining);
63
+ if (providerModel !== null) {
64
+ lines.push(`Provider model: ${providerModel}`);
65
+ }
66
+ const providerModelSource = formatProviderModelSource(mining);
67
+ if (providerModelSource !== null) {
68
+ lines.push(`Provider model source: ${providerModelSource}`);
69
+ }
70
+ if (mining.provider.estimatedDailyCostDisplay !== null) {
71
+ lines.push(`Estimated daily cost: ${mining.provider.estimatedDailyCostDisplay}`);
72
+ }
45
73
  if (mining.provider.message !== null) {
46
74
  lines.push(`Provider note: ${mining.provider.message}`);
47
75
  }
@@ -105,6 +133,12 @@ export function formatMineStatusReport(mining) {
105
133
  if (mining.runtime.miningState === "repair-required") {
106
134
  lines.push("Next: run `cogcoin repair` before mining again.");
107
135
  }
136
+ else if (mining.runtime.providerState === "not-found") {
137
+ lines.push(resolveProviderNotFoundNextStep(mining));
138
+ }
139
+ else if (mining.runtime.currentPublishDecision === "publish-paused-insufficient-funds") {
140
+ lines.push(resolveInsufficientFundsNextStep());
141
+ }
108
142
  else if (mining.runtime.pauseReason === "zero-reward") {
109
143
  lines.push("Next: wait for the next positive-reward target height; mining resumes automatically.");
110
144
  }
@@ -122,3 +156,32 @@ export function formatMineStatusReport(mining) {
122
156
  export function formatMiningEventRecord(event) {
123
157
  return `${new Date(event.timestampUnixMs).toISOString()} ${event.level.toUpperCase()} ${event.kind} ${event.message}`;
124
158
  }
159
+ export function formatMiningPromptMutationReport(result) {
160
+ const lines = [
161
+ `Domain: ${result.domain.name}`,
162
+ `Domain prompt: ${result.prompt ?? "none"}`,
163
+ `Global fallback prompt: ${result.fallbackPromptConfigured ? "configured" : "not configured"}`,
164
+ ];
165
+ if (result.previousPrompt !== null) {
166
+ lines.push(`Previous domain prompt: ${result.previousPrompt}`);
167
+ }
168
+ lines.push(result.status === "updated"
169
+ ? "Per-domain mining prompt updated."
170
+ : "Per-domain mining prompt cleared.");
171
+ return lines.join("\n");
172
+ }
173
+ export function formatMiningPromptListReport(result) {
174
+ const lines = [
175
+ "Mining Prompt List",
176
+ `Global fallback prompt: ${result.fallbackPromptConfigured ? "configured" : "not configured"}`,
177
+ ];
178
+ if (result.prompts.length === 0) {
179
+ lines.push("No mineable root domains or stored per-domain mining prompts are configured.");
180
+ return lines.join("\n");
181
+ }
182
+ for (const entry of result.prompts) {
183
+ lines.push(`${entry.domain.name} domainId=${entry.domain.domainId ?? "none"} ${entry.mineable ? "mineable" : "dormant"} source=${entry.effectivePromptSource}`);
184
+ lines.push(` prompt: ${entry.prompt ?? "none"}`);
185
+ }
186
+ return lines.join("\n");
187
+ }
@@ -1,4 +1,4 @@
1
- import type { MiningControlPlaneView, MiningRuntimeStatusV1 } from "../wallet/mining/index.js";
1
+ import type { MiningControlPlaneView, MiningDomainPromptMutationResult, MiningRuntimeStatusV1 } from "../wallet/mining/index.js";
2
2
  export declare function buildMineSetupData(view: MiningControlPlaneView): {
3
3
  resultType: "state-change";
4
4
  stateChange: {
@@ -29,3 +29,13 @@ export declare function buildMineStopData(snapshot: MiningRuntimeStatusV1 | null
29
29
  };
30
30
  state: Record<string, unknown>;
31
31
  };
32
+ export declare function buildMinePromptData(result: MiningDomainPromptMutationResult): {
33
+ domain: {
34
+ name: string;
35
+ domainId: number | null;
36
+ };
37
+ previousPrompt: string | null;
38
+ prompt: string | null;
39
+ status: "updated" | "cleared";
40
+ fallbackPromptConfigured: boolean;
41
+ };
@@ -18,8 +18,14 @@ export function buildMineSetupData(view) {
18
18
  configured: view.provider.configured,
19
19
  provider: view.provider.provider,
20
20
  status: view.provider.status,
21
+ modelId: view.provider.modelId,
21
22
  modelOverride: view.provider.modelOverride,
23
+ modelSelectionSource: view.provider.modelSelectionSource,
24
+ effectiveModel: view.provider.effectiveModel,
25
+ usingDefaultModel: view.provider.usingDefaultModel,
22
26
  extraPromptConfigured: view.provider.extraPromptConfigured,
27
+ estimatedDailyCostUsd: view.provider.estimatedDailyCostUsd,
28
+ estimatedDailyCostDisplay: view.provider.estimatedDailyCostDisplay,
23
29
  },
24
30
  runtime: summarizeRuntime(view.runtime),
25
31
  };
@@ -52,3 +58,12 @@ export function buildMineStopData(snapshot) {
52
58
  after,
53
59
  });
54
60
  }
61
+ export function buildMinePromptData(result) {
62
+ return {
63
+ domain: result.domain,
64
+ previousPrompt: result.previousPrompt,
65
+ prompt: result.prompt,
66
+ status: result.status,
67
+ fallbackPromptConfigured: result.fallbackPromptConfigured,
68
+ };
69
+ }
@@ -127,7 +127,9 @@ export function classifyCliError(error) {
127
127
  return { exitCode: 2, errorCode: message, message };
128
128
  }
129
129
  if (message === "mining_setup_invalid_provider"
130
- || message === "mining_setup_missing_api_key") {
130
+ || message === "mining_setup_missing_api_key"
131
+ || message === "mining_setup_missing_model_id"
132
+ || message === "mining_setup_canceled") {
131
133
  return { exitCode: 2, errorCode: message, message };
132
134
  }
133
135
  if (message.endsWith("_typed_ack_required")) {
@@ -183,6 +185,8 @@ function isBlockedError(message) {
183
185
  || message === "indexer_daemon_wallet_root_mismatch"
184
186
  || message === "indexer_daemon_schema_mismatch"
185
187
  || message === "mine_setup_requires_tty"
188
+ || message === "mine_prompt_requires_tty"
189
+ || message === "mine_prompt_domain_not_mineable"
186
190
  || message === "mining_preemption_timeout"
187
191
  || message === "wallet_client_password_setup_required"
188
192
  || message === "wallet_client_password_migration_required"
@@ -507,6 +511,27 @@ export function createCliErrorPresentation(errorCode, fallbackMessage, error) {
507
511
  next: "Rerun `cogcoin mine setup` and enter the provider API key when prompted.",
508
512
  };
509
513
  }
514
+ if (errorCode === "mining_setup_missing_model_id") {
515
+ return {
516
+ what: "Mining model ID is required.",
517
+ why: "Built-in mining setup cannot save a custom mining model choice unless it has a non-empty model ID.",
518
+ next: "Rerun `cogcoin mine setup`, choose `Custom model ID...`, and enter the model ID when prompted.",
519
+ };
520
+ }
521
+ if (errorCode === "mining_setup_canceled") {
522
+ return {
523
+ what: "Mining setup was canceled.",
524
+ why: "The interactive mining-model selection was canceled before any provider configuration was saved.",
525
+ next: "Rerun `cogcoin mine setup` when you are ready to choose a provider model.",
526
+ };
527
+ }
528
+ if (errorCode === "mine_prompt_domain_not_mineable") {
529
+ return {
530
+ what: "A new mining prompt override can only target a mineable anchored root domain.",
531
+ why: "Cogcoin only creates new domain prompt overrides for locally controlled anchored root domains that are currently mineable. Existing stored prompt entries can still be edited or cleared by name even when they are dormant.",
532
+ next: "Run `cogcoin domains --mineable` to see eligible domains, or rerun `cogcoin mine prompt <domain>` for an existing stored prompt entry.",
533
+ };
534
+ }
510
535
  if (errorCode.endsWith("_confirmation_rejected")) {
511
536
  return {
512
537
  what: "Confirmation was declined.",
@@ -561,7 +586,7 @@ export function createCliErrorPresentation(errorCode, fallbackMessage, error) {
561
586
  next: "Restore or otherwise recover the wallet into the current format, then retry the command.",
562
587
  };
563
588
  }
564
- if (errorCode.endsWith("_requires_tty")) {
589
+ if (errorCode.endsWith("_requires_tty") && errorCode !== "cli_update_requires_tty") {
565
590
  return {
566
591
  what: "Interactive terminal input is required.",
567
592
  why: "This command needs terminal input before it can continue safely.",
@@ -575,6 +600,13 @@ export function createCliErrorPresentation(errorCode, fallbackMessage, error) {
575
600
  next: "Check `cogcoin status`, wait for services to settle, and retry. If the state stays degraded, run `cogcoin repair`.",
576
601
  };
577
602
  }
603
+ if (errorCode === "indexer_daemon_background_follow_recovery_failed") {
604
+ return {
605
+ what: "The managed indexer daemon could not recover automatic background follow.",
606
+ why: "Cogcoin tried to resume or restart the compatible managed indexer daemon, but it still failed to enter background follow.",
607
+ next: "Run `cogcoin repair` if this persists, then retry.",
608
+ };
609
+ }
578
610
  if (errorCode === "indexer_daemon_service_version_mismatch") {
579
611
  return {
580
612
  what: "The live indexer daemon is running an incompatible service API version.",
@@ -860,6 +892,34 @@ export function createCliErrorPresentation(errorCode, fallbackMessage, error) {
860
892
  next: "Rerun the command in an interactive terminal, or add `--yes` if that is appropriate for your workflow.",
861
893
  };
862
894
  }
895
+ if (errorCode === "cli_update_requires_tty") {
896
+ return {
897
+ what: "Updating Cogcoin needs an interactive terminal or `--yes`.",
898
+ why: "When a newer client release is available, `cogcoin update` prompts before running the global npm install unless `--yes` is provided.",
899
+ next: "Rerun `cogcoin update` in an interactive terminal, or add `--yes` to apply the update non-interactively.",
900
+ };
901
+ }
902
+ if (errorCode === "cli_update_registry_unavailable") {
903
+ return {
904
+ what: "Cogcoin could not read the latest client version from the npm registry.",
905
+ why: "The explicit update command requires a fresh registry lookup before it can compare versions or run the install.",
906
+ next: "Check network access and rerun `cogcoin update`.",
907
+ };
908
+ }
909
+ if (errorCode === "cli_update_npm_not_found") {
910
+ return {
911
+ what: "Cogcoin could not find npm to install the update.",
912
+ why: "The update command runs `npm install -g @cogcoin/client`, and no usable `npm` executable was available on PATH.",
913
+ next: "Install Node.js/npm or fix PATH, then rerun `cogcoin update`.",
914
+ };
915
+ }
916
+ if (errorCode === "cli_update_install_failed") {
917
+ return {
918
+ what: "Cogcoin update installation failed.",
919
+ why: "The global npm install exited unsuccessfully before the client update completed.",
920
+ next: "Review the npm output above, fix the installation issue, then rerun `cogcoin update`.",
921
+ };
922
+ }
863
923
  if (errorCode === "wallet_claim_sender_not_local") {
864
924
  return {
865
925
  what: "The claim sender is not locally controlled.",
@@ -918,6 +978,8 @@ export function describeCanonicalCommand(parsed) {
918
978
  return "cogcoin reset";
919
979
  case "repair":
920
980
  return "cogcoin repair";
981
+ case "update":
982
+ return "cogcoin update";
921
983
  case "anchor":
922
984
  case "domain-anchor":
923
985
  return `cogcoin anchor ${args[0] ?? "<domain>"}`;
@@ -981,6 +1043,10 @@ export function describeCanonicalCommand(parsed) {
981
1043
  return "cogcoin wallet status";
982
1044
  case "mine-setup":
983
1045
  return "cogcoin mine setup";
1046
+ case "mine-prompt":
1047
+ return `cogcoin mine prompt ${args[0] ?? "<domain>"}`;
1048
+ case "mine-prompt-list":
1049
+ return "cogcoin mine prompt";
984
1050
  case "mine-start":
985
1051
  return "cogcoin mine start";
986
1052
  case "mine-stop":
@@ -1052,6 +1118,8 @@ export function resolveStableJsonSchema(parsed) {
1052
1118
  return "cogcoin/mine-status/v1";
1053
1119
  case "mine-log":
1054
1120
  return "cogcoin/mine-log/v1";
1121
+ case "mine-prompt-list":
1122
+ return "cogcoin/mine-prompt-list/v1";
1055
1123
  case "balance":
1056
1124
  case "cog-balance":
1057
1125
  return "cogcoin/balance/v1";
@@ -1096,6 +1164,8 @@ export function resolveStableMutationJsonSchema(parsed) {
1096
1164
  return "cogcoin/reset/v1";
1097
1165
  case "repair":
1098
1166
  return "cogcoin/repair/v1";
1167
+ case "update":
1168
+ return "cogcoin/update/v1";
1099
1169
  case "anchor":
1100
1170
  case "domain-anchor":
1101
1171
  return "cogcoin/anchor/v1";
@@ -1157,6 +1227,8 @@ export function resolveStableMiningControlJsonSchema(parsed) {
1157
1227
  switch (parsed.command) {
1158
1228
  case "mine-setup":
1159
1229
  return "cogcoin/mine-setup/v1";
1230
+ case "mine-prompt":
1231
+ return "cogcoin/mine-prompt/v1";
1160
1232
  case "mine-start":
1161
1233
  return "cogcoin/mine-start/v1";
1162
1234
  case "mine-stop":
@@ -1,3 +1,3 @@
1
1
  import type { ParsedCliArgs } from "./types.js";
2
- export declare const HELP_TEXT = "Usage: cogcoin <command> [options]\n\nCommands:\n status Show wallet-aware local service and chain status\n status --output json Emit the stable v1 machine-readable status envelope\n client unlock Unlock password-protected local wallet secrets for a limited time\n client lock Flush the cached client password unlock session\n client change-password Rotate the client password that protects local wallet secrets\n bitcoin start Start the managed Bitcoin daemon\n bitcoin stop Stop the managed Bitcoin daemon and paired indexer\n bitcoin status Show managed Bitcoin daemon status without starting it\n bitcoin transfer <sats> --to <address>\n Send plain BTC from the wallet address\n indexer start Start the managed Cogcoin indexer (and bitcoind if needed)\n indexer stop Stop the managed Cogcoin indexer only\n indexer status Show managed Cogcoin indexer status without starting it\n init Initialize a new local wallet root\n init --output json Emit the stable v1 machine-readable init result envelope\n restore Restore an imported named seed from a 24-word mnemonic; run sync afterward\n reset Factory-reset local Cogcoin state with interactive retention prompts\n repair Recover bounded wallet/indexer/runtime state\n wallet address Alias for address\n wallet ids Alias for ids\n mine Run the miner in the foreground\n mine start Start the miner as a background worker\n mine stop Stop the active background miner\n mine setup Configure the built-in mining provider\n mine setup --output json\n Emit the stable v1 machine-readable mine setup result envelope\n mine status Show mining control-plane health and readiness\n mine log Show recent mining control-plane events\n anchor <domain> Anchor an owned unanchored domain with the wallet address\n register <domain>\n Register a root domain or subdomain\n transfer <domain> --to <btc-target>\n Transfer an unanchored domain to another BTC address or script\n sell <domain> <price> List an unanchored domain for sale in COG\n unsell <domain> Clear an active domain listing\n buy <domain>\n Buy an unanchored listed domain in COG\n send <amount> --to <btc-target>\n Send COG from the wallet address to another BTC target\n claim <lock-id> --preimage <32-byte-hex>\n Claim an active COG lock before timeout\n reclaim <lock-id> Reclaim an expired COG lock as the original locker\n cog lock <amount> --to-domain <domain> (--for <blocks-or-duration> | --until-height <height>) --condition <sha256hex>\n Lock COG to an anchored recipient domain\n wallet status Show detailed wallet-local status and service health\n wallet init Initialize a new local wallet root\n wallet restore Restore an imported named seed from a 24-word mnemonic; run sync afterward\n wallet delete Delete one imported named seed without affecting main\n wallet show-mnemonic Reveal the initialized wallet recovery phrase after typed confirmation\n address Show the BTC wallet address for this wallet\n ids Show the local wallet address\n balance Show local wallet COG balances\n locks Show locally related active COG locks\n domain list Alias for domains\n domain show <domain> Alias for show <domain>\n domains [--anchored] [--listed] [--mineable]\n Show locally related domains\n show <domain> Show one domain and its local-wallet relationship\n fields <domain> List current fields on a domain\n field <domain> <field> Show one current field value\n field create <domain> <field>\n Create a new empty anchored field\n field set <domain> <field>\n Update an existing anchored field value\n field clear <domain> <field>\n Clear an existing anchored field value\n rep give <source-domain> <target-domain> <amount>\n Burn COG as anchored-domain reputation support\n rep revoke <source-domain> <target-domain> <amount>\n Revoke visible support without refunding burned COG\n\nOptions:\n --db <path> Override the SQLite database path\n --data-dir <path> Override the managed bitcoin datadir\n --for <duration> Relative timeout for cog lock, like 15m, 2h, or 1d\n --message <text> Founding message text for anchor\n --to <btc-target> Transfer or send target as an address or spk:<hex>\n --to-domain <domain>\n Recipient domain for cog lock\n --condition <sha256hex>\n 32-byte lock condition hash\n --until-height <height>\n Absolute timeout height for cog lock\n --preimage <32-byte-hex>\n Claim preimage for an active lock\n --review <text> Optional public review text for reputation operations\n --satvb <n> Override the mutation fee rate in sat/vB\n --text <utf8> UTF-8 payload text for endpoint or field writes\n --json <json> UTF-8 payload JSON text for endpoint or field writes\n --bytes <spec> Payload bytes as hex:<hex> or @<path>\n --permanent Create the field as permanent\n --format <spec> Advanced field format as raw:<u8>\n --value <spec> Advanced field value as hex:<hex>, @<path>, or utf8:<text>\n --claimable Show only currently claimable locks\n --reclaimable Show only currently reclaimable locks\n --anchored Show only anchored domains\n --listed Show only currently listed domains\n --mineable Show only locally mineable root domains\n --limit <n> Limit list rows (1..1000)\n --all Show all rows for list commands\n --follow Follow mining log output\n --output <mode> Output mode: text, json, or preview-json\n --progress <mode> Progress output mode: auto, tty, or none\n --seed <name> Select an imported wallet seed for wallet-aware commands\n --force Reserved for future use\n --force-race Allow a visible root registration race\n --yes Approve eligible plain yes/no mutation confirmations non-interactively\n --help Show help\n --version Show package version\n\nQuickstart:\n 1. Run `cogcoin init` to create the wallet.\n 2. Run `cogcoin sync` to bootstrap assumeutxo and the managed Bitcoin/indexer state.\n 3. Run `cogcoin address`, then fund the wallet with about 0.0015 BTC so you can buy a 6+ character domain to start mining and still keep BTC available for mining transaction fees.\n\nExamples:\n cogcoin status --output json\n cogcoin bitcoin status\n cogcoin indexer status\n cogcoin init --output json\n cogcoin restore --seed trading\n cogcoin wallet address\n cogcoin domain list --mineable\n cogcoin register alpha-child\n cogcoin anchor alpha\n cogcoin register alpha --satvb 12.5\n cogcoin buy alpha\n cogcoin field set alpha bio --text \"hello\"\n cogcoin rep give alpha beta 10 --review \"great operator\"\n cogcoin mine setup --output json\n cogcoin register alpha-child --output preview-json\n cogcoin mine status\n";
2
+ export declare const HELP_TEXT = "Usage: cogcoin <command> [options]\n\nCommands:\n status Show wallet-aware local service and chain status\n status --output json Emit the stable v1 machine-readable status envelope\n update Show the current and latest client versions and install updates\n update --output json Emit the stable v1 machine-readable update result envelope\n client unlock Unlock password-protected local wallet secrets for a limited time\n client lock Flush the cached client password unlock session\n client change-password Rotate the client password that protects local wallet secrets\n bitcoin start Start the managed Bitcoin daemon\n bitcoin stop Stop the managed Bitcoin daemon and paired indexer\n bitcoin status Show managed Bitcoin daemon status without starting it\n bitcoin transfer <sats> --to <address>\n Send plain BTC from the wallet address\n indexer start Start the managed Cogcoin indexer (and bitcoind if needed)\n indexer stop Stop the managed Cogcoin indexer only\n indexer status Show managed Cogcoin indexer status without starting it\n init Initialize a new local wallet root\n init --output json Emit the stable v1 machine-readable init result envelope\n restore Restore an imported named seed from a 24-word mnemonic; run sync afterward\n reset Factory-reset local Cogcoin state with interactive retention prompts\n repair Recover bounded wallet/indexer/runtime state\n wallet address Alias for address\n wallet ids Alias for ids\n mine Run the miner in the foreground\n mine start Start the miner as a background worker\n mine stop Stop the active background miner\n mine setup Configure the built-in mining provider\n mine setup --output json\n Emit the stable v1 machine-readable mine setup result envelope\n mine prompt Show per-domain mining prompt state\n mine prompt --output json\n Emit the stable v1 machine-readable mine prompt list envelope\n mine prompt <domain> Configure a per-domain mining prompt override\n mine prompt <domain> --output json\n Emit the stable v1 machine-readable mine prompt result envelope\n mine prompt list Alias for mine prompt\n mine status Show mining control-plane health and readiness\n mine log Show recent mining control-plane events\n anchor <domain> Anchor an owned unanchored domain with the wallet address\n register <domain>\n Register a root domain or subdomain\n transfer <domain> --to <btc-target>\n Transfer an unanchored domain to another BTC address or script\n sell <domain> <price> List an unanchored domain for sale in COG\n unsell <domain> Clear an active domain listing\n buy <domain>\n Buy an unanchored listed domain in COG\n send <amount> --to <btc-target>\n Send COG from the wallet address to another BTC target\n claim <lock-id> --preimage <32-byte-hex>\n Claim an active COG lock before timeout\n reclaim <lock-id> Reclaim an expired COG lock as the original locker\n cog lock <amount> --to-domain <domain> (--for <blocks-or-duration> | --until-height <height>) --condition <sha256hex>\n Lock COG to an anchored recipient domain\n wallet status Show detailed wallet-local status and service health\n wallet init Initialize a new local wallet root\n wallet restore Restore an imported named seed from a 24-word mnemonic; run sync afterward\n wallet delete Delete one imported named seed without affecting main\n wallet show-mnemonic Reveal the initialized wallet recovery phrase after typed confirmation\n address Show the BTC wallet address for this wallet\n ids Show the local wallet address\n balance Show local wallet COG balances\n locks Show locally related active COG locks\n domain list Alias for domains\n domain show <domain> Alias for show <domain>\n domains [--anchored] [--listed] [--mineable]\n Show locally related domains\n show <domain> Show one domain and its local-wallet relationship\n fields <domain> List current fields on a domain\n field <domain> <field> Show one current field value\n field create <domain> <field>\n Create a new empty anchored field\n field set <domain> <field>\n Update an existing anchored field value\n field clear <domain> <field>\n Clear an existing anchored field value\n rep give <source-domain> <target-domain> <amount>\n Burn COG as anchored-domain reputation support\n rep revoke <source-domain> <target-domain> <amount>\n Revoke visible support without refunding burned COG\n\nOptions:\n --db <path> Override the SQLite database path\n --data-dir <path> Override the managed bitcoin datadir\n --for <duration> Relative timeout for cog lock, like 15m, 2h, or 1d\n --message <text> Founding message text for anchor\n --to <btc-target> Transfer or send target as an address or spk:<hex>\n --to-domain <domain>\n Recipient domain for cog lock\n --condition <sha256hex>\n 32-byte lock condition hash\n --until-height <height>\n Absolute timeout height for cog lock\n --preimage <32-byte-hex>\n Claim preimage for an active lock\n --review <text> Optional public review text for reputation operations\n --satvb <n> Override the mutation fee rate in sat/vB\n --text <utf8> UTF-8 payload text for endpoint or field writes\n --json <json> UTF-8 payload JSON text for endpoint or field writes\n --bytes <spec> Payload bytes as hex:<hex> or @<path>\n --permanent Create the field as permanent\n --format <spec> Advanced field format as raw:<u8>\n --value <spec> Advanced field value as hex:<hex>, @<path>, or utf8:<text>\n --claimable Show only currently claimable locks\n --reclaimable Show only currently reclaimable locks\n --anchored Show only anchored domains\n --listed Show only currently listed domains\n --mineable Show only locally mineable root domains\n --limit <n> Limit list rows (1..1000)\n --all Show all rows for list commands\n --follow Follow mining log output\n --output <mode> Output mode: text, json, or preview-json\n --progress <mode> Progress output mode: auto, tty, or none\n --seed <name> Select an imported wallet seed for wallet-aware commands\n --force Reserved for future use\n --force-race Allow a visible root registration race\n --yes Approve eligible plain yes/no mutation confirmations non-interactively\n --help Show help\n --version Show package version\n\nQuickstart:\n 1. Run `cogcoin init` to create the wallet.\n 2. Run `cogcoin sync` to bootstrap assumeutxo and the managed Bitcoin/indexer state.\n 3. Run `cogcoin address`, then fund the wallet with about 0.0015 BTC so you can buy a 6+ character domain to start mining and still keep BTC available for mining transaction fees.\n\nExamples:\n cogcoin status --output json\n cogcoin bitcoin status\n cogcoin indexer status\n cogcoin init --output json\n cogcoin restore --seed trading\n cogcoin wallet address\n cogcoin domain list --mineable\n cogcoin register alpha-child\n cogcoin anchor alpha\n cogcoin register alpha --satvb 12.5\n cogcoin buy alpha\n cogcoin field set alpha bio --text \"hello\"\n cogcoin rep give alpha beta 10 --review \"great operator\"\n cogcoin mine setup --output json\n cogcoin mine prompt\n cogcoin mine prompt alpha\n cogcoin register alpha-child --output preview-json\n cogcoin mine status\n";
3
3
  export declare function parseCliArgs(argv: string[]): ParsedCliArgs;
package/dist/cli/parse.js CHANGED
@@ -5,6 +5,8 @@ export const HELP_TEXT = `Usage: cogcoin <command> [options]
5
5
  Commands:
6
6
  status Show wallet-aware local service and chain status
7
7
  status --output json Emit the stable v1 machine-readable status envelope
8
+ update Show the current and latest client versions and install updates
9
+ update --output json Emit the stable v1 machine-readable update result envelope
8
10
  client unlock Unlock password-protected local wallet secrets for a limited time
9
11
  client lock Flush the cached client password unlock session
10
12
  client change-password Rotate the client password that protects local wallet secrets
@@ -29,6 +31,13 @@ Commands:
29
31
  mine setup Configure the built-in mining provider
30
32
  mine setup --output json
31
33
  Emit the stable v1 machine-readable mine setup result envelope
34
+ mine prompt Show per-domain mining prompt state
35
+ mine prompt --output json
36
+ Emit the stable v1 machine-readable mine prompt list envelope
37
+ mine prompt <domain> Configure a per-domain mining prompt override
38
+ mine prompt <domain> --output json
39
+ Emit the stable v1 machine-readable mine prompt result envelope
40
+ mine prompt list Alias for mine prompt
32
41
  mine status Show mining control-plane health and readiness
33
42
  mine log Show recent mining control-plane events
34
43
  anchor <domain> Anchor an owned unanchored domain with the wallet address
@@ -133,11 +142,14 @@ Examples:
133
142
  cogcoin field set alpha bio --text "hello"
134
143
  cogcoin rep give alpha beta 10 --review "great operator"
135
144
  cogcoin mine setup --output json
145
+ cogcoin mine prompt
146
+ cogcoin mine prompt alpha
136
147
  cogcoin register alpha-child --output preview-json
137
148
  cogcoin mine status
138
149
  `;
139
150
  function supportsYesFlag(command) {
140
151
  switch (command) {
152
+ case "update":
141
153
  case "sync":
142
154
  case "follow":
143
155
  case "bitcoin-transfer":
@@ -220,6 +232,8 @@ function supportsSeedFlag(command) {
220
232
  case "mine-start":
221
233
  case "mine-stop":
222
234
  case "mine-setup":
235
+ case "mine-prompt":
236
+ case "mine-prompt-list":
223
237
  case "mine-status":
224
238
  case "mine-log":
225
239
  case "wallet-delete":
@@ -645,6 +659,14 @@ export function parseCliArgs(argv) {
645
659
  index += 1;
646
660
  continue;
647
661
  }
662
+ if (subcommand === "prompt") {
663
+ const action = argv[index + 2] ?? null;
664
+ command = action === null || action.startsWith("--") || action === "list"
665
+ ? "mine-prompt-list"
666
+ : "mine-prompt";
667
+ index += action === "list" ? 2 : 1;
668
+ continue;
669
+ }
648
670
  if (subcommand === "status") {
649
671
  command = "mine-status";
650
672
  index += 1;
@@ -834,6 +856,7 @@ export function parseCliArgs(argv) {
834
856
  || token === "restore"
835
857
  || token === "reset"
836
858
  || token === "repair"
859
+ || token === "update"
837
860
  || token === "sync"
838
861
  || token === "status"
839
862
  || token === "follow"
@@ -864,6 +887,7 @@ export function parseCliArgs(argv) {
864
887
  args.push(token);
865
888
  }
866
889
  if ((command === "status"
890
+ || command === "update"
867
891
  || command === "bitcoin-start"
868
892
  || command === "bitcoin-stop"
869
893
  || command === "bitcoin-status"
@@ -884,6 +908,7 @@ export function parseCliArgs(argv) {
884
908
  || command === "mine-start"
885
909
  || command === "mine-stop"
886
910
  || command === "mine-setup"
911
+ || command === "mine-prompt-list"
887
912
  || command === "mine-status"
888
913
  || command === "mine-log"
889
914
  || command === "wallet-address"
@@ -907,6 +932,9 @@ export function parseCliArgs(argv) {
907
932
  && args.length !== 1) {
908
933
  throw new Error("cli_missing_domain_argument");
909
934
  }
935
+ if (command === "mine-prompt" && args.length !== 1) {
936
+ throw new Error("cli_missing_domain_argument");
937
+ }
910
938
  if ((command === "domain-endpoint-set"
911
939
  || command === "domain-endpoint-clear"
912
940
  || command === "domain-delegate-clear"
@@ -1,5 +1,9 @@
1
1
  import { createInterface } from "node:readline/promises";
2
2
  const CLEAR_SENSITIVE_DISPLAY_SEQUENCE = "\u001B[2J\u001B[3J\u001B[H";
3
+ const CLEAR_MENU_SEQUENCE = "\u001B[0J";
4
+ function countRenderedLines(text) {
5
+ return text.endsWith("\n") ? text.split("\n").length - 1 : text.split("\n").length;
6
+ }
3
7
  export function createTerminalPrompter(input, output) {
4
8
  const ensureReadableInput = () => {
5
9
  if (!("on" in input) || !("off" in input)) {
@@ -8,6 +12,12 @@ export function createTerminalPrompter(input, output) {
8
12
  return input;
9
13
  };
10
14
  const ensureWritableOutput = () => output;
15
+ const supportsRawSelection = () => {
16
+ const readable = ensureReadableInput();
17
+ return Boolean(readable.isTTY
18
+ && output.isTTY
19
+ && typeof readable.setRawMode === "function");
20
+ };
11
21
  const ask = async (message, questionOutput) => {
12
22
  const readline = createInterface({
13
23
  input: ensureReadableInput(),
@@ -20,6 +30,99 @@ export function createTerminalPrompter(input, output) {
20
30
  readline.close();
21
31
  }
22
32
  };
33
+ const promptSelectionFallback = async (options) => {
34
+ const writableOutput = ensureWritableOutput();
35
+ writableOutput.write(`${options.message}\n`);
36
+ for (const [index, option] of options.options.entries()) {
37
+ const description = option.description == null || option.description.length === 0
38
+ ? ""
39
+ : ` - ${option.description}`;
40
+ writableOutput.write(`${index + 1}. ${option.label}${description}\n`);
41
+ }
42
+ if (options.footer != null && options.footer.length > 0) {
43
+ writableOutput.write(`${options.footer}\n`);
44
+ }
45
+ while (true) {
46
+ const answer = (await ask(`Choice [1-${options.options.length}]: `, writableOutput)).trim();
47
+ if (/^(q|quit|esc|escape)$/i.test(answer)) {
48
+ throw new Error("mining_setup_canceled");
49
+ }
50
+ const selection = Number.parseInt(answer, 10);
51
+ if (Number.isInteger(selection) && selection >= 1 && selection <= options.options.length) {
52
+ return options.options[selection - 1].value;
53
+ }
54
+ writableOutput.write(`Enter a number from 1 to ${options.options.length}, or q to cancel.\n`);
55
+ }
56
+ };
57
+ const promptSelectionRaw = async (options) => {
58
+ const readableInput = ensureReadableInput();
59
+ const writableOutput = ensureWritableOutput();
60
+ const initialIndex = options.initialValue == null
61
+ ? -1
62
+ : options.options.findIndex((option) => option.value === options.initialValue);
63
+ let selectedIndex = initialIndex === -1 ? 0 : initialIndex;
64
+ let renderedLineCount = 0;
65
+ const renderMenu = () => {
66
+ if (renderedLineCount > 0) {
67
+ writableOutput.write(`\u001B[${renderedLineCount}A${CLEAR_MENU_SEQUENCE}`);
68
+ }
69
+ const lines = [
70
+ options.message,
71
+ "Use Up/Down to choose, Enter to confirm, or q/Esc/Ctrl+C to cancel.",
72
+ ...options.options.map((option, index) => {
73
+ const prefix = index === selectedIndex ? ">" : " ";
74
+ const description = option.description == null || option.description.length === 0
75
+ ? ""
76
+ : ` - ${option.description}`;
77
+ return `${prefix} ${option.label}${description}`;
78
+ }),
79
+ ];
80
+ if (options.footer != null && options.footer.length > 0) {
81
+ lines.push(options.footer);
82
+ }
83
+ const rendered = `${lines.join("\n")}\n`;
84
+ renderedLineCount = countRenderedLines(rendered);
85
+ writableOutput.write(rendered);
86
+ };
87
+ return await new Promise((resolve, reject) => {
88
+ const finish = (handler) => {
89
+ readableInput.off("data", onData);
90
+ readableInput.setRawMode(false);
91
+ readableInput.pause?.();
92
+ writableOutput.write("\n");
93
+ handler();
94
+ };
95
+ const onData = (chunk) => {
96
+ const value = Buffer.isBuffer(chunk) ? chunk.toString("utf8") : chunk;
97
+ switch (value) {
98
+ case "\u001B[A":
99
+ selectedIndex = selectedIndex === 0 ? options.options.length - 1 : selectedIndex - 1;
100
+ renderMenu();
101
+ return;
102
+ case "\u001B[B":
103
+ selectedIndex = selectedIndex === options.options.length - 1 ? 0 : selectedIndex + 1;
104
+ renderMenu();
105
+ return;
106
+ case "\r":
107
+ case "\n":
108
+ finish(() => resolve(options.options[selectedIndex].value));
109
+ return;
110
+ case "\u001B":
111
+ case "\u0003":
112
+ case "q":
113
+ case "Q":
114
+ finish(() => reject(new Error("mining_setup_canceled")));
115
+ return;
116
+ default:
117
+ return;
118
+ }
119
+ };
120
+ readableInput.setRawMode(true);
121
+ readableInput.resume?.();
122
+ readableInput.on("data", onData);
123
+ renderMenu();
124
+ });
125
+ };
23
126
  return {
24
127
  isInteractive: Boolean(input.isTTY && output.isTTY),
25
128
  writeLine(message) {
@@ -44,6 +147,12 @@ export function createTerminalPrompter(input, output) {
44
147
  output.write("\n");
45
148
  }
46
149
  },
150
+ async selectOption(options) {
151
+ if (!supportsRawSelection()) {
152
+ return await promptSelectionFallback(options);
153
+ }
154
+ return await promptSelectionRaw(options);
155
+ },
47
156
  clearSensitiveDisplay(scope) {
48
157
  if (!input.isTTY || !output.isTTY) {
49
158
  return;
@@ -1,5 +1,5 @@
1
1
  import type { WalletDomainView, WalletFieldView, WalletLockView, WalletReadContext } from "../wallet/read/index.js";
2
- import type { MiningControlPlaneView, MiningEventRecord } from "../wallet/mining/index.js";
2
+ import type { MiningControlPlaneView, MiningDomainPromptListResult, MiningEventRecord } from "../wallet/mining/index.js";
3
3
  import type { PendingMutationRecord } from "../wallet/types.js";
4
4
  import type { JsonAvailabilityEntry, JsonPage } from "./output.js";
5
5
  export interface ReadJsonResult<T> {
@@ -114,6 +114,19 @@ export declare function buildMineStatusJson(mining: MiningControlPlaneView): Rea
114
114
  phase: string;
115
115
  lastSuspendDetectedAtUnixMs: number | null;
116
116
  pauseReason: string | null;
117
+ providerState: string | null;
118
+ provider: {
119
+ configured: boolean;
120
+ kind: string | null;
121
+ modelId: string | null;
122
+ effectiveModel: string | null;
123
+ modelOverride: string | null;
124
+ modelSelectionSource: string | null;
125
+ usingDefaultModel: boolean | null;
126
+ extraPromptConfigured: boolean;
127
+ estimatedDailyCostUsd: number | null;
128
+ estimatedDailyCostDisplay: string | null;
129
+ };
117
130
  fees: Record<string, unknown>;
118
131
  worker: Record<string, unknown>;
119
132
  availability: Record<string, JsonAvailabilityEntry>;
@@ -124,6 +137,18 @@ export declare function buildMineLogJson(events: MiningEventRecord[], page: Json
124
137
  rotation: number[];
125
138
  page: JsonPage;
126
139
  }>;
140
+ export declare function buildMinePromptListJson(result: MiningDomainPromptListResult): ReadJsonResult<{
141
+ fallbackPromptConfigured: boolean;
142
+ prompts: Array<{
143
+ domain: {
144
+ name: string;
145
+ domainId: number | null;
146
+ };
147
+ mineable: boolean;
148
+ prompt: string | null;
149
+ effectivePromptSource: "domain" | "global-fallback" | "none";
150
+ }>;
151
+ }>;
127
152
  export declare function buildBalanceJson(context: WalletReadContext): ReadJsonResult<{
128
153
  assetLabel: string;
129
154
  totalCogtoshi: string | null;