@did-btcr2/cli 0.22.0 → 0.23.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/README.md +36 -396
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/cjs/index.js +404 -83
  4. package/dist/esm/src/cli.js +2 -1
  5. package/dist/esm/src/cli.js.map +1 -1
  6. package/dist/esm/src/commands/completion.js +20 -7
  7. package/dist/esm/src/commands/completion.js.map +1 -1
  8. package/dist/esm/src/commands/create.js +33 -41
  9. package/dist/esm/src/commands/create.js.map +1 -1
  10. package/dist/esm/src/commands/genesis.js +119 -0
  11. package/dist/esm/src/commands/genesis.js.map +1 -0
  12. package/dist/esm/src/commands/identifier.js +3 -17
  13. package/dist/esm/src/commands/identifier.js.map +1 -1
  14. package/dist/esm/src/commands/index.js +1 -0
  15. package/dist/esm/src/commands/index.js.map +1 -1
  16. package/dist/esm/src/commands/resolve.js +15 -1
  17. package/dist/esm/src/commands/resolve.js.map +1 -1
  18. package/dist/esm/src/commands/write.js +10 -7
  19. package/dist/esm/src/commands/write.js.map +1 -1
  20. package/dist/esm/src/genesis-document-file.js +26 -0
  21. package/dist/esm/src/genesis-document-file.js.map +1 -0
  22. package/dist/esm/src/genesis-spec.js +93 -0
  23. package/dist/esm/src/genesis-spec.js.map +1 -0
  24. package/dist/esm/src/genesis-wizard.js +132 -0
  25. package/dist/esm/src/genesis-wizard.js.map +1 -0
  26. package/dist/esm/src/hints.js +14 -3
  27. package/dist/esm/src/hints.js.map +1 -1
  28. package/dist/esm/src/network-option.js +39 -0
  29. package/dist/esm/src/network-option.js.map +1 -0
  30. package/dist/esm/src/resolution-options.js +19 -2
  31. package/dist/esm/src/resolution-options.js.map +1 -1
  32. package/dist/types/src/cli.d.ts.map +1 -1
  33. package/dist/types/src/commands/completion.d.ts.map +1 -1
  34. package/dist/types/src/commands/create.d.ts +5 -4
  35. package/dist/types/src/commands/create.d.ts.map +1 -1
  36. package/dist/types/src/commands/genesis.d.ts +20 -0
  37. package/dist/types/src/commands/genesis.d.ts.map +1 -0
  38. package/dist/types/src/commands/identifier.d.ts.map +1 -1
  39. package/dist/types/src/commands/index.d.ts +1 -0
  40. package/dist/types/src/commands/index.d.ts.map +1 -1
  41. package/dist/types/src/commands/resolve.d.ts +7 -0
  42. package/dist/types/src/commands/resolve.d.ts.map +1 -1
  43. package/dist/types/src/commands/write.d.ts +2 -1
  44. package/dist/types/src/commands/write.d.ts.map +1 -1
  45. package/dist/types/src/genesis-document-file.d.ts +11 -0
  46. package/dist/types/src/genesis-document-file.d.ts.map +1 -0
  47. package/dist/types/src/genesis-spec.d.ts +70 -0
  48. package/dist/types/src/genesis-spec.d.ts.map +1 -0
  49. package/dist/types/src/genesis-wizard.d.ts +30 -0
  50. package/dist/types/src/genesis-wizard.d.ts.map +1 -0
  51. package/dist/types/src/hints.d.ts +7 -0
  52. package/dist/types/src/hints.d.ts.map +1 -1
  53. package/dist/types/src/network-option.d.ts +15 -0
  54. package/dist/types/src/network-option.d.ts.map +1 -0
  55. package/dist/types/src/resolution-options.d.ts +6 -2
  56. package/dist/types/src/resolution-options.d.ts.map +1 -1
  57. package/dist/types/src/types.d.ts +18 -1
  58. package/dist/types/src/types.d.ts.map +1 -1
  59. package/package.json +2 -2
  60. package/src/cli.ts +2 -0
  61. package/src/commands/completion.ts +21 -7
  62. package/src/commands/create.ts +39 -55
  63. package/src/commands/genesis.ts +149 -0
  64. package/src/commands/identifier.ts +3 -25
  65. package/src/commands/index.ts +1 -0
  66. package/src/commands/resolve.ts +20 -1
  67. package/src/commands/write.ts +10 -7
  68. package/src/genesis-document-file.ts +35 -0
  69. package/src/genesis-spec.ts +149 -0
  70. package/src/genesis-wizard.ts +163 -0
  71. package/src/hints.ts +13 -2
  72. package/src/network-option.ts +50 -0
  73. package/src/resolution-options.ts +21 -2
  74. package/src/types.ts +17 -2
