@did-btcr2/cli 0.14.0 → 0.16.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.
- package/README.md +106 -17
- package/dist/.tsbuildinfo +1 -1
- package/dist/cjs/index.js +1160 -131
- package/dist/esm/src/cli.js +29 -5
- package/dist/esm/src/cli.js.map +1 -1
- package/dist/esm/src/commands/config.js +130 -18
- package/dist/esm/src/commands/config.js.map +1 -1
- package/dist/esm/src/commands/create.js +13 -1
- package/dist/esm/src/commands/create.js.map +1 -1
- package/dist/esm/src/commands/deactivate.js +16 -2
- package/dist/esm/src/commands/deactivate.js.map +1 -1
- package/dist/esm/src/commands/index.js +2 -0
- package/dist/esm/src/commands/index.js.map +1 -1
- package/dist/esm/src/commands/init.js +63 -0
- package/dist/esm/src/commands/init.js.map +1 -0
- package/dist/esm/src/commands/keystore.js +81 -0
- package/dist/esm/src/commands/keystore.js.map +1 -0
- package/dist/esm/src/commands/profile.js +6 -4
- package/dist/esm/src/commands/profile.js.map +1 -1
- package/dist/esm/src/commands/update.js +16 -2
- package/dist/esm/src/commands/update.js.map +1 -1
- package/dist/esm/src/config-schema.js +149 -0
- package/dist/esm/src/config-schema.js.map +1 -0
- package/dist/esm/src/config.js +579 -55
- package/dist/esm/src/config.js.map +1 -1
- package/dist/esm/src/keystore/file-key-store.js +340 -32
- package/dist/esm/src/keystore/file-key-store.js.map +1 -1
- package/dist/esm/src/keystore/passphrase.js +40 -10
- package/dist/esm/src/keystore/passphrase.js.map +1 -1
- package/dist/esm/src/keystore/paths.js +6 -17
- package/dist/esm/src/keystore/paths.js.map +1 -1
- package/dist/esm/src/output.js +49 -0
- package/dist/esm/src/output.js.map +1 -1
- package/dist/esm/src/paths.js +59 -0
- package/dist/esm/src/paths.js.map +1 -0
- package/dist/esm/src/types.js +11 -0
- package/dist/esm/src/types.js.map +1 -1
- package/dist/types/src/cli.d.ts.map +1 -1
- package/dist/types/src/commands/config.d.ts.map +1 -1
- package/dist/types/src/commands/create.d.ts.map +1 -1
- package/dist/types/src/commands/deactivate.d.ts.map +1 -1
- package/dist/types/src/commands/index.d.ts +2 -0
- package/dist/types/src/commands/index.d.ts.map +1 -1
- package/dist/types/src/commands/init.d.ts +13 -0
- package/dist/types/src/commands/init.d.ts.map +1 -0
- package/dist/types/src/commands/keystore.d.ts +10 -0
- package/dist/types/src/commands/keystore.d.ts.map +1 -0
- package/dist/types/src/commands/profile.d.ts.map +1 -1
- package/dist/types/src/commands/update.d.ts.map +1 -1
- package/dist/types/src/config-schema.d.ts +24 -0
- package/dist/types/src/config-schema.d.ts.map +1 -0
- package/dist/types/src/config.d.ts +252 -14
- package/dist/types/src/config.d.ts.map +1 -1
- package/dist/types/src/keystore/file-key-store.d.ts +86 -10
- package/dist/types/src/keystore/file-key-store.d.ts.map +1 -1
- package/dist/types/src/keystore/passphrase.d.ts +16 -1
- package/dist/types/src/keystore/passphrase.d.ts.map +1 -1
- package/dist/types/src/keystore/paths.d.ts +6 -10
- package/dist/types/src/keystore/paths.d.ts.map +1 -1
- package/dist/types/src/output.d.ts +22 -0
- package/dist/types/src/output.d.ts.map +1 -1
- package/dist/types/src/paths.d.ts +54 -0
- package/dist/types/src/paths.d.ts.map +1 -0
- package/dist/types/src/types.d.ts +70 -0
- package/dist/types/src/types.d.ts.map +1 -1
- package/package.json +5 -5
- package/src/cli.ts +32 -4
- package/src/commands/config.ts +143 -18
- package/src/commands/create.ts +16 -1
- package/src/commands/deactivate.ts +24 -2
- package/src/commands/index.ts +2 -0
- package/src/commands/init.ts +74 -0
- package/src/commands/keystore.ts +98 -0
- package/src/commands/profile.ts +6 -4
- package/src/commands/update.ts +24 -2
- package/src/config-schema.ts +178 -0
- package/src/config.ts +752 -58
- package/src/keystore/file-key-store.ts +455 -43
- package/src/keystore/passphrase.ts +48 -8
- package/src/keystore/paths.ts +6 -18
- package/src/output.ts +53 -0
- package/src/paths.ts +79 -0
- package/src/types.ts +34 -0
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* State-location overrides that influence where the CLI keeps its home
|
|
3
|
+
* directory and its two state files. A subset of the broader
|
|
4
|
+
* `ConnectionOverrides`, restated here so this module (the single source of
|
|
5
|
+
* truth for on-disk locations) has no runtime dependency on `config.ts`.
|
|
6
|
+
*/
|
|
7
|
+
export interface PathOverrides {
|
|
8
|
+
/** Explicit home root from the `--home` flag. Wins over `$BTCR2_HOME`. */
|
|
9
|
+
home?: string;
|
|
10
|
+
/** Explicit config-file path from the `--config` flag. Overrides the home default. */
|
|
11
|
+
config?: string;
|
|
12
|
+
/** Explicit keystore path from the `--keystore` flag. Overrides the home default. */
|
|
13
|
+
keystore?: string;
|
|
14
|
+
}
|
|
15
|
+
/** Environment variable naming the CLI home directory (all state colocated). */
|
|
16
|
+
export declare const ENV_HOME = "BTCR2_HOME";
|
|
17
|
+
/** The config and keystore file names, kept side by side under the home root. */
|
|
18
|
+
export declare const CONFIG_FILENAME = "config.json";
|
|
19
|
+
export declare const KEYSTORE_FILENAME = "keystore.json";
|
|
20
|
+
/**
|
|
21
|
+
* Resolves the CLI home directory: the single root that holds `config.json` and
|
|
22
|
+
* `keystore.json` side by side (ADR 079). Resolution order, highest wins:
|
|
23
|
+
*
|
|
24
|
+
* 1. `--home <dir>` (the {@link PathOverrides.home} flag)
|
|
25
|
+
* 2. `$BTCR2_HOME`
|
|
26
|
+
* 3. the platform default (see {@link platformDefaultHome})
|
|
27
|
+
*
|
|
28
|
+
* A blank value at any layer defers to the next, mirroring the `blankToUndef`
|
|
29
|
+
* treatment every other precedence layer uses, so an exported-but-empty
|
|
30
|
+
* `BTCR2_HOME` does not resolve the home to a bare relative path.
|
|
31
|
+
*/
|
|
32
|
+
export declare function resolveHome(overrides?: PathOverrides): string;
|
|
33
|
+
/**
|
|
34
|
+
* The default home when no `--home` / `$BTCR2_HOME` override is present, chosen
|
|
35
|
+
* per OS so the location is idiomatic while staying a single colocated dir:
|
|
36
|
+
*
|
|
37
|
+
* - Windows: `%LOCALAPPDATA%\btcr2` (fallback `%APPDATA%\btcr2`, then the user
|
|
38
|
+
* profile), the native place for per-user application state.
|
|
39
|
+
* - Linux / macOS: `~/.btcr2`, the short, teachable dot-directory in the same
|
|
40
|
+
* family as `~/.ssh`, `~/.aws`, and `~/.gnupg`.
|
|
41
|
+
*/
|
|
42
|
+
export declare function platformDefaultHome(): string;
|
|
43
|
+
/**
|
|
44
|
+
* Default config-file path: `<home>/config.json`. The `--config` flag, when
|
|
45
|
+
* present, overrides it wholesale (it names a specific file, not a home).
|
|
46
|
+
*/
|
|
47
|
+
export declare function defaultConfigPath(overrides?: PathOverrides): string;
|
|
48
|
+
/**
|
|
49
|
+
* Default keystore path: `<home>/keystore.json`. The `--keystore` flag and a
|
|
50
|
+
* profile's `identity.keystore` (resolved in `config.ts`) override it; this
|
|
51
|
+
* function is the final fallback in that chain.
|
|
52
|
+
*/
|
|
53
|
+
export declare function defaultKeystorePath(overrides?: PathOverrides): string;
|
|
54
|
+
//# sourceMappingURL=paths.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../../../src/paths.ts"],"names":[],"mappings":"AAIA;;;;;GAKG;AACH,MAAM,WAAW,aAAa;IAC5B,0EAA0E;IAC1E,IAAI,CAAC,EAAO,MAAM,CAAC;IACnB,sFAAsF;IACtF,MAAM,CAAC,EAAK,MAAM,CAAC;IACnB,qFAAqF;IACrF,QAAQ,CAAC,EAAG,MAAM,CAAC;CACpB;AAED,gFAAgF;AAChF,eAAO,MAAM,QAAQ,eAAe,CAAC;AAErC,iFAAiF;AACjF,eAAO,MAAM,eAAe,gBAAgB,CAAC;AAC7C,eAAO,MAAM,iBAAiB,kBAAkB,CAAC;AAEjD;;;;;;;;;;;GAWG;AACH,wBAAgB,WAAW,CAAC,SAAS,CAAC,EAAE,aAAa,GAAG,MAAM,CAI7D;AAED;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,IAAI,MAAM,CAQ5C;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,SAAS,CAAC,EAAE,aAAa,GAAG,MAAM,CAEnE;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,SAAS,CAAC,EAAE,aAAa,GAAG,MAAM,CAErE"}
|
|
@@ -2,9 +2,24 @@ import type { DidUpdateResult } from '@did-btcr2/api';
|
|
|
2
2
|
import type { PatchOperation } from '@did-btcr2/common';
|
|
3
3
|
import type { Btcr2DidDocument, ResolutionOptions } from '@did-btcr2/method';
|
|
4
4
|
import type { DidResolutionResult } from '@web5/dids';
|
|
5
|
+
import type { DoctorReport, EffectiveConfig } from './config.js';
|
|
6
|
+
import type { ConfigIssue } from './config-schema.js';
|
|
5
7
|
export type NetworkOption = 'bitcoin' | 'testnet3' | 'testnet4' | 'signet' | 'mutinynet' | 'regtest';
|
|
6
8
|
export type OutputFormat = 'json' | 'text';
|
|
9
|
+
/**
|
|
10
|
+
* How a keystore protects its secrets, as reported by `keystore status` and
|
|
11
|
+
* `btcr2 init`: `encrypted` (passphrase-sealed), `dev` (plaintext, testnet-only),
|
|
12
|
+
* or `absent` (no keystore file yet).
|
|
13
|
+
*/
|
|
14
|
+
export type KeystoreProtectionLabel = 'encrypted' | 'dev' | 'absent';
|
|
7
15
|
export declare const SUPPORTED_NETWORKS: NetworkOption[];
|
|
16
|
+
/**
|
|
17
|
+
* Normalizes a blank value (empty or whitespace-only) to `undefined`, so a
|
|
18
|
+
* blank at any precedence layer defers to the next layer instead of masking it.
|
|
19
|
+
* A non-blank value is returned unchanged. This mirrors the `|| undefined`
|
|
20
|
+
* treatment the environment layer already applies in `readEnvOverrides`.
|
|
21
|
+
*/
|
|
22
|
+
export declare function blankToUndef(value: string | undefined): string | undefined;
|
|
8
23
|
export interface ResolveCommandOptions {
|
|
9
24
|
identifier: string;
|
|
10
25
|
options?: ResolutionOptions;
|
|
@@ -79,6 +94,15 @@ export type CommandResult = {
|
|
|
79
94
|
keyId: string;
|
|
80
95
|
active: true;
|
|
81
96
|
};
|
|
97
|
+
} | {
|
|
98
|
+
action: 'init';
|
|
99
|
+
data: {
|
|
100
|
+
home: string;
|
|
101
|
+
config: string;
|
|
102
|
+
keystore: string;
|
|
103
|
+
created: string[];
|
|
104
|
+
protection: KeystoreProtectionLabel;
|
|
105
|
+
};
|
|
82
106
|
} | {
|
|
83
107
|
action: 'config-init';
|
|
84
108
|
data: {
|
|
@@ -100,6 +124,46 @@ export type CommandResult = {
|
|
|
100
124
|
} | {
|
|
101
125
|
action: 'config-list';
|
|
102
126
|
data: unknown;
|
|
127
|
+
} | {
|
|
128
|
+
action: 'config-validate';
|
|
129
|
+
data: {
|
|
130
|
+
ok: boolean;
|
|
131
|
+
issues: ConfigIssue[];
|
|
132
|
+
};
|
|
133
|
+
} | {
|
|
134
|
+
action: 'config-effective';
|
|
135
|
+
data: EffectiveConfig;
|
|
136
|
+
} | {
|
|
137
|
+
action: 'config-path';
|
|
138
|
+
data: {
|
|
139
|
+
home: string;
|
|
140
|
+
config: string;
|
|
141
|
+
keystore: string;
|
|
142
|
+
};
|
|
143
|
+
} | {
|
|
144
|
+
action: 'config-doctor';
|
|
145
|
+
data: DoctorReport;
|
|
146
|
+
} | {
|
|
147
|
+
action: 'keystore-init';
|
|
148
|
+
data: {
|
|
149
|
+
path: string;
|
|
150
|
+
protection: 'encrypted' | 'dev';
|
|
151
|
+
};
|
|
152
|
+
} | {
|
|
153
|
+
action: 'keystore-status';
|
|
154
|
+
data: {
|
|
155
|
+
path: string;
|
|
156
|
+
protection: KeystoreProtectionLabel;
|
|
157
|
+
established: boolean;
|
|
158
|
+
keyCount: number;
|
|
159
|
+
active: string | undefined;
|
|
160
|
+
};
|
|
161
|
+
} | {
|
|
162
|
+
action: 'keystore-change-passphrase';
|
|
163
|
+
data: {
|
|
164
|
+
path: string;
|
|
165
|
+
rekeyed: number;
|
|
166
|
+
};
|
|
103
167
|
} | {
|
|
104
168
|
action: 'profile-add';
|
|
105
169
|
data: {
|
|
@@ -123,6 +187,7 @@ export interface GlobalOptions {
|
|
|
123
187
|
output: OutputFormat;
|
|
124
188
|
verbose: boolean;
|
|
125
189
|
quiet: boolean;
|
|
190
|
+
home?: string;
|
|
126
191
|
config?: string;
|
|
127
192
|
profile?: string;
|
|
128
193
|
btcRest?: string;
|
|
@@ -131,6 +196,11 @@ export interface GlobalOptions {
|
|
|
131
196
|
btcRpcPass?: string;
|
|
132
197
|
casGateway?: string;
|
|
133
198
|
casRpcUrl?: string;
|
|
199
|
+
btcTimeout?: string;
|
|
200
|
+
casTimeout?: string;
|
|
201
|
+
btcRestHeader?: string[];
|
|
202
|
+
btcRpcWallet?: string;
|
|
203
|
+
btcRpcHeader?: string[];
|
|
134
204
|
keystore?: string;
|
|
135
205
|
passphraseFile?: string;
|
|
136
206
|
signingKey?: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACtD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACxD,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,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,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,GACtE;IAAE,MAAM,EAAE,SAAS,CAAC;IAAC,IAAI,EAAE,mBAAmB,CAAA;CAAE,GAChD;IAAE,MAAM,EAAE,QAAQ,CAAC;IAAC,IAAI,EAAE,eAAe,CAAA;CAAE,GAC3C;IAAE,MAAM,EAAE,YAAY,CAAC;IAAC,IAAI,EAAE,eAAe,CAAA;CAAE,GAC/C;IAAE,MAAM,EAAE,cAAc,CAAC;IAAC,IAAI,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,OAAO,CAAA;KAAE,CAAA;CAAE,GACvF;IAAE,MAAM,EAAE,UAAU,CAAC;IAAC,IAAI,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,OAAO,CAAA;KAAE,CAAC,CAAA;CAAE,GAC3G;IAAE,MAAM,EAAE,UAAU,CAAC;IAAC,IAAI,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;KAAE,CAAA;CAAE,GACjG;IAAE,MAAM,EAAE,YAAY,CAAC;IAAC,IAAI,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,OAAO,CAAC;QAAC,MAAM,EAAE,OAAO,CAAA;KAAE,CAAA;CAAE,GACzG;IAAE,MAAM,EAAE,YAAY,CAAC;IAAC,IAAI,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAC;QAAC,eAAe,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,GAC/F;IAAE,MAAM,EAAE,YAAY,CAAC;IAAC,IAAI,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,IAAI,CAAA;KAAE,CAAA;CAAE,GAChE;IAAE,MAAM,EAAE,SAAS,CAAC;IAAC,IAAI,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,IAAI,CAAA;KAAE,CAAA;CAAE,GAC5D;IAAE,MAAM,EAAE,aAAa,CAAC;IAAC,IAAI,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,GACjD;IAAE,MAAM,EAAE,YAAY,CAAC;IAAC,IAAI,EAAE,OAAO,CAAA;CAAE,GACvC;IAAE,MAAM,EAAE,YAAY,CAAC;IAAC,IAAI,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,GAChD;IAAE,MAAM,EAAE,cAAc,CAAC;IAAC,IAAI,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,GAClD;IAAE,MAAM,EAAE,aAAa,CAAC;IAAC,IAAI,EAAE,OAAO,CAAA;CAAE,GACxC;IAAE,MAAM,EAAE,aAAa,CAAC;IAAC,IAAI,EAAE;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,GACpD;IAAE,MAAM,EAAE,aAAa,CAAC;IAAC,IAAI,EAAE;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,GACpD;IAAE,MAAM,EAAE,cAAc,CAAC;IAAC,IAAI,EAAE,OAAO,CAAA;CAAE,GACzC;IAAE,MAAM,EAAE,gBAAgB,CAAC;IAAC,IAAI,EAAE;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,CAAC;AAE5D,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAW,YAAY,CAAC;IAC9B,OAAO,EAAU,OAAO,CAAC;IACzB,KAAK,EAAY,OAAO,CAAC;IACzB,MAAM,CAAC,EAAU,MAAM,CAAC;IACxB,OAAO,CAAC,EAAS,MAAM,CAAC;IACxB,OAAO,CAAC,EAAS,MAAM,CAAC;IACxB,SAAS,CAAC,EAAO,MAAM,CAAC;IACxB,UAAU,CAAC,EAAM,MAAM,CAAC;IACxB,UAAU,CAAC,EAAM,MAAM,CAAC;IACxB,UAAU,CAAC,EAAM,MAAM,CAAC;IACxB,SAAS,CAAC,EAAO,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAQ,MAAM,CAAC;IACxB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,UAAU,CAAC,EAAM,MAAM,CAAC;CACzB"}
|
|
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.
|
|
3
|
+
"version": "0.16.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",
|
|
@@ -61,11 +61,11 @@
|
|
|
61
61
|
"@scure/base": "^1.2.6",
|
|
62
62
|
"@web5/dids": "^1.2.0",
|
|
63
63
|
"commander": "^13.1.0",
|
|
64
|
-
"@did-btcr2/
|
|
64
|
+
"@did-btcr2/api": "^0.16.1",
|
|
65
65
|
"@did-btcr2/keypair": "^0.13.1",
|
|
66
|
-
"@did-btcr2/
|
|
67
|
-
"@did-btcr2/
|
|
68
|
-
"@did-btcr2/
|
|
66
|
+
"@did-btcr2/common": "^9.1.0",
|
|
67
|
+
"@did-btcr2/key-manager": "^0.7.0",
|
|
68
|
+
"@did-btcr2/method": "^0.54.1"
|
|
69
69
|
},
|
|
70
70
|
"devDependencies": {
|
|
71
71
|
"@eslint/js": "^9.21.0",
|
package/src/cli.ts
CHANGED
|
@@ -5,12 +5,14 @@ import {
|
|
|
5
5
|
registerConfigCommand,
|
|
6
6
|
registerCreateCommand,
|
|
7
7
|
registerDeactivateCommand,
|
|
8
|
+
registerInitCommand,
|
|
8
9
|
registerKeyCommand,
|
|
10
|
+
registerKeystoreCommand,
|
|
9
11
|
registerProfileCommand,
|
|
10
12
|
registerResolveCommand,
|
|
11
13
|
registerUpdateCommand,
|
|
12
14
|
} from './commands/index.js';
|
|
13
|
-
import { defaultApiFactory, keystoreApiFactory, type ApiFactory } from './config.js';
|
|
15
|
+
import { defaultApiFactory, keystoreApiFactory, resolveOutputFormat, type ApiFactory } from './config.js';
|
|
14
16
|
import type { GlobalOptions } from './types.js';
|
|
15
17
|
import { VERSION } from './version.js';
|
|
16
18
|
|
|
@@ -41,10 +43,11 @@ export class DidBtcr2Cli {
|
|
|
41
43
|
this.program = new Command('btcr2')
|
|
42
44
|
.version(`btcr2 ${VERSION}`, '-v, --version', 'Output the current version')
|
|
43
45
|
.description('CLI tool for the did:btcr2 method')
|
|
44
|
-
.option('-o, --output <format>', 'Output format <json|text>
|
|
46
|
+
.option('-o, --output <format>', 'Output format <json|text> (default: config defaults.output, else text)')
|
|
45
47
|
.option('--verbose', 'Verbose output', false)
|
|
46
48
|
.option('--quiet', 'Suppress non-essential output', false)
|
|
47
|
-
.option('
|
|
49
|
+
.option('--home <dir>', 'btcr2 home directory holding config.json + keystore.json (default: ~/.btcr2, %LOCALAPPDATA%\\btcr2 on Windows; overrides $BTCR2_HOME)')
|
|
50
|
+
.option('-c, --config <path>', 'Path to config file (default: <home>/config.json)')
|
|
48
51
|
.option('--profile <name>', 'Config profile name (default: auto-detected from network)')
|
|
49
52
|
.option('--btc-rest <url>', 'Override Bitcoin REST endpoint (Esplora API)')
|
|
50
53
|
.option('--btc-rpc-url <url>', 'Override Bitcoin Core RPC endpoint')
|
|
@@ -52,17 +55,34 @@ export class DidBtcr2Cli {
|
|
|
52
55
|
.option('--btc-rpc-pass <pass>', 'Bitcoin Core RPC password')
|
|
53
56
|
.option('--cas-gateway <url>', 'IPFS HTTP gateway for CAS reads (read-only)')
|
|
54
57
|
.option('--cas-rpc-url <url>', 'IPFS HTTP RPC endpoint for a writable CAS (reads + writes; enables --publish-to-cas)')
|
|
55
|
-
.option('--
|
|
58
|
+
.option('--btc-timeout <ms>', 'Bitcoin REST/RPC request timeout in milliseconds (default: unbounded)')
|
|
59
|
+
.option('--cas-timeout <ms>', 'CAS request timeout in milliseconds (default: 30000; 0 disables)')
|
|
60
|
+
.option('--btc-rest-header <header>', 'Extra Bitcoin REST header "Key: Value" (repeatable)', collectHeader, [])
|
|
61
|
+
.option('--btc-rpc-wallet <name>', 'Bitcoin Core wallet name for wallet-scoped RPCs')
|
|
62
|
+
.option('--btc-rpc-header <header>', 'Extra Bitcoin Core RPC header "Key: Value" (repeatable)', collectHeader, [])
|
|
63
|
+
.option('--keystore <path>', 'Path to the keystore file (default: <home>/keystore.json)')
|
|
56
64
|
.option('--passphrase-file <path>', 'Read the keystore passphrase from a file (unattended use)')
|
|
57
65
|
.option('--signing-key <ref>', 'Key for create/update/deactivate signing: a URN, fingerprint prefix, or name');
|
|
58
66
|
|
|
59
67
|
const globals = (): GlobalOptions => this.program.opts() as GlobalOptions;
|
|
60
68
|
|
|
69
|
+
// Resolve the effective output format (flag -> BTCR2_OUTPUT -> config
|
|
70
|
+
// defaults.output -> 'text') before any command action runs, so a configured
|
|
71
|
+
// default is honored while an explicit flag still wins. The commander option
|
|
72
|
+
// carries no hard default (which would mask defaults.output); the resolved
|
|
73
|
+
// value is written back so every command reads it through globals().output.
|
|
74
|
+
this.program.hook('preAction', () => {
|
|
75
|
+
const opts = this.program.opts() as GlobalOptions;
|
|
76
|
+
opts.output = resolveOutputFormat({ output: opts.output, config: opts.config, home: opts.home });
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
registerInitCommand(this.program, globals);
|
|
61
80
|
registerCreateCommand(this.program, factory, keystoreFactory, globals);
|
|
62
81
|
registerResolveCommand(this.program, factory, globals);
|
|
63
82
|
registerUpdateCommand(this.program, keystoreFactory, globals);
|
|
64
83
|
registerDeactivateCommand(this.program, keystoreFactory, globals);
|
|
65
84
|
registerKeyCommand(this.program, keystoreFactory, globals);
|
|
85
|
+
registerKeystoreCommand(this.program, globals);
|
|
66
86
|
registerConfigCommand(this.program, globals);
|
|
67
87
|
registerProfileCommand(this.program, globals);
|
|
68
88
|
registerCompletionCommand(this.program, globals);
|
|
@@ -84,6 +104,14 @@ export class DidBtcr2Cli {
|
|
|
84
104
|
}
|
|
85
105
|
}
|
|
86
106
|
|
|
107
|
+
/**
|
|
108
|
+
* Commander collector for a repeatable header option: accumulates each
|
|
109
|
+
* `Key: Value` occurrence into an array, preserving order.
|
|
110
|
+
*/
|
|
111
|
+
function collectHeader(value: string, previous: string[]): string[] {
|
|
112
|
+
return [ ...previous, value ];
|
|
113
|
+
}
|
|
114
|
+
|
|
87
115
|
/**
|
|
88
116
|
* Normalizes argv to ensure it has at least two elements.
|
|
89
117
|
* @param {string[]} argv - The original argv array.
|
package/src/commands/config.ts
CHANGED
|
@@ -1,25 +1,31 @@
|
|
|
1
1
|
import type { Command } from 'commander';
|
|
2
2
|
import { existsSync } from 'node:fs';
|
|
3
|
-
import { dirname } from 'node:path';
|
|
4
3
|
import {
|
|
5
4
|
CONFIG_SCHEMA_VERSION,
|
|
6
5
|
defaultConfigPath,
|
|
7
6
|
getConfigPath,
|
|
7
|
+
parseConfigFileRaw,
|
|
8
8
|
readConfigFile,
|
|
9
|
+
resolveDefaultNetwork,
|
|
10
|
+
resolveEffectiveConfig,
|
|
11
|
+
resolveKeystorePath,
|
|
12
|
+
runDoctor,
|
|
9
13
|
setConfigPath,
|
|
10
14
|
unsetConfigPath,
|
|
11
15
|
writeConfigFile,
|
|
16
|
+
writeDefaultConfigFile,
|
|
12
17
|
} from '../config.js';
|
|
18
|
+
import { findConfigIssues, validateConfigSet } from '../config-schema.js';
|
|
13
19
|
import { CLIError } from '../error.js';
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import type { CommandResult, GlobalOptions } from '../types.js';
|
|
20
|
+
import { formatResult, REDACTED, redactSecrets, scrubUrlUserinfo } from '../output.js';
|
|
21
|
+
import { resolveHome } from '../paths.js';
|
|
22
|
+
import type { CommandResult, GlobalOptions, NetworkOption } from '../types.js';
|
|
17
23
|
import { SUPPORTED_NETWORKS } from '../types.js';
|
|
18
24
|
|
|
19
25
|
/** Registers the `config` command group for reading and writing CLI configuration. */
|
|
20
26
|
export function registerConfigCommand(program: Command, globals: () => GlobalOptions): void {
|
|
21
27
|
const config = program.command('config').description('Read and write CLI configuration.');
|
|
22
|
-
const path = (): string => globals().config ?? defaultConfigPath();
|
|
28
|
+
const path = (): string => globals().config ?? defaultConfigPath(globals());
|
|
23
29
|
const print = (result: CommandResult): void => console.log(formatResult(result, globals()));
|
|
24
30
|
|
|
25
31
|
config
|
|
@@ -31,29 +37,33 @@ export function registerConfigCommand(program: Command, globals: () => GlobalOpt
|
|
|
31
37
|
if (existsSync(p) && !options.force) {
|
|
32
38
|
throw new CLIError(`Config already exists at ${p}. Use --force to overwrite.`, 'INVALID_ARGUMENT_ERROR', { path: p });
|
|
33
39
|
}
|
|
34
|
-
|
|
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);
|
|
40
|
+
writeDefaultConfigFile(p);
|
|
41
41
|
print({ action: 'config-init', data: { path: p } });
|
|
42
42
|
});
|
|
43
43
|
|
|
44
44
|
config
|
|
45
45
|
.command('get [path]')
|
|
46
46
|
.description('Print a value at a dotted path, or the whole config.')
|
|
47
|
-
.
|
|
47
|
+
.option('--show-secrets', 'Reveal secret values (RPC password, etc.) instead of redacting them.', false)
|
|
48
|
+
.action((dotted: string | undefined, opts: { showSecrets?: boolean }) => {
|
|
48
49
|
const file = (readConfigFile(path()) ?? {}) as Record<string, unknown>;
|
|
49
|
-
|
|
50
|
+
const raw = (dotted ? getConfigPath(file, dotted) : file) ?? null;
|
|
51
|
+
const leaf = dotted ? dotted.split('.').pop() : undefined;
|
|
52
|
+
print({ action: 'config-get', data: opts.showSecrets ? raw : redactSecrets(raw, leaf) });
|
|
50
53
|
});
|
|
51
54
|
|
|
52
55
|
config
|
|
53
56
|
.command('set <path> <value>')
|
|
54
57
|
.description('Set a value at a dotted path. The value is parsed as JSON when valid, else stored as a string.')
|
|
55
58
|
.action((dotted: string, value: string) => {
|
|
56
|
-
|
|
59
|
+
const parsed = parseValue(dotted, value);
|
|
60
|
+
// Reject an invalid enum value for a known key up-front; warn (but still
|
|
61
|
+
// write) an unknown path so forward-compatible and third-party keys work.
|
|
62
|
+
const { unknownPath } = validateConfigSet(dotted, parsed);
|
|
63
|
+
if (unknownPath && !globals().quiet) {
|
|
64
|
+
console.error(`Warning: "${dotted}" is not a known config path; writing it anyway.`);
|
|
65
|
+
}
|
|
66
|
+
writeConfigFile(path(), raw => setConfigPath(raw, dotted, parsed));
|
|
57
67
|
print({ action: 'config-set', data: { path: dotted } });
|
|
58
68
|
});
|
|
59
69
|
|
|
@@ -69,16 +79,131 @@ export function registerConfigCommand(program: Command, globals: () => GlobalOpt
|
|
|
69
79
|
.command('list')
|
|
70
80
|
.alias('ls')
|
|
71
81
|
.description('Print the entire config file.')
|
|
82
|
+
.option('--show-secrets', 'Reveal secret values (RPC password, etc.) instead of redacting them.', false)
|
|
83
|
+
.action((opts: { showSecrets?: boolean }) => {
|
|
84
|
+
const file = readConfigFile(path()) ?? {};
|
|
85
|
+
print({ action: 'config-list', data: opts.showSecrets ? file : redactSecrets(file) });
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
config
|
|
89
|
+
.command('validate')
|
|
90
|
+
.description('Check the config for unknown keys, invalid enum values, and an unsupported schema version.')
|
|
72
91
|
.action(() => {
|
|
73
|
-
|
|
92
|
+
// Read raw (bypassing the schema-version ceiling that readConfigFile
|
|
93
|
+
// enforces) so a newer-than-supported version is reported as a finding
|
|
94
|
+
// rather than aborting the very command meant to diagnose it.
|
|
95
|
+
const file = parseConfigFileRaw(path()) ?? {};
|
|
96
|
+
const issues = findConfigIssues(file, CONFIG_SCHEMA_VERSION);
|
|
97
|
+
if (issues.length > 0) process.exitCode ??= 1;
|
|
98
|
+
print({ action: 'config-validate', data: { ok: issues.length === 0, issues } });
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
config
|
|
102
|
+
.command('effective')
|
|
103
|
+
.description('Print the resolved connection config with per-value provenance (flag|env|file|default).')
|
|
104
|
+
.option('-n, --network <network>', 'Network to resolve for (default: config default network)')
|
|
105
|
+
.option('--show-secrets', 'Reveal secret values (RPC password) instead of redacting them.', false)
|
|
106
|
+
.action((opts: { network?: string; showSecrets?: boolean }) => {
|
|
107
|
+
const network = resolveIntrospectionNetwork(opts.network, globals());
|
|
108
|
+
const data = resolveEffectiveConfig(network, globals());
|
|
109
|
+
// Redact the resolved RPC password and any password embedded in an endpoint
|
|
110
|
+
// URL by default; provenance still shows where each value came from.
|
|
111
|
+
// --show-secrets reveals them for deliberate debugging.
|
|
112
|
+
if (!opts.showSecrets) {
|
|
113
|
+
if (data.btc.rpcPass.value !== undefined) {
|
|
114
|
+
data.btc.rpcPass = { ...data.btc.rpcPass, value: REDACTED };
|
|
115
|
+
}
|
|
116
|
+
for (const entry of [ data.btc.rest, data.btc.rpcUrl, data.cas.gateway, data.cas.rpcUrl ]) {
|
|
117
|
+
if (typeof entry.value === 'string') entry.value = scrubUrlUserinfo(entry.value);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
print({ action: 'config-effective', data });
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
config
|
|
124
|
+
.command('path')
|
|
125
|
+
.description('Print the resolved home directory, config-file, and keystore paths.')
|
|
126
|
+
.action(() => {
|
|
127
|
+
const g = globals();
|
|
128
|
+
const data = {
|
|
129
|
+
home : resolveHome(g),
|
|
130
|
+
config : g.config ?? defaultConfigPath(g),
|
|
131
|
+
// Diagnostic command: report the path even when the config is malformed
|
|
132
|
+
// (this is a command you run to find and fix a broken config).
|
|
133
|
+
keystore : resolveKeystorePath(g, { lenient: true }),
|
|
134
|
+
};
|
|
135
|
+
print({ action: 'config-path', data });
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
config
|
|
139
|
+
.command('doctor')
|
|
140
|
+
.description('Probe reachability of the resolved endpoints (read-only; touches the network).')
|
|
141
|
+
.option('-n, --network <network>', 'Network to resolve for (default: config default network)')
|
|
142
|
+
.action(async (opts: { network?: string }) => {
|
|
143
|
+
const network = resolveIntrospectionNetwork(opts.network, globals());
|
|
144
|
+
const report = await runDoctor(network, globals());
|
|
145
|
+
if (report.checks.some(check => !check.ok)) process.exitCode ??= 1;
|
|
146
|
+
print({ action: 'config-doctor', data: report });
|
|
74
147
|
});
|
|
75
148
|
}
|
|
76
149
|
|
|
77
|
-
/**
|
|
78
|
-
|
|
150
|
+
/**
|
|
151
|
+
* Resolves the network an introspection command (`effective`/`doctor`) operates
|
|
152
|
+
* on: an explicit `--network`, validated, else the config's default network.
|
|
153
|
+
*/
|
|
154
|
+
function resolveIntrospectionNetwork(explicit: string | undefined, globals: GlobalOptions): NetworkOption {
|
|
155
|
+
if (explicit) {
|
|
156
|
+
if (!SUPPORTED_NETWORKS.includes(explicit as NetworkOption)) {
|
|
157
|
+
throw new CLIError(
|
|
158
|
+
`Invalid network "${explicit}". Must be one of ${SUPPORTED_NETWORKS.join(', ')}.`,
|
|
159
|
+
'INVALID_ARGUMENT_ERROR',
|
|
160
|
+
{ network: explicit },
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
return explicit as NetworkOption;
|
|
164
|
+
}
|
|
165
|
+
return resolveDefaultNetwork(globals);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Parses a `config set` value. Known scalar endpoint, credential, and defaults
|
|
170
|
+
* paths are stored as raw strings so a bare `8080` is not coerced to the number
|
|
171
|
+
* `8080` (which would flow into a host field as a non-string). Every other path
|
|
172
|
+
* is parsed as JSON when valid, else stored as a plain string, so structured
|
|
173
|
+
* values (objects, arrays, booleans, and genuinely numeric fields) still work.
|
|
174
|
+
*/
|
|
175
|
+
function parseValue(dotted: string, value: string): unknown {
|
|
176
|
+
if (isStringScalarPath(dotted)) return value;
|
|
79
177
|
try {
|
|
80
178
|
return JSON.parse(value);
|
|
81
179
|
} catch {
|
|
82
180
|
return value;
|
|
83
181
|
}
|
|
84
182
|
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Whether a dotted config path addresses a leaf that must be stored as a string
|
|
186
|
+
* (an endpoint URL, a credential, a network/profile/output name), so JSON
|
|
187
|
+
* coercion never turns it into a non-string.
|
|
188
|
+
*/
|
|
189
|
+
function isStringScalarPath(dotted: string): boolean {
|
|
190
|
+
const segments = dotted.split('.');
|
|
191
|
+
|
|
192
|
+
if (segments.length === 2 && segments[0] === 'defaults') {
|
|
193
|
+
return [ 'profile', 'network', 'output' ].includes(segments[1]);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
if (segments.length === 3 && segments[0] === 'profiles' && segments[2] === 'network') {
|
|
197
|
+
return true;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
if (segments.length === 4 && segments[0] === 'profiles') {
|
|
201
|
+
const group = segments[2];
|
|
202
|
+
const leaf = segments[3];
|
|
203
|
+
if (group === 'btc') return [ 'rest', 'rpcUrl', 'rpcUser', 'rpcPass', 'changeAddress', 'wallet' ].includes(leaf);
|
|
204
|
+
if (group === 'cas') return [ 'gateway', 'rpcUrl' ].includes(leaf);
|
|
205
|
+
if (group === 'identity') return [ 'keystore', 'default' ].includes(leaf);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
return false;
|
|
209
|
+
}
|
package/src/commands/create.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { bytesToHex, hexToBytes } from '@noble/hashes/utils.js';
|
|
2
2
|
import type { Command } from 'commander';
|
|
3
3
|
import type { ApiFactory, ConnectionOverrides } from '../config.js';
|
|
4
|
-
import { resolveDefaultNetwork } from '../config.js';
|
|
4
|
+
import { assertKeystoreAllowedForNetwork, profileNetworkMismatch, resolveDefaultNetwork } from '../config.js';
|
|
5
5
|
import { CLIError } from '../error.js';
|
|
6
6
|
import { resolveKeyRef } from '../keystore/resolve-key-ref.js';
|
|
7
7
|
import { formatResult } from '../output.js';
|
|
@@ -63,6 +63,18 @@ export function registerCreateCommand(
|
|
|
63
63
|
const network = resolveNetwork(options.network, overrides);
|
|
64
64
|
const signingKey = g.signingKey;
|
|
65
65
|
|
|
66
|
+
// Warn (never block) when the network being encoded disagrees with the
|
|
67
|
+
// active profile's declared network, so a `production` profile holding
|
|
68
|
+
// mainnet endpoints cannot silently mint a regtest DID.
|
|
69
|
+
const mismatch = profileNetworkMismatch(network, overrides);
|
|
70
|
+
if (mismatch && !g.quiet) {
|
|
71
|
+
process.stderr.write(
|
|
72
|
+
`Warning: creating a "${network}" identifier while the active profile `
|
|
73
|
+
+ `"${mismatch.profile}" declares network "${mismatch.declared}". The `
|
|
74
|
+
+ 'identifier\'s network and the profile\'s endpoints may not match.\n'
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
66
78
|
/** Prints the result, plus a stderr provenance line in text mode. */
|
|
67
79
|
const print = (result: CommandResult, note?: string): void => {
|
|
68
80
|
console.log(formatResult(result, g));
|
|
@@ -120,6 +132,8 @@ export function registerCreateCommand(
|
|
|
120
132
|
}
|
|
121
133
|
|
|
122
134
|
// Generate: mint a fresh key, persist it, and set it active (passphrase prompt).
|
|
135
|
+
// Refuse to seal a fresh mainnet key into an unencrypted dev keystore (ADR 080).
|
|
136
|
+
assertKeystoreAllowedForNetwork(network, overrides);
|
|
123
137
|
const api = keystoreFactory(undefined, overrides);
|
|
124
138
|
const { did, keyId } = api.generateDid({ network, setActive: true });
|
|
125
139
|
const publicKey = bytesToHex(api.kms.getPublicKey(keyId));
|
|
@@ -133,6 +147,7 @@ export function registerCreateCommand(
|
|
|
133
147
|
/** Builds the keystore- and config-resolution overrides from the global flags. */
|
|
134
148
|
function overridesFromGlobals(g: GlobalOptions): ConnectionOverrides {
|
|
135
149
|
return {
|
|
150
|
+
home : g.home,
|
|
136
151
|
config : g.config,
|
|
137
152
|
profile : g.profile,
|
|
138
153
|
keystore : g.keystore,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { PublishToCasMode } from '@did-btcr2/api';
|
|
2
2
|
import { KeyManagerSigner } from '@did-btcr2/key-manager';
|
|
3
3
|
import type { Command } from 'commander';
|
|
4
|
-
import { deriveNetwork, type ApiFactory } from '../config.js';
|
|
4
|
+
import { assertKeystoreAllowedForNetwork, deriveNetwork, resolveBroadcastOptions, resolveSigningKeyRef, type ApiFactory } from '../config.js';
|
|
5
5
|
import { CLIError } from '../error.js';
|
|
6
6
|
import { resolveKeyRef } from '../keystore/resolve-key-ref.js';
|
|
7
7
|
import { formatResult } from '../output.js';
|
|
@@ -44,12 +44,24 @@ export function registerDeactivateCommand(
|
|
|
44
44
|
parsePublishToCasMode,
|
|
45
45
|
'never',
|
|
46
46
|
)
|
|
47
|
+
.option(
|
|
48
|
+
'--fee-rate <satsPerVByte>',
|
|
49
|
+
'Fee rate in sats/vByte for the beacon transaction (default: 5). '
|
|
50
|
+
+ 'Raise it under congestion so the transaction confirms.',
|
|
51
|
+
)
|
|
52
|
+
.option(
|
|
53
|
+
'--change-address <address>',
|
|
54
|
+
'Send transaction change to this address instead of the beacon address, '
|
|
55
|
+
+ 'so a DID\'s announcements are not linked on-chain (ADR 044).',
|
|
56
|
+
)
|
|
47
57
|
.action(async (options: {
|
|
48
58
|
sourceDocument : unknown;
|
|
49
59
|
sourceVersionId : string;
|
|
50
60
|
verificationMethodId : string;
|
|
51
61
|
beaconId : unknown;
|
|
52
62
|
publishToCas : PublishToCasMode;
|
|
63
|
+
feeRate? : string;
|
|
64
|
+
changeAddress? : string;
|
|
53
65
|
}) => {
|
|
54
66
|
if (!/^\d+$/.test(options.sourceVersionId)) {
|
|
55
67
|
throw new CLIError(
|
|
@@ -76,9 +88,18 @@ export function registerDeactivateCommand(
|
|
|
76
88
|
// Deactivation is an update that applies the deactivation patch. The core
|
|
77
89
|
// method has no separate deactivate path, so this routes through update.
|
|
78
90
|
const network = deriveNetwork(did);
|
|
91
|
+
// Refuse to sign a mainnet deactivation with an unencrypted dev keystore (ADR 080).
|
|
92
|
+
assertKeystoreAllowedForNetwork(network, globals());
|
|
79
93
|
const api = factory(network, globals());
|
|
80
|
-
const keyId = resolveKeyRef(api.kms.kms, globals()
|
|
94
|
+
const keyId = resolveKeyRef(api.kms.kms, resolveSigningKeyRef(globals()));
|
|
81
95
|
const signer = new KeyManagerSigner(api.kms.kms, keyId);
|
|
96
|
+
// Resolve fee-rate/change-address through the flag -> env -> profile chain
|
|
97
|
+
// into beacon broadcast options. Undefined when neither is set, so the
|
|
98
|
+
// SDK defaults (5 sat/vB, change back to the beacon address) still apply.
|
|
99
|
+
const broadcastOptions = resolveBroadcastOptions(network, globals(), {
|
|
100
|
+
feeRate : options.feeRate,
|
|
101
|
+
changeAddress : options.changeAddress,
|
|
102
|
+
});
|
|
82
103
|
// CAS publication is optional and never required: every beacon update can
|
|
83
104
|
// be completed and shared via sidecar alone. It is opt-in and defaults to
|
|
84
105
|
// 'never'; pass --publish-to-cas auto|always to publish the signed update
|
|
@@ -93,6 +114,7 @@ export function registerDeactivateCommand(
|
|
|
93
114
|
beaconId : parsed.beaconId,
|
|
94
115
|
signer,
|
|
95
116
|
publishToCas : options.publishToCas,
|
|
117
|
+
...(broadcastOptions ? { broadcastOptions } : {}),
|
|
96
118
|
});
|
|
97
119
|
console.log(formatResult({ action: 'deactivate', data }, globals()));
|
|
98
120
|
});
|
package/src/commands/index.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
export { registerInitCommand } from './init.js';
|
|
1
2
|
export { registerCreateCommand } from './create.js';
|
|
2
3
|
export { registerResolveCommand } from './resolve.js';
|
|
3
4
|
export { registerUpdateCommand } from './update.js';
|
|
4
5
|
export { registerDeactivateCommand } from './deactivate.js';
|
|
5
6
|
export { registerKeyCommand } from './key.js';
|
|
7
|
+
export { registerKeystoreCommand } from './keystore.js';
|
|
6
8
|
export { registerConfigCommand } from './config.js';
|
|
7
9
|
export { registerProfileCommand } from './profile.js';
|
|
8
10
|
export { registerCompletionCommand } from './completion.js';
|