@thirdfy/agent-cli 0.2.86 → 0.4.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 (65) hide show
  1. package/CHANGELOG.md +116 -0
  2. package/README.md +61 -25
  3. package/package.json +2 -2
  4. package/scripts/validate-cli-contract-schemas.mjs +5 -0
  5. package/src/cli/errors.mjs +58 -8
  6. package/src/cli/help.mjs +0 -3
  7. package/src/cli/manifest.mjs +54 -27
  8. package/src/cli/options/bootstrap.mjs +15 -3
  9. package/src/cli/options/chains.mjs +5 -0
  10. package/src/cli/options/credits.mjs +9 -0
  11. package/src/cli/options/data.mjs +13 -1
  12. package/src/cli/options/index.mjs +10 -4
  13. package/src/cli/options/walletControl.mjs +8 -0
  14. package/src/commands/bootstrap.mjs +44 -5
  15. package/src/commands/chat.mjs +3 -0
  16. package/src/commands/credits.mjs +39 -4
  17. package/src/commands/delegation.mjs +3 -1
  18. package/src/commands/discovery.mjs +156 -13
  19. package/src/commands/doctor.mjs +5 -2
  20. package/src/commands/execute.mjs +96 -3
  21. package/src/commands/prompt.mjs +5 -4
  22. package/src/commands/wallet.mjs +119 -3
  23. package/src/commands/walletControl.mjs +66 -0
  24. package/src/contracts/cli/schemas/actions.response.schema.json +45 -0
  25. package/src/contracts/cli/schemas/chains-non-evm.response.schema.json +76 -0
  26. package/src/contracts/cli/schemas/chains.response.schema.json +151 -0
  27. package/src/contracts/cli/schemas/error.response.schema.json +61 -0
  28. package/src/contracts/cli/schemas/preview.response.schema.json +179 -0
  29. package/src/contracts/cli/schemas/readiness.response.schema.json +94 -0
  30. package/src/core/args.mjs +10 -2
  31. package/src/core/chainRegistry.mjs +223 -1
  32. package/src/core/context.mjs +37 -14
  33. package/src/core/errorEnvelope.mjs +209 -0
  34. package/src/core/fundingTokenIn.mjs +24 -1
  35. package/src/core/http.mjs +6 -1
  36. package/src/core/readOnlyActions.mjs +10 -0
  37. package/src/core/walletExecuteChainId.mjs +25 -0
  38. package/src/lib/creditsUsdSummary.mjs +48 -4
  39. package/src/runtime/actions/actionList.mjs +87 -0
  40. package/src/runtime/actions/selection.mjs +17 -22
  41. package/src/runtime/execution/actionPreflight.mjs +50 -14
  42. package/src/runtime/execution/payloads.mjs +36 -28
  43. package/src/runtime/execution/runners.mjs +75 -21
  44. package/src/runtime/execution/svmLane.mjs +106 -0
  45. package/src/runtime/execution/walletControl.mjs +104 -0
  46. package/src/runtime/handlers.mjs +16 -2
  47. package/src/runtime/index.mjs +1 -1
  48. package/src/runtime/main.mjs +21 -5
  49. package/src/runtime/providerHints/apps.mjs +36 -0
  50. package/src/runtime/providerHints/catalogHint.mjs +55 -0
  51. package/src/runtime/providerHints/cex.mjs +130 -0
  52. package/src/runtime/providerHints/data.mjs +144 -0
  53. package/src/runtime/providerHints/dispatch.mjs +177 -0
  54. package/src/runtime/providerHints/dogeos.mjs +38 -0
  55. package/src/runtime/providerHints/earn.mjs +201 -0
  56. package/src/runtime/providerHints/perps.mjs +346 -0
  57. package/src/runtime/providerHints/prediction.mjs +128 -0
  58. package/src/runtime/providerHints/solana.mjs +85 -0
  59. package/src/runtime/providerHints/trading.mjs +78 -0
  60. package/src/runtime/providerHints/walletControl.mjs +39 -0
  61. package/src/runtime/risk/notional.mjs +59 -0
  62. package/src/runtime/risk/orderPreview.mjs +154 -0
  63. package/src/runtime/risk/riskRefusal.mjs +74 -0
  64. package/src/runtime/validation/actionParamsGate.mjs +2 -0
  65. package/src/runtime/providerHints.mjs +0 -1017