package/dist/cjs/index.js CHANGED
@@ -51,6 +51,7 @@ __export(index_exports, {
51
51
  registerConfigCommand: () => registerConfigCommand,
52
52
  registerCreateCommand: () => registerCreateCommand,
53
53
  registerDeactivateCommand: () => registerDeactivateCommand,
54
+ registerGenesisCommand: () => registerGenesisCommand,
54
55
  registerIdentifierCommand: () => registerIdentifierCommand,
55
56
  registerInitCommand: () => registerInitCommand,
56
57
  registerKeyCommand: () => registerKeyCommand,
@@ -2097,13 +2098,35 @@ function printNextSteps(init, unlocked, session, doctor) {
2097
2098
  // src/commands/create.ts
2098
2099
  var import_utils4 = require("@noble/hashes/utils.js");
2099
2100
 
2101
+ // src/genesis-document-file.ts
2102
+ var import_promises = require("fs/promises");
2103
+ var GENESIS_DOCUMENT_HELP = "Path to the JSON genesis document of an external identifier (x). Fills sidecar.genesisDocument of the resolution options";
2104
+ async function readGenesisDocumentFile(path) {
2105
+ let parsed;
2106
+ try {
2107
+ parsed = JSON.parse(await (0, import_promises.readFile)(path, "utf-8"));
2108
+ } catch {
2109
+ throw new CLIError(
2110
+ "Invalid genesis document path. Must be a valid path to a JSON file.",
2111
+ "INVALID_ARGUMENT_ERROR",
2112
+ { genesisDocument: path }
2113
+ );
2114
+ }
2115
+ if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
2116
+ throw new CLIError(
2117
+ "Invalid genesis document. The file must contain a JSON object.",
2118
+ "INVALID_ARGUMENT_ERROR",
2119
+ { genesisDocument: path }
2120
+ );
2121
+ }
2122
+ return parsed;
2123
+ }
2124
+
2100
2125
  // src/hints.ts
2101
2126
  var import_api3 = require("@did-btcr2/api");
2102
2127
  var import_method2 = require("@did-btcr2/method");
2103
2128
  function printCreateFundingHint(g, network, did) {
2104
2129
  if (g.quiet || g.output === "json") return;
2105
- const faucet = (0, import_api3.faucetUrl)(network);
2106
- if (!faucet) return;
2107
2130
  let beaconAddress;
2108
2131
  try {
2109
2132
  const { serviceEndpoint } = import_method2.BeaconUtils.createBeaconService(did, "p2wpkh", "SingletonBeacon");
@@ -2111,6 +2134,12 @@ function printCreateFundingHint(g, network, did) {
2111
2134
  } catch {
2112
2135
  return;
2113
2136
  }
2137
+ printBeaconFundingHint(g, network, beaconAddress);
2138
+ }
2139
+ function printBeaconFundingHint(g, network, beaconAddress) {
2140
+ if (g.quiet || g.output === "json") return;
2141
+ const faucet = (0, import_api3.faucetUrl)(network);
2142
+ if (!faucet) return;
2114
2143
  const explorer = (0, import_api3.explorerAddressUrl)(network, beaconAddress);
2115
2144
  const lines = [
2116
2145
  "Fund the initial beacon to anchor updates:",
@@ -2166,33 +2195,59 @@ function resolveKeyRef(kms, ref) {
2166
2195
  throw new CLIError(`No key matches reference "${ref}".`, "KEY_NOT_FOUND_ERROR", { ref });
2167
2196
  }
2168
2197
 
2198
+ // src/network-option.ts
2199
+ var NETWORK_OPTION_HELP = "Identifier bitcoin network <bitcoin|testnet3|testnet4|signet|mutinynet|regtest> (default: config defaults.network, else regtest)";
2200
+ function overridesFromGlobals(g) {
2201
+ return {
2202
+ home: g.home,
2203
+ config: g.config,
2204
+ profile: g.profile,
2205
+ keystore: g.keystore,
2206
+ passphraseFile: g.passphraseFile
2207
+ };
2208
+ }
2209
+ function resolveNetworkOption(explicit, overrides) {
2210
+ if (!explicit) return resolveDefaultNetwork(overrides);
2211
+ if (!SUPPORTED_NETWORKS.includes(explicit)) {
2212
+ throw new CLIError(
2213
+ 'Invalid network. Must be one of "bitcoin", "testnet3", "testnet4", "signet", "mutinynet", or "regtest".',
2214
+ "INVALID_ARGUMENT_ERROR",
2215
+ { network: explicit }
2216
+ );
2217
+ }
2218
+ return explicit;
2219
+ }
2220
+ function warnProfileNetworkMismatch(g, network, overrides) {
2221
+ const mismatch = profileNetworkMismatch(network, overrides);
2222
+ if (mismatch && !g.quiet) {
2223
+ process.stderr.write(
2224
+ `Warning: creating a "${network}" identifier while the active profile "${mismatch.profile}" declares network "${mismatch.declared}". The identifier's network and the profile's endpoints may not match.
2225
+ `
2226
+ );
2227
+ }
2228
+ }
2229
+
2169
2230
  // src/commands/create.ts
2170
2231
  var EXPECTED_BYTES = {
2171
2232
  k: { length: 33, label: "secp256k1 compressed public key (33 bytes)" },
2172
2233
  x: { length: 32, label: "SHA-256 hash (32 bytes)" }
2173
2234
  };
2174
2235
  function registerCreateCommand(program, factory, keystoreFactory, globals) {
2175
- program.command("create").description("Create an identifier and initial DID document").option("-t, --type <type>", "Identifier type <k|x>", "k").option(
2176
- "-n, --network <network>",
2177
- "Identifier bitcoin network <bitcoin|testnet3|testnet4|signet|mutinynet|regtest> (default: config defaults.network, else regtest)"
2178
- ).option(
2236
+ program.command("create").description("Create an identifier and initial DID document").option("-t, --type <type>", "Identifier type <k|x>", "k").option("-n, --network <network>", NETWORK_OPTION_HELP).option(
2179
2237
  "-b, --bytes <bytes>",
2180
2238
  "Genesis bytes as a hex string. For type=k, a 33-byte secp256k1 public key (omit to generate a key). For type=x, the 32-byte SHA-256 hash of a genesis document."
2239
+ ).option(
2240
+ "--document <path>",
2241
+ 'For type=x, the path of the JSON genesis document to hash (see "btcr2 genesis build"). Exclusive with --bytes.'
2181
2242
  ).action(async (options) => {
2182
2243
  const g = globals();
2183
2244
  if (options.type !== "k" && options.type !== "x") {
2184
2245
  throw new CLIError('Invalid type. Must be "k" or "x".', "INVALID_ARGUMENT_ERROR", options);
2185
2246
  }
2186
2247
  const overrides = overridesFromGlobals(g);
2187
- const network = resolveNetwork(options.network, overrides);
2248
+ const network = resolveNetworkOption(options.network, overrides);
2188
2249
  const signingKey = g.signingKey;
2189
- const mismatch = profileNetworkMismatch(network, overrides);
2190
- if (mismatch && !g.quiet) {
2191
- process.stderr.write(
2192
- `Warning: creating a "${network}" identifier while the active profile "${mismatch.profile}" declares network "${mismatch.declared}". The identifier's network and the profile's endpoints may not match.
2193
- `
2194
- );
2195
- }
2250
+ warnProfileNetworkMismatch(g, network, overrides);
2196
2251
  const print = (result, note) => {
2197
2252
  console.log(formatResult(result, g));
2198
2253
  if (note && g.output !== "json") process.stderr.write(`${note}
@@ -2205,9 +2260,21 @@ function registerCreateCommand(program, factory, keystoreFactory, globals) {
2205
2260
  "INVALID_ARGUMENT_ERROR"
2206
2261
  );
2207
2262
  }
2263
+ if (options.bytes !== void 0 && options.document !== void 0) {
2264
+ throw new CLIError("Provide at most one of --bytes or --document.", "INVALID_ARGUMENT_ERROR");
2265
+ }
2266
+ if (options.document !== void 0) {
2267
+ const genesisDocument = await readGenesisDocumentFile(options.document);
2268
+ const api2 = factory();
2269
+ const { did: did3, genesisBytes: genesisBytes2, didDocument } = api2.btcr2.createExternalFromDocument(genesisDocument, { network });
2270
+ print({ action: "create", data: did3, genesisBytes: (0, import_utils4.bytesToHex)(genesisBytes2) });
2271
+ const beacons = api2.btcr2.getBeacons(didDocument);
2272
+ if (beacons.length > 0) printBeaconFundingHint(g, network, beacons[0].address);
2273
+ return;
2274
+ }
2208
2275
  if (options.bytes === void 0) {
2209
2276
  throw new CLIError(
2210
- "External identifiers (-t x) require --bytes <hex>, the 32-byte genesis document hash. Key generation is only available for -t k.",
2277
+ "External identifiers (-t x) require --document <path>, the genesis document, or --bytes <hex>, its 32-byte hash. Key generation is only available for -t k.",
2211
2278
  "INVALID_ARGUMENT_ERROR"
2212
2279
  );
2213
2280
  }
@@ -2216,6 +2283,9 @@ function registerCreateCommand(program, factory, keystoreFactory, globals) {
2216
2283
  print({ action: "create", data: did2 });
2217
2284
  return;
2218
2285
  }
2286
+ if (options.document !== void 0) {
2287
+ throw new CLIError("--document applies only to external identifiers (-t x).", "INVALID_ARGUMENT_ERROR");
2288
+ }
2219
2289
  if (options.bytes !== void 0 && signingKey) {
2220
2290
  throw new CLIError(
2221
2291
  "Provide at most one of --bytes or --signing-key.",
@@ -2252,26 +2322,6 @@ function registerCreateCommand(program, factory, keystoreFactory, globals) {
2252
2322
  printCreateFundingHint(g, network, did);
2253
2323
  });
2254
2324
  }
2255
- function overridesFromGlobals(g) {
2256
- return {
2257
- home: g.home,
2258
- config: g.config,
2259
- profile: g.profile,
2260
- keystore: g.keystore,
2261
- passphraseFile: g.passphraseFile
2262
- };
2263
- }
2264
- function resolveNetwork(explicit, overrides) {
2265
- if (!explicit) return resolveDefaultNetwork(overrides);
2266
- if (!SUPPORTED_NETWORKS.includes(explicit)) {
2267
- throw new CLIError(
2268
- 'Invalid network. Must be one of "bitcoin", "testnet3", "testnet4", "signet", "mutinynet", or "regtest".',
2269
- "INVALID_ARGUMENT_ERROR",
2270
- { network: explicit }
2271
- );
2272
- }
2273
- return explicit;
2274
- }
2275
2325
  function parseGenesisBytes(hex, type) {
2276
2326
  const expected = EXPECTED_BYTES[type];
2277
2327
  let bytes;
@@ -2299,7 +2349,10 @@ var import_api5 = require("@did-btcr2/api");
2299
2349
 
2300
2350
  // src/resolution-options.ts
2301
2351
  var import_api4 = require("@did-btcr2/api");
2302
- var import_promises = require("fs/promises");
2352
+ var import_promises2 = require("fs/promises");
2353
+ function hasResolutionFlags(flags) {
2354
+ return flags.resolutionOptions !== void 0 || flags.resolutionOptionsPath !== void 0 || flags.minConf !== void 0 || flags.genesisDocument !== void 0;
2355
+ }
2303
2356
  var MIN_CONF_HELP = `Minimum block confirmations a beacon signal needs before resolution applies it (positive integer, default: ${import_api4.DEFAULT_MIN_CONF}). Overrides a minConf inside the JSON resolution options`;
2304
2357
  async function readResolutionOptions(flags) {
2305
2358
  let resolutionOptions;
@@ -2315,7 +2368,7 @@ async function readResolutionOptions(flags) {
2315
2368
  }
2316
2369
  } else if (flags.resolutionOptionsPath) {
2317
2370
  try {
2318
- const content = await (0, import_promises.readFile)(flags.resolutionOptionsPath, "utf-8");
2371
+ const content = await (0, import_promises2.readFile)(flags.resolutionOptionsPath, "utf-8");
2319
2372
  resolutionOptions = JSON.parse(content);
2320
2373
  } catch {
2321
2374
  throw new CLIError(
@@ -2328,6 +2381,13 @@ async function readResolutionOptions(flags) {
2328
2381
  if (flags.minConf !== void 0) {
2329
2382
  resolutionOptions = { ...resolutionOptions ?? {}, minConf: flags.minConf };
2330
2383
  }
2384
+ if (flags.genesisDocument !== void 0) {
2385
+ const genesisDocument = await readGenesisDocumentFile(flags.genesisDocument);
2386
+ resolutionOptions = {
2387
+ ...resolutionOptions ?? {},
2388
+ sidecar: { ...resolutionOptions?.sidecar ?? {}, genesisDocument }
2389
+ };
2390
+ }
2331
2391
  return resolutionOptions;
2332
2392
  }
2333
2393
  function parseMinConf(value) {
@@ -2343,7 +2403,7 @@ function parseMinConf(value) {
2343
2403
 
2344
2404
  // src/commands/resolve.ts
2345
2405
  function registerResolveCommand(program, factory, globals) {
2346
- program.command("resolve").alias("read").description("Resolve the DID document of the identifier.").requiredOption("-i, --identifier <identifier>", "did:btcr2 identifier").option("-r, --resolution-options <json>", "JSON string containing resolution options").option("-p, --resolution-options-path <path>", "Path to a JSON file containing resolution options").option("--min-conf <n>", MIN_CONF_HELP, parseMinConf).action(async (options) => {
2406
+ program.command("resolve").alias("read").description("Resolve the DID document of the identifier.").requiredOption("-i, --identifier <identifier>", "did:btcr2 identifier").option("-r, --resolution-options <json>", "JSON string containing resolution options").option("-p, --resolution-options-path <path>", "Path to a JSON file containing resolution options").option("--min-conf <n>", MIN_CONF_HELP, parseMinConf).option("--genesis-document <path>", GENESIS_DOCUMENT_HELP).action(async (options) => {
2347
2407
  const parsed = await validateResolveOptions(options);
2348
2408
  const network = deriveNetwork(parsed.identifier);
2349
2409
  const api = factory(network, globals());
@@ -2353,13 +2413,24 @@ function registerResolveCommand(program, factory, globals) {
2353
2413
  });
2354
2414
  }
2355
2415
  async function validateResolveOptions(options) {
2356
- import_api5.Identifier.decode(options.identifier);
2416
+ const components = import_api5.Identifier.decode(options.identifier);
2417
+ assertGenesisDocumentApplies(options, components.hrp);
2357
2418
  const resolutionOptions = await readResolutionOptions(options);
2358
2419
  return { identifier: options.identifier, options: resolutionOptions };
2359
2420
  }
2421
+ function assertGenesisDocumentApplies(flags, hrp) {
2422
+ if (flags.genesisDocument !== void 0 && hrp === "k") {
2423
+ throw new CLIError(
2424
+ "--genesis-document applies only to external identifiers (x).",
2425
+ "INVALID_ARGUMENT_ERROR",
2426
+ { genesisDocument: flags.genesisDocument }
2427
+ );
2428
+ }
2429
+ }
2360
2430
 
2361
2431
  // src/commands/write.ts
2362
2432
  var import_key_manager2 = require("@did-btcr2/key-manager");
2433
+ var import_api6 = require("@did-btcr2/api");
2363
2434
  function registerWriteOptions(command) {
2364
2435
  return command.option(
2365
2436
  "-s, --source-document <json>",
@@ -2381,7 +2452,7 @@ function registerWriteOptions(command) {
2381
2452
  ).option(
2382
2453
  "--resolution-options-path <path>",
2383
2454
  "Path to a JSON file with resolution options, for the resolution of the source document"
2384
- ).option("--min-conf <n>", MIN_CONF_HELP, parseMinConf).option(
2455
+ ).option("--min-conf <n>", MIN_CONF_HELP, parseMinConf).option("--genesis-document <path>", `${GENESIS_DOCUMENT_HELP}, for the resolution of the source document`).option(
2385
2456
  "--publish-to-cas <mode>",
2386
2457
  "Publish update artifacts to a writable CAS before broadcast: auto|always|never. CAS publication is optional; the default distributes the returned artifacts via sidecar.",
2387
2458
  parsePublishToCasMode,
@@ -2406,14 +2477,14 @@ async function prepareWrite(options, factory, g) {
2406
2477
  { did }
2407
2478
  );
2408
2479
  }
2409
- const hasResolutionFlags = options.resolutionOptions !== void 0 || options.resolutionOptionsPath !== void 0 || options.minConf !== void 0;
2410
- if (hasDocument && hasResolutionFlags) {
2480
+ if (hasDocument && hasResolutionFlags(options)) {
2411
2481
  throw new CLIError(
2412
- "--resolution-options, --resolution-options-path, and --min-conf apply only when --source-document and --source-version-id are omitted. A supplied source pair skips resolution.",
2482
+ "--resolution-options, --resolution-options-path, --min-conf, and --genesis-document apply only when --source-document and --source-version-id are omitted. A supplied source pair skips resolution.",
2413
2483
  "INVALID_ARGUMENT_ERROR",
2414
2484
  { did }
2415
2485
  );
2416
2486
  }
2487
+ assertGenesisDocumentApplies(options, import_api6.Identifier.decode(did).hrp);
2417
2488
  assertKeystoreAllowedForNetwork(network, g);
2418
2489
  const resolutionOptions = await readResolutionOptions(options);
2419
2490
  const api = factory(network, g);
@@ -2502,9 +2573,8 @@ function registerDeactivateCommand(program, factory, globals) {
2502
2573
  }
2503
2574
 
2504
2575
  // src/commands/identifier.ts
2505
- var import_api6 = require("@did-btcr2/api");
2576
+ var import_api7 = require("@did-btcr2/api");
2506
2577
  var import_utils5 = require("@noble/hashes/utils.js");
2507
- var import_promises2 = require("fs/promises");
2508
2578
  function registerIdentifierCommand(program, factory, globals) {
2509
2579
  const identifier = program.command("identifier").description("Decode and validate did:btcr2 identifiers (offline).");
2510
2580
  const print = (result) => console.log(formatResult(result, globals()));
@@ -2545,7 +2615,7 @@ function registerIdentifierCommand(program, factory, globals) {
2545
2615
  { did }
2546
2616
  );
2547
2617
  }
2548
- const genesisDocument = options.genesisDocument === void 0 ? void 0 : await readGenesisDocument(options.genesisDocument);
2618
+ const genesisDocument = options.genesisDocument === void 0 ? void 0 : await readGenesisDocumentFile(options.genesisDocument);
2549
2619
  data.initialDocument = api.btcr2.getInitialDocument(did, genesisDocument);
2550
2620
  }
2551
2621
  print({ action: "identifier-decode", data });
@@ -2560,14 +2630,14 @@ function registerIdentifierCommand(program, factory, globals) {
2560
2630
  const genesisBytes = options.bytes === void 0 ? void 0 : parseHexBytes(options.bytes);
2561
2631
  let genesisDocument;
2562
2632
  if (options.genesisDocument !== void 0) {
2563
- if (import_api6.Identifier.isValid(did) && import_api6.Identifier.decode(did).hrp === "k") {
2633
+ if (import_api7.Identifier.isValid(did) && import_api7.Identifier.decode(did).hrp === "k") {
2564
2634
  throw new CLIError(
2565
2635
  "--genesis-document applies only to external identifiers (x).",
2566
2636
  "INVALID_ARGUMENT_ERROR",
2567
2637
  { did }
2568
2638
  );
2569
2639
  }
2570
- genesisDocument = await readGenesisDocument(options.genesisDocument);
2640
+ genesisDocument = await readGenesisDocumentFile(options.genesisDocument);
2571
2641
  }
2572
2642
  const report = factory().did.validate(did, { genesisBytes, genesisDocument });
2573
2643
  print({ action: "identifier-validate", data: report });
@@ -2575,7 +2645,7 @@ function registerIdentifierCommand(program, factory, globals) {
2575
2645
  });
2576
2646
  }
2577
2647
  function assertValidIdentifier(did) {
2578
- const report = import_api6.Identifier.validate(did);
2648
+ const report = import_api7.Identifier.validate(did);
2579
2649
  if (report.valid) return;
2580
2650
  const failed = report.checks[report.checks.length - 1];
2581
2651
  throw new CLIError(
@@ -2591,31 +2661,275 @@ function parseHexBytes(value) {
2591
2661
  throw new CLIError("Invalid bytes: not valid hex.", "INVALID_ARGUMENT_ERROR", { bytes: value });
2592
2662
  }
2593
2663
  }
2594
- async function readGenesisDocument(path) {
2664
+
2665
+ // src/commands/genesis.ts
2666
+ var import_utils7 = require("@noble/hashes/utils.js");
2667
+ var import_node_fs9 = require("fs");
2668
+ var import_promises3 = require("fs/promises");
2669
+ var import_node_path6 = require("path");
2670
+ var import_promises4 = require("readline/promises");
2671
+
2672
+ // src/genesis-spec.ts
2673
+ var import_utils6 = require("@noble/hashes/utils.js");
2674
+ function parseGenesisSpec(value, source) {
2675
+ const fail = (message) => {
2676
+ throw new CLIError(`Invalid genesis spec: ${message}`, "INVALID_ARGUMENT_ERROR", { spec: source });
2677
+ };
2678
+ if (value === null || typeof value !== "object" || Array.isArray(value)) {
2679
+ return fail("the file must contain a JSON object.");
2680
+ }
2681
+ const spec = value;
2682
+ if (!Array.isArray(spec.verificationMethods) || spec.verificationMethods.length === 0) {
2683
+ return fail('"verificationMethods" must be a non-empty array.');
2684
+ }
2685
+ spec.verificationMethods.forEach((vm, index) => {
2686
+ if (vm === null || typeof vm !== "object") return fail(`verificationMethods[${index}] must be an object.`);
2687
+ const hasKey = typeof vm.key === "string" && vm.key.length > 0;
2688
+ const hasPublicKey = typeof vm.publicKey === "string" && vm.publicKey.length > 0;
2689
+ if (hasKey === hasPublicKey) {
2690
+ return fail(`verificationMethods[${index}] needs exactly one of "key" (a keystore reference) or "publicKey" (hex).`);
2691
+ }
2692
+ });
2693
+ if (spec.beacons !== void 0) {
2694
+ if (!Array.isArray(spec.beacons)) return fail('"beacons" must be an array.');
2695
+ spec.beacons.forEach((beacon, index) => {
2696
+ if (beacon === null || typeof beacon !== "object") return fail(`beacons[${index}] must be an object.`);
2697
+ const sources = ["key", "publicKey", "address"].filter((name) => beacon[name] !== void 0);
2698
+ if (sources.length > 1) {
2699
+ return fail(`beacons[${index}] names ${sources.join(" and ")}. Give only one of "key", "publicKey", or "address".`);
2700
+ }
2701
+ });
2702
+ }
2703
+ if (spec.services !== void 0 && !Array.isArray(spec.services)) {
2704
+ return fail('"services" must be an array.');
2705
+ }
2706
+ return spec;
2707
+ }
2708
+ function toApiSpec(spec, network, resolvePublicKey) {
2709
+ const keyBytes = (entry, label) => entry.key !== void 0 ? resolvePublicKey(entry.key) : parseHexPublicKey(entry.publicKey, label);
2710
+ const verificationMethods = spec.verificationMethods.map((vm, index) => ({
2711
+ publicKey: keyBytes(vm, `verificationMethods[${index}].publicKey`),
2712
+ relationships: vm.relationships,
2713
+ fragment: vm.fragment
2714
+ }));
2715
+ const beacons = spec.beacons?.map((beacon, index) => ({
2716
+ type: beacon.type,
2717
+ publicKey: beacon.key !== void 0 || beacon.publicKey !== void 0 ? keyBytes(beacon, `beacons[${index}].publicKey`) : void 0,
2718
+ addressType: beacon.addressType,
2719
+ address: beacon.address,
2720
+ fragment: beacon.fragment
2721
+ }));
2722
+ return {
2723
+ network,
2724
+ verificationMethods,
2725
+ beacons,
2726
+ services: spec.services
2727
+ };
2728
+ }
2729
+ function parseHexPublicKey(hex, label) {
2730
+ try {
2731
+ return (0, import_utils6.hexToBytes)(hex.trim());
2732
+ } catch {
2733
+ throw new CLIError(`Invalid ${label}: not valid hex.`, "INVALID_ARGUMENT_ERROR", { publicKey: hex });
2734
+ }
2735
+ }
2736
+
2737
+ // src/genesis-wizard.ts
2738
+ var import_api8 = require("@did-btcr2/api");
2739
+ var PUBLIC_KEY_HEX = /^0[23][0-9a-fA-F]{64}$/;
2740
+ async function collectGenesisSpec(ask, ctx) {
2741
+ const verificationMethods = await collectVerificationMethods(ask, ctx);
2742
+ const beacons = await collectBeacons(ask, ctx, verificationMethods[0]);
2743
+ const services = await collectServices(ask, ctx);
2744
+ return { verificationMethods, beacons, ...services.length > 0 && { services } };
2745
+ }
2746
+ async function collectVerificationMethods(ask, ctx) {
2747
+ if (ctx.keys.length === 0) {
2748
+ ctx.say("The keystore has no keys. Enter each key as a 33-byte compressed public key in hex.");
2749
+ } else {
2750
+ ctx.say("Keys in the keystore:");
2751
+ for (const key of ctx.keys) {
2752
+ ctx.say(` ${key.fingerprint}${key.name ? ` ${key.name}` : ""}${key.active ? " (active)" : ""}`);
2753
+ }
2754
+ }
2755
+ const active = ctx.keys.find((key) => key.active);
2756
+ const methods = [];
2757
+ for (let n = 1; ; n++) {
2758
+ const defaultRef = n === 1 && active ? active.name ?? active.fingerprint : void 0;
2759
+ const source = await askKeySource(ask, ctx, `Verification method ${n}: key reference or public key hex`, defaultRef);
2760
+ const relationships = await askRelationships(ask, ctx, n);
2761
+ methods.push({ ...source, ...relationships && { relationships } });
2762
+ if (!await askYesNo(ask, "Add another verification method?")) break;
2763
+ }
2764
+ return methods;
2765
+ }
2766
+ async function collectBeacons(ask, ctx, firstMethod) {
2767
+ const beacons = [];
2768
+ for (let n = 1; ; n++) {
2769
+ const type = await askChoice(ask, ctx, `Beacon ${n} type`, import_api8.BEACON_TYPES, "SingletonBeacon");
2770
+ const address = (await ask(`Beacon ${n} address (blank: derive the address from the key of method 1): `)).trim();
2771
+ if (address.length === 0) {
2772
+ const addressType = await askChoice(ask, ctx, `Beacon ${n} address type`, import_api8.BEACON_ADDRESS_TYPES, import_api8.DEFAULT_BEACON_ADDRESS_TYPE);
2773
+ const keySource = firstMethod.key !== void 0 ? { key: firstMethod.key } : { publicKey: firstMethod.publicKey };
2774
+ beacons.push({ type, ...keySource, addressType });
2775
+ } else {
2776
+ beacons.push({ type, address });
2777
+ }
2778
+ if (!await askYesNo(ask, "Add another beacon?")) break;
2779
+ }
2780
+ return beacons;
2781
+ }
2782
+ async function collectServices(ask, ctx) {
2783
+ const services = [];
2784
+ while (await askYesNo(ask, "Add a service?")) {
2785
+ const n = services.length + 1;
2786
+ const fragment = (await ask(`Service ${n} id fragment (blank: service-<position>): `)).trim();
2787
+ const type = await askRequired(ask, ctx, `Service ${n} type`);
2788
+ const serviceEndpoint = await askRequired(ask, ctx, `Service ${n} endpoint`);
2789
+ services.push({ ...fragment.length > 0 && { id: `#${fragment.replace(/^#/, "")}` }, type, serviceEndpoint });
2790
+ }
2791
+ return services;
2792
+ }
2793
+ async function askKeySource(ask, ctx, question, defaultRef) {
2794
+ for (; ; ) {
2795
+ const raw = (await ask(`${question}${defaultRef ? ` [${defaultRef}]` : ""}: `)).trim();
2796
+ const answer = raw.length > 0 ? raw : defaultRef;
2797
+ if (answer === void 0) {
2798
+ ctx.say("A key is required.");
2799
+ continue;
2800
+ }
2801
+ if (PUBLIC_KEY_HEX.test(answer)) return { publicKey: answer.toLowerCase() };
2802
+ try {
2803
+ ctx.resolveKey(answer);
2804
+ return { key: answer };
2805
+ } catch (error) {
2806
+ ctx.say(error.message);
2807
+ }
2808
+ }
2809
+ }
2810
+ async function askRelationships(ask, ctx, n) {
2811
+ const names = import_api8.VERIFICATION_RELATIONSHIPS.join(", ");
2812
+ for (; ; ) {
2813
+ const raw = (await ask(`Relationships of method ${n} (comma-separated: ${names}) [all]: `)).trim();
2814
+ if (raw.length === 0) return void 0;
2815
+ const chosen = raw.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
2816
+ const unknown = chosen.filter((name) => !import_api8.VERIFICATION_RELATIONSHIPS.includes(name));
2817
+ if (unknown.length === 0) return chosen;
2818
+ ctx.say(`Unknown relationship: ${unknown.join(", ")}. Expected one of ${names}.`);
2819
+ }
2820
+ }
2821
+ async function askChoice(ask, ctx, question, choices, defaultValue) {
2822
+ for (; ; ) {
2823
+ const raw = (await ask(`${question} (${choices.join(", ")}) [${defaultValue}]: `)).trim();
2824
+ if (raw.length === 0) return defaultValue;
2825
+ if (choices.includes(raw)) return raw;
2826
+ ctx.say(`Unknown value "${raw}". Expected one of ${choices.join(", ")}.`);
2827
+ }
2828
+ }
2829
+ async function askRequired(ask, ctx, question) {
2830
+ for (; ; ) {
2831
+ const raw = (await ask(`${question}: `)).trim();
2832
+ if (raw.length > 0) return raw;
2833
+ ctx.say("A value is required.");
2834
+ }
2835
+ }
2836
+ async function askYesNo(ask, question) {
2837
+ const raw = (await ask(`${question} [y/N]: `)).trim();
2838
+ return /^y(es)?$/i.test(raw);
2839
+ }
2840
+
2841
+ // src/commands/genesis.ts
2842
+ var DEFAULT_GENESIS_PATH = "genesis.json";
2843
+ function registerGenesisCommand(program, factory, keystoreFactory, globals) {
2844
+ const genesis = program.command("genesis").description("Build the genesis document of an external identifier (offline).");
2845
+ const print = (result) => console.log(formatResult(result, globals()));
2846
+ genesis.command("build").description("Build a genesis document, write it to a file, and print the external identifier.").option("-n, --network <network>", NETWORK_OPTION_HELP).option("--spec <path>", "Path to a JSON spec file with the keys, beacons, and services. Runs without prompts.").option("--out <path>", "Path of the genesis document to write.", DEFAULT_GENESIS_PATH).option("--force", "Overwrite an existing --out file.", false).action(async (options) => {
2847
+ const g = globals();
2848
+ const overrides = overridesFromGlobals(g);
2849
+ const network = resolveNetworkOption(options.network, overrides);
2850
+ warnProfileNetworkMismatch(g, network, overrides);
2851
+ const outPath = (0, import_node_path6.resolve)(options.out);
2852
+ if (!options.force && (0, import_node_fs9.existsSync)(outPath)) {
2853
+ throw new CLIError(
2854
+ `The file ${outPath} exists. Pass --force to overwrite it, or --out <path> for another file.`,
2855
+ "INVALID_ARGUMENT_ERROR",
2856
+ { out: outPath }
2857
+ );
2858
+ }
2859
+ let keystoreApi;
2860
+ const kms = () => (keystoreApi ??= keystoreFactory(void 0, overrides)).kms.kms;
2861
+ const resolvePublicKey = (ref) => kms().getPublicKey(resolveKeyRef(kms(), ref));
2862
+ const spec = options.spec !== void 0 ? await readSpecFile(options.spec) : await runWizard(kms);
2863
+ const api = factory();
2864
+ const document2 = api.btcr2.buildGenesisDocument(toApiSpec(spec, network, resolvePublicKey));
2865
+ const json = `${JSON.stringify(document2, null, 2)}
2866
+ `;
2867
+ const { did, genesisBytes, didDocument } = api.btcr2.createExternalFromDocument(JSON.parse(json), { network });
2868
+ await (0, import_promises3.writeFile)(outPath, json, { encoding: "utf-8", flag: options.force ? "w" : "wx" });
2869
+ const beacons = api.btcr2.getBeacons(didDocument);
2870
+ print({
2871
+ action: "genesis-build",
2872
+ data: { did, network, genesisBytes: (0, import_utils7.bytesToHex)(genesisBytes), path: outPath, beacons }
2873
+ });
2874
+ if (!g.quiet && g.output !== "json") {
2875
+ process.stderr.write(
2876
+ `Wrote the genesis document to ${outPath}. Keep it: the identifier resolves only with it. Pass --genesis-document <path> to resolve, update, and deactivate, or publish it to a CAS.
2877
+ `
2878
+ );
2879
+ if (beacons.length > 0) printBeaconFundingHint(g, network, beacons[0].address);
2880
+ }
2881
+ });
2882
+ }
2883
+ async function readSpecFile(path) {
2595
2884
  let parsed;
2596
2885
  try {
2597
- parsed = JSON.parse(await (0, import_promises2.readFile)(path, "utf-8"));
2886
+ parsed = JSON.parse(await (0, import_promises3.readFile)(path, "utf-8"));
2598
2887
  } catch {
2599
2888
  throw new CLIError(
2600
- "Invalid genesis document path. Must be a valid path to a JSON file.",
2889
+ "Invalid genesis spec path. Must be a valid path to a JSON file.",
2601
2890
  "INVALID_ARGUMENT_ERROR",
2602
- { genesisDocument: path }
2891
+ { spec: path }
2603
2892
  );
2604
2893
  }
2605
- if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
2894
+ return parseGenesisSpec(parsed, path);
2895
+ }
2896
+ async function runWizard(kms) {
2897
+ if (!process.stdin.isTTY) {
2606
2898
  throw new CLIError(
2607
- "Invalid genesis document. The file must contain a JSON object.",
2608
- "INVALID_ARGUMENT_ERROR",
2609
- { genesisDocument: path }
2899
+ "No terminal is attached. Pass --spec <path> to build the genesis document without prompts.",
2900
+ "INVALID_ARGUMENT_ERROR"
2610
2901
  );
2611
2902
  }
2612
- return parsed;
2903
+ const store = kms();
2904
+ const rl = (0, import_promises4.createInterface)({ input: process.stdin, output: process.stderr });
2905
+ try {
2906
+ return await collectGenesisSpec((question) => rl.question(question), {
2907
+ say: (line) => process.stderr.write(`${line}
2908
+ `),
2909
+ keys: listKeys(store),
2910
+ resolveKey: (ref) => resolveKeyRef(store, ref)
2911
+ });
2912
+ } finally {
2913
+ rl.close();
2914
+ }
2915
+ }
2916
+ function listKeys(kms) {
2917
+ const active = kms.activeKeyId;
2918
+ return kms.listKeys().map((id) => {
2919
+ const entry = kms.getEntry(id);
2920
+ return {
2921
+ keyId: id,
2922
+ fingerprint: id.split(":").pop() ?? id,
2923
+ ...entry.tags?.name && { name: entry.tags.name },
2924
+ active: id === active
2925
+ };
2926
+ });
2613
2927
  }
2614
2928
 
2615
2929
  // src/commands/key.ts
2616
2930
  var import_keypair = require("@did-btcr2/keypair");
2617
- var import_utils6 = require("@noble/hashes/utils.js");
2618
- var import_node_fs9 = require("fs");
2931
+ var import_utils8 = require("@noble/hashes/utils.js");
2932
+ var import_node_fs10 = require("fs");
2619
2933
  function registerKeyCommand(program, factory, globals) {
2620
2934
  const key = program.command("key").description("Manage keypairs in the encrypted keystore.");
2621
2935
  const print = (result) => console.log(formatResult(result, globals()));
@@ -2624,7 +2938,7 @@ function registerKeyCommand(program, factory, globals) {
2624
2938
  assertNameAvailable(api.kms.kms, options.name);
2625
2939
  const setActive = options.setActive ?? false;
2626
2940
  const id = api.kms.generateKey({ ...options.name && { tags: { name: options.name } }, setActive });
2627
- print({ action: "key-generate", data: { keyId: id, publicKey: (0, import_utils6.bytesToHex)(api.kms.getPublicKey(id)), active: setActive } });
2941
+ print({ action: "key-generate", data: { keyId: id, publicKey: (0, import_utils8.bytesToHex)(api.kms.getPublicKey(id)), active: setActive } });
2628
2942
  });
2629
2943
  key.command("list").alias("ls").description("List stored keys.").action(() => {
2630
2944
  const kms = factory(void 0, globals()).kms.kms;
@@ -2644,7 +2958,7 @@ function registerKeyCommand(program, factory, globals) {
2644
2958
  const kms = factory(void 0, globals()).kms.kms;
2645
2959
  const id = resolveKeyRef(kms, ref);
2646
2960
  const entry = kms.getEntry(id);
2647
- print({ action: "key-show", data: { keyId: id, publicKey: (0, import_utils6.bytesToHex)(entry.publicKey), ...entry.tags && { tags: entry.tags } } });
2961
+ print({ action: "key-show", data: { keyId: id, publicKey: (0, import_utils8.bytesToHex)(entry.publicKey), ...entry.tags && { tags: entry.tags } } });
2648
2962
  });
2649
2963
  key.command("import").description("Import a key: a secret from a hex file, or a public key as watch-only.").option("--secret-file <path>", "Path to a file containing a 32-byte secret key as hex.").option("--public <hex>", "A 33-byte compressed public key as hex (imported watch-only).").option("--name <name>", "A human-friendly name, stored as a tag.").option("--set-active", "Make this the active key.", false).action((options) => {
2650
2964
  if (Boolean(options.secretFile) === Boolean(options.public)) {
@@ -2655,13 +2969,13 @@ function registerKeyCommand(program, factory, globals) {
2655
2969
  const keyPair = options.secretFile ? new import_keypair.SchnorrKeyPair({ secretKey: readHexFile(options.secretFile, 32, "--secret-file") }) : new import_keypair.SchnorrKeyPair({ publicKey: parseHex(options.public ?? "", 33, "--public") });
2656
2970
  const setActive = options.setActive ?? false;
2657
2971
  const id = api.kms.import(keyPair, { ...options.name && { tags: { name: options.name } }, setActive });
2658
- print({ action: "key-import", data: { keyId: id, publicKey: (0, import_utils6.bytesToHex)(api.kms.getPublicKey(id)), watchOnly: !options.secretFile, active: setActive } });
2972
+ print({ action: "key-import", data: { keyId: id, publicKey: (0, import_utils8.bytesToHex)(api.kms.getPublicKey(id)), watchOnly: !options.secretFile, active: setActive } });
2659
2973
  });
2660
2974
  key.command("export <ref>").description("Export a key. Public material by default; --secret writes the secret to a file.").option("--secret", "Export the secret key. Requires --out.", false).option("--out <path>", "Write the exported secret to this file (created 0600).").action((ref, options) => {
2661
2975
  const api = factory(void 0, globals());
2662
2976
  const id = resolveKeyRef(api.kms.kms, ref);
2663
2977
  if (!options.secret) {
2664
- print({ action: "key-export", data: { keyId: id, publicKey: (0, import_utils6.bytesToHex)(api.kms.getPublicKey(id)) } });
2978
+ print({ action: "key-export", data: { keyId: id, publicKey: (0, import_utils8.bytesToHex)(api.kms.getPublicKey(id)) } });
2665
2979
  return;
2666
2980
  }
2667
2981
  if (!options.out) {
@@ -2672,7 +2986,7 @@ function registerKeyCommand(program, factory, globals) {
2672
2986
  throw new CLIError(`Key ${id} is watch-only and has no secret to export.`, "INVALID_ARGUMENT_ERROR", { keyId: id });
2673
2987
  }
2674
2988
  process.stderr.write("warning: writing an unencrypted secret key to disk. Protect this file and delete it when done.\n");
2675
- writeSecretFile(options.out, (0, import_utils6.bytesToHex)(keyPair.secretKey.bytes));
2989
+ writeSecretFile(options.out, (0, import_utils8.bytesToHex)(keyPair.secretKey.bytes));
2676
2990
  print({ action: "key-export", data: { keyId: id, secretWrittenTo: options.out } });
2677
2991
  });
2678
2992
  key.command("delete <ref>").alias("rm").description("Delete a key from the keystore.").option("--force", "Delete even if it is the active key.", false).action((ref, options) => {
@@ -2697,7 +3011,7 @@ function assertNameAvailable(kms, name) {
2697
3011
  function parseHex(hex, expectedBytes, label) {
2698
3012
  let bytes;
2699
3013
  try {
2700
- bytes = (0, import_utils6.hexToBytes)(hex.trim());
3014
+ bytes = (0, import_utils8.hexToBytes)(hex.trim());
2701
3015
  } catch {
2702
3016
  throw new CLIError(`Invalid hex for ${label}.`, "INVALID_ARGUMENT_ERROR", { label });
2703
3017
  }
@@ -2713,7 +3027,7 @@ function parseHex(hex, expectedBytes, label) {
2713
3027
  function readHexFile(path, expectedBytes, label) {
2714
3028
  let content;
2715
3029
  try {
2716
- content = (0, import_node_fs9.readFileSync)(path, "utf-8");
3030
+ content = (0, import_node_fs10.readFileSync)(path, "utf-8");
2717
3031
  } catch {
2718
3032
  throw new CLIError(`Cannot read ${label} at ${path}.`, "INVALID_ARGUMENT_ERROR", { label, path });
2719
3033
  }
@@ -2722,7 +3036,7 @@ function readHexFile(path, expectedBytes, label) {
2722
3036
  function writeSecretFile(path, contents) {
2723
3037
  let fd;
2724
3038
  try {
2725
- fd = (0, import_node_fs9.openSync)(path, "wx", 384);
3039
+ fd = (0, import_node_fs10.openSync)(path, "wx", 384);
2726
3040
  } catch (error) {
2727
3041
  if (error.code === "EEXIST") {
2728
3042
  throw new CLIError(`Refusing to overwrite existing file ${path}. Choose a new --out path.`, "INVALID_ARGUMENT_ERROR", { path });
@@ -2730,14 +3044,14 @@ function writeSecretFile(path, contents) {
2730
3044
  throw error;
2731
3045
  }
2732
3046
  try {
2733
- (0, import_node_fs9.writeFileSync)(fd, contents);
3047
+ (0, import_node_fs10.writeFileSync)(fd, contents);
2734
3048
  } finally {
2735
- (0, import_node_fs9.closeSync)(fd);
3049
+ (0, import_node_fs10.closeSync)(fd);
2736
3050
  }
2737
3051
  }
2738
3052
 
2739
3053
  // src/commands/config.ts
2740
- var import_node_fs10 = require("fs");
3054
+ var import_node_fs11 = require("fs");
2741
3055
 
2742
3056
  // src/config-schema.ts
2743
3057
  var CONFIG_SCHEMA = {
@@ -2874,7 +3188,7 @@ function registerConfigCommand(program, globals) {
2874
3188
  const print = (result) => console.log(formatResult(result, globals()));
2875
3189
  config.command("init").description("Create a default config file with one profile per network.").option("--force", "Overwrite an existing config file.", false).action((options) => {
2876
3190
  const p = path();
2877
- if ((0, import_node_fs10.existsSync)(p) && !options.force) {
3191
+ if ((0, import_node_fs11.existsSync)(p) && !options.force) {
2878
3192
  throw new CLIError(`Config already exists at ${p}. Use --force to overwrite.`, "INVALID_ARGUMENT_ERROR", { path: p });
2879
3193
  }
2880
3194
  writeDefaultConfigFile(p);
@@ -3024,30 +3338,35 @@ function registerProfileCommand(program, globals) {
3024
3338
  }
3025
3339
 
3026
3340
  // src/commands/completion.ts
3027
- var COMMANDS = "create resolve read update deactivate delete identifier key config profile completion";
3341
+ var HELP_COMMAND = "help";
3028
3342
  function registerCompletionCommand(program, _globals) {
3029
3343
  program.command("completion [shell]").description("Print a shell completion script (bash, zsh, or fish) to stdout.").action((shell = "bash") => {
3030
- console.log(completionScript(shell));
3344
+ console.log(completionScript(shell, completionWords(program)));
3031
3345
  });
3032
3346
  }
3033
- function completionScript(shell) {
3347
+ function completionWords(program) {
3348
+ const words = program.commands.flatMap((command) => [command.name(), ...command.aliases()]);
3349
+ return [...words, HELP_COMMAND];
3350
+ }
3351
+ function completionScript(shell, words) {
3352
+ const list = words.join(" ");
3034
3353
  switch (shell) {
3035
3354
  case "bash":
3036
3355
  return [
3037
3356
  '# btcr2 bash completion. Install with: eval "$(btcr2 completion bash)"',
3038
- '_btcr2() { COMPREPLY=( $(compgen -W "' + COMMANDS + '" -- "${COMP_WORDS[COMP_CWORD]}") ); }',
3357
+ '_btcr2() { COMPREPLY=( $(compgen -W "' + list + '" -- "${COMP_WORDS[COMP_CWORD]}") ); }',
3039
3358
  "complete -F _btcr2 btcr2"
3040
3359
  ].join("\n");
3041
3360
  case "zsh":
3042
3361
  return [
3043
3362
  '# btcr2 zsh completion. Install with: eval "$(btcr2 completion zsh)"',
3044
- "_btcr2() { compadd " + COMMANDS + " }",
3363
+ "_btcr2() { compadd " + list + " }",
3045
3364
  "compdef _btcr2 btcr2"
3046
3365
  ].join("\n");
3047
3366
  case "fish":
3048
3367
  return [
3049
3368
  "# btcr2 fish completion. Save to ~/.config/fish/completions/btcr2.fish",
3050
- 'complete -c btcr2 -f -a "' + COMMANDS + '"'
3369
+ 'complete -c btcr2 -f -a "' + list + '"'
3051
3370
  ].join("\n");
3052
3371
  default:
3053
3372
  throw new CLIError(`Unsupported shell "${shell}". Use bash, zsh, or fish.`, "INVALID_ARGUMENT_ERROR", { shell });
@@ -3055,18 +3374,18 @@ function completionScript(shell) {
3055
3374
  }
3056
3375
 
3057
3376
  // src/version.ts
3058
- var import_node_fs11 = require("fs");
3059
- var import_node_path6 = require("path");
3377
+ var import_node_fs12 = require("fs");
3378
+ var import_node_path7 = require("path");
3060
3379
  var import_node_url = require("url");
3061
3380
  function readVersion() {
3062
- let dir = (0, import_node_path6.dirname)((0, import_node_url.fileURLToPath)(importMetaUrl));
3381
+ let dir = (0, import_node_path7.dirname)((0, import_node_url.fileURLToPath)(importMetaUrl));
3063
3382
  for (let i = 0; i < 5; i++) {
3064
3383
  try {
3065
- const pkg = JSON.parse((0, import_node_fs11.readFileSync)((0, import_node_path6.join)(dir, "package.json"), "utf-8"));
3384
+ const pkg = JSON.parse((0, import_node_fs12.readFileSync)((0, import_node_path7.join)(dir, "package.json"), "utf-8"));
3066
3385
  if (pkg.name === "@did-btcr2/cli") return pkg.version;
3067
3386
  } catch {
3068
3387
  }
3069
- dir = (0, import_node_path6.dirname)(dir);
3388
+ dir = (0, import_node_path7.dirname)(dir);
3070
3389
  }
3071
3390
  return "0.0.0";
3072
3391
  }
@@ -3103,6 +3422,7 @@ var DidBtcr2Cli = class {
3103
3422
  registerUpdateCommand(this.program, keystoreFactory, globals);
3104
3423
  registerDeactivateCommand(this.program, keystoreFactory, globals);
3105
3424
  registerIdentifierCommand(this.program, factory, globals);
3425
+ registerGenesisCommand(this.program, factory, keystoreFactory, globals);
3106
3426
  registerKeyCommand(this.program, keystoreFactory, globals);
3107
3427
  registerKeystoreCommand(this.program, globals);
3108
3428
  registerConfigCommand(this.program, globals);
@@ -3177,6 +3497,7 @@ function handleError(error, verbose) {
3177
3497
  registerConfigCommand,
3178
3498
  registerCreateCommand,
3179
3499
  registerDeactivateCommand,
3500
+ registerGenesisCommand,
3180
3501
  registerIdentifierCommand,
3181
3502
  registerInitCommand,
3182
3503
  registerKeyCommand,