@did-btcr2/cli 0.10.2 → 0.11.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 (89) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/cjs/index.js +889 -43
  3. package/dist/esm/src/cli.js +30 -12
  4. package/dist/esm/src/cli.js.map +1 -1
  5. package/dist/esm/src/commands/completion.js +36 -0
  6. package/dist/esm/src/commands/completion.js.map +1 -0
  7. package/dist/esm/src/commands/config.js +69 -0
  8. package/dist/esm/src/commands/config.js.map +1 -0
  9. package/dist/esm/src/commands/deactivate.js +21 -8
  10. package/dist/esm/src/commands/deactivate.js.map +1 -1
  11. package/dist/esm/src/commands/index.js +4 -0
  12. package/dist/esm/src/commands/index.js.map +1 -1
  13. package/dist/esm/src/commands/key.js +175 -0
  14. package/dist/esm/src/commands/key.js.map +1 -0
  15. package/dist/esm/src/commands/profile.js +63 -0
  16. package/dist/esm/src/commands/profile.js.map +1 -0
  17. package/dist/esm/src/commands/update.js +19 -9
  18. package/dist/esm/src/commands/update.js.map +1 -1
  19. package/dist/esm/src/config.js +100 -13
  20. package/dist/esm/src/config.js.map +1 -1
  21. package/dist/esm/src/keystore/atomic.js +64 -0
  22. package/dist/esm/src/keystore/atomic.js.map +1 -0
  23. package/dist/esm/src/keystore/envelope.js +123 -0
  24. package/dist/esm/src/keystore/envelope.js.map +1 -0
  25. package/dist/esm/src/keystore/error.js +16 -0
  26. package/dist/esm/src/keystore/error.js.map +1 -0
  27. package/dist/esm/src/keystore/file-backed-key-manager.js +78 -0
  28. package/dist/esm/src/keystore/file-backed-key-manager.js.map +1 -0
  29. package/dist/esm/src/keystore/file-key-store.js +184 -0
  30. package/dist/esm/src/keystore/file-key-store.js.map +1 -0
  31. package/dist/esm/src/keystore/passphrase.js +87 -0
  32. package/dist/esm/src/keystore/passphrase.js.map +1 -0
  33. package/dist/esm/src/keystore/paths.js +20 -0
  34. package/dist/esm/src/keystore/paths.js.map +1 -0
  35. package/dist/esm/src/keystore/resolve-key-ref.js +47 -0
  36. package/dist/esm/src/keystore/resolve-key-ref.js.map +1 -0
  37. package/dist/types/src/cli.d.ts +6 -2
  38. package/dist/types/src/cli.d.ts.map +1 -1
  39. package/dist/types/src/commands/completion.d.ts +5 -0
  40. package/dist/types/src/commands/completion.d.ts.map +1 -0
  41. package/dist/types/src/commands/config.d.ts +5 -0
  42. package/dist/types/src/commands/config.d.ts.map +1 -0
  43. package/dist/types/src/commands/deactivate.d.ts.map +1 -1
  44. package/dist/types/src/commands/index.d.ts +4 -0
  45. package/dist/types/src/commands/index.d.ts.map +1 -1
  46. package/dist/types/src/commands/key.d.ts +10 -0
  47. package/dist/types/src/commands/key.d.ts.map +1 -0
  48. package/dist/types/src/commands/profile.d.ts +5 -0
  49. package/dist/types/src/commands/profile.d.ts.map +1 -0
  50. package/dist/types/src/commands/update.d.ts.map +1 -1
  51. package/dist/types/src/config.d.ts +50 -7
  52. package/dist/types/src/config.d.ts.map +1 -1
  53. package/dist/types/src/keystore/atomic.d.ts +19 -0
  54. package/dist/types/src/keystore/atomic.d.ts.map +1 -0
  55. package/dist/types/src/keystore/envelope.d.ts +64 -0
  56. package/dist/types/src/keystore/envelope.d.ts.map +1 -0
  57. package/dist/types/src/keystore/error.d.ts +14 -0
  58. package/dist/types/src/keystore/error.d.ts.map +1 -0
  59. package/dist/types/src/keystore/file-backed-key-manager.d.ts +41 -0
  60. package/dist/types/src/keystore/file-backed-key-manager.d.ts.map +1 -0
  61. package/dist/types/src/keystore/file-key-store.d.ts +52 -0
  62. package/dist/types/src/keystore/file-key-store.d.ts.map +1 -0
  63. package/dist/types/src/keystore/passphrase.d.ts +20 -0
  64. package/dist/types/src/keystore/passphrase.d.ts.map +1 -0
  65. package/dist/types/src/keystore/paths.d.ts +13 -0
  66. package/dist/types/src/keystore/paths.d.ts.map +1 -0
  67. package/dist/types/src/keystore/resolve-key-ref.d.ts +19 -0
  68. package/dist/types/src/keystore/resolve-key-ref.d.ts.map +1 -0
  69. package/dist/types/src/types.d.ts +91 -0
  70. package/dist/types/src/types.d.ts.map +1 -1
  71. package/package.json +9 -4
  72. package/src/cli.ts +36 -11
  73. package/src/commands/completion.ts +40 -0
  74. package/src/commands/config.ts +84 -0
  75. package/src/commands/deactivate.ts +25 -12
  76. package/src/commands/index.ts +4 -0
  77. package/src/commands/key.ts +193 -0
  78. package/src/commands/profile.ts +65 -0
  79. package/src/commands/update.ts +23 -13
  80. package/src/config.ts +144 -22
  81. package/src/keystore/atomic.ts +73 -0
  82. package/src/keystore/envelope.ts +172 -0
  83. package/src/keystore/error.ts +16 -0
  84. package/src/keystore/file-backed-key-manager.ts +99 -0
  85. package/src/keystore/file-key-store.ts +242 -0
  86. package/src/keystore/passphrase.ts +99 -0
  87. package/src/keystore/paths.ts +20 -0
  88. package/src/keystore/resolve-key-ref.ts +62 -0
  89. package/src/types.ts +30 -11