@@ -0,0 +1,66 @@
1
+ import { loadProfileConfig } from '../core/context.mjs';
2
+ import { parseJsonFlag, requireFlag } from '../core/args.mjs';
3
+ import { apiGet, apiPost } from '../core/http.mjs';
4
+
5
+ /**
6
+ * The owner's side of API 4.0 wallet control (`/api/v1/agent/:agentKey/wallet-control/*`), for a
7
+ * user-owned agent wallet. Owner auth only (`--owner-session-token` or a Privy `--auth-token`); the
8
+ * API refuses agent API keys. Thirdfy never signs these: `request` returns the unsigned Privy
9
+ * request(s), the owner signs `signatureInput` with their own key, and `submit` relays it. This is
10
+ * how an owner answers AGENT_POLICY_UPDATE_REQUIRED (`--action update_policy`) from the CLI.
11
+ * Same routes as Console and thirdfy-mcp `walletControlRequest` / `walletControlSubmit`.
12
+ */
13
+ export function createWalletControlCommands({ printEnvelope, buildOwnerAuthHeaders }) {
14
+ const ownerHeaders = (flags, command) =>
15
+ buildOwnerAuthHeaders(flags, `Missing owner auth for wallet-control ${command}: provide --auth-token (THIRDFY_AUTH_TOKEN) or --owner-session-token`);
16
+
17
+ function agentKeyOf(flags) {
18
+ const key = String(flags.agentKey || loadProfileConfig()?.agent?.agentKey || '').trim();
19
+ if (!key) throw new Error('Missing --agent-key (or a configured agent)');
20
+ return key;
21
+ }
22
+
23
+ const base = (agentKey) => `/api/v1/agent/${encodeURIComponent(agentKey)}/wallet-control`;
24
+ const meta = (ctx, capabilities) => ({ apiBase: ctx.apiBase, capabilitiesVersion: capabilities?.contractVersion || null });
25
+
26
+ async function commandWalletControlState(ctx, flags, capabilities) {
27
+ const headers = ownerHeaders(flags, 'state');
28
+ const agentKey = agentKeyOf(flags);
29
+ const query = flags.walletAddress ? `?walletAddress=${encodeURIComponent(String(flags.walletAddress).trim())}` : '';
30
+ const response = await apiGet(ctx, `${base(agentKey)}/state${query}`, headers);
31
+ printEnvelope({ success: true, code: 'WALLET_CONTROL_STATE', message: 'Owner wallet state', data: response?.data ?? response, meta: meta(ctx, capabilities) });
32
+ }
33
+
34
+ async function commandWalletControlRequest(ctx, flags, capabilities) {
35
+ const headers = ownerHeaders(flags, 'request');
36
+ const action = requireFlag(flags, 'action', 'Missing --action (update_policy, stop_agent, resume_agent, change_payout, export)');
37
+ const agentKey = agentKeyOf(flags);
38
+ const payload = { action: String(action).trim() };
39
+ if (flags.walletAddress) payload.walletAddress = String(flags.walletAddress).trim();
40
+ if (flags.payoutAddress) payload.payoutAddress = String(flags.payoutAddress).trim();
41
+ if (flags.recipientPublicKey) payload.recipientPublicKey = String(flags.recipientPublicKey).trim();
42
+ const response = await apiPost(ctx, `${base(agentKey)}/requests`, payload, headers);
43
+ printEnvelope({
44
+ success: true,
45
+ code: 'WALLET_CONTROL_REQUESTS',
46
+ message: 'Unsigned owner request(s): sign each signatureInput with the owner key, then run wallet-control submit',
47
+ data: response?.data ?? response,
48
+ meta: meta(ctx, capabilities),
49
+ });
50
+ }
51
+
52
+ async function commandWalletControlSubmit(ctx, flags, capabilities) {
53
+ const headers = ownerHeaders(flags, 'submit');
54
+ const action = requireFlag(flags, 'action', 'Missing --action');
55
+ const signatureInput = parseJsonFlag(flags, 'signatureInput');
56
+ if (!signatureInput || typeof signatureInput !== 'object') throw new Error('Missing --signature-input (the signatureInput JSON of the request the owner signed)');
57
+ const signature = requireFlag(flags, 'signature', 'Missing --signature (the owner signature of signatureInput)');
58
+ const agentKey = agentKeyOf(flags);
59
+ const payload = { action: String(action).trim(), signatureInput, signature: String(signature).trim() };
60
+ if (flags.payoutAddress) payload.payoutAddress = String(flags.payoutAddress).trim();
61
+ const response = await apiPost(ctx, `${base(agentKey)}/submit`, payload, headers);
62
+ printEnvelope({ success: true, code: 'WALLET_CONTROL_SUBMITTED', message: 'Owner action relayed', data: response?.data ?? response, meta: meta(ctx, capabilities) });
63
+ }
64
+
65
+ return { commandWalletControlState, commandWalletControlRequest, commandWalletControlSubmit };
66
+ }
@@ -44,6 +44,51 @@
44
44
  },
