@1sat/cli 0.0.22 → 0.0.23

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/package.json CHANGED
@@ -1,34 +1,41 @@
1
1
  {
2
- "name": "@1sat/cli",
3
- "version": "0.0.22",
4
- "description": "CLI for 1Sat Ordinals SDK",
5
- "type": "module",
6
- "main": "./src/cli.ts",
7
- "bin": {
8
- "1sat": "./src/cli.ts"
9
- },
10
- "files": ["src"],
11
- "scripts": {
12
- "build": "bun build ./src/cli.ts --compile --outfile=bin/1sat --external pg --external pg-native --external pg-query-stream --external tedious --external oracledb --external mysql",
13
- "dev": "bun run src/cli.ts",
14
- "lint": "biome check src"
15
- },
16
- "keywords": ["1sat", "bsv", "ordinals", "cli"],
17
- "license": "MIT",
18
- "dependencies": {
19
- "@1sat/actions": "0.0.82",
20
- "@1sat/client": "0.0.20",
21
- "@1sat/types": "0.0.14",
22
- "@1sat/wallet-node": ">=0.0.13",
23
- "@bsv/sdk": "^2.0.6",
24
- "chalk": "^5.0.0",
25
- "@clack/prompts": "^0.8.0",
26
- "bitcoin-backup": "^0.0.11",
27
- "dotenv": "^17.0.0",
28
- "evlog": "^2.10.0"
29
- },
30
- "devDependencies": {
31
- "@types/bun": "^1.3.9",
32
- "typescript": "^5.9.3"
33
- }
34
- }
2
+ "name": "@1sat/cli",
3
+ "version": "0.0.23",
4
+ "description": "CLI for 1Sat Ordinals SDK",
5
+ "type": "module",
6
+ "main": "./src/cli.ts",
7
+ "bin": {
8
+ "1sat": "./src/cli.ts"
9
+ },
10
+ "files": [
11
+ "src"
12
+ ],
13
+ "scripts": {
14
+ "build": "bun build ./src/cli.ts --compile --outfile=bin/1sat --external pg --external pg-native --external pg-query-stream --external tedious --external oracledb --external mysql",
15
+ "dev": "bun run src/cli.ts",
16
+ "lint": "biome check src"
17
+ },
18
+ "keywords": [
19
+ "1sat",
20
+ "bsv",
21
+ "ordinals",
22
+ "cli"
23
+ ],
24
+ "license": "MIT",
25
+ "dependencies": {
26
+ "@1sat/actions": "0.0.83",
27
+ "@1sat/client": "0.0.20",
28
+ "@1sat/types": "0.0.15",
29
+ "@1sat/wallet-node": "0.0.23",
30
+ "@bsv/sdk": "^2.0.6",
31
+ "chalk": "^5.0.0",
32
+ "@clack/prompts": "^0.8.0",
33
+ "bitcoin-backup": "^0.0.11",
34
+ "dotenv": "^17.0.0",
35
+ "evlog": "^2.10.0"
36
+ },
37
+ "devDependencies": {
38
+ "@types/bun": "^1.3.9",
39
+ "typescript": "^5.9.3"
40
+ }
41
+ }
package/src/args.ts CHANGED
@@ -93,7 +93,7 @@ export function extractFlags(args: string[], flag: string): string[] {
93
93
  const value = args[i + 1]
94
94
  if (value && !value.startsWith('--')) {
95
95
  // Split comma-delimited values
96
- values.push(...value.split(',').filter(v => v.length > 0))
96
+ values.push(...value.split(',').filter((v) => v.length > 0))
97
97
  }
98
98
  }
99
99
  }