@@ -0,0 +1,20 @@
1
+ /** Environment variable that supplies the keystore passphrase for unattended use. */
2
+ export declare const ENV_KEYSTORE_PASSPHRASE = "BTCR2_KEYSTORE_PASSPHRASE";
3
+ /** Options controlling how a passphrase is acquired. */
4
+ export type PassphraseOptions = {
5
+ /** Path to a file whose contents (a trailing newline is trimmed) are the passphrase. */
6
+ passphraseFile?: string;
7
+ /** Prompt label shown on a terminal. */
8
+ prompt?: string;
9
+ /** When true, prompt twice and require the entries to match (for a new keystore). */
10
+ confirm?: boolean;
11
+ };
12
+ /**
13
+ * Acquires a passphrase without ever reading it from a command-line flag value
14
+ * (which would leak into process listings and shell history). Resolution order:
15
+ * the {@link ENV_KEYSTORE_PASSPHRASE} environment variable, a passphrase file,
16
+ * then a non-echoing terminal prompt. Throws if none is available and standard
17
+ * input is not a terminal.
18
+ */
19
+ export declare function acquirePassphrase(options?: PassphraseOptions): string;
20
+ //# sourceMappingURL=passphrase.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"passphrase.d.ts","sourceRoot":"","sources":["../../../../src/keystore/passphrase.ts"],"names":[],"mappings":"AAGA,qFAAqF;AACrF,eAAO,MAAM,uBAAuB,8BAA8B,CAAC;AAEnE,wDAAwD;AACxD,MAAM,MAAM,iBAAiB,GAAG;IAC9B,wFAAwF;IACxF,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,wCAAwC;IACxC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,qFAAqF;IACrF,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,CAAC;AAEF;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,GAAE,iBAAsB,GAAG,MAAM,CAyBzE"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Default keystore file path, following the XDG Base Directory Specification's
3
+ * data directory. Secret key material is data a user accumulates, so it lives
4
+ * under the data directory, kept separate from the configuration directory used
5
+ * for portable settings.
6
+ *
7
+ * Resolution order:
8
+ * 1. `$XDG_DATA_HOME/btcr2/keystore.json`
9
+ * 2. `%LOCALAPPDATA%/btcr2/keystore.json` (Windows)
10
+ * 3. `~/.local/share/btcr2/keystore.json` (fallback)
11
+ */
12
+ export declare function defaultKeystorePath(): string;
13
+ //# sourceMappingURL=paths.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../../../../src/keystore/paths.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;GAUG;AACH,wBAAgB,mBAAmB,IAAI,MAAM,CAK5C"}
@@ -0,0 +1,19 @@
1
+ import type { KeyIdentifier, KeyManager } from '@did-btcr2/key-manager';
2
+ /**
3
+ * Resolves a user-supplied key reference to a key identifier. Resolution order:
4
+ * 1. No reference: the active key (errors if none is set).
5
+ * 2. Exact URN identifier match.
6
+ * 3. Unique fingerprint-prefix match (against the hex tail of the URN).
7
+ * 4. Unique `name` tag match.
8
+ *
9
+ * Reads only public material (listKeys + getEntry), so resolving a reference
10
+ * never decrypts a secret or prompts for a passphrase.
11
+ *
12
+ * @param kms The key manager to resolve against.
13
+ * @param ref The reference to resolve. When omitted, the active key is used.
14
+ * @returns The resolved key identifier.
15
+ * @throws {CLIError} If no key matches, the reference is ambiguous, or no
16
+ * reference is given and no active key is set.
17
+ */
18
+ export declare function resolveKeyRef(kms: KeyManager, ref?: string): KeyIdentifier;
19
+ //# sourceMappingURL=resolve-key-ref.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve-key-ref.d.ts","sourceRoot":"","sources":["../../../../src/keystore/resolve-key-ref.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAQxE;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,UAAU,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,aAAa,CAqC1E"}
@@ -33,6 +33,94 @@ export type CommandResult = {
33
33
  } | {
34
34
  action: 'deactivate';
35
35
  data: SignedBTCR2Update;
36
+ } | {
37
+ action: 'key-generate';
38
+ data: {
39
+ keyId: string;
40
+ publicKey: string;
41
+ active: boolean;
42
+ };
43
+ } | {
44
+ action: 'key-list';
45
+ data: Array<{
46
+ keyId: string;
47
+ fingerprint: string;
48
+ name?: string;
49
+ active: boolean;
50
+ }>;
51
+ } | {
52
+ action: 'key-show';
53
+ data: {
54
+ keyId: string;
55
+ publicKey: string;
56
+ tags?: Record<string, string>;
57
+ };
58
+ } | {
59
+ action: 'key-import';
60
+ data: {
61
+ keyId: string;
62
+ publicKey: string;
63
+ watchOnly: boolean;
64
+ active: boolean;
65
+ };
66
+ } | {
67
+ action: 'key-export';
68
+ data: {
69
+ keyId: string;
70
+ publicKey?: string;
71
+ secretWrittenTo?: string;
72
+ };
73
+ } | {
74
+ action: 'key-delete';
75
+ data: {
76
+ keyId: string;
77
+ deleted: true;
78
+ };
79
+ } | {
80
+ action: 'key-use';
81
+ data: {
82
+ keyId: string;
83
+ active: true;
84
+ };
85
+ } | {
86
+ action: 'config-init';
87
+ data: {
88
+ path: string;
89
+ };
90
+ } | {
91
+ action: 'config-get';
92
+ data: unknown;
93
+ } | {
94
+ action: 'config-set';
95
+ data: {
96
+ path: string;
97
+ };
98
+ } | {
99
+ action: 'config-unset';
100
+ data: {
101
+ path: string;
102
+ };
103
+ } | {
104
+ action: 'config-list';
105
+ data: unknown;
106
+ } | {
107
+ action: 'profile-add';
108
+ data: {
109
+ profile: string;
110
+ };
111
+ } | {
112
+ action: 'profile-use';
113
+ data: {
114
+ profile: string;
115
+ };
116
+ } | {
117
+ action: 'profile-show';
118
+ data: unknown;
119
+ } | {
120
+ action: 'profile-remove';
121
+ data: {
122
+ profile: string;
123
+ };
36
124
  };