45
45
  "additionalProperties": true
46
46
  }
47
+ },
48
+ "pagination": {
49
+ "type": "object",
50
+ "description": "Present with --search, --limit, --offset or --view (thirdfy-mcp getAgentActions paging).",
51
+ "required": [
52
+ "total",
53
+ "matched",
54
+ "offset",
55
+ "limit",
56
+ "returned",
57
+ "hasMore"
58
+ ],
59
+ "properties": {
60
+ "total": {
61
+ "type": "integer"
62
+ },
63
+ "matched": {
64
+ "type": "integer"
65
+ },
66
+ "offset": {
67
+ "type": "integer"
68
+ },
69
+ "limit": {
70
+ "type": "integer"
71
+ },
72
+ "returned": {
73
+ "type": "integer"
74
+ },
75
+ "hasMore": {
76
+ "type": "boolean"
77
+ }
78
+ },
79
+ "additionalProperties": true
80
+ },
81
+ "view": {
82
+ "enum": [
83
+ "concise",
84
+ "detailed"
85
+ ]
86
+ },
87
+ "hint": {
88
+ "type": "string"
89
+ },
90
+ "providerHint": {
91
+ "type": "object"
47
92
  }
48
93
  },
49
94
  "additionalProperties": true
@@ -0,0 +1,76 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "title": "CLI response data: chains --non-evm (thirdfy-mcp getNonEvmCapabilities parity)",
4
+ "type": "object",
5
+ "required": [
6
+ "supportedChains",
7
+ "executionEnabled",
8
+ "contractVersion",
9
+ "solana",
10
+ "notes"
11
+ ],
12
+ "properties": {
13
+ "supportedChains": {
14
+ "type": "array",
15
+ "items": {
16
+ "type": "string"
17
+ }
18
+ },
19
+ "executionEnabled": {
20
+ "type": "boolean"
21
+ },
22
+ "contractVersion": {
23
+ "type": "string"
24
+ },
25
+ "chainRegistrySource": {
26
+ "enum": [
27
+ "api",
28
+ "default"
29
+ ]
30
+ },
31
+ "solana": {
32
+ "type": "object",
33
+ "required": [
34
+ "enabled",
35
+ "chainFamily",
36
+ "chainIds",
37
+ "requiredRunMode"
38
+ ],
39
+ "properties": {
40
+ "enabled": {
41
+ "type": "boolean"
42
+ },
43
+ "chainFamily": {
44
+ "enum": [
45
+ "svm"
46
+ ]
47
+ },
48
+ "chainIds": {
49
+ "type": "array",
50
+ "items": {
51
+ "type": "integer"
52
+ }
53
+ },
54
+ "caip2": {
55
+ "type": "object"
56
+ },
57
+ "requiredRunMode": {
58
+ "enum": [
59
+ "agent_wallet"
60
+ ]
61
+ },
62
+ "providers": {
63
+ "type": "array",
64
+ "items": {
65
+ "type": "string"
66
+ }
67
+ }
68
+ },
69
+ "additionalProperties": true
70
+ },
71
+ "notes": {
72
+ "type": "string"
73
+ }
74
+ },
75
+ "additionalProperties": true
76
+ }
@@ -0,0 +1,151 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "title": "CLI response data: chains (thirdfy-mcp getChainCapabilities parity)",
4
+ "type": "object",
5
+ "required": [
6
+ "supportedEvmChainIds",
7
+ "configuredEvmChainIds",
8
+ "catalogEvmChainIds",
9
+ "supportedNonEvmChains",
10
+ "supportedSvmChainIds",
11
+ "chainRegistrySource",
12
+ "caip2",
13
+ "supportedExecutionLanes",
14
+ "defaultChainId",
15
+ "nonEvmExecutionContract"
16
+ ],
17
+ "properties": {
18
+ "supportedEvmChainIds": {
19
+ "type": "array",
20
+ "items": {
21
+ "type": "integer"
22
+ }
23
+ },
24
+ "configuredEvmChainIds": {
25
+ "type": "array",
26
+ "items": {
27
+ "type": "integer"
28
+ }
29
+ },
30
+ "catalogEvmChainIds": {
31
+ "type": "array",
32
+ "items": {
33
+ "type": "integer"
34
+ }
35
+ },
36
+ "catalogSource": {
37
+ "enum": [
38
+ "live",
39
+ "unavailable"
40
+ ]
41
+ },
42
+ "catalogError": {
43
+ "type": "string"
44
+ },
45
+ "supportedNonEvmChains": {
46
+ "type": "array",
47
+ "items": {
48
+ "type": "string"
49
+ }
50
+ },
51
+ "supportedSvmChainIds": {
52
+ "type": "array",
53
+ "items": {
54
+ "type": "integer"
55
+ }
56
+ },
57
+ "chainRegistrySource": {
58
+ "enum": [
59
+ "api",
60
+ "default"
61
+ ]
62
+ },
63
+ "caip2": {
64
+ "type": "object"
65
+ },
66
+ "supportedExecutionLanes": {
67
+ "type": "array",
68
+ "items": {
69
+ "type": "string"
70
+ }
71
+ },
72
+ "defaultChainId": {
73
+ "type": "integer"
74
+ },
75
+ "defaultExecutionLane": {
76
+ "type": "string"
77
+ },
78
+ "nonEvmExecutionContract": {
79
+ "type": "object",
80
+ "required": [
81
+ "enabled",
82
+ "chainFamily",
83
+ "chainIds",
84
+ "requiredRunMode"
85
+ ],
86
+ "properties": {
87
+ "enabled": {
88
+ "type": "boolean"
89
+ },
90
+ "chainFamily": {
91
+ "enum": [
92
+ "svm"
93
+ ]
94
+ },
95
+ "chainIds": {
96
+ "type": "array",
97
+ "items": {
98
+ "type": "integer"
99
+ }
100
+ },
101
+ "caip2": {
102
+ "type": "object"
103
+ },
104
+ "requiredRunMode": {
105
+ "enum": [
106
+ "agent_wallet"
107
+ ]
108
+ },
109
+ "providers": {
110
+ "type": "array",
111
+ "items": {
112
+ "type": "string"
113
+ }
114
+ }
115
+ },
116
+ "additionalProperties": true
117
+ },
118
+ "chains": {
119
+ "type": "array",
120
+ "items": {
121
+ "type": "object",
122
+ "required": [
123
+ "chainId",
124
+ "chainFamily"
125
+ ],
126
+ "properties": {
127
+ "chainId": {
128
+ "type": "integer"
129
+ },
130
+ "chainFamily": {
131
+ "enum": [
132
+ "evm",
133
+ "svm"
134
+ ]
135
+ },
136
+ "caip2": {
137
+ "type": "string"
138
+ },
139
+ "supportedExecutionLanes": {
140
+ "type": "array",
141
+ "items": {
142
+ "type": "string"
143
+ }
144
+ }
145
+ },
146
+ "additionalProperties": true
147
+ }
148
+ }
149
+ },
150
+ "additionalProperties": true
151
+ }
@@ -0,0 +1,61 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "title": "CLI response data on any failure: the error fields (thirdfy-mcp tool error envelope parity)",
4
+ "type": "object",
5
+ "required": [
6
+ "errorCode",
7
+ "retryable",
8
+ "nextAction"
9
+ ],
10
+ "properties": {
11
+ "errorCode": {
12
+ "type": "string"
13
+ },
14
+ "retryable": {
15
+ "type": "boolean"
16
+ },
17
+ "retryAfterSeconds": {
18
+ "type": "integer"
19
+ },
20
+ "nextAction": {
21
+ "type": "string"
22
+ },
23
+ "hint": {
24
+ "type": "string"
25
+ },
26
+ "blockedReason": {
27
+ "type": [
28
+ "string",
29
+ "null"
30
+ ]
31
+ },
32
+ "blockedStage": {
33
+ "type": [
34
+ "string",
35
+ "null"
36
+ ]
37
+ },
38
+ "statusCode": {
39
+ "type": [
40
+ "integer",
41
+ "null"
42
+ ]
43
+ },
44
+ "riskRefusal": {
45
+ "type": "object",
46
+ "required": [
47
+ "code"
48
+ ],
49
+ "properties": {
50
+ "code": {
51
+ "type": "string"
52
+ }
53
+ },
54
+ "additionalProperties": true
55
+ },
56
+ "message": {
57
+ "type": "string"
58
+ }
59
+ },
60
+ "additionalProperties": true
61
+ }
@@ -0,0 +1,179 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "title": "CLI response data: preview (thirdfy-mcp previewOrder parity)",
4
+ "type": "object",
5
+ "required": [
6
+ "action",
7
+ "chainId",
8
+ "success",
9
+ "wouldSign"
10
+ ],
11
+ "properties": {
12
+ "action": {
13
+ "type": "string"
14
+ },
15
+ "chainId": {
16
+ "type": "integer"
17
+ },
18
+ "success": {
19
+ "type": "boolean"
20
+ },
21
+ "wouldSign": {
22
+ "type": "boolean"
23
+ },
24
+ "blockedReason": {
25
+ "type": "string"
26
+ },
27
+ "blockedStage": {
28
+ "type": "string"
29
+ },
30
+ "reason": {
31
+ "type": "string"
32
+ },
33
+ "nextAction": {
34
+ "type": "string"
35
+ },
36
+ "riskRefusal": {
37
+ "type": "object",
38
+ "required": [
39
+ "code"
40
+ ],
41
+ "properties": {
42
+ "code": {
43
+ "type": "string"
44
+ },
45
+ "field": {
46
+ "type": "string"
47
+ }
48
+ },
49
+ "additionalProperties": true
50
+ },
51
+ "message": {
52
+ "type": "string"
53
+ },
54
+ "gas": {
55
+ "type": "object",
56
+ "required": [
57
+ "sponsored",
58
+ "note"
59
+ ],
60
+ "properties": {
61
+ "sponsored": {
62
+ "type": "boolean"
63
+ },
64
+ "payer": {
65
+ "type": "string"
66
+ },
67
+ "nativeToken": {
68
+ "type": "string"
69
+ },
70
+ "faucet": {
71
+ "type": "string"
72
+ },
73
+ "rentPayer": {
74
+ "type": "string"
75
+ },
76
+ "note": {
77
+ "type": "string"
78
+ }
79
+ },
80
+ "additionalProperties": true
81
+ },
82
+ "amountUsd": {
83
+ "type": "number"
84
+ },
85
+ "orderPreview": {
86
+ "type": "object",
87
+ "properties": {
88
+ "previewKind": {
89
+ "type": "string"
90
+ },
91
+ "wouldSign": {
92
+ "type": "boolean"
93
+ },
94
+ "notionalUsd": {
95
+ "type": "number"
96
+ }
97
+ },
98
+ "additionalProperties": true
99
+ },
100
+ "warnings": {
101
+ "type": "array",
102
+ "items": {
103
+ "type": "object",
104
+ "required": [
105
+ "message"
106
+ ],
107
+ "properties": {
108
+ "code": {
109
+ "type": [
110
+ "string",
111
+ "null"
112
+ ]
113
+ },
114
+ "message": {
115
+ "type": "string"
116
+ }
117
+ }
118
+ }
119
+ },
120
+ "note": {
121
+ "type": "string"
122
+ },
123
+ "venueSetup": {
124
+ "type": "object",
125
+ "properties": {
126
+ "runsWithOrder": {
127
+ "type": "boolean"
128
+ }
129
+ },
130
+ "additionalProperties": true
131
+ },
132
+ "order": {
133
+ "type": "object",
134
+ "required": [
135
+ "action",
136
+ "params",
137
+ "chainId",
138
+ "runMode",
139
+ "executionIdempotencyKey",
140
+ "command"
141
+ ],
142
+ "properties": {
143
+ "action": {
144
+ "type": "string"
145
+ },
146
+ "params": {
147
+ "type": "object"
148
+ },
149
+ "chainId": {
150
+ "type": "integer"
151
+ },
152
+ "runMode": {
153
+ "enum": [
154
+ "agent_wallet"
155
+ ]
156
+ },
157
+ "estimatedAmountUsd": {
158
+ "type": "number"
159
+ },
160
+ "confirmWrites": {
161
+ "enum": [
162
+ true
163
+ ]
164
+ },
165
+ "executionIdempotencyKey": {
166
+ "type": "string"
167
+ },
168
+ "command": {
169
+ "type": "array",
170
+ "items": {
171
+ "type": "string"
172
+ }
173
+ }
174
+ },
175
+ "additionalProperties": false
176
+ }
177
+ },
178
+ "additionalProperties": true
179
+ }
@@ -0,0 +1,94 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "title": "CLI response data: readiness (thirdfy-mcp getSelfReadiness parity)",
4
+ "type": "object",
5
+ "required": [
6
+ "runMode",
7
+ "chainId",
8
+ "source",
9
+ "readiness"
10
+ ],
11
+ "properties": {
12
+ "runMode": {
13
+ "enum": [
14
+ "agent_wallet",
15
+ "self",
16
+ "hybrid",
17
+ "thirdfy"
18
+ ]
19
+ },
20
+ "custodyMode": {
21
+ "type": [
22
+ "string",
23
+ "null"
24
+ ]
25
+ },
26
+ "chainId": {
27
+ "type": "integer"
28
+ },
29
+ "chainIdAssumed": {
30
+ "enum": [
31
+ true
32
+ ]
33
+ },
34
+ "chainIdNote": {
35
+ "type": "string"
36
+ },
37
+ "walletAddress": {
38
+ "type": [
39
+ "string",
40
+ "null"
41
+ ]
42
+ },
43
+ "gas": {
44
+ "type": "object",
45
+ "required": [
46
+ "sponsored",
47
+ "note"
48
+ ],
49
+ "properties": {
50
+ "sponsored": {
51
+ "type": "boolean"
52
+ },
53
+ "payer": {
54
+ "type": "string"
55
+ },
56
+ "nativeToken": {
57
+ "type": "string"
58
+ },
59
+ "faucet": {
60
+ "type": "string"
61
+ },
62
+ "rentPayer": {
63
+ "type": "string"
64
+ },
65
+ "note": {
66
+ "type": "string"
67
+ }
68
+ },
69
+ "additionalProperties": true
70
+ },
71
+ "source": {
72
+ "enum": [
73
+ "/api/v1/agent/execution-wallet",
74
+ "/api/v1/agent/delegation/check"
75
+ ]
76
+ },
77
+ "ready": {
78
+ "type": "boolean"
79
+ },
80
+ "walletControl": {
81
+ "type": "object"
82
+ },
83
+ "nextAction": {
84
+ "type": "string"
85
+ },
86
+ "userMessage": {
87
+ "type": "string"
88
+ },
89
+ "readiness": {
90
+ "type": "object"
91
+ }
92
+ },
93
+ "additionalProperties": true
94
+ }
package/src/core/args.mjs CHANGED
@@ -50,9 +50,17 @@ export function loadEnvFile(filePath) {
50
50
  }
51
51
  }
52
52
 
53
+ /** An error in how the command was called; describeCliError reports it as INVALID_ARGUMENTS. */
54
+ export function usageError(message, field) {
55
+ const error = new Error(message);
56
+ error.usage = true;
57
+ error.field = field;
58
+ return error;
59
+ }
60
+
53
61
  export function requireFlag(flags, key, message) {
54
62
  if (flags[key] === undefined || flags[key] === null || String(flags[key]).trim() === '') {
55
- throw new Error(message || `Missing required flag --${key}`);
63
+ throw usageError(message || `Missing required flag --${key}`, key);
56
64
  }
57
65
  return flags[key];
58
66
  }
@@ -63,6 +71,6 @@ export function parseJsonFlag(flags, key) {
63
71
  try {
64
72
  return JSON.parse(String(raw));
65
73
  } catch {
66
- throw new Error(`Invalid JSON for --${key}`);
74
+ throw usageError(`Invalid JSON for --${key}`, key);
67
75
  }
68
76
  }