@thirdfy/agent-cli 0.1.48 → 0.2.2

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 (61) hide show
  1. package/CHANGELOG.md +48 -0
  2. package/README.md +25 -23
  3. package/bin/thirdfy-agent.mjs +3 -5106
  4. package/package.json +17 -4
  5. package/scripts/validate-npm-pack.mjs +43 -0
  6. package/src/cli/errors.mjs +103 -0
  7. package/src/cli/flags.mjs +66 -0
  8. package/src/cli/help.mjs +89 -0
  9. package/src/cli/manifest.mjs +422 -0
  10. package/src/cli/options/agent.mjs +9 -0
  11. package/src/cli/options/analytics.mjs +5 -0
  12. package/src/cli/options/auth.mjs +10 -0
  13. package/src/cli/options/bootstrap.mjs +19 -0
  14. package/src/cli/options/chains.mjs +3 -0
  15. package/src/cli/options/credentials.mjs +15 -0
  16. package/src/cli/options/delegation.mjs +22 -0
  17. package/src/cli/options/execution.mjs +24 -0
  18. package/src/cli/options/global.mjs +16 -0
  19. package/src/cli/options/index.mjs +37 -0
  20. package/src/cli/options/polymarket.mjs +3 -0
  21. package/src/cli/options/tracking.mjs +18 -0
  22. package/src/cli/options/wallet.mjs +7 -0
  23. package/src/cli/program.mjs +5 -0
  24. package/src/cli/register.mjs +118 -0
  25. package/src/commands/agent.mjs +194 -0
  26. package/src/commands/bootstrap.mjs +762 -0
  27. package/src/commands/credentials.mjs +102 -0
  28. package/src/commands/delegation.mjs +355 -0
  29. package/src/commands/discovery.mjs +77 -0
  30. package/src/commands/doctor.mjs +320 -0
  31. package/src/commands/execute.mjs +255 -0
  32. package/src/commands/hyperliquid.mjs +51 -0
  33. package/src/commands/index.mjs +6 -0
  34. package/src/commands/polymarket.mjs +89 -0
  35. package/src/commands/prompt.mjs +33 -0
  36. package/src/commands/telemetry.mjs +146 -0
  37. package/src/commands/wallet.mjs +129 -0
  38. package/src/core/args.mjs +68 -0
  39. package/src/core/constants.mjs +31 -0
  40. package/src/core/context.mjs +145 -0
  41. package/src/core/envelope.mjs +20 -0
  42. package/src/core/http.mjs +77 -0
  43. package/src/core/index.mjs +6 -0
  44. package/src/core/runMode.mjs +72 -0
  45. package/src/runtime/actions/selection.mjs +339 -0
  46. package/src/runtime/agentExtract.mjs +52 -0
  47. package/src/runtime/authHelpers.mjs +53 -0
  48. package/src/runtime/bitfinexProbe.mjs +121 -0
  49. package/src/runtime/context.mjs +28 -0
  50. package/src/runtime/createThirdfyAgentRuntime.mjs +2 -0
  51. package/src/runtime/errors.mjs +3 -0
  52. package/src/runtime/execution/amounts.mjs +193 -0
  53. package/src/runtime/execution/lanes.mjs +75 -0
  54. package/src/runtime/execution/payloads.mjs +148 -0
  55. package/src/runtime/execution/runners.mjs +702 -0
  56. package/src/runtime/handlers.mjs +181 -0
  57. package/src/runtime/index.mjs +4 -0
  58. package/src/runtime/main.mjs +204 -0
  59. package/src/runtime/onboardingHints.mjs +54 -0
  60. package/src/runtime/owsSigning.mjs +118 -0
  61. package/src/runtime/providerHints.mjs +414 -0
