@beclab/olaresid 0.1.12 → 0.2.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 (182) hide show
  1. package/CLI-TREE.md +107 -0
  2. package/CLI.md +122 -1329
  3. package/README.md +30 -12
  4. package/SDK-TREE.md +151 -0
  5. package/TAG.md +95 -41
  6. package/config.json +6 -4
  7. package/dist/abi/TerminusDIDQueryABI.d.ts +397 -0
  8. package/dist/abi/TerminusDIDQueryABI.d.ts.map +1 -0
  9. package/dist/abi/TerminusDIDQueryABI.js +519 -0
  10. package/dist/abi/TerminusDIDQueryABI.js.map +1 -0
  11. package/dist/business/index.d.ts +1 -1
  12. package/dist/business/index.d.ts.map +1 -1
  13. package/dist/business/index.js +11 -24
  14. package/dist/business/index.js.map +1 -1
  15. package/dist/business/tag-context.d.ts +1 -0
  16. package/dist/business/tag-context.d.ts.map +1 -1
  17. package/dist/business/tag-context.js +13 -7
  18. package/dist/business/tag-context.js.map +1 -1
  19. package/dist/cli.js +238 -107
  20. package/dist/cli.js.map +1 -1
  21. package/dist/config/index.d.ts +16 -4
  22. package/dist/config/index.d.ts.map +1 -1
  23. package/dist/config/index.js +28 -14
  24. package/dist/config/index.js.map +1 -1
  25. package/dist/domain/core.d.ts +65 -0
  26. package/dist/domain/core.d.ts.map +1 -0
  27. package/dist/domain/core.js +317 -0
  28. package/dist/domain/core.js.map +1 -0
  29. package/dist/domain/index.d.ts +104 -57
  30. package/dist/domain/index.d.ts.map +1 -1
  31. package/dist/domain/index.js +188 -428
  32. package/dist/domain/index.js.map +1 -1
  33. package/dist/domain/types.d.ts +56 -0
  34. package/dist/domain/types.d.ts.map +1 -0
  35. package/dist/domain/types.js +3 -0
  36. package/dist/domain/types.js.map +1 -0
  37. package/dist/index.d.ts +81 -24
  38. package/dist/index.d.ts.map +1 -1
  39. package/dist/index.js +153 -143
  40. package/dist/index.js.map +1 -1
  41. package/dist/utils/crypto-utils.d.ts +124 -0
  42. package/dist/utils/crypto-utils.d.ts.map +1 -1
  43. package/dist/utils/crypto-utils.js +156 -8
  44. package/dist/utils/crypto-utils.js.map +1 -1
  45. package/dist/utils/error-parser.d.ts.map +1 -1
  46. package/dist/utils/error-parser.js +2 -1
  47. package/dist/utils/error-parser.js.map +1 -1
  48. package/dist/utils/event-parser.d.ts +161 -0
  49. package/dist/utils/event-parser.d.ts.map +1 -0
  50. package/dist/utils/event-parser.js +140 -0
  51. package/dist/utils/event-parser.js.map +1 -0
  52. package/dist/utils/tag-type-builder.d.ts +43 -0
  53. package/dist/utils/tag-type-builder.d.ts.map +1 -1
  54. package/dist/utils/tag-type-builder.js +122 -0
  55. package/dist/utils/tag-type-builder.js.map +1 -1
  56. package/dist/utils/tag-type-parser.d.ts +70 -0
  57. package/dist/utils/tag-type-parser.d.ts.map +1 -0
  58. package/dist/utils/tag-type-parser.js +190 -0
  59. package/dist/utils/tag-type-parser.js.map +1 -0
  60. package/examples/create-with-rpc-demo.ts +142 -0
  61. package/examples/fetch-all-flat-demo.ts +159 -0
  62. package/examples/fetch-by-indices-demo.ts +235 -0
  63. package/examples/fetch-domain-demo.ts +137 -0
  64. package/examples/fetch-domains-demo.ts +221 -0
  65. package/examples/frontend-demo/index.html +2 -2
  66. package/examples/frontend-demo/package-lock.json +4 -1
  67. package/examples/index.ts +3 -5
  68. package/jest.config.js +25 -0
  69. package/package.json +6 -2
  70. package/src/abi/TerminusDIDQueryABI.ts +516 -0
  71. package/src/business/index.ts +10 -33
  72. package/src/business/tag-context.ts +35 -7
  73. package/src/cli.ts +344 -121
  74. package/src/config/index.ts +34 -19
  75. package/src/domain/core.ts +382 -0
  76. package/src/domain/index.ts +271 -641
  77. package/src/domain/types.ts +59 -0
  78. package/src/index.ts +222 -207
  79. package/src/utils/crypto-utils.ts +239 -2
  80. package/src/utils/error-parser.ts +2 -1
  81. package/src/utils/event-parser.ts +353 -0
  82. package/src/utils/tag-type-builder.ts +138 -0
  83. package/src/utils/tag-type-parser.ts +246 -0
  84. package/tests/unit/crypto-utils.test.ts +338 -0
  85. package/tests/unit/ed25519-jwk.test.ts +201 -0
  86. package/tests/unit/event-parser.test.ts +690 -0
  87. package/tests/unit/generate-mnemonic.test.ts +268 -0
  88. package/tests/unit/olares-id-format.test.ts +321 -0
  89. package/tests/unit/tag-type-parser.test.ts +802 -0
  90. package/tests/unit/tag-types.test.ts +821 -0
  91. package/tsconfig.json +3 -2
  92. package/dist/abi/ABITypeABI.d.ts +0 -88
  93. package/dist/abi/ABITypeABI.d.ts.map +0 -1
  94. package/dist/abi/ABITypeABI.js +0 -382
  95. package/dist/abi/ABITypeABI.js.map +0 -1
  96. package/dist/abi/RegistryABI.d.ts +0 -77
  97. package/dist/abi/RegistryABI.d.ts.map +0 -1
  98. package/dist/abi/RegistryABI.js +0 -462
  99. package/dist/abi/RegistryABI.js.map +0 -1
  100. package/dist/tag/address.d.ts +0 -11
  101. package/dist/tag/address.d.ts.map +0 -1
  102. package/dist/tag/address.js +0 -44
  103. package/dist/tag/address.js.map +0 -1
  104. package/dist/tag/array.d.ts +0 -14
  105. package/dist/tag/array.d.ts.map +0 -1
  106. package/dist/tag/array.js +0 -72
  107. package/dist/tag/array.js.map +0 -1
  108. package/dist/tag/bool.d.ts +0 -11
  109. package/dist/tag/bool.d.ts.map +0 -1
  110. package/dist/tag/bool.js +0 -43
  111. package/dist/tag/bool.js.map +0 -1
  112. package/dist/tag/bytes.d.ts +0 -11
  113. package/dist/tag/bytes.d.ts.map +0 -1
  114. package/dist/tag/bytes.js +0 -37
  115. package/dist/tag/bytes.js.map +0 -1
  116. package/dist/tag/flarray.d.ts +0 -15
  117. package/dist/tag/flarray.d.ts.map +0 -1
  118. package/dist/tag/flarray.js +0 -81
  119. package/dist/tag/flarray.js.map +0 -1
  120. package/dist/tag/flbytes.d.ts +0 -11
  121. package/dist/tag/flbytes.d.ts.map +0 -1
  122. package/dist/tag/flbytes.js +0 -47
  123. package/dist/tag/flbytes.js.map +0 -1
  124. package/dist/tag/index.d.ts +0 -32
  125. package/dist/tag/index.d.ts.map +0 -1
  126. package/dist/tag/index.js +0 -121
  127. package/dist/tag/index.js.map +0 -1
  128. package/dist/tag/int.d.ts +0 -12
  129. package/dist/tag/int.d.ts.map +0 -1
  130. package/dist/tag/int.js +0 -49
  131. package/dist/tag/int.js.map +0 -1
  132. package/dist/tag/string.d.ts +0 -11
  133. package/dist/tag/string.d.ts.map +0 -1
  134. package/dist/tag/string.js +0 -37
  135. package/dist/tag/string.js.map +0 -1
  136. package/dist/tag/tag.d.ts +0 -67
  137. package/dist/tag/tag.d.ts.map +0 -1
  138. package/dist/tag/tag.js +0 -157
  139. package/dist/tag/tag.js.map +0 -1
  140. package/dist/tag/tuple.d.ts +0 -17
  141. package/dist/tag/tuple.d.ts.map +0 -1
  142. package/dist/tag/tuple.js +0 -162
  143. package/dist/tag/tuple.js.map +0 -1
  144. package/dist/tag/uint.d.ts +0 -12
  145. package/dist/tag/uint.d.ts.map +0 -1
  146. package/dist/tag/uint.js +0 -49
  147. package/dist/tag/uint.js.map +0 -1
  148. package/dist/test/did.d.ts +0 -2
  149. package/dist/test/did.d.ts.map +0 -1
  150. package/dist/test/did.js +0 -177
  151. package/dist/test/did.js.map +0 -1
  152. package/dist/utils/tag-abi-codec.d.ts +0 -69
  153. package/dist/utils/tag-abi-codec.d.ts.map +0 -1
  154. package/dist/utils/tag-abi-codec.js +0 -144
  155. package/dist/utils/tag-abi-codec.js.map +0 -1
  156. package/examples/crypto-utilities.ts +0 -140
  157. package/examples/ed25519-jwk.ts +0 -73
  158. package/examples/generate-mnemonic.ts +0 -149
  159. package/examples/legacy.ts +0 -33
  160. package/examples/olares-id-format.ts +0 -197
  161. package/examples/tag-builder.ts +0 -235
  162. package/examples/tag-nested-tuple.ts +0 -190
  163. package/examples/tag-simple.ts +0 -149
  164. package/examples/tag-tagger.ts +0 -217
  165. package/examples/test-nested-tuple-conversion.ts +0 -143
  166. package/examples/test-type-bytes-parser.ts +0 -70
  167. package/src/abi/ABITypeABI.ts +0 -379
  168. package/src/abi/RegistryABI.ts +0 -459
  169. package/src/tag/address.ts +0 -48
  170. package/src/tag/array.ts +0 -80
  171. package/src/tag/bool.ts +0 -43
  172. package/src/tag/bytes.ts +0 -38
  173. package/src/tag/flarray.ts +0 -99
  174. package/src/tag/flbytes.ts +0 -48
  175. package/src/tag/index.ts +0 -170
  176. package/src/tag/int.ts +0 -51
  177. package/src/tag/string.ts +0 -38
  178. package/src/tag/tag.ts +0 -229
  179. package/src/tag/tuple.ts +0 -193
  180. package/src/tag/uint.ts +0 -51
  181. package/src/test/did.ts +0 -346
  182. package/src/utils/tag-abi-codec.ts +0 -158