37
125
  export interface GlobalOptions {
38
126
  output: OutputFormat;
@@ -45,5 +133,8 @@ export interface GlobalOptions {
45
133
  btcRpcUser?: string;
46
134
  btcRpcPass?: string;
47
135
  casGateway?: string;
136
+ keystore?: string;
137
+ passphraseFile?: string;
138
+ signingKey?: string;
48
139
  }
49
140
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAChE,OAAO,KAAK,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAC7E,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAEtD,MAAM,MAAM,aAAa,GAAG,SAAS,GAAG,UAAU,GAAG,UAAU,GAAG,QAAQ,GAAG,WAAW,GAAG,SAAS,CAAC;AACrG,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,MAAM,CAAC;AAE3C,eAAO,MAAM,kBAAkB,EAAE,aAAa,EAE7C,CAAC;AAEF,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAM,GAAG,GAAG,GAAG,CAAC;IACpB,KAAK,EAAK,MAAM,CAAC;IACjB,OAAO,EAAG,aAAa,CAAC;CACzB;AAED,MAAM,WAAW,qBAAqB;IACpC,UAAU,EAAG,MAAM,CAAC;IACpB,OAAO,CAAC,EAAK,iBAAiB,CAAC;CAChC;AAED,MAAM,WAAW,oBAAoB;IACnC,cAAc,EAAS,gBAAgB,CAAC;IACxC,OAAO,EAAgB,cAAc,EAAE,CAAC;IACxC,eAAe,EAAQ,MAAM,CAAC;IAC9B,oBAAoB,EAAG,MAAM,CAAC;IAC9B,QAAQ,EAAe,MAAM,CAAC;CAC/B;AAED,MAAM,MAAM,aAAa,GACrB;IAAE,MAAM,EAAE,QAAQ,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAClC;IAAE,MAAM,EAAE,SAAS,CAAC;IAAC,IAAI,EAAE,mBAAmB,CAAA;CAAE,GAChD;IAAE,MAAM,EAAE,QAAQ,CAAC;IAAC,IAAI,EAAE,iBAAiB,CAAA;CAAE,GAC7C;IAAE,MAAM,EAAE,YAAY,CAAC;IAAC,IAAI,EAAE,iBAAiB,CAAA;CAAE,CAAC;AAEtD,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAO,YAAY,CAAC;IAC1B,OAAO,EAAM,OAAO,CAAC;IACrB,KAAK,EAAQ,OAAO,CAAC;IACrB,MAAM,CAAC,EAAM,MAAM,CAAC;IACpB,OAAO,CAAC,EAAK,MAAM,CAAC;IACpB,OAAO,CAAC,EAAK,MAAM,CAAC;IACpB,SAAS,CAAC,EAAG,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/types.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@did-btcr2/cli",
3
- "version": "0.10.2",
3
+ "version": "0.11.0",
4
4
  "type": "module",
5
5
  "description": "CLI for interacting with did-btcr2-js, the JavaScript/TypeScript reference implementation of the did:btcr2 method. Exposes various parts of multiple packages in the did-btcr2-js monorepo.",
6
6
  "main": "./dist/cjs/index.js",
@@ -56,12 +56,17 @@
56
56
  "cli"
57
57
  ],