@@ -0,0 +1,320 @@
1
+ import { createRequire } from 'module';
2
+ import { requireFlag } from '../core/args.mjs';
3
+ import { apiGet, requestWithRouteFallback } from '../core/http.mjs';
4
+ import { loadProfileConfig, getProfileConfigPath } from '../core/context.mjs';
5
+ import { DEFAULT_API_BASE, PROFILE_DEFAULTS } from '../core/constants.mjs';
6
+ import { normalizeRunMode, normalizeManagedRunMode } from '../core/runMode.mjs';
7
+
8
+ const require = createRequire(import.meta.url);
9
+ const { normalizeDelegationStatus } = require('../utils/cli/delegationNormalization.cjs');
10
+
11
+ export function createDoctorCommands({
12
+ printEnvelope,
13
+ extractAgentApiKey,
14
+ extractAgentKey,
15
+ maskSecret,
16
+ resolveAgentApiKey,
17
+ buildOwnerAuthHeaders,
18
+ resolveEffectiveUserDid,
19
+ loadEthers,
20
+ resolveOwsBinary,
21
+ }) {
22
+ async function commandDoctorSelf(ctx, flags) {
23
+ const walletAddress = String(flags.walletAddress || process.env.MARKET_MAKER_WALLET_ADDRESS || '').trim();
24
+ const owsWalletName = String(flags.owsWalletName || process.env.OWS_WALLET_NAME || '').trim();
25
+ const rpcUrl = String(
26
+ flags.rpcUrl || process.env.BASE_RPC_URL || process.env.BASE_RPC_HTTP_URL || process.env.RPC_URL || 'https://mainnet.base.org'
27
+ ).trim();
28
+ const passphrasePresent = Boolean(String(process.env.OWS_PASSPHRASE || process.env.OWS_API_KEY || '').trim());
29
+ const checks = [];
30
+
31
+ checks.push({
32
+ name: 'wallet-address',
33
+ ok: Boolean(walletAddress),
34
+ detail: walletAddress || 'missing --wallet-address / MARKET_MAKER_WALLET_ADDRESS',
35
+ });
36
+ checks.push({
37
+ name: 'ows-wallet-name',
38
+ ok: Boolean(owsWalletName),
39
+ detail: owsWalletName || 'missing --ows-wallet-name / OWS_WALLET_NAME',
40
+ });
41
+ checks.push({
42
+ name: 'ows-passphrase',
43
+ ok: passphrasePresent,
44
+ detail: passphrasePresent ? 'present' : 'missing OWS_PASSPHRASE (or OWS_API_KEY fallback)',
45
+ });
46
+
47
+ try {
48
+ resolveOwsBinary();
49
+ checks.push({ name: 'ows-binary', ok: true, detail: 'found' });
50
+ } catch (error) {
51
+ checks.push({ name: 'ows-binary', ok: false, detail: error?.message || 'not found' });
52
+ }
53
+
54
+ try {
55
+ await loadEthers();
56
+ checks.push({ name: 'ethers-runtime', ok: true, detail: 'loaded' });
57
+ } catch (error) {
58
+ checks.push({ name: 'ethers-runtime', ok: false, detail: error?.message || 'missing ethers dependency' });
59
+ }
60
+
61
+ try {
62
+ const ethers = await loadEthers();
63
+ const provider = new ethers.JsonRpcProvider(rpcUrl);
64
+ const block = await provider.getBlockNumber();
65
+ checks.push({ name: 'rpc-connectivity', ok: Number.isFinite(block), detail: `latestBlock=${block}` });
66
+ } catch (error) {
67
+ checks.push({ name: 'rpc-connectivity', ok: false, detail: error?.message || 'rpc check failed' });
68
+ }
69
+
70
+ const ok = checks.every((entry) => entry.ok);
71
+ printEnvelope({
72
+ success: ok,
73
+ code: ok ? 'DOCTOR_SELF_OK' : 'DOCTOR_SELF_FAILED',
74
+ message: ok ? 'Self lane signer diagnostics passed' : 'Self lane signer diagnostics found blocking issues',
75
+ data: {
76
+ checks,
77
+ hints: [
78
+ 'For one-command self execution use: thirdfy-agent run --run-mode self --broadcast ...',
79
+ 'Set --effect-check strict|relaxed|off to control post-trade effect validation behavior.',
80
+ ],
81
+ },
82
+ meta: {
83
+ apiBase: ctx.apiBase,
84
+ runMode: 'self',
85
+ rpcUrl,
86
+ },
87
+ });
88
+ if (!ok) process.exit(1);
89
+ }
90
+
91
+ function buildOnboardingHelp(ctx) {
92
+ return {
93
+ title: 'Thirdfy Agent CLI onboarding',
94
+ summary: 'Use email OTP, wallet-sign, or existing credentials to create owner auth, wallets, and hidden agent execution credentials. Fresh solo profiles default to agent_wallet.',
95
+ paths: [
96
+ {
97
+ id: 'email_otp',
98
+ recommended: true,
99
+ description: 'Email OTP first-run flow for humans and agents that can ask the user for a verification code.',
100
+ commands: [
101
+ 'thirdfy-agent login email user@example.com',
102
+ 'thirdfy-agent login email user@example.com --code <otp> --accept-terms --key-name "My Agent"',
103
+ 'thirdfy-agent whoami',
104
+ 'thirdfy-agent wallet list --json',
105
+ 'thirdfy-agent preflight --run-mode agent_wallet --action <action> --params <json> --json',
106
+ ],
107
+ note: 'Email OTP authenticates the owner. Fund the returned agent execution wallet for the target chain; do not fund the owner embedded/login wallet for agent_wallet writes.',
108
+ },
109
+ {
110
+ id: 'wallet_sign',
111
+ description: 'Headless wallet proof for operators with an EVM signer.',
112
+ commands: [
113
+ 'thirdfy-agent agent auth challenge --agent-key 0xYOUR_AGENT_KEY --json',
114
+ 'thirdfy-agent login --agent-key 0xYOUR_AGENT_KEY --challenge-id <id> --signature <hex> --json',
115
+ ],
116
+ },
117
+ {
118
+ id: 'existing_credentials',
119
+ description: 'Use already-issued owner or agent credentials from a secure secret store.',
120
+ commands: [
121
+ 'thirdfy-agent login --owner-session-token "$THIRDFY_OWNER_SESSION_TOKEN"',
122
+ 'thirdfy-agent login --auth-token "$THIRDFY_AUTH_TOKEN"',
123
+ 'thirdfy-agent login --agent-api-key "$THIRDFY_AGENT_API_KEY"',
124
+ ],
125
+ },
126
+ {
127
+ id: 'mcp_bootstrap',
128
+ description: 'After owner proof, issue an MCP bootstrap token for agents.',
129
+ commands: [
130
+ 'thirdfy-agent bootstrap complete --agent-key 0xYOUR_AGENT_KEY --owner-session-token "$THIRDFY_OWNER_SESSION_TOKEN" --lane thirdfy --json',
131
+ ],
132
+ },
133
+ ],
134
+ nonInteractive: {
135
+ flag: '--not-interactive or --ni',
136
+ example:
137
+ 'thirdfy-agent login email user@example.com --code 123456 --accept-terms --ni (place --ni after the subcommand so it is not parsed as a flag value)',
138
+ rule: 'Commands must fail fast instead of waiting for prompts when required input is missing.',
139
+ },
140
+ executionModes: {
141
+ agent_wallet: 'Solo managed server-wallet execution through /execution-wallet + /execute; no subscriber binding when owner userDid matches. Network gas may be sponsored, but Thirdfy credits/free-quota gates still apply.',
142
+ self: 'BYOW/OWS local signing through build-tx, wallet sign, and wallet submit.',
143
+ hybrid: 'Primary self or agent_wallet execution with optional Thirdfy mirror validation.',
144
+ thirdfy: 'Publisher/fanout execute-intent; requires active subscriber/delegation bindings for writes.',
145
+ externalDelegation: 'Use delegation create/grant -> sign -> activate -> inspect/balance -> redeem for Gator/ERC-7710 external-wallet authority.',
146
+ },
147
+ frameworkGuidance: {
148
+ hermes:
149
+ 'Hermes agents should receive a pre-provisioned Thirdfy profile/env, run doctor auth/self at startup, use agent_wallet for managed solo writes, fund the agent execution wallet on the target chain, and keep strategy logic separate from CLI secrets.',
150
+ openclaw:
151
+ 'OpenClaw agents should keep Thirdfy credentials in runtime secrets, use actions discovery before planning, prefer run --run-mode agent_wallet for own-wallet execution, and use thirdfy only for publisher/fanout.',
152
+ claudeManagedAgents:
153
+ 'Claude Managed Agents should use MCP agentRun/walletExecute for solo managed execution, walletSign/buildTx for BYOW, and executeIntent only for fanout or mirror previews.',
154
+ },
155
+ apiBase: ctx.apiBase,
156
+ };
157
+ }
158
+
159
+ async function commandDoctorAuth(ctx, _flags) {
160
+ const config = loadProfileConfig();
161
+ const auth = config.auth && typeof config.auth === 'object' ? config.auth : {};
162
+ const agent = config.agent && typeof config.agent === 'object' ? config.agent : {};
163
+ const runMode = normalizeRunMode(ctx.runMode || config.runMode || 'agent_wallet');
164
+ const userDid = resolveEffectiveUserDid({});
165
+ const ownerSessionTokenFromEnv = String(process.env.THIRDFY_OWNER_SESSION_TOKEN || '').trim();
166
+ const authTokenFromEnv = String(process.env.THIRDFY_AUTH_TOKEN || '').trim();
167
+ const agentApiKeyFromEnv = String(process.env.THIRDFY_AGENT_API_KEY || '').trim();
168
+ const checks = [
169
+ {
170
+ name: 'owner-auth',
171
+ ok: Boolean(auth.ownerSessionToken || auth.authToken || ownerSessionTokenFromEnv || authTokenFromEnv),
172
+ detail: auth.ownerSessionToken
173
+ ? 'owner session stored'
174
+ : auth.authToken
175
+ ? 'auth token stored'
176
+ : ownerSessionTokenFromEnv
177
+ ? 'owner session from THIRDFY_OWNER_SESSION_TOKEN'
178
+ : authTokenFromEnv
179
+ ? 'auth token from THIRDFY_AUTH_TOKEN'
180
+ : 'missing owner auth; run `thirdfy-agent login email <email>`',
181
+ },
182
+ {
183
+ name: 'agent-api-key',
184
+ optional: true,
185
+ ok: Boolean(agent.apiKey || agentApiKeyFromEnv),
186
+ detail: agent.apiKey
187
+ ? 'agent API key stored'
188
+ : agentApiKeyFromEnv
189
+ ? 'agent API key from THIRDFY_AGENT_API_KEY'
190
+ : 'not stored yet (optional until execution); complete onboarding or bootstrap to add one',
191
+ },
192
+ {
193
+ name: 'user-did',
194
+ optional: runMode !== 'agent_wallet',
195
+ ok: Boolean(userDid),
196
+ detail: userDid
197
+ ? `user DID available (${userDid})`
198
+ : 'missing user DID; run `thirdfy-agent login email <email>` or set THIRDFY_USER_DID',
199
+ },
200
+ {
201
+ name: 'wallet-profile',
202
+ ok: Boolean(config.wallets?.primaryEvmWallet || (Array.isArray(config.wallets?.evm) && config.wallets.evm.length)),
203
+ detail: config.wallets?.primaryEvmWallet
204
+ || (Array.isArray(config.wallets?.evm) && config.wallets.evm.length
205
+ ? `${config.wallets.evm.length} linked EVM wallet${config.wallets.evm.length === 1 ? '' : 's'}`
206
+ : 'missing wallet profile; run email onboarding or wallet-sign proof'),
207
+ },
208
+ {
209
+ name: 'agent-execution-wallets',
210
+ optional: runMode !== 'agent_wallet',
211
+ ok: Boolean(config.wallets?.executionWallets && Object.keys(config.wallets.executionWallets).length),
212
+ detail: config.wallets?.executionWallets && Object.keys(config.wallets.executionWallets).length
213
+ ? `agent execution wallets available for chains: ${Object.keys(config.wallets.executionWallets).join(', ')}`
214
+ : 'missing agent execution wallets; run `thirdfy-agent login email <email> --code <otp> --accept-terms --json` or execution-wallet preflight',
215
+ },
216
+ {
217
+ name: 'thirdfy-credits-vs-gas-sponsorship',
218
+ optional: true,
219
+ ok: true,
220
+ detail: 'Thirdfy credits/free quota gate action access; Privy gas sponsorship only pays network gas when chain/action sponsorship is enabled.',
221
+ },
222
+ ];
223
+ const ok = checks.every((entry) => entry.optional || entry.ok);
224
+ printEnvelope({
225
+ success: ok,
226
+ code: ok ? 'DOCTOR_AUTH_OK' : 'DOCTOR_AUTH_NEEDS_SETUP',
227
+ message: ok ? 'Auth profile is ready' : 'Auth profile needs setup',
228
+ data: {
229
+ checks,
230
+ help: buildOnboardingHelp(ctx),
231
+ },
232
+ meta: { apiBase: ctx.apiBase },
233
+ });
234
+ if (!ok) process.exit(1);
235
+ }
236
+
237
+ async function commandManagedSetup(ctx, flags, capabilities) {
238
+ const mode = normalizeManagedRunMode(flags.runMode || 'agent_wallet');
239
+ const authHeaders = buildOwnerAuthHeaders(
240
+ flags,
241
+ 'Missing --auth-token/--owner-session-token (or THIRDFY_AUTH_TOKEN/THIRDFY_OWNER_SESSION_TOKEN) for managed setup'
242
+ );
243
+ const agentKey = requireFlag(flags, 'agentKey', 'Missing --agent-key');
244
+ const chainId = Number(flags.chainId || 8453);
245
+ const tokenAddress = String(flags.tokenAddress || '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913').trim();
246
+ const maxUsdPerDay = Number(flags.maxUsdPerDay || 1000);
247
+ const initResponse = await requestWithRouteFallback(ctx, {
248
+ method: 'POST',
249
+ routes: ['/api/v1/agent/delegation/init-custodial'],
250
+ body: { chainId },
251
+ headers: { ...authHeaders },
252
+ });
253
+ const grantResponse = await requestWithRouteFallback(ctx, {
254
+ method: 'POST',
255
+ routes: ['/api/v1/agent/delegation/custodial-grant'],
256
+ body: {
257
+ chainId,
258
+ agentKey,
259
+ tokenAddress,
260
+ maxUsdPerDay,
261
+ periodDuration: Number(flags.periodDuration || 86_400),
262
+ expirySeconds: Number(flags.expirySeconds || 86_400),
263
+ },
264
+ headers: { ...authHeaders },
265
+ });
266
+ let readiness = null;
267
+ let readinessError = null;
268
+ try {
269
+ readiness = await apiGet(
270
+ ctx,
271
+ `/api/v1/agent/delegation/check?agentKey=${encodeURIComponent(agentKey)}&chainId=${encodeURIComponent(String(chainId))}`,
272
+ { ...authHeaders }
273
+ );
274
+ } catch (error) {
275
+ readinessError = {
276
+ message: error?.message || 'Delegation readiness check failed',
277
+ statusCode: Number(error?.statusCode || 0) || undefined,
278
+ payload: error?.payload && typeof error.payload === 'object' ? error.payload : undefined,
279
+ };
280
+ }
281
+ const success = initResponse?.success === true && grantResponse?.success === true;
282
+ printEnvelope({
283
+ success,
284
+ code: success ? 'MANAGED_SETUP_READY' : 'MANAGED_SETUP_FAILED',
285
+ message: success
286
+ ? readinessError
287
+ ? `Managed wallet setup completed, but readiness check failed. Run with --run-mode ${mode}.`
288
+ : `Managed wallet setup completed. Run with --run-mode ${mode}.`
289
+ : 'Managed wallet setup failed',
290
+ data: {
291
+ runMode: mode,
292
+ init: initResponse,
293
+ grant: grantResponse,
294
+ readiness: readiness ? normalizeDelegationStatus(readiness) : null,
295
+ readinessError,
296
+ },
297
+ meta: {
298
+ apiBase: ctx.apiBase,
299
+ lane: 'managed_easy_wallet',
300
+ agentKey,
301
+ chainId,
302
+ tokenAddress,
303
+ maxUsdPerDay,
304
+ upgradeHint:
305
+ mode === 'agent_wallet'
306
+ ? 'Use --run-mode self for strict BYOW local-sign, or --run-mode agent_wallet for managed execution.'
307
+ : null,
308
+ capabilitiesVersion: capabilities?.contractVersion || null,
309
+ },
310
+ });
311
+ if (!success) process.exit(1);
312
+ }
313
+
314
+ return {
315
+ commandDoctorSelf,
316
+ buildOnboardingHelp,
317
+ commandDoctorAuth,
318
+ commandManagedSetup,
319
+ };
320
+ }
@@ -0,0 +1,255 @@
1
+ import { requireFlag } from '../core/args.mjs';
2
+ import { createCliError } from '../core/envelope.mjs';
3
+ import {
4
+ getEffectiveRunMode,
5
+ normalizeHybridWalletMode,
6
+ normalizeRunMode,
7
+ parseBooleanFlag,
8
+ } from '../core/runMode.mjs';
9
+ import { apiGet } from '../core/http.mjs';
10
+ import { createRequire } from 'module';
11
+
12
+ const require = createRequire(import.meta.url);
13
+ const { normalizeIntentResponse } = require('../utils/cli/intentNormalization.cjs');
14
+
15
+ export function createExecuteCommands(deps) {
16
+ const {
17
+ resolveActionSelection,
18
+ prepareActionParamsForFlags,
19
+ enforceOffchainVenueLaneCompatibility,
20
+ enforceChainCompatibility,
21
+ applyHyperliquidAgentWalletAddressDefault,
22
+ runPreflightByMode,
23
+ runExecutionByMode,
24
+ applyExecutionFallbackHints,
25
+ performSelfBroadcast,
26
+ executeManagedWalletRun,
27
+ executeSelfRun,
28
+ printEnvelope,
29
+ commandWalletSign,
30
+ commandWalletExecute,
31
+ } = deps;
32
+
33
+
34
+ async function commandPreflight(ctx, flags, capabilities) {
35
+ const resolved = await resolveActionSelection(ctx, flags);
36
+ prepareActionParamsForFlags(flags, resolved.resolvedAction);
37
+ const runMode = getEffectiveRunMode(ctx, flags);
38
+ enforceOffchainVenueLaneCompatibility(resolved.resolvedAction, runMode);
39
+ const hybridWalletMode = runMode === 'hybrid' ? normalizeHybridWalletMode(flags.hybridWalletMode) : null;
40
+ enforceChainCompatibility(capabilities, flags, runMode);
41
+ const managedPreflight = await applyHyperliquidAgentWalletAddressDefault(ctx, flags, resolved, runMode);
42
+ const backendResult = await runPreflightByMode(runMode, ctx, flags, resolved, { hybridWalletMode, managedPreflight });
43
+ const normalized = applyExecutionFallbackHints(backendResult.normalized, {
44
+ flags,
45
+ runMode,
46
+ resolvedAction: resolved.resolvedAction,
47
+ });
48
+ const data = flags.__polymarketSetupGuide
49
+ ? {
50
+ ...normalized,
51
+ setupGuide: {
52
+ message:
53
+ 'Review onboarding plan output above. Run agent-wallet setup via API when liveOrderReady is false. Fund pUSD on deposit wallet before live orders.',
54
+ nextCommands: [
55
+ 'thirdfy-agent polymarket status --agent-key <key> --json',
56
+ 'thirdfy-agent preflight --action get_polymarket_setup_status --params \'{"agentKey":"<key>","includeGeoblock":true}\' --json',
57
+ ],
58
+ },
59
+ }
60
+ : normalized;
61
+ const preflightBlocked = !normalized.success && Boolean(normalized.preflightBlocked);
62
+ printEnvelope({
63
+ success: normalized.success,
64
+ code: normalized.success ? 'PREFLIGHT_OK' : preflightBlocked ? 'PREFLIGHT_BLOCKED' : 'PREFLIGHT_FAILED',
65
+ message: backendResult.message,
66
+ data,
67
+ meta: {
68
+ apiBase: ctx.apiBase,
69
+ catalog: flags.catalog || null,
70
+ requestedAction: resolved.requestedAction,
71
+ resolvedAction: resolved.resolvedAction,
72
+ preflightRoute: backendResult.route,
73
+ runMode,
74
+ hybridWalletMode,
75
+ profile: ctx.profile || null,
76
+ swapInput: flags.__swapInput || null,
77
+ capabilitiesVersion: capabilities?.contractVersion || null,
78
+ },
79
+ });
80
+ if (!normalized.success) process.exit(1);
81
+ }
82
+
83
+
84
+ async function commandRun(ctx, flags, capabilities) {
85
+ const resolved = await resolveActionSelection(ctx, flags);
86
+ prepareActionParamsForFlags(flags, resolved.resolvedAction);
87
+ const runMode = getEffectiveRunMode(ctx, flags);
88
+ enforceOffchainVenueLaneCompatibility(resolved.resolvedAction, runMode);
89
+ const hybridWalletMode = runMode === 'hybrid' ? normalizeHybridWalletMode(flags.hybridWalletMode) : null;
90
+ enforceChainCompatibility(capabilities, flags, runMode);
91
+ const managedPreflight = await applyHyperliquidAgentWalletAddressDefault(ctx, flags, resolved, runMode);
92
+ const skipPreflight = Boolean(flags.skipPreflight);
93
+ const backendResult = await runExecutionByMode(runMode, ctx, flags, resolved, {
94
+ skipPreflight,
95
+ hybridWalletMode,
96
+ managedPreflight,
97
+ });
98
+ const normalized = applyExecutionFallbackHints(backendResult.normalized, {
99
+ flags,
100
+ runMode,
101
+ resolvedAction: resolved.resolvedAction,
102
+ });
103
+ if (runMode === 'self' && parseBooleanFlag(flags.broadcast, false) && normalized?.success) {
104
+ const executed = await performSelfBroadcast(ctx, flags, resolved.resolvedAction, normalized);
105
+ printEnvelope({
106
+ success: true,
107
+ code: 'SELF_EXECUTED',
108
+ message: 'Self-custody execution completed',
109
+ data: executed,
110
+ meta: {
111
+ apiBase: ctx.apiBase,
112
+ requestedAction: resolved.requestedAction,
113
+ resolvedAction: resolved.resolvedAction,
114
+ runMode,
115
+ profile: ctx.profile || null,
116
+ skippedPreflight: skipPreflight,
117
+ swapInput: flags.__swapInput || null,
118
+ capabilitiesVersion: capabilities?.contractVersion || null,
119
+ },
120
+ });
121
+ return;
122
+ }
123
+ printEnvelope({
124
+ success: normalized.success,
125
+ code: backendResult.code,
126
+ message: backendResult.message,
127
+ data: normalized,
128
+ meta: {
129
+ apiBase: ctx.apiBase,
130
+ requestedAction: resolved.requestedAction,
131
+ resolvedAction: resolved.resolvedAction,
132
+ idempotencyKey: backendResult.idempotencyKey || null,
133
+ runMode,
134
+ hybridWalletMode,
135
+ profile: ctx.profile || null,
136
+ skippedPreflight: skipPreflight,
137
+ swapInput: flags.__swapInput || null,
138
+ capabilitiesVersion: capabilities?.contractVersion || null,
139
+ },
140
+ });
141
+ if (!normalized.success) process.exit(1);
142
+ }
143
+
144
+
145
+ async function commandAgentRun(ctx, flags, capabilities) {
146
+ const signerMethod = String(flags.signerMethod || '').trim().toLowerCase();
147
+ const strategyIntent = String(flags.strategyIntent || '').trim().toLowerCase();
148
+ const requestedRunMode = String(flags.runMode || '').trim().toLowerCase();
149
+ if (signerMethod && !['managed_wallet_server', 'byow', 'fanout_intent'].includes(signerMethod)) {
150
+ throw createCliError(
151
+ 'SIGNER_METHOD_INVALID',
152
+ `Unsupported --signer-method "${signerMethod}". Use managed_wallet_server, fanout_intent, or byow.`
153
+ );
154
+ }
155
+ if (signerMethod === 'byow' || requestedRunMode === 'self') {
156
+ await commandWalletSign(ctx, { ...flags, runMode: 'self' }, capabilities);
157
+ return;
158
+ }
159
+ if (signerMethod === 'fanout_intent') {
160
+ await commandRun(ctx, { ...flags, runMode: 'thirdfy' }, capabilities);
161
+ return;
162
+ }
163
+ if (signerMethod === 'managed_wallet_server') {
164
+ await commandWalletExecute(ctx, { ...flags, runMode: 'agent_wallet' }, capabilities);
165
+ return;
166
+ }
167
+ if (requestedRunMode) {
168
+ const runMode = normalizeRunMode(requestedRunMode);
169
+ if (runMode === 'self') {
170
+ await commandWalletSign(ctx, { ...flags, runMode }, capabilities);
171
+ return;
172
+ }
173
+ if (runMode === 'thirdfy' || runMode === 'hybrid') {
174
+ await commandRun(ctx, { ...flags, runMode }, capabilities);
175
+ return;
176
+ }
177
+ await commandWalletExecute(ctx, { ...flags, runMode: 'agent_wallet' }, capabilities);
178
+ return;
179
+ }
180
+ if (strategyIntent === 'fanout') {
181
+ await commandRun(ctx, { ...flags, runMode: 'thirdfy' }, capabilities);
182
+ return;
183
+ }
184
+ await commandWalletExecute(ctx, { ...flags, runMode: 'agent_wallet' }, capabilities);
185
+ }
186
+
187
+
188
+ async function commandIntentStatus(ctx, flags, capabilities) {
189
+ const intentId = requireFlag(flags, 'intentId', 'Missing --intent-id');
190
+ const response = await apiGet(ctx, `/api/v1/agent/execute-intent/status?intentId=${encodeURIComponent(intentId)}`);
191
+ const normalized = normalizeIntentResponse(response);
192
+ printEnvelope({
193
+ success: normalized.success,
194
+ code: normalized.success ? 'OK' : 'INTENT_STATUS_FAILED',
195
+ message: normalized.success ? 'Intent status loaded' : 'Intent status failed',
196
+ data: normalized,
197
+ meta: {
198
+ apiBase: ctx.apiBase,
199
+ intentId,
200
+ capabilitiesVersion: capabilities?.contractVersion || null,
201
+ },
202
+ });
203
+ if (!normalized.success) process.exit(1);
204
+ }
205
+
206
+
207
+ async function commandSelfExec(ctx, flags, capabilities) {
208
+ const resolved = await resolveActionSelection(ctx, flags);
209
+ prepareActionParamsForFlags(flags, resolved.resolvedAction);
210
+ enforceOffchainVenueLaneCompatibility(resolved.resolvedAction, 'self');
211
+ enforceChainCompatibility(capabilities, flags, 'self');
212
+ const selfResult = await executeSelfRun(ctx, flags, resolved, {
213
+ skipPreflight: Boolean(flags.skipPreflight),
214
+ });
215
+
216
+ if (!selfResult?.success) {
217
+ const blocked = Boolean(selfResult?.preflightBlocked);
218
+ printEnvelope({
219
+ success: false,
220
+ code: blocked ? 'PREFLIGHT_BLOCKED' : 'SELF_EXECUTION_FAILED',
221
+ message: blocked ? 'Preflight blocked self execution' : 'Self execution preparation failed',
222
+ data: selfResult || {},
223
+ meta: {
224
+ apiBase: ctx.apiBase,
225
+ runMode: 'self',
226
+ resolvedAction: resolved.resolvedAction,
227
+ },
228
+ });
229
+ process.exit(1);
230
+ }
231
+
232
+ const executed = await performSelfBroadcast(ctx, flags, resolved.resolvedAction, selfResult);
233
+
234
+ printEnvelope({
235
+ success: true,
236
+ code: 'SELF_EXECUTED',
237
+ message: 'Self-custody execution completed',
238
+ data: executed,
239
+ meta: {
240
+ apiBase: ctx.apiBase,
241
+ runMode: 'self',
242
+ resolvedAction: resolved.resolvedAction,
243
+ swapInput: flags.__swapInput || null,
244
+ },
245
+ });
246
+ }
247
+
248
+ return {
249
+ commandPreflight,
250
+ commandRun,
251
+ commandAgentRun,
252
+ commandIntentStatus,
253
+ commandSelfExec,
254
+ };
255
+ }
@@ -0,0 +1,51 @@
1
+ import { parseJsonFlag } from '../core/args.mjs';
2
+
3
+ export function createHyperliquidHelpers(deps) {
4
+ const { getPreparedParams, resolveManagedExecutionPreflight } = deps;
5
+
6
+
7
+ function hyperliquidDefaultAddressParamForAction(action) {
8
+ const canonical = String(action || '').trim().toLowerCase().replace(/_/g, '-');
9
+ if (
10
+ canonical === 'get-hyperliquid-open-orders'
11
+ || canonical === 'get-hyperliquid-user-state'
12
+ || canonical === 'get-hyperliquid-builder-fee-status'
13
+ || canonical === 'get-hyperliquid-referral-status'
14
+ ) {
15
+ return 'userAddress';
16
+ }
17
+ if (
18
+ canonical === 'get-hyperliquid-setup-status'
19
+ || canonical === 'get-hyperliquid-funding-status'
20
+ || canonical === 'get-hyperliquid-onboarding-plan'
21
+ || canonical === 'prepare-hyperliquid-onboarding'
22
+ ) {
23
+ return 'mainWalletAddress';
24
+ }
25
+ return null;
26
+ }
27
+
28
+
29
+ async function applyHyperliquidAgentWalletAddressDefault(ctx, flags, resolved, runMode) {
30
+ if (runMode !== 'agent_wallet') return null;
31
+ const paramName = hyperliquidDefaultAddressParamForAction(resolved?.resolvedAction);
32
+ if (!paramName) return null;
33
+ const params = getPreparedParams(flags);
34
+ if (String(params?.[paramName] || '').trim()) return null;
35
+ const preflight = await resolveManagedExecutionPreflight(ctx, flags, resolved, {
36
+ runMode: 'agent_wallet',
37
+ requireFundingCheck: false,
38
+ });
39
+ if (!preflight.success || !preflight.executionWalletAddress) return preflight;
40
+ flags.__preparedParams = {
41
+ ...(params || {}),
42
+ [paramName]: preflight.executionWalletAddress,
43
+ };
44
+ return preflight;
45
+ }
46
+
47
+ return {
48
+ hyperliquidDefaultAddressParamForAction,
49
+ applyHyperliquidAgentWalletAddressDefault,
50
+ };
51
+ }
@@ -0,0 +1,6 @@
1
+ export { createPolymarketCommands, resolveConfigAgentKey } from './polymarket.mjs';
2
+ export { createHyperliquidHelpers } from './hyperliquid.mjs';
3
+ export { createExecuteCommands } from './execute.mjs';
4
+ export { createWalletCommands } from './wallet.mjs';
5
+ export { createDelegationCommands } from './delegation.mjs';
6
+ export { createBootstrapCommands } from './bootstrap.mjs';