package/src/cli.ts CHANGED
@@ -8,6 +8,7 @@ import OlaresID, {
8
8
  bytes4ToIpv4,
9
9
  generateMnemonic,
10
10
  getEthereumAddressFromMnemonic,
11
+ getEthereumAddressFromPrivateKey,
11
12
  getEVMPrivateKeyFromMnemonic,
12
13
  getDIDFromMnemonic,
13
14
  generateDIDKeyData,
@@ -38,6 +39,7 @@ const CLI_VERSION = packageJson.version;
38
39
 
39
40
  interface CliOptions {
40
41
  network: string;
42
+ from?: string;
41
43
  rpc?: string;
42
44
  contractDid?: string;
43
45
  contractResolver?: string;
@@ -84,7 +86,8 @@ function parseArgs(): {
84
86
  'is-owner',
85
87
  'transfer',
86
88
  'fetch',
87
- 'fetch-domain'
89
+ 'fetch-domain',
90
+ 'fetch-all'
88
91
  ];
89
92
 
90
93
  for (let i = 0; i < args.length; i++) {
@@ -97,6 +100,8 @@ function parseArgs(): {
97
100
  process.exit(0);
98
101
  } else if (arg === '--network' || arg === '-n') {
99
102
  options.network = args[++i] || 'mainnet';
103
+ } else if (arg === '--from') {
104
+ options.from = args[++i];
100
105
  } else if (arg === '--rpc') {
101
106
  options.rpc = args[++i];
102
107
  } else if (arg === '--contract-did') {
@@ -143,6 +148,26 @@ function parseArgs(): {
143
148
  return { command, subCommand, domain, value, options };
144
149
  }
145
150
 
151
+ function normalizeTagFromDomain(
152
+ from: string | undefined,
153
+ toDomain: string
154
+ ): string {
155
+ if (!from) {
156
+ return toDomain;
157
+ }
158
+
159
+ const normalized = from.trim().toLowerCase();
160
+ if (
161
+ normalized === 'root' ||
162
+ normalized === ':root' ||
163
+ normalized === '""'
164
+ ) {
165
+ return '';
166
+ }
167
+
168
+ return normalizeToDomain(from);
169
+ }
170
+
146
171
  function showHelp(): void {
147
172
  console.log(`
148
173
  DID CLI Tool v${CLI_VERSION}
@@ -196,15 +221,18 @@ COMMANDS:
196
221
  Define a new tag type (requires PRIVATE_KEY_OR_MNEMONIC)
197
222
  Supported types: string, uint8, uint256, int8, int256, bool, address, bytes, bytes32
198
223
  Array types: string[], uint8[], etc.
199
- tag set <domain> <tag-name> <value>
224
+ tag set <domain> <tag-name> <value> [--from <domain|root>]
200
225
  Set tag value (requires PRIVATE_KEY_OR_MNEMONIC)
201
- Value can be a string or JSON for arrays/objects
202
- tag get <domain> <tag-name>
226
+ Value can be a string or JSON for arrays/objects
227
+ --from specifies where tag type is defined (default: same as <domain>)
228
+ tag get <domain> <tag-name> [--from <domain|root>]
203
229
  Get tag value (read-only)
204
- tag remove <domain> <tag-name>
230
+ tag remove <domain> <tag-name> [--from <domain|root>]
205
231
  Remove tag value (requires PRIVATE_KEY_OR_MNEMONIC)
206
- tag list <domain> List all tags with values for domain (read-only)
207
- tag list-defined <domain> List all defined tag types for domain (read-only)
232
+ tag list <domain> [--from <domain|root>]
233
+ List all tags with values for domain (read-only)
234
+ tag list-defined [domain] List all tag types defined by the domain (read-only)
235
+ Omit domain to query tag types defined by root
208
236
  tag set-tagger <domain> <tag-name> <address>
209
237
  Set tagger address for a tag (requires PRIVATE_KEY_OR_MNEMONIC)
210
238
  tag get-tagger <domain> <tag-name>
@@ -227,16 +255,17 @@ COMMANDS:
227
255
  config set <key> <value> [--network <network>]
228
256
  Set configuration value (default network: mainnet)
229
257
  Keys: rpc, contractDid, contractRootResolver,
230
- contractAbiType, contractRootResolver2, supportSvcUrl
258
+ contractAbiType, contractRootResolver2,
259
+ supportSvcUrl, queryContract
231
260
 
232
261
  Crypto Utility Commands:
233
262
  crypto generate Generate a new mnemonic phrase (--words option)
234
263
  Saves to file, use --output to specify file path
235
- crypto address Get Ethereum address from mnemonic (requires MNEMONIC env var)
236
- crypto did Get DID from mnemonic (requires MNEMONIC env var)
237
- crypto privatekey Get EVM private key from mnemonic (requires MNEMONIC env var)
264
+ crypto address Get Ethereum address from private key or mnemonic (requires PRIVATE_KEY_OR_MNEMONIC)
265
+ crypto did Get DID from mnemonic (requires PRIVATE_KEY_OR_MNEMONIC with mnemonic)
266
+ crypto privatekey Get/save EVM private key (requires PRIVATE_KEY_OR_MNEMONIC)
238
267
  Saves to file, use --output to specify file path
239
- crypto derive Derive all keys from mnemonic (requires MNEMONIC env var)
268
+ crypto derive Derive all keys from mnemonic (requires PRIVATE_KEY_OR_MNEMONIC with mnemonic)
240
269
  Saves to file, use --output to specify file path
241
270
 
242
271
  Utility Commands:
@@ -247,12 +276,13 @@ COMMANDS:
247
276
 
248
277
  Legacy Commands:
249
278
  fetch <domain> Fetch domain information (alias: fetch-domain)
250
- fetch-all Fetch all domains from contract
251
- config Show network configurations
279
+ fetch-all Fetch all domains from contract and save to JSON file
280
+ Use --output to specify file path (default: domains-<network>-<date>.json)
252
281
  help Show this help message
253
282
 
254
283
  OPTIONS:
255
284
  -n, --network <network> Network to use (sepolia|mainnet) [default: mainnet]
285
+ --from <domain|root> Tag source domain for tag set/get/remove/list (default: target domain)
256
286
  --rpc <url> Custom RPC endpoint URL
257
287
  --contract-did <address> Custom DID contract address
258
288
  --contract-resolver <address> Custom RootResolver contract address
@@ -298,7 +328,6 @@ EXAMPLES:
298
328
  # Subdomain management (auto-generate mnemonic for subdomain owner)
299
329
  export PRIVATE_KEY_OR_MNEMONIC=0xYOUR_PRIVATE_KEY
300
330
  did-cli subdomain register parent.com child
301
- did-cli subdomain register parent.com child --words 24
302
331
 
303
332
  # Subdomain management (use existing mnemonic for subdomain owner)
304
333
  export SUBDOMAIN_OWNER_MNEMONIC="your twelve word mnemonic here"
@@ -310,7 +339,6 @@ EXAMPLES:
310
339
  # Transfer domain ownership (auto-generate mnemonic for new owner)
311
340
  export PRIVATE_KEY_OR_MNEMONIC=0xYOUR_PRIVATE_KEY
312
341
  did-cli transfer example.com
313
- did-cli transfer example.com --words 24
314
342
 
315
343
  # Transfer domain ownership (use existing mnemonic for new owner)
316
344
  export TO_MNEMONIC="your twelve word mnemonic here"
@@ -318,8 +346,8 @@ EXAMPLES:
318
346
 
319
347
  # Crypto utilities
320
348
  did-cli crypto generate --words 12
321
- did-cli crypto generate --words 24 --output ./custom-path.txt
322
- export MNEMONIC="your twelve word mnemonic here"
349
+ did-cli crypto generate --output ./custom-path.txt
350
+ export PRIVATE_KEY_OR_MNEMONIC="your twelve word mnemonic here"
323
351
  did-cli crypto address
324
352
  did-cli crypto did
325
353
  did-cli crypto privatekey --output ./my-private-key.txt
@@ -389,8 +417,15 @@ EXAMPLES:
389
417
  did-cli config list # List all networks
390
418
  did-cli config set rpc https://optimism.llamarpc.com # Set mainnet RPC
391
419
  did-cli config set contractDid 0x1234... --network sepolia # Set sepolia contract
420
+ did-cli config set queryContract 0x637197... --network sepolia # Set query contract
392
421
  did-cli config set rpc https://my-rpc.com --network custom_net # Create new network
393
422
 
423
+ # Fetch all domains
424
+ did-cli fetch-all # Fetch all mainnet domains (auto-named file)
425
+ did-cli fetch-all --network sepolia # Fetch all sepolia domains
426
+ did-cli fetch-all --output ./my-domains.json # Custom output file path
427
+ did-cli fetch-all --network sepolia --verbose # With progress details
428
+
394
429
  # Utilities
395
430
  did-cli convert pem-to-der ./public-key.pem
396
431
  did-cli convert ip-to-bytes 192.168.1.1
@@ -398,13 +433,12 @@ EXAMPLES:
398
433
 
399
434
  ENVIRONMENT VARIABLES:
400
435
  PRIVATE_KEY_OR_MNEMONIC Private key (0x...) or mnemonic phrase for signing transactions
401
- (required for write operations)
436
+ and crypto utility commands (required for write operations
437
+ and crypto address/did/privatekey/derive)
402
438
  SUBDOMAIN_OWNER_MNEMONIC Mnemonic phrase for generating subdomain owner identity
403
439
  (used in subdomain registration)
404
440
  TO_MNEMONIC Mnemonic phrase for generating new owner identity
405
441
  (used in domain transfer)
406
- MNEMONIC Mnemonic phrase for crypto utility commands
407
- (used in crypto address, did, privatekey, derive)
408
442
  EVM_PRIVATE_KEY EVM wallet private key for wallet management operations
409
443
  (required for wallet evm add/remove commands)
410
444
  SOLANA_PRIVATE_KEY Solana wallet private key for wallet management operations
@@ -421,39 +455,13 @@ async function fetchDomain(domain: string, options: CliOptions): Promise<void> {
421
455
  debug.setLevel(options.debugLevel as any);
422
456
  }
423
457
 
424
- // Get network configuration
425
- const config = getNetworkConfig(options.network);
426
- if (!config) {
427
- console.error(`❌ Unknown network: ${options.network}`);
428
- console.error(
429
- 'Available networks:',
430
- getAvailableNetworks().join(', ')
431
- );
432
- process.exit(1);
433
- }
434
-
435
- // Command line parameters take priority
436
- const finalConfig = {
437
- rpc: options.rpc || config.rpc,
438
- contractDid: options.contractDid || config.contractDid,
439
- contractRootResolver:
440
- options.contractResolver || config.contractRootResolver,
441
- contractAbiType: options.contractAbi || config.contractAbiType
442
- };
443
-
444
458
  if (debug.isEnabled()) {
445
459
  debug.info(`🔍 Fetching domain: ${domain}`);
446
460
  debug.info(`🌐 Network: ${options.network}`);
447
- debug.info('Using configuration:', finalConfig);
448
461
  }
449
462
 
450
- // Create DID console instance
451
- const olaresId = OlaresID.createConsole(
452
- finalConfig.rpc,
453
- finalConfig.contractDid,
454
- finalConfig.contractRootResolver,
455
- finalConfig.contractAbiType
456
- );
463
+ // Use getConsole helper to create console instance
464
+ const olaresId = getConsole(options);
457
465
 
458
466
  // Fetch domain data
459
467
  const domainData = await olaresId.fetchDomain(domain);
@@ -476,6 +484,93 @@ async function fetchDomain(domain: string, options: CliOptions): Promise<void> {
476
484
  }
477
485
  }
478
486
 
487
+ async function fetchAll(options: CliOptions): Promise<void> {
488
+ try {
489
+ // Set debug options
490
+ if (options.verbose || options.debug) {
491
+ debug.enable();
492
+ debug.setLevel(options.debugLevel as any);
493
+ }
494
+
495
+ if (debug.isEnabled()) {
496
+ debug.info('🔍 Fetching all domains');
497
+ debug.info(`🌐 Network: ${options.network}`);
498
+ }
499
+
500
+ // Use getConsole helper to create console instance
501
+ const olaresId = getConsole(options);
502
+
503
+ // Check if query contract is configured
504
+ if (!olaresId.queryContractAddress) {
505
+ console.error(
506
+ '❌ Query contract address not configured for this network'
507
+ );
508
+ console.error(
509
+ ` Use: did-cli config set queryContract <address> --network ${options.network}`
510
+ );
511
+ process.exit(1);
512
+ }
513
+
514
+ console.log('📡 Fetching all domains from contract...');
515
+ console.log(` Network: ${options.network}`);
516
+ console.log(` Query Contract: ${olaresId.queryContractAddress}`);
517
+ console.log();
518
+
519
+ let totalDomains = 0;
520
+ const startTime = Date.now();
521
+
522
+ // Fetch all domains with progress
523
+ const domains = await olaresId.fetchAllFlat({
524
+ batchSize: 100,
525
+ onProgress: (current, total) => {
526
+ totalDomains = total;
527
+ const percent = ((current / total) * 100).toFixed(1);
528
+ process.stdout.write(
529
+ `\r⏳ Progress: ${current}/${total} (${percent}%)`
530
+ );
531
+ }
532
+ });
533
+
534
+ const elapsed = ((Date.now() - startTime) / 1000).toFixed(2);
535
+ process.stdout.write('\r' + ' '.repeat(50) + '\r'); // Clear progress line
536
+ console.log(`✅ Fetched ${totalDomains} domains in ${elapsed}s`);
537
+ console.log();
538
+
539
+ // Determine output file path
540
+ const timestamp = new Date()
541
+ .toISOString()
542
+ .replace(/[:.]/g, '-')
543
+ .split('T')[0];
544
+ const defaultFilename = `domains-${options.network}-${timestamp}.json`;
545
+ const outputPath =
546
+ options.output || path.join(process.cwd(), defaultFilename);
547
+
548
+ // Save to file
549
+ const jsonContent = JSON.stringify(domains, null, 2);
550
+ fs.writeFileSync(outputPath, jsonContent, 'utf-8');
551
+
552
+ console.log(`💾 Saved to: ${outputPath}`);
553
+ console.log(
554
+ ` File size: ${(jsonContent.length / 1024).toFixed(2)} KB`
555
+ );
556
+ console.log(` Total domains: ${domains.length}`);
557
+
558
+ if (debug.isEnabled()) {
559
+ debug.info('Fetch all completed successfully');
560
+ debug.info(`Output file: ${outputPath}`);
561
+ }
562
+ } catch (error) {
563
+ console.error(
564
+ '❌ Error fetching all domains:',
565
+ error instanceof Error ? error.message : String(error)
566
+ );
567
+ if (debug.isEnabled()) {
568
+ debug.error('Full error details:', error);
569
+ }
570
+ process.exit(1);
571
+ }
572
+ }
573
+
479
574
  // ============================================================================
480
575
  // Helper Functions
481
576
  // ============================================================================
@@ -502,7 +597,8 @@ function getConsole(options: CliOptions) {
502
597
  contractRootResolver2:
503
598
  options.contractRootResolver2 ||
504
599
  networkConfig.contractRootResolver2,
505
- supportSvcUrl: options.supportSvcUrl || networkConfig.supportSvcUrl
600
+ supportSvcUrl: options.supportSvcUrl || networkConfig.supportSvcUrl,
601
+ queryContract: networkConfig.queryContract
506
602
  };
507
603
 
508
604
  return OlaresID.createConsole(
@@ -511,7 +607,8 @@ function getConsole(options: CliOptions) {
511
607
  config.contractRootResolver,
512
608
  config.contractAbiType,
513
609
  config.contractRootResolver2,
514
- config.supportSvcUrl
610
+ config.supportSvcUrl,
611
+ config.queryContract
515
612
  );
516
613
  }
517
614
 
@@ -1386,7 +1483,9 @@ async function cryptoGenerate(options: CliOptions): Promise<void> {
1386
1483
  );
1387
1484
  console.log(` • Command: rm ${outputFile}`);
1388
1485
  console.log('\n💡 To use the mnemonic:');
1389
- console.log(` export MNEMONIC="$(cat ${outputFile})"`);
1486
+ console.log(
1487
+ ` export PRIVATE_KEY_OR_MNEMONIC="$(cat ${outputFile})"`
1488
+ );
1390
1489
  }
1391
1490
  } catch (error) {
1392
1491
  console.error('❌ Failed:', error);
@@ -1398,31 +1497,53 @@ async function cryptoGenerate(options: CliOptions): Promise<void> {
1398
1497
  }
1399
1498
 
1400
1499
  /**
1401
- * Get mnemonic from environment variable
1500
+ * Get private key or mnemonic from environment variable
1501
+ * Returns: { value: string, isMnemonic: boolean }
1402
1502
  */
1403
- function getMnemonic(): string {
1404
- const mnemonic = process.env.MNEMONIC;
1405
- if (!mnemonic) {
1406
- console.error('❌ Error: MNEMONIC environment variable is required');
1407
- console.error('Please set MNEMONIC environment variable');
1503
+ function getPrivateKeyOrMnemonicWithType(): {
1504
+ value: string;
1505
+ isMnemonic: boolean;
1506
+ } {
1507
+ const key = process.env.PRIVATE_KEY_OR_MNEMONIC;
1508
+ if (!key) {
1509
+ console.error(
1510
+ '❌ Error: Private key or mnemonic is required for this operation'
1511
+ );
1408
1512
  console.error(
1409
- 'Example: export MNEMONIC="your twelve word mnemonic phrase here"'
1513
+ 'Please set PRIVATE_KEY_OR_MNEMONIC environment variable'
1514
+ );
1515
+ console.error(
1516
+ 'Example: export PRIVATE_KEY_OR_MNEMONIC=0xYOUR_PRIVATE_KEY'
1517
+ );
1518
+ console.error(
1519
+ 'Or: export PRIVATE_KEY_OR_MNEMONIC="your twelve word mnemonic phrase here"'
1410
1520
  );
1411
1521
  console.error(
1412
1522
  '\n⚠️ Important: Always use quotes for mnemonic phrases!'
1413
1523
  );
1414
1524
  process.exit(1);
1415
1525
  }
1416
- return mnemonic;
1526
+
1527
+ const trimmed = key.trim();
1528
+ const wordCount = trimmed.split(/\s+/).length;
1529
+ const isMnemonic = wordCount >= 12 && wordCount <= 24;
1530
+
1531
+ return { value: trimmed, isMnemonic };
1417
1532
  }
1418
1533
 
1419
1534
  /**
1420
- * Crypto utility: Get Ethereum address from mnemonic
1535
+ * Crypto utility: Get Ethereum address from private key or mnemonic
1421
1536
  */
1422
1537
  async function cryptoGetAddress(options: CliOptions): Promise<void> {
1423
1538
  try {
1424
- const mnemonic = getMnemonic();
1425
- const address = await getEthereumAddressFromMnemonic(mnemonic);
1539
+ const { value, isMnemonic } = getPrivateKeyOrMnemonicWithType();
1540
+
1541
+ let address: string;
1542
+ if (isMnemonic) {
1543
+ address = await getEthereumAddressFromMnemonic(value);
1544
+ } else {
1545
+ address = getEthereumAddressFromPrivateKey(value);
1546
+ }
1426
1547
 
1427
1548
  if (options.json) {
1428
1549
  console.log(
@@ -1448,12 +1569,24 @@ async function cryptoGetAddress(options: CliOptions): Promise<void> {
1448
1569
  }
1449
1570
 
1450
1571
  /**
1451
- * Crypto utility: Get DID from mnemonic
1572
+ * Crypto utility: Get DID from mnemonic (only works with mnemonic)
1452
1573
  */
1453
1574
  async function cryptoGetDID(options: CliOptions): Promise<void> {
1454
1575
  try {
1455
- const mnemonic = getMnemonic();
1456
- const did = await getDIDFromMnemonic(mnemonic);
1576
+ const { value, isMnemonic } = getPrivateKeyOrMnemonicWithType();
1577
+
1578
+ if (!isMnemonic) {
1579
+ console.error(
1580
+ '❌ Error: DID derivation requires a mnemonic phrase'
1581
+ );
1582
+ console.error(' Private keys cannot be used to derive DID');
1583
+ console.error(
1584
+ ' Please provide a mnemonic phrase in PRIVATE_KEY_OR_MNEMONIC'
1585
+ );
1586
+ process.exit(1);
1587
+ }
1588
+
1589
+ const did = await getDIDFromMnemonic(value);
1457
1590
 
1458
1591
  if (options.json) {
1459
1592
  console.log(
@@ -1479,12 +1612,19 @@ async function cryptoGetDID(options: CliOptions): Promise<void> {
1479
1612
  }
1480
1613
 
1481
1614
  /**
1482
- * Crypto utility: Get EVM private key from mnemonic
1615
+ * Crypto utility: Get/display EVM private key (from mnemonic or show existing private key)
1483
1616
  */
1484
1617
  async function cryptoGetPrivateKey(options: CliOptions): Promise<void> {
1485
1618
  try {
1486
- const mnemonic = getMnemonic();
1487
- const privateKey = await getEVMPrivateKeyFromMnemonic(mnemonic);
1619
+ const { value, isMnemonic } = getPrivateKeyOrMnemonicWithType();
1620
+
1621
+ let privateKey: string;
1622
+ if (isMnemonic) {
1623
+ privateKey = await getEVMPrivateKeyFromMnemonic(value);
1624
+ } else {
1625
+ // Already a private key, just use it
1626
+ privateKey = value;
1627
+ }
1488
1628
 
1489
1629
  // Save private key to file
1490
1630
  const outputFile = options.output || './private-key.txt';
@@ -1501,7 +1641,7 @@ async function cryptoGetPrivateKey(options: CliOptions): Promise<void> {
1501
1641
  )
1502
1642
  );
1503
1643
  } else {
1504
- console.log('🔑 EVM Private Key generated');
1644
+ console.log('🔑 EVM Private Key');
1505
1645
  console.log(`📄 Private key saved to: ${outputFile}`);
1506
1646
  console.log('\n⚠️ SECURITY WARNING:');
1507
1647
  console.log(' • Keep this private key secure! Never share it!');
@@ -1520,13 +1660,30 @@ async function cryptoGetPrivateKey(options: CliOptions): Promise<void> {
1520
1660
  }
1521
1661
 
1522
1662
  /**
1523
- * Crypto utility: Derive all keys from mnemonic
1663
+ * Crypto utility: Derive all keys from mnemonic (only works with mnemonic)
1524
1664
  */
1525
1665
  async function cryptoDerive(options: CliOptions): Promise<void> {
1526
1666
  try {
1527
- const mnemonic = getMnemonic();
1528
- const { owner, did } = await deriveDIDFromMnemonic(mnemonic);
1529
- const privateKey = await getEVMPrivateKeyFromMnemonic(mnemonic);
1667
+ const { value, isMnemonic } = getPrivateKeyOrMnemonicWithType();
1668
+
1669
+ if (!isMnemonic) {
1670
+ console.error(
1671
+ '❌ Error: Key derivation requires a mnemonic phrase'
1672
+ );
1673
+ console.error(
1674
+ ' Private keys cannot be used to derive additional keys'
1675
+ );
1676
+ console.error(
1677
+ ' Please provide a mnemonic phrase in PRIVATE_KEY_OR_MNEMONIC'
1678
+ );
1679
+ console.error(
1680
+ ' Note: You can derive address from a private key using "crypto address"'
1681
+ );
1682
+ process.exit(1);
1683
+ }
1684
+
1685
+ const { owner, did } = await deriveDIDFromMnemonic(value);
1686
+ const privateKey = await getEVMPrivateKeyFromMnemonic(value);
1530
1687
 
1531
1688
  // Save all keys to file
1532
1689
  const outputFile = options.output || './derived-keys.txt';
@@ -1936,6 +2093,11 @@ async function configShow(options: CliOptions): Promise<void> {
1936
2093
  console.log(
1937
2094
  ` Support Service URL: ${networkConfig.supportSvcUrl}`
1938
2095
  );
2096
+ console.log(
2097
+ ` Query Contract: ${
2098
+ networkConfig.queryContract || '(not set)'
2099
+ }`
2100
+ );
1939
2101
  console.log(`\n📄 Configuration file: ${getConfigFilePath()}`);
1940
2102
  }
1941
2103
  } catch (error) {
@@ -2004,7 +2166,8 @@ async function configSet(
2004
2166
  'contractRootResolver',
2005
2167
  'contractAbiType',
2006
2168
  'contractRootResolver2',
2007
- 'supportSvcUrl'
2169
+ 'supportSvcUrl',
2170
+ 'queryContract'
2008
2171
  ];
2009
2172
  if (!validKeys.includes(key)) {
2010
2173
  console.error(`❌ Invalid config key: ${key}`);
@@ -2158,7 +2321,8 @@ async function tagDefine(
2158
2321
  * Set tag value
2159
2322
  */
2160
2323
  async function tagSet(
2161
- domain: string,
2324
+ toDomain: string,
2325
+ fromDomain: string,
2162
2326
  tagName: string,
2163
2327
  valueStr: string,
2164
2328
  options: CliOptions
@@ -2168,10 +2332,13 @@ async function tagSet(
2168
2332
  const privateKeyOrMnemonic = getPrivateKeyOrMnemonic();
2169
2333
  await didConsole.setSigner(privateKeyOrMnemonic);
2170
2334
 
2171
- const domainContext = didConsole.domain(domain);
2335
+ const domainContext = didConsole.domain(fromDomain);
2172
2336
  const tagCtx = domainContext.tag();
2173
2337
 
2174
- console.log(`\n📝 Setting tag "${tagName}" for domain: ${domain}`);
2338
+ const fromLabel = fromDomain === '' ? '(root)' : fromDomain;
2339
+ console.log(`\n📝 Setting tag "${tagName}"`);
2340
+ console.log(` From: ${fromLabel}`);
2341
+ console.log(` To: ${toDomain}`);
2175
2342
  console.log(` Value: ${valueStr}`);
2176
2343
 
2177
2344
  // Try to parse value as JSON, otherwise use as string
@@ -2182,7 +2349,7 @@ async function tagSet(
2182
2349
  value = valueStr;
2183
2350
  }
2184
2351
 
2185
- const result = await tagCtx.setTag(domain, tagName, value);
2352
+ const result = await tagCtx.setTag(toDomain, tagName, value);
2186
2353
 
2187
2354
  if (result.success) {
2188
2355
  if (options.json) {
@@ -2190,7 +2357,8 @@ async function tagSet(
2190
2357
  JSON.stringify(
2191
2358
  {
2192
2359
  success: true,
2193
- domain,
2360
+ from: fromDomain,
2361
+ to: toDomain,
2194
2362
  tagName,
2195
2363
  value,
2196
2364
  transactionHash: result.transactionHash,
@@ -2226,18 +2394,22 @@ async function tagSet(
2226
2394
  * Get tag value
2227
2395
  */
2228
2396
  async function tagGet(
2229
- domain: string,
2397
+ toDomain: string,
2398
+ fromDomain: string,
2230
2399
  tagName: string,
2231
2400
  options: CliOptions
2232
2401
  ): Promise<void> {
2233
2402
  try {
2234
2403
  const didConsole = getConsole(options);
2235
- const domainContext = didConsole.domain(domain);
2404
+ const domainContext = didConsole.domain(fromDomain);
2236
2405
  const tagCtx = domainContext.tag();
2406
+ const fromLabel = fromDomain === '' ? '(root)' : fromDomain;
2237
2407
 
2238
- console.log(`\n📖 Getting tag "${tagName}" for domain: ${domain}`);
2408
+ console.log(`\n📖 Getting tag "${tagName}"`);
2409
+ console.log(` From: ${fromLabel}`);
2410
+ console.log(` To: ${toDomain}`);
2239
2411
 
2240
- const value = await tagCtx.getTag(domain, tagName);
2412
+ const value = await tagCtx.getTag(toDomain, tagName);
2241
2413
 
2242
2414
  if (value !== null && value !== undefined) {
2243
2415
  if (options.json) {
@@ -2245,7 +2417,8 @@ async function tagGet(
2245
2417
  JSON.stringify(
2246
2418
  {
2247
2419
  success: true,
2248
- domain,
2420
+ from: fromDomain,
2421
+ to: toDomain,
2249
2422
  tagName,
2250
2423
  value
2251
2424
  },
@@ -2273,7 +2446,8 @@ async function tagGet(
2273
2446
  console.log(
2274
2447
  JSON.stringify({
2275
2448
  success: true,
2276
- domain,
2449
+ from: fromDomain,
2450
+ to: toDomain,
2277
2451
  tagName,
2278
2452
  value: null
2279
2453
  })
@@ -2297,7 +2471,8 @@ async function tagGet(
2297
2471
  * Remove tag value
2298
2472
  */
2299
2473
  async function tagRemove(
2300
- domain: string,
2474
+ toDomain: string,
2475
+ fromDomain: string,
2301
2476
  tagName: string,
2302
2477
  options: CliOptions
2303
2478
  ): Promise<void> {
@@ -2306,12 +2481,15 @@ async function tagRemove(
2306
2481
  const privateKeyOrMnemonic = getPrivateKeyOrMnemonic();
2307
2482
  await didConsole.setSigner(privateKeyOrMnemonic);
2308
2483
 
2309
- const domainContext = didConsole.domain(domain);
2484
+ const domainContext = didConsole.domain(fromDomain);
2310
2485
  const tagCtx = domainContext.tag();
2486
+ const fromLabel = fromDomain === '' ? '(root)' : fromDomain;
2311
2487
 
2312
- console.log(`\n🗑️ Removing tag "${tagName}" for domain: ${domain}`);
2488
+ console.log(`\n🗑️ Removing tag "${tagName}"`);
2489
+ console.log(` From: ${fromLabel}`);
2490
+ console.log(` To: ${toDomain}`);
2313
2491
 
2314
- const result = await tagCtx.removeTag(domain, tagName);
2492
+ const result = await tagCtx.removeTag(toDomain, tagName);
2315
2493
 
2316
2494
  if (result.success) {
2317
2495
  if (options.json) {
@@ -2319,7 +2497,8 @@ async function tagRemove(
2319
2497
  JSON.stringify(
2320
2498
  {
2321
2499
  success: true,
2322
- domain,
2500
+ from: fromDomain,
2501
+ to: toDomain,
2323
2502
  tagName,
2324
2503
  transactionHash: result.transactionHash,
2325
2504
  gasUsed: result.gasUsed?.toString(),
@@ -2353,21 +2532,30 @@ async function tagRemove(
2353
2532
  /**
2354
2533
  * List all tags for a domain
2355
2534
  */
2356
- async function tagList(domain: string, options: CliOptions): Promise<void> {
2535
+ async function tagList(
2536
+ toDomain: string,
2537
+ fromDomain: string,
2538
+ options: CliOptions
2539
+ ): Promise<void> {
2357
2540
  try {
2358
2541
  const didConsole = getConsole(options);
2359
- const domainContext = didConsole.domain(domain);
2542
+ const domainContext = didConsole.domain(fromDomain);
2543
+ const tagCtx = domainContext.tag();
2544
+ const fromLabel = fromDomain === '' ? '(root)' : fromDomain;
2360
2545
 
2361
- console.log(`\n📋 Listing all tags for domain: ${domain}`);
2546
+ console.log(`\n📋 Listing all tags`);
2547
+ console.log(` From: ${fromLabel}`);
2548
+ console.log(` To: ${toDomain}`);
2362
2549
 
2363
- const tags = await domainContext.getAllTags();
2550
+ const tags = await tagCtx.getAllTags(toDomain);
2364
2551
 
2365
2552
  if (options.json) {
2366
2553
  console.log(
2367
2554
  JSON.stringify(
2368
2555
  {
2369
2556
  success: true,
2370
- domain,
2557
+ from: fromDomain,
2558
+ to: toDomain,
2371
2559
  count: tags.length,
2372
2560
  tags
2373
2561
  },
@@ -2410,7 +2598,8 @@ async function tagListDefined(
2410
2598
  const didConsole = getConsole(options);
2411
2599
  const domainContext = didConsole.domain(domain);
2412
2600
 
2413
- console.log(`\n📋 Listing defined tag types for domain: ${domain}`);
2601
+ const domainLabel = domain === '' ? '(root)' : domain;
2602
+ console.log(`\n📋 Listing tag types defined by: ${domainLabel}`);
2414
2603
 
2415
2604
  const tags = await domainContext.getDefinedTags();
2416
2605
 
@@ -2848,11 +3037,14 @@ async function main(): Promise<void> {
2848
3037
  '❌ Error: Usage: did-cli config set <key> <value> [--network <network>]'
2849
3038
  );
2850
3039
  console.error(
2851
- ' Valid keys: rpc, contractDid, contractRootResolver, contractAbiType, contractRootResolver2, supportSvcUrl'
3040
+ ' Valid keys: rpc, contractDid, contractRootResolver, contractAbiType, contractRootResolver2, supportSvcUrl, queryContract'
2852
3041
  );
2853
3042
  console.error(
2854
3043
  '\n Example: did-cli config set rpc https://optimism.llamarpc.com --network mainnet'
2855
3044
  );
3045
+ console.error(
3046
+ ' Example: did-cli config set queryContract 0xAf0430AB9f2450c52E7401846C7CA702D53eCFC2 --network sepolia'
3047
+ );
2856
3048
  process.exit(1);
2857
3049
  }
2858
3050
  break;
@@ -3062,7 +3254,7 @@ async function main(): Promise<void> {
3062
3254
  '❌ Error: tag set requires <domain>, <tag-name> and <value>'
3063
3255
  );
3064
3256
  console.error(
3065
- 'Usage: did-cli tag set <domain> <tag-name> <value>'
3257
+ 'Usage: did-cli tag set <domain> <tag-name> <value> [--from <domain|root>]'
3066
3258
  );
3067
3259
  console.error(
3068
3260
  ' Value can be a string or JSON (e.g., \'["item1","item2"]\' for arrays)'
@@ -3076,11 +3268,18 @@ async function main(): Promise<void> {
3076
3268
  const args = process.argv.slice(2);
3077
3269
  const setIdx = args.indexOf('set');
3078
3270
  if (setIdx >= 0 && args.length > setIdx + 3) {
3079
- const actualDomain = args[setIdx + 1];
3271
+ const actualDomain = normalizeToDomain(
3272
+ args[setIdx + 1]
3273
+ );
3274
+ const fromDomain = normalizeTagFromDomain(
3275
+ options.from,
3276
+ actualDomain
3277
+ );
3080
3278
  const tagName = args[setIdx + 2];
3081
3279
  const valueStr = args[setIdx + 3];
3082
3280
  await tagSet(
3083
3281
  actualDomain,
3282
+ fromDomain,
3084
3283
  tagName,
3085
3284
  valueStr,
3086
3285
  options
@@ -3090,7 +3289,7 @@ async function main(): Promise<void> {
3090
3289
  '❌ Error: Missing arguments for tag set'
3091
3290
  );
3092
3291
  console.error(
3093
- 'Usage: did-cli tag set <domain> <tag-name> <value>'
3292
+ 'Usage: did-cli tag set <domain> <tag-name> <value> [--from <domain|root>]'
3094
3293
  );
3095
3294
  process.exit(1);
3096
3295
  }
@@ -3103,7 +3302,7 @@ async function main(): Promise<void> {
3103
3302
  '❌ Error: tag get requires <domain> and <tag-name>'
3104
3303
  );
3105
3304
  console.error(
3106
- 'Usage: did-cli tag get <domain> <tag-name>'
3305
+ 'Usage: did-cli tag get <domain> <tag-name> [--from <domain|root>]'
3107
3306
  );
3108
3307
  process.exit(1);
3109
3308
  }
@@ -3111,15 +3310,26 @@ async function main(): Promise<void> {
3111
3310
  const args = process.argv.slice(2);
3112
3311
  const getIdx = args.indexOf('get');
3113
3312
  if (getIdx >= 0 && args.length > getIdx + 2) {
3114
- const actualDomain = args[getIdx + 1];
3313
+ const actualDomain = normalizeToDomain(
3314
+ args[getIdx + 1]
3315
+ );
3316
+ const fromDomain = normalizeTagFromDomain(
3317
+ options.from,
3318
+ actualDomain
3319
+ );
3115
3320
  const tagName = args[getIdx + 2];
3116
- await tagGet(actualDomain, tagName, options);
3321
+ await tagGet(
3322
+ actualDomain,
3323
+ fromDomain,
3324
+ tagName,
3325
+ options
3326
+ );
3117
3327
  } else {
3118
3328
  console.error(
3119
3329
  '❌ Error: Missing arguments for tag get'
3120
3330
  );
3121
3331
  console.error(
3122
- 'Usage: did-cli tag get <domain> <tag-name>'
3332
+ 'Usage: did-cli tag get <domain> <tag-name> [--from <domain|root>]'
3123
3333
  );
3124
3334
  process.exit(1);
3125
3335
  }
@@ -3132,7 +3342,7 @@ async function main(): Promise<void> {
3132
3342
  '❌ Error: tag remove requires <domain> and <tag-name>'
3133
3343
  );
3134
3344
  console.error(
3135
- 'Usage: did-cli tag remove <domain> <tag-name>'
3345
+ 'Usage: did-cli tag remove <domain> <tag-name> [--from <domain|root>]'
3136
3346
  );
3137
3347
  console.error(
3138
3348
  '\nNote: Set PRIVATE_KEY_OR_MNEMONIC environment variable first'
@@ -3143,15 +3353,26 @@ async function main(): Promise<void> {
3143
3353
  const args = process.argv.slice(2);
3144
3354
  const removeIdx = args.indexOf('remove');
3145
3355
  if (removeIdx >= 0 && args.length > removeIdx + 2) {
3146
- const actualDomain = args[removeIdx + 1];
3356
+ const actualDomain = normalizeToDomain(
3357
+ args[removeIdx + 1]
3358
+ );
3359
+ const fromDomain = normalizeTagFromDomain(
3360
+ options.from,
3361
+ actualDomain
3362
+ );
3147
3363
  const tagName = args[removeIdx + 2];
3148
- await tagRemove(actualDomain, tagName, options);
3364
+ await tagRemove(
3365
+ actualDomain,
3366
+ fromDomain,
3367
+ tagName,
3368
+ options
3369
+ );
3149
3370
  } else {
3150
3371
  console.error(
3151
3372
  '❌ Error: Missing arguments for tag remove'
3152
3373
  );
3153
3374
  console.error(
3154
- 'Usage: did-cli tag remove <domain> <tag-name>'
3375
+ 'Usage: did-cli tag remove <domain> <tag-name> [--from <domain|root>]'
3155
3376
  );
3156
3377
  process.exit(1);
3157
3378
  }
@@ -3163,23 +3384,21 @@ async function main(): Promise<void> {
3163
3384
  console.error(
3164
3385
  '❌ Error: Domain argument is required'
3165
3386
  );
3166
- console.error('Usage: did-cli tag list <domain>');
3387
+ console.error(
3388
+ 'Usage: did-cli tag list <domain> [--from <domain|root>]'
3389
+ );
3167
3390
  process.exit(1);
3168
3391
  }
3169
- await tagList(domain, options);
3392
+ const fromDomain = normalizeTagFromDomain(
3393
+ options.from,
3394
+ domain
3395
+ );
3396
+ await tagList(domain, fromDomain, options);
3170
3397
  break;
3171
3398
 
3172
3399
  case 'list-defined':
3173
- if (!domain) {
3174
- console.error(
3175
- '❌ Error: Domain argument is required'
3176
- );
3177
- console.error(
3178
- 'Usage: did-cli tag list-defined <domain>'
3179
- );
3180
- process.exit(1);
3181
- }
3182
- await tagListDefined(domain, options);
3400
+ // domain defaults to "" (root) when not provided
3401
+ await tagListDefined(domain ?? '', options);
3183
3402
  break;
3184
3403
 
3185
3404
  case 'set-tagger':
@@ -3281,6 +3500,10 @@ async function main(): Promise<void> {
3281
3500
  await fetchDomain(domain, options);
3282
3501
  break;
3283
3502
 
3503
+ case 'fetch-all':
3504
+ await fetchAll(options);
3505
+ break;
3506
+
3284
3507
  default:
3285
3508
  console.error(`❌ Unknown command: ${command}`);
3286
3509
  console.error('Run "did-cli help" for usage information');