58
58
  "dependencies": {
59
+ "@noble/ciphers": "^2.1.1",
60
+ "@noble/hashes": "^2.0.1",
61
+ "@scure/base": "^1.2.6",
59
62
  "@web5/dids": "^1.2.0",
60
63
  "commander": "^13.1.0",
61
- "@did-btcr2/api": "^0.9.2",
64
+ "@did-btcr2/api": "^0.10.0",
65
+ "@did-btcr2/cryptosuite": "^8.0.0",
62
66
  "@did-btcr2/common": "^9.1.0",
63
- "@did-btcr2/method": "^0.36.0",
64
- "@did-btcr2/cryptosuite": "^8.0.0"
67
+ "@did-btcr2/key-manager": "^0.7.0",
68
+ "@did-btcr2/keypair": "^0.13.1",
69
+ "@did-btcr2/method": "^0.39.0"
65
70
  },
66
71
  "devDependencies": {
67
72
  "@eslint/js": "^9.21.0",
package/src/cli.ts CHANGED
@@ -1,12 +1,16 @@
1
+ import { DidMethodError } from '@did-btcr2/common';
1
2
  import { Command, CommanderError } from 'commander';
2
3
  import {
4
+ registerCompletionCommand,
5
+ registerConfigCommand,
3
6
  registerCreateCommand,
4
7
  registerDeactivateCommand,
8
+ registerKeyCommand,
9
+ registerProfileCommand,
5
10
  registerResolveCommand,
6
11
  registerUpdateCommand,
7
12
  } from './commands/index.js';
8
- import { defaultApiFactory, type ApiFactory } from './config.js';
9
- import { CLIError } from './error.js';
13
+ import { defaultApiFactory, keystoreApiFactory, type ApiFactory } from './config.js';
10
14
  import type { GlobalOptions } from './types.js';
11
15
  import { VERSION } from './version.js';
12
16
 
@@ -24,9 +28,16 @@ export class DidBtcr2Cli {
24
28
  * {@link defaultApiFactory} which uses public endpoints (mempool.space)
25
29
  * for known networks and localhost Polar for regtest.
26
30
  *
27
- * @param factory - Optional API factory. Defaults to {@link defaultApiFactory}.
31
+ * @param factory - Optional API factory for keystore-free commands (create,
32
+ * resolve). Defaults to {@link defaultApiFactory}.
33
+ * @param keystoreFactory - Optional keystore-aware API factory for commands
34
+ * that need a signing identity (key, update, deactivate). Defaults to
35
+ * {@link keystoreApiFactory}.
28
36
  */
29
- constructor(factory: ApiFactory = defaultApiFactory) {
37
+ constructor(
38
+ factory: ApiFactory = defaultApiFactory,
39
+ keystoreFactory: ApiFactory = keystoreApiFactory,
40
+ ) {
30
41
  this.program = new Command('btcr2')
31
42
  .version(`btcr2 ${VERSION}`, '-v, --version', 'Output the current version')
32
43
  .description('CLI tool for the did:btcr2 method')
@@ -39,14 +50,21 @@ export class DidBtcr2Cli {
39
50
  .option('--btc-rpc-url <url>', 'Override Bitcoin Core RPC endpoint')
40
51
  .option('--btc-rpc-user <user>', 'Bitcoin Core RPC username')
41
52
  .option('--btc-rpc-pass <pass>', 'Bitcoin Core RPC password')
42
- .option('--cas-gateway <url>', 'IPFS HTTP gateway for CAS reads');
53
+ .option('--cas-gateway <url>', 'IPFS HTTP gateway for CAS reads')
54
+ .option('--keystore <path>', 'Path to the keystore file (default: $XDG_DATA_HOME/btcr2/keystore.json)')
55
+ .option('--passphrase-file <path>', 'Read the keystore passphrase from a file (unattended use)')
56
+ .option('--signing-key <ref>', 'Key for update/deactivate signing: a URN, fingerprint prefix, or name');
43
57
 
44
58
  const globals = (): GlobalOptions => this.program.opts() as GlobalOptions;
45
59
 
46
60
  registerCreateCommand(this.program, factory, globals);
47
61
  registerResolveCommand(this.program, factory, globals);
48
- registerUpdateCommand(this.program, factory, globals);
49
- registerDeactivateCommand(this.program, factory, globals);
62
+ registerUpdateCommand(this.program, keystoreFactory, globals);
63
+ registerDeactivateCommand(this.program, keystoreFactory, globals);
64
+ registerKeyCommand(this.program, keystoreFactory, globals);
65
+ registerConfigCommand(this.program, globals);
66
+ registerProfileCommand(this.program, globals);
67
+ registerCompletionCommand(this.program, globals);
50
68
  }
51
69
 
52
70
  /**
@@ -60,7 +78,7 @@ export class DidBtcr2Cli {
60
78
  await this.program.parseAsync(normalized, { from: 'node' });
61
79
  if (!this.program.args.length) this.program.outputHelp();
62
80
  } catch (error: unknown) {
63
- handleError(error);
81
+ handleError(error, Boolean(this.program.opts().verbose));
64
82
  }
65
83
  }
66
84
  }
@@ -78,18 +96,25 @@ function normalizeArgv(argv: string[]): string[] {
78
96
 
79
97
  /**
80
98
  * Handles errors thrown during CLI execution.
99
+ *
100
+ * Known method errors ({@link DidMethodError} and its subclasses, including
101
+ * {@link CLIError} and the keystore errors) print only their message, never the
102
+ * stack or the structured `data` payload, so internal shapes are not leaked.
103
+ * The full error object and stack are shown only under `--verbose`.
104
+ *
81
105
  * @param {unknown} error - The error to handle.
106
+ * @param {boolean} verbose - Whether to print the full error object and stack.
82
107
  * @returns {void}
83
108
  */
84
- function handleError(error: unknown): void {
109
+ function handleError(error: unknown, verbose: boolean): void {
85
110
  if (
86
111
  error instanceof CommanderError &&
87
112
  (error.code === 'commander.helpDisplayed' || error.code === 'commander.help')
88
113
  ) {
89
114
  return;
90
115
  }
91
- if (error instanceof CLIError) {
92
- console.error(error.message);
116
+ if (error instanceof DidMethodError) {
117
+ console.error(verbose ? error : error.message);
93
118
  process.exitCode ??= 1;
94
119
  return;
95
120
  }
@@ -0,0 +1,40 @@
1
+ import type { Command } from 'commander';
2
+ import { CLIError } from '../error.js';
3
+ import type { GlobalOptions } from '../types.js';
4
+
5
+ const COMMANDS = 'create resolve read update deactivate delete key config profile completion';
6
+
7
+ /** Registers the `completion` command, which prints a shell completion script to stdout. */
8
+ export function registerCompletionCommand(program: Command, _globals: () => GlobalOptions): void {
9
+ program
10
+ .command('completion [shell]')
11
+ .description('Print a shell completion script (bash, zsh, or fish) to stdout.')
12
+ .action((shell = 'bash') => {
13
+ console.log(completionScript(shell));
14
+ });
15
+ }
16
+
17
+ /** Returns a completion script for the given shell. */
18
+ function completionScript(shell: string): string {
19
+ switch (shell) {
20
+ case 'bash':
21
+ return [
22
+ '# btcr2 bash completion. Install with: eval "$(btcr2 completion bash)"',
23
+ '_btcr2() { COMPREPLY=( $(compgen -W "' + COMMANDS + '" -- "${COMP_WORDS[COMP_CWORD]}") ); }',
24
+ 'complete -F _btcr2 btcr2',
25
+ ].join('\n');
26
+ case 'zsh':
27
+ return [
28
+ '# btcr2 zsh completion. Install with: eval "$(btcr2 completion zsh)"',
29
+ '_btcr2() { compadd ' + COMMANDS + ' }',
30
+ 'compdef _btcr2 btcr2',
31
+ ].join('\n');
32
+ case 'fish':
33
+ return [
34
+ '# btcr2 fish completion. Save to ~/.config/fish/completions/btcr2.fish',
35
+ 'complete -c btcr2 -f -a "' + COMMANDS + '"',
36
+ ].join('\n');
37
+ default:
38
+ throw new CLIError(`Unsupported shell "${shell}". Use bash, zsh, or fish.`, 'INVALID_ARGUMENT_ERROR', { shell });
39
+ }
40
+ }
@@ -0,0 +1,84 @@
1
+ import type { Command } from 'commander';
2
+ import { existsSync } from 'node:fs';
3
+ import { dirname } from 'node:path';
4
+ import {
5
+ CONFIG_SCHEMA_VERSION,
6
+ defaultConfigPath,
7
+ getConfigPath,
8
+ readConfigFile,
9
+ setConfigPath,
10
+ unsetConfigPath,
11
+ writeConfigFile,
12
+ } from '../config.js';
13
+ import { CLIError } from '../error.js';
14
+ import { ensureDir, writeFileAtomic } from '../keystore/atomic.js';
15
+ import { formatResult } from '../output.js';
16
+ import type { CommandResult, GlobalOptions } from '../types.js';
17
+ import { SUPPORTED_NETWORKS } from '../types.js';
18
+
19
+ /** Registers the `config` command group for reading and writing CLI configuration. */
20
+ export function registerConfigCommand(program: Command, globals: () => GlobalOptions): void {
21
+ const config = program.command('config').description('Read and write CLI configuration.');
22
+ const path = (): string => globals().config ?? defaultConfigPath();
23
+ const print = (result: CommandResult): void => console.log(formatResult(result, globals()));
24
+
25
+ config
26
+ .command('init')
27
+ .description('Create a default config file with one profile per network.')
28
+ .option('--force', 'Overwrite an existing config file.', false)
29
+ .action((options: { force?: boolean }) => {
30
+ const p = path();
31
+ if (existsSync(p) && !options.force) {
32
+ throw new CLIError(`Config already exists at ${p}. Use --force to overwrite.`, 'INVALID_ARGUMENT_ERROR', { path: p });
33
+ }
34
+ const scaffold = {
35
+ schemaVersion : CONFIG_SCHEMA_VERSION,
36
+ defaults : { output: 'text' },
37
+ profiles : Object.fromEntries(SUPPORTED_NETWORKS.map(n => [ n, {} ])),
38
+ };
39
+ ensureDir(dirname(p), 0o700);
40
+ writeFileAtomic(p, `${JSON.stringify(scaffold, null, 2)}\n`, 0o600);
41
+ print({ action: 'config-init', data: { path: p } });
42
+ });
43
+
44
+ config
45
+ .command('get [path]')
46
+ .description('Print a value at a dotted path, or the whole config.')
47
+ .action((dotted?: string) => {
48
+ const file = (readConfigFile(path()) ?? {}) as Record<string, unknown>;
49
+ print({ action: 'config-get', data: (dotted ? getConfigPath(file, dotted) : file) ?? null });
50
+ });
51
+
52
+ config
53
+ .command('set <path> <value>')
54
+ .description('Set a value at a dotted path. The value is parsed as JSON when valid, else stored as a string.')
55
+ .action((dotted: string, value: string) => {
56
+ writeConfigFile(path(), raw => setConfigPath(raw, dotted, parseValue(value)));
57
+ print({ action: 'config-set', data: { path: dotted } });
58
+ });
59
+
60
+ config
61
+ .command('unset <path>')
62
+ .description('Delete a value at a dotted path.')
63
+ .action((dotted: string) => {
64
+ writeConfigFile(path(), raw => unsetConfigPath(raw, dotted));
65
+ print({ action: 'config-unset', data: { path: dotted } });
66
+ });
67
+
68
+ config
69
+ .command('list')
70
+ .alias('ls')
71
+ .description('Print the entire config file.')
72
+ .action(() => {
73
+ print({ action: 'config-list', data: readConfigFile(path()) ?? {} });
74
+ });
75
+ }
76
+
77
+ /** Parses a value as JSON when valid, otherwise treats it as a plain string. */
78
+ function parseValue(value: string): unknown {
79
+ try {
80
+ return JSON.parse(value);
81
+ } catch {
82
+ return value;
83
+ }
84
+ }
@@ -1,6 +1,9 @@
1
+ import { KeyManagerSigner } from '@did-btcr2/key-manager';
1
2
  import type { Command } from 'commander';
2
3
  import { deriveNetwork, type ApiFactory } from '../config.js';
3
4
  import { CLIError } from '../error.js';
5
+ import { resolveKeyRef } from '../keystore/resolve-key-ref.js';
6
+ import { formatResult } from '../output.js';
4
7
  import type { GlobalOptions, UpdateCommandOptions } from '../types.js';
5
8
 
6
9
  /** The JSON Patch that marks a DID document as permanently deactivated. */
@@ -39,6 +42,13 @@ export function registerDeactivateCommand(
39
42
  verificationMethodId : string;
40
43
  beaconId : unknown;
41
44
  }) => {
45
+ if (!/^\d+$/.test(options.sourceVersionId)) {
46
+ throw new CLIError(
47
+ '--source-version-id must be a non-negative integer.',
48
+ 'INVALID_ARGUMENT_ERROR',
49
+ { value: options.sourceVersionId },
50
+ );
51
+ }
42
52
  const parsed: UpdateCommandOptions = {
43
53
  sourceDocument : options.sourceDocument as UpdateCommandOptions['sourceDocument'],
44
54
  patches : DEACTIVATION_PATCH,
@@ -54,18 +64,21 @@ export function registerDeactivateCommand(
54
64
  options
55
65
  );
56
66
  }
57
- // CLI signing is not yet wired up; deactivate uses the same update path
58
- // and inherits the same gap. Drive the SDK directly with a `Signer` for now.
59
- // Variables above are kept so command parsing + validation still works.
60
- void deriveNetwork(did);
61
- void factory;
62
- void globals;
63
- void parsed;
64
- throw new CLIError(
65
- 'CLI signing is not yet implemented. Use @did-btcr2/api with a Signer directly.',
66
- 'NOT_IMPLEMENTED_ERROR',
67
- { command: 'deactivate' }
68
- );
67
+ // Deactivation is an update that applies the deactivation patch. The core
68
+ // method has no separate deactivate path, so this routes through update.
69
+ const network = deriveNetwork(did);
70
+ const api = factory(network, globals());
71
+ const keyId = resolveKeyRef(api.kms.kms, globals().signingKey);
72
+ const signer = new KeyManagerSigner(api.kms.kms, keyId);
73
+ const data = await api.btcr2.update({
74
+ sourceDocument : parsed.sourceDocument,
75
+ patches : parsed.patches,
76
+ sourceVersionId : parsed.sourceVersionId,
77
+ verificationMethodId : parsed.verificationMethodId,
78
+ beaconId : parsed.beaconId,
79
+ signer,
80
+ });
81
+ console.log(formatResult({ action: 'deactivate', data }, globals()));
69
82
  });
70
83
  }
71
84
 
@@ -2,3 +2,7 @@ export { registerCreateCommand } from './create.js';
2
2
  export { registerResolveCommand } from './resolve.js';
3
3
  export { registerUpdateCommand } from './update.js';
4
4
  export { registerDeactivateCommand } from './deactivate.js';
5
+ export { registerKeyCommand } from './key.js';
6
+ export { registerConfigCommand } from './config.js';
7
+ export { registerProfileCommand } from './profile.js';
8
+ export { registerCompletionCommand } from './completion.js';