@@ -105,7 +105,9 @@ function configSet(args: string[], opts: GlobalFlags): void {
105
105
  if (key === 'monitorIntervalMinutes') {
106
106
  const n = Number(value)
107
107
  if (!Number.isFinite(n) || n < 0) {
108
- fatal('monitorIntervalMinutes must be a non-negative number (0 to disable)')
108
+ fatal(
109
+ 'monitorIntervalMinutes must be a non-negative number (0 to disable)',
110
+ )
109
111
  }
110
112
  }
111
113
 
@@ -4,7 +4,7 @@
4
4
  * BAP (Bitcoin Attestation Protocol) identity management.
5
5
  */
6
6
 
7
- import { publishIdentity, signBsm } from '@1sat/actions'
7
+ import { publishIdentity, signBsm, updateProfile } from '@1sat/actions'
8
8
  import type { GlobalFlags } from '../args'
9
9
  import { extractFlag } from '../args'
10
10
  import { loadContext } from '../context'
@@ -21,6 +21,8 @@ export async function handleIdentityCommand(
21
21
  switch (subcommand) {
22
22
  case 'create':
23
23
  return identityCreate(rest, opts)
24
+ case 'update-profile':
25
+ return identityUpdateProfile(rest, opts)
24
26
  case 'info':
25
27
  return identityInfo(rest, opts)
26
28
  case 'sign':
@@ -30,6 +32,7 @@ export async function handleIdentityCommand(
30
32
  default:
31
33
  printCommandHelp('identity', {
32
34
  create: 'Create/publish a BAP identity',
35
+ 'update-profile': 'Update BAP identity profile (--profile <json>)',
33
36
  info: 'Show BAP identity information',
34
37
  sign: 'Sign a message with identity key (--message <text>)',
35
38
  verify:
@@ -128,3 +131,36 @@ async function identityVerify(
128
131
  'identity verify is not yet implemented (needs direct BSM.verify integration)',
129
132
  )
130
133
  }
134
+
135
+ async function identityUpdateProfile(
136
+ args: string[],
137
+ opts: GlobalFlags,
138
+ ): Promise<void> {
139
+ const profileStr = extractFlag(args, '--profile')
140
+
141
+ if (!profileStr) fatal('Missing --profile <json>')
142
+
143
+ let profile: Record<string, unknown>
144
+ try {
145
+ profile = JSON.parse(profileStr)
146
+ } catch {
147
+ fatal(`Invalid JSON in --profile: ${profileStr}`)
148
+ }
149
+
150
+ const privateKey = await loadKey(resolvePassword())
151
+ const { ctx, destroy } = await loadContext(privateKey, {
152
+ chain: opts.chain,
153
+ })
154
+
155
+ try {
156
+ const result = await updateProfile.execute(ctx, { profile })
157
+
158
+ if (result.error) {
159
+ fatal(result.error)
160
+ }
161
+
162
+ output(result, opts)
163
+ } finally {
164
+ await destroy()
165
+ }
166
+ }
@@ -61,33 +61,54 @@ async function handshake(key: PrivateKey): Promise<Session> {
61
61
  if (!res.ok) throw new Error(`Handshake failed: HTTP ${res.status}`)
62
62
 
63
63
  const data = await res.json()
64
- if (data.messageType !== 'initialResponse') throw new Error(`Unexpected: ${data.messageType}`)
64
+ if (data.messageType !== 'initialResponse')
65
+ throw new Error(`Unexpected: ${data.messageType}`)
65
66
 
66
67
  const serverNonce = data.nonce as string
67
68
  const deriver = new KeyDeriver(key)
68
69
  const serverPub = deriver.derivePublicKey(
69
- AUTH_PROTOCOL_ID, `${serverNonce} ${clientNonce}`, data.identityKey as string, false,
70
+ AUTH_PROTOCOL_ID,
71
+ `${serverNonce} ${clientNonce}`,
72
+ data.identityKey as string,
73
+ false,
70
74
  )
71
75
 
72
76
  const clientNonceBytes = toArray(clientNonce, 'base64')
73
77
  const serverNonceBytes = toArray(serverNonce, 'base64')
74
- const msgHash = Hash.sha256(Array.from(new Uint8Array([...clientNonceBytes, ...serverNonceBytes])))
78
+ const msgHash = Hash.sha256(
79
+ Array.from(new Uint8Array([...clientNonceBytes, ...serverNonceBytes])),
80
+ )
75
81
 
76
82
  const sig = Signature.fromDER(data.signature as string, 'hex')
77
- if (!serverPub.verify(Array.from(msgHash), sig)) throw new Error('Server signature verification failed')
83
+ if (!serverPub.verify(Array.from(msgHash), sig))
84
+ throw new Error('Server signature verification failed')
78
85
 
79
86
  const log = createLogger({ context: 'auth' })
80
- log.set({ event: 'handshake_complete', serverIdentityKey: (data.identityKey as string).slice(0, 16) })
87
+ log.set({
88
+ event: 'handshake_complete',
89
+ serverIdentityKey: (data.identityKey as string).slice(0, 16),
90
+ })
81
91
  log.emit()
82
92
 
83
- return { serverIdentityKey: data.identityKey as string, serverNonce, clientKey: key }
93
+ return {
94
+ serverIdentityKey: data.identityKey as string,
95
+ serverNonce,
96
+ clientKey: key,
97
+ }
84
98
  }
85
99
 
86
- function signHeaders(session: Session, pathname: string): Record<string, string> {
100
+ function signHeaders(
101
+ session: Session,
102
+ pathname: string,
103
+ ): Record<string, string> {
87
104
  const { serverIdentityKey, serverNonce, clientKey: key } = session
88
105
  const nonce = generateNonce()
89
106
  const deriver = new KeyDeriver(key)
90
- const derivedKey = deriver.derivePrivateKey(AUTH_PROTOCOL_ID, `${nonce} ${serverNonce}`, serverIdentityKey)
107
+ const derivedKey = deriver.derivePrivateKey(
108
+ AUTH_PROTOCOL_ID,
109
+ `${nonce} ${serverNonce}`,
110
+ serverIdentityKey,
111
+ )
91
112
  const payload = new TextEncoder().encode(pathname)
92
113
  const msgHash = Hash.sha256(Array.from(payload))
93
114
  const sig = derivedKey.sign(Array.from(msgHash))
@@ -109,7 +130,9 @@ export async function handleMcpProxyCommand(): Promise<void> {
109
130
  } catch {
110
131
  startLog.set({ event: 'server_unreachable', url: MCP_URL })
111
132
  startLog.emit()
112
- process.stderr.write(`[1sat mcp-proxy] Server not reachable at ${MCP_URL} — is 1Sat wallet running?\n`)
133
+ process.stderr.write(
134
+ `[1sat mcp-proxy] Server not reachable at ${MCP_URL} — is 1Sat wallet running?\n`,
135
+ )
113
136
  process.exit(1)
114
137
  }
115
138
 
@@ -156,7 +179,11 @@ export async function handleMcpProxyCommand(): Promise<void> {
156
179
  method = JSON.parse(line).method
157
180
  } catch {}
158
181
 
159
- const res = await fetch(`${MCP_URL}/mcp`, { method: 'POST', headers, body: line })
182
+ const res = await fetch(`${MCP_URL}/mcp`, {
183
+ method: 'POST',
184
+ headers,
185
+ body: line,
186
+ })
160
187
 
161
188
  const sessionHeader = res.headers.get('mcp-session-id')
162
189
  if (sessionHeader) mcpSessionId = sessionHeader
@@ -202,13 +229,19 @@ export async function handleMcpProxyCommand(): Promise<void> {
202
229
  }
203
230
  } catch (err) {
204
231
  const msg = err instanceof Error ? err.message : String(err)
205
- reqLog.set({ event: 'request_failed', requestNum: requestCount, error: msg })
232
+ reqLog.set({
233
+ event: 'request_failed',
234
+ requestNum: requestCount,
235
+ error: msg,
236
+ })
206
237
  reqLog.emit()
207
- process.stdout.write(`${JSON.stringify({
208
- jsonrpc: '2.0',
209
- error: { code: -32000, message: `MCP proxy error: ${msg}` },
210
- id: null,
211
- })}\n`)
238
+ process.stdout.write(
239
+ `${JSON.stringify({
240
+ jsonrpc: '2.0',
241
+ error: { code: -32000, message: `MCP proxy error: ${msg}` },
242
+ id: null,
243
+ })}\n`,
244
+ )
212
245
  }
213
246
  }
214
247
  }
@@ -133,9 +133,11 @@ async function remoteList(_args: string[], opts: GlobalFlags): Promise<void> {
133
133
  })
134
134
 
135
135
  try {
136
- const backups = walletResult.storage.getBackupStores?.() ?? []
137
136
  const config = loadConfig()
138
137
 
138
+ // Use config.backups for display (remoteClients only populated for active connections)
139
+ const backupUrls = config.backups ?? []
140
+
139
141
  // Use config for active determination — WalletStorageManager internal state
140
142
  // can be misleading (a backup may appear as active after addWalletStorageProvider)
141
143
  const isRemoteActive = Boolean(config.activeRemote)
@@ -144,7 +146,7 @@ async function remoteList(_args: string[], opts: GlobalFlags): Promise<void> {
144
146
  output(
145
147
  {
146
148
  activeStorage: isRemoteActive ? 'remote' : 'local',
147
- backups: walletResult.storage.getAllStores?.() ?? [],
149
+ backups: backupUrls,
148
150
  config: {
149
151
  activeRemote: config.activeRemote ?? null,
150
152
  backups: config.backups ?? [],
@@ -156,27 +158,19 @@ async function remoteList(_args: string[], opts: GlobalFlags): Promise<void> {
156
158
  }
157
159
 
158
160
  console.log()
159
- console.log(` ${bold('Active Storage:')} ${isRemoteActive ? 'remote' : 'local'}`)
161
+ console.log(
162
+ ` ${bold('Active Storage:')} ${isRemoteActive ? 'remote' : 'local'}`,
163
+ )
160
164
  if (isRemoteActive) {
161
- console.log(
162
- ` ${bold('Active Remote:')} ${config.activeRemote}`,
163
- )
165
+ console.log(` ${bold('Active Remote:')} ${config.activeRemote}`)
164
166
  }
165
167
  console.log()
166
- if (backups.length === 0 && !config.backups?.length) {
168
+ if (backupUrls.length === 0 && !config.backups?.length) {
167
169
  console.log(' No remote storages configured')
168
170
  } else {
169
171
  console.log(` ${bold('Backups:')}`)
170
- const known = new Set(config.backups ?? [])
171
- for (const b of backups) {
172
- const isKnown = known.has(b)
173
- console.log(` ${isKnown ? '●' : '○'} ${b}`)
174
- }
175
- // Show configured but not yet connected
176
- for (const url of config.backups ?? []) {
177
- if (!backups.includes(url)) {
178
- console.log(` ? ${url} (not connected)`)
179
- }
172
+ for (const url of backupUrls) {
173
+ console.log(` ● ${url}`)
180
174
  }
181
175
  }
182
176
  console.log()
@@ -106,9 +106,7 @@ async function sweepScan(args: string[], opts: GlobalFlags): Promise<void> {
106
106
  }
107
107
  }
108
108
 
109
- console.log(
110
- `\n ${formatLabel('RUN Tokens:')} ${result.run.length}`,
111
- )
109
+ console.log(`\n ${formatLabel('RUN Tokens:')} ${result.run.length}`)
112
110
  if (result.run.length > 0) {
113
111
  const runSats = result.run.reduce((sum, r) => sum + (r.satoshis ?? 0), 0)
114
112
  console.log(
@@ -161,11 +159,15 @@ async function sweepImport(args: string[], opts: GlobalFlags): Promise<void> {
161
159
 
162
160
  const hasFunding = scan.funding.length > 0
163
161
  const hasOrdinals = scan.ordinals.length > 0
164
- const hasTokens = scan.bsv21Tokens.some((t) => t.isActive && t.outputs.length > 0)
162
+ const hasTokens = scan.bsv21Tokens.some(
163
+ (t) => t.isActive && t.outputs.length > 0,
164
+ )
165
165
 
166
166
  if (!hasFunding && !hasOrdinals && !hasTokens) {
167
167
  if (scan.run.length > 0) {
168
- fatal(`No sweepable UTXOs found at ${address} (${scan.run.length} RUN token output(s) excluded)`)
168
+ fatal(
169
+ `No sweepable UTXOs found at ${address} (${scan.run.length} RUN token output(s) excluded)`,
170
+ )
169
171
  }
170
172
  fatal(`No UTXOs found at ${address}`)
171
173
  }
@@ -52,14 +52,17 @@ export async function handleWalletCommand(
52
52
  send: 'Send BSV to an address (--to <addr> --sats <amount>)',
53
53
  'send-all': 'Send all BSV to an address (--to <addr>)',
54
54
  info: 'Show wallet info (address, balance, network)',
55
- 'list-outputs': 'List wallet outputs (--basket <name> [--tags <t1,t2>] [--limit N])',
56
- 'relinquish-output': 'Remove output from basket (--basket <name> --output <txid.vout>)',
55
+ 'list-outputs':
56
+ 'List wallet outputs (--basket <name> [--tags <t1,t2>] [--limit N] [--include-tags] [--include <val>])',
57
+ 'relinquish-output':
58
+ 'Remove output from basket (--basket <name> --output <txid.vout>)',
57
59
  'list-actions': 'List wallet actions [--labels <l1,l2>] [--limit N]',
58
60
  'create-action': 'Create action (JSON args)',
59
61
  'sign-action': 'Sign action (JSON args)',
60
62
  'abort-action': 'Abort action (--reference <ref>)',
61
63
  'list-certificates': 'List certificates',
62
- 'relinquish-certificate': 'Relinquish certificate (--type <t> --serialNumber <s> --certifier <c>)',
64
+ 'relinquish-certificate':
65
+ 'Relinquish certificate (--type <t> --serialNumber <s> --certifier <c>)',
63
66
  })
64
67
  if (subcommand && subcommand !== 'help') {
65
68
  process.exit(1)
@@ -244,17 +247,23 @@ async function walletInfo(_args: string[], opts: GlobalFlags): Promise<void> {
244
247
 
245
248
  // BRC-100 Interface Commands
246
249
 
247
- async function walletListOutputs(args: string[], opts: GlobalFlags): Promise<void> {
250
+ async function walletListOutputs(
251
+ args: string[],
252
+ opts: GlobalFlags,
253
+ ): Promise<void> {
248
254
  const basket = extractFlag(args, '--basket')
249
255
  if (!basket) fatal('Missing required --basket <name>')
250
256
 
251
257
  const tags = extractFlags(args, '--tags')
252
258
  const limitStr = extractFlag(args, '--limit')
253
- const limit = limitStr ? parseInt(limitStr, 10) : 10
259
+ const limit = limitStr ? Number.parseInt(limitStr, 10) : 10
254
260
  if (!Number.isFinite(limit) || limit < 1 || limit > 10000) {
255
261
  fatal('--limit must be between 1 and 10000')
256
262
  }
257
263
 
264
+ const includeTags = args.includes('--include-tags')
265
+ const include = extractFlag(args, '--include')
266
+
258
267
  const privateKey = await loadKey(resolvePassword())
259
268
  const { ctx, destroy } = await loadContext(privateKey, {
260
269
  chain: opts.chain,
@@ -264,6 +273,8 @@ async function walletListOutputs(args: string[], opts: GlobalFlags): Promise<voi
264
273
  const listArgs: Parameters<typeof ctx.wallet.listOutputs>[0] = {
265
274
  basket,
266
275
  limit,
276
+ includeTags,
277
+ include,
267
278
  }
268
279
  if (tags.length > 0) {
269
280
  listArgs.tags = tags
@@ -274,7 +285,9 @@ async function walletListOutputs(args: string[], opts: GlobalFlags): Promise<voi
274
285
  if (opts.json) {
275
286
  output(result, opts)
276
287
  } else {
277
- console.log(`\n${result.totalOutputs} total outputs in basket '${basket}':\n`)
288
+ console.log(
289
+ `\n${result.totalOutputs} total outputs in basket '${basket}':\n`,
290
+ )
278
291
  for (const out of result.outputs) {
279
292
  const tags = out.tags?.join(', ') || 'none'
280
293
  console.log(` ${out.outpoint} | ${out.satoshis} sats | tags: ${tags}`)
@@ -307,7 +320,10 @@ async function walletRelinquishOutput(
307
320
  })
308
321
 
309
322
  try {
310
- const result = await ctx.wallet.relinquishOutput({ basket, output: outpoint })
323
+ const result = await ctx.wallet.relinquishOutput({
324
+ basket,
325
+ output: outpoint,
326
+ })
311
327
 
312
328
  if (opts.json) {
313
329
  output(result, opts)
@@ -319,10 +335,13 @@ async function walletRelinquishOutput(
319
335
  }
320
336
  }
321
337
 
322
- async function walletListActions(args: string[], opts: GlobalFlags): Promise<void> {
338
+ async function walletListActions(
339
+ args: string[],
340
+ opts: GlobalFlags,
341
+ ): Promise<void> {
323
342
  const labels = extractFlags(args, '--labels')
324
343
  const limitStr = extractFlag(args, '--limit')
325
- const limit = limitStr ? parseInt(limitStr, 10) : 10
344
+ const limit = limitStr ? Number.parseInt(limitStr, 10) : 10
326
345
  if (!Number.isFinite(limit) || limit < 1 || limit > 10000) {
327
346
  fatal('--limit must be between 1 and 10000')
328
347
  }
@@ -354,10 +373,14 @@ async function walletListActions(args: string[], opts: GlobalFlags): Promise<voi
354
373
  }
355
374
  }
356
375
 
357
- async function walletCreateAction(args: string[], opts: GlobalFlags): Promise<void> {
376
+ async function walletCreateAction(
377
+ args: string[],
378
+ opts: GlobalFlags,
379
+ ): Promise<void> {
358
380
  const jsonInput = args[0]
359
381
 
360
- if (!jsonInput) fatal('Missing JSON arguments. Usage: wallet create-action \'{...}\'')
382
+ if (!jsonInput)
383
+ fatal("Missing JSON arguments. Usage: wallet create-action '{...}'")
361
384
 
362
385
  let actionArgs: Parameters<typeof ctx.wallet.createAction>[0]
363
386
  try {
@@ -379,10 +402,14 @@ async function walletCreateAction(args: string[], opts: GlobalFlags): Promise<vo
379
402
  }
380
403
  }
381
404
 
382
- async function walletSignAction(args: string[], opts: GlobalFlags): Promise<void> {
405
+ async function walletSignAction(
406
+ args: string[],
407
+ opts: GlobalFlags,
408
+ ): Promise<void> {
383
409
  const jsonInput = args[0]
384
410
 
385
- if (!jsonInput) fatal('Missing JSON arguments. Usage: wallet sign-action \'{...}\'')
411
+ if (!jsonInput)
412
+ fatal("Missing JSON arguments. Usage: wallet sign-action '{...}'")
386
413
 
387
414
  let signArgs: Parameters<typeof ctx.wallet.signAction>[0]
388
415
  try {
@@ -404,7 +431,10 @@ async function walletSignAction(args: string[], opts: GlobalFlags): Promise<void
404
431
  }
405
432
  }
406
433
 
407
- async function walletAbortAction(args: string[], opts: GlobalFlags): Promise<void> {
434
+ async function walletAbortAction(
435
+ args: string[],
436
+ opts: GlobalFlags,
437
+ ): Promise<void> {
408
438
  const reference = extractFlag(args, '--reference')
409
439
 
410
440
  if (!reference) fatal('Missing required --reference <ref>')
@@ -429,7 +459,7 @@ async function walletListCertificates(
429
459
  const certifiers = extractFlags(args, '--certifiers')
430
460
  const types = extractFlags(args, '--types')
431
461
  const limitStr = extractFlag(args, '--limit')
432
- const limit = limitStr ? parseInt(limitStr, 10) : 10
462
+ const limit = limitStr ? Number.parseInt(limitStr, 10) : 10
433
463
 
434
464
  if (!Number.isFinite(limit) || limit < 1 || limit > 10000) {
435
465
  fatal('--limit must be between 1 and 10000')
package/src/context.ts CHANGED
@@ -38,7 +38,20 @@ async function runMonitorIfStale(
38
38
  const intervalMs = intervalMinutes * 60 * 1000
39
39
 
40
40
  if (elapsed >= intervalMs) {
41
- await walletResult.monitor.runOnce()
41
+ const originalLog = console.log
42
+ const originalInfo = console.info
43
+ const originalWarn = console.warn
44
+ console.log = () => {}
45
+ console.info = () => {}
46
+ console.warn = () => {}
47
+
48
+ try {
49
+ await walletResult.monitor.runOnce()
50
+ } finally {
51
+ console.log = originalLog
52
+ console.info = originalInfo
53
+ console.warn = originalWarn
54
+ }
42
55
  saveMonitorState({ lastMonitorRun: Date.now() })
43
56
  }
44
57
  }
package/src/help.ts CHANGED
@@ -85,6 +85,7 @@ ${bold('Locks:')}
85
85
 
86
86
  ${bold('Identity (BAP):')}
87
87
  ${cyan('identity create')} Create a new BAP identity
88
+ ${cyan('identity update-profile')} Update BAP identity profile (--profile <json>)
88
89
  ${cyan('identity info')} Show identity information
89
90
  ${cyan('identity sign')} Sign a message with identity key
90
91
  ${cyan('identity verify')} Verify a signed message