@thirdfy/agent-cli 0.1.35 → 0.1.37

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/CHANGELOG.md CHANGED
@@ -4,6 +4,27 @@ All notable changes to `@thirdfy/agent-cli` are documented here. The format is b
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [0.1.37] - 2026-05-17
8
+
9
+ ### Added
10
+
11
+ - Email OTP `thirdfy-agent login email` two-step onboarding (`send OTP` then `--code --accept-terms`) plus `help onboarding`, `doctor auth`, and `wallet list` so agents can learn and verify setup directly from the CLI.
12
+
13
+ ### Changed
14
+
15
+ - `whoami` now reports masked auth, agent, and wallet profile state instead of dumping raw local config secrets.
16
+
17
+ ### Fixed
18
+
19
+ - Onboarding help: neutral email-OTP wording, safer `maskSecret` for short tokens, and a non-interactive example that places `--ni` after `login email` so argv parsing does not swallow the `login` token.
20
+ - `doctor auth`: agent API key is diagnostic-only (optional until execution); missing key no longer fails the command while the detail calls it optional.
21
+
22
+ ## [0.1.36] - 2026-05-16
23
+
24
+ ### Added
25
+
26
+ - Hyperliquid perps cancel and expanded Bitfinex/Hyperliquid discovery hints in `thirdfy-agent actions`, plus preflight routing tests aligned with the Thirdfy action catalog.
27
+
7
28
  ## [0.1.35] - 2026-05-14
8
29
 
9
30
  ### Added
package/README.md CHANGED
@@ -40,7 +40,7 @@ npx @thirdfy/agent-cli --help
40
40
 
41
41
  ## Release notes
42
42
 
43
- Version history and highlights: [CHANGELOG.md](./CHANGELOG.md). Highlights through **v0.1.31** include provider discovery parity, portfolio analytics, managed-wallet swap normalization, and **self/build-tx** swap human-decimal `amountIn` parity when using `amountInHuman` + `tokenInDecimals`.
43
+ Version history and highlights: [CHANGELOG.md](./CHANGELOG.md). **v0.1.35** extends `thirdfy-agent actions` hints for Polymarket Gamma-style discovery (`gamma_search_*` where catalog-backed) and legacy `search_polymarket` / `get_polymarket_*` reads, with public docs aligned to the deposit-wallet (pUSD / POLY_1271) operator path. Earlier releases added provider discovery parity, portfolio analytics, managed-wallet swap normalization, and **self/build-tx** swap human-decimal `amountIn` parity when using `amountInHuman` + `tokenInDecimals`.
44
44
 
45
45
  **Maintainers — npm:** follow [docs/releasing.md](./docs/releasing.md). From a clone with gitignored `.env`, use `npm run publish:npm:local -- --dry-run` then `npm run publish:npm:local`. Or run `npm run release:npm` after exporting tokens in the shell. Never commit npm tokens — use [.env.example](./.env.example) as a template only.
46
46
 
@@ -77,6 +77,13 @@ Execution-only workflows can run with only `AGENT_API_KEY` after onboarding is c
77
77
  ### Login / logout / config UX
78
78
 
79
79
  ```bash
80
+ # Email OTP first-run flow
81
+ thirdfy-agent login email you@example.com --json
82
+ thirdfy-agent login email you@example.com --code "<otp>" --accept-terms --key-name "My Agent" --json
83
+ thirdfy-agent whoami --json
84
+ thirdfy-agent wallet list --json
85
+ thirdfy-agent doctor auth --json
86
+
80
87
  # Store auth defaults once
81
88
  thirdfy-agent login --auth-token "$THIRDFY_AUTH_TOKEN" --agent-api-key "$AGENT_API_KEY" --json
82
89
 
@@ -95,6 +102,8 @@ thirdfy-agent logout --json
95
102
  thirdfy-agent logout --all --json
96
103
  ```
97
104
 
105
+ `login email` is a two-step headless-safe flow. Without `--code`, the CLI asks the Thirdfy API onboarding broker to send a Privy email OTP and exits with the exact next command. With `--code --accept-terms`, it stores a short-lived owner session in `~/.thirdfy/config.json`, records linked EVM/Solana wallets when Privy returns them, and stores a new agent API key when first-run bootstrap issues one. Use `--not-interactive` / `--ni` in automation so missing input fails fast instead of prompting.
106
+
98
107
  `logout --all` is provider-agnostic: it attempts owner-session revocation when a session token exists, then always clears local credentials.
99
108
 
100
109
  ### Wallet-sign onboarding path
@@ -406,7 +415,7 @@ npm run e2e:managed:delegation:parity
406
415
 
407
416
  ## Release
408
417
 
409
- Version history and user-facing notes: [CHANGELOG.md](./CHANGELOG.md) (for example [v0.1.24](https://github.com/thirdfy/agent-cli/releases/tag/v0.1.24) — npm README and discovery docs aligned with MCP).
418
+ Version history and user-facing notes: [CHANGELOG.md](./CHANGELOG.md) (for example [v0.1.35](https://github.com/thirdfy/agent-cli/releases/tag/v0.1.35) — Polymarket Gamma discovery hints and public docs; keep npm README, `CHANGELOG.md`, and MCP provider summaries in sync when discovery surfaces change).
410
419
 
411
420
  ```bash
412
421
  npm version patch
@@ -157,6 +157,9 @@ async function main() {
157
157
  case 'config set':
158
158
  await commandConfigSet(context, subFlags);
159
159
  return;
160
+ case 'login email':
161
+ await commandLoginEmail(context, { ...subFlags, address: parsed.positionals[2] });
162
+ return;
160
163
  case 'login':
161
164
  await commandLogin(context, subFlags);
162
165
  return;
@@ -164,11 +167,21 @@ async function main() {
164
167
  await commandLogout(context, subFlags);
165
168
  return;
166
169
  case 'whoami':
167
- await commandProfileShow(context, subFlags);
170
+ await commandWhoami(context, subFlags);
168
171
  return;
169
172
  case 'doctor self':
170
173
  await commandDoctorSelf(context, subFlags);
171
174
  return;
175
+ case 'doctor auth':
176
+ await commandDoctorAuth(context, subFlags);
177
+ return;
178
+ case 'help onboarding':
179
+ case 'onboarding help':
180
+ await commandHelpOnboarding(context, subFlags);
181
+ return;
182
+ case 'wallet list':
183
+ await commandWalletList(context, subFlags);
184
+ return;
172
185
  default:
173
186
  break;
174
187
  }
@@ -361,6 +374,10 @@ function parseArgs(argv) {
361
374
  return { flags, positionals };
362
375
  }
363
376
 
377
+ function isNonInteractive(flags = {}) {
378
+ return Boolean(flags.notInteractive || flags.ni || process.env.THIRDFY_NOT_INTERACTIVE === '1');
379
+ }
380
+
364
381
  function toCamel(input) {
365
382
  return String(input || '')
366
383
  .trim()
@@ -434,6 +451,7 @@ function getTradingProviderHint(providerId) {
434
451
  ],
435
452
  orderManagementActions: [
436
453
  'place_hyperliquid_perps_order',
454
+ 'cancel_hyperliquid_perps_order',
437
455
  'get_hyperliquid_open_orders',
438
456
  ],
439
457
  setupActions: [
@@ -482,6 +500,38 @@ function getTradingProviderHint(providerId) {
482
500
  'thirdfy-agent actions --provider hyperliquid && thirdfy-agent preflight --action place_hyperliquid_perps_order --params \'{"coin":"ETH","isBuy":true,"size":0.01,"limitPx":3500,"orderType":"market"}\'',
483
501
  };
484
502
  }
503
+ if (provider === 'bitfinex') {
504
+ return {
505
+ provider,
506
+ category: 'cex_funding',
507
+ canonicalReadAction: 'get_bitfinex_funding_ticker',
508
+ canonicalWriteAction: 'submit_bitfinex_funding_offer',
509
+ readActions: [
510
+ 'get_bitfinex_funding_ticker',
511
+ 'get_bitfinex_funding_stats_hist',
512
+ 'get_bitfinex_funding_book',
513
+ 'get_bitfinex_funding_info',
514
+ 'get_bitfinex_wallets',
515
+ 'list_bitfinex_funding_offers',
516
+ 'list_bitfinex_funding_credits',
517
+ ],
518
+ orderManagementActions: [
519
+ 'submit_bitfinex_funding_offer',
520
+ 'cancel_bitfinex_funding_offer',
521
+ 'submit_bitfinex_order',
522
+ 'update_bitfinex_order',
523
+ 'cancel_bitfinex_order',
524
+ ],
525
+ credentialType: 'bitfinex_api_key',
526
+ credentialRef: 'bitfinex:funding-primary',
527
+ credentialUx:
528
+ 'Save and verify Bitfinex funding credentials in EarnClaw/Thirdfy first. MCP/CLI order intents reference credentialRef only; raw keys are never sent through agentRun.',
529
+ laneCompatibility:
530
+ 'Bitfinex CEX writes require thirdfy, hybrid, or agent_wallet so Thirdfy can inject stored credentials and run policy/preflight. Reads and setup discovery can be listed without raw credentials.',
531
+ example:
532
+ 'thirdfy-agent actions --provider bitfinex && thirdfy-agent run --run-mode thirdfy --action submit_bitfinex_funding_offer --params \'{"symbol":"fUSD","amount":"100","rate":"0.0002","period":2,"credentialRef":"bitfinex:funding-primary"}\'',
533
+ };
534
+ }
485
535
  if (provider === 'polymarket') {
486
536
  return {
487
537
  provider,
@@ -701,6 +751,8 @@ function formatProviderUnknownActionHint(providerHint) {
701
751
  const OFFCHAIN_VENUE_ORDER_ACTIONS = new Set([
702
752
  'place_hyperliquid_perps_order',
703
753
  'place-hyperliquid-perps-order',
754
+ 'cancel_hyperliquid_perps_order',
755
+ 'cancel-hyperliquid-perps-order',
704
756
  'place_polymarket_order',
705
757
  'place-polymarket-order',
706
758
  ]);
@@ -2392,6 +2444,183 @@ async function commandProfileShow(ctx, _flags) {
2392
2444
  });
2393
2445
  }
2394
2446
 
2447
+ function maskSecret(value) {
2448
+ const raw = String(value || '').trim();
2449
+ if (!raw) return null;
2450
+ if (raw.length <= 12) return '***';
2451
+ if (raw.length <= 20) return `${raw.slice(0, 4)}***${raw.slice(-2)}`;
2452
+ return `${raw.slice(0, 6)}...${raw.slice(-4)}`;
2453
+ }
2454
+
2455
+ function buildOnboardingHelp(ctx) {
2456
+ return {
2457
+ title: 'Thirdfy Agent CLI onboarding',
2458
+ summary: 'Use email OTP, wallet-sign, or existing credentials to create owner auth, wallets, and agent execution credentials.',
2459
+ paths: [
2460
+ {
2461
+ id: 'email_otp',
2462
+ recommended: true,
2463
+ description: 'Email OTP first-run flow for humans and agents that can ask the user for a verification code.',
2464
+ commands: [
2465
+ 'thirdfy-agent login email user@example.com',
2466
+ 'thirdfy-agent login email user@example.com --code <otp> --accept-terms --key-name "My Agent"',
2467
+ 'thirdfy-agent whoami',
2468
+ 'thirdfy-agent managed wallet init --chain-id 8453',
2469
+ ],
2470
+ },
2471
+ {
2472
+ id: 'wallet_sign',
2473
+ description: 'Headless wallet proof for operators with an EVM signer.',
2474
+ commands: [
2475
+ 'thirdfy-agent agent auth challenge --agent-key 0xYOUR_AGENT_KEY --json',
2476
+ 'thirdfy-agent login --agent-key 0xYOUR_AGENT_KEY --challenge-id <id> --signature <hex> --json',
2477
+ ],
2478
+ },
2479
+ {
2480
+ id: 'existing_credentials',
2481
+ description: 'Use already-issued owner or agent credentials from a secure secret store.',
2482
+ commands: [
2483
+ 'thirdfy-agent login --owner-session-token "$THIRDFY_OWNER_SESSION_TOKEN"',
2484
+ 'thirdfy-agent login --auth-token "$THIRDFY_AUTH_TOKEN"',
2485
+ 'thirdfy-agent login --agent-api-key "$THIRDFY_AGENT_API_KEY"',
2486
+ ],
2487
+ },
2488
+ {
2489
+ id: 'mcp_bootstrap',
2490
+ description: 'After owner proof, issue an MCP bootstrap token for agents.',
2491
+ commands: [
2492
+ 'thirdfy-agent bootstrap complete --agent-key 0xYOUR_AGENT_KEY --owner-session-token "$THIRDFY_OWNER_SESSION_TOKEN" --lane thirdfy --json',
2493
+ ],
2494
+ },
2495
+ ],
2496
+ nonInteractive: {
2497
+ flag: '--not-interactive or --ni',
2498
+ example:
2499
+ '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)',
2500
+ rule: 'Commands must fail fast instead of waiting for prompts when required input is missing.',
2501
+ },
2502
+ apiBase: ctx.apiBase,
2503
+ };
2504
+ }
2505
+
2506
+ async function commandHelpOnboarding(ctx, _flags) {
2507
+ printEnvelope({
2508
+ success: true,
2509
+ code: 'ONBOARDING_HELP_OK',
2510
+ message: 'Thirdfy onboarding help',
2511
+ data: buildOnboardingHelp(ctx),
2512
+ meta: { apiBase: ctx.apiBase },
2513
+ });
2514
+ }
2515
+
2516
+ async function commandWhoami(ctx, _flags) {
2517
+ const config = loadProfileConfig();
2518
+ const auth = config.auth && typeof config.auth === 'object' ? config.auth : {};
2519
+ const agent = config.agent && typeof config.agent === 'object' ? config.agent : {};
2520
+ const wallets = config.wallets && typeof config.wallets === 'object' ? config.wallets : {};
2521
+ printEnvelope({
2522
+ success: true,
2523
+ code: 'WHOAMI_OK',
2524
+ message: 'Thirdfy CLI identity loaded',
2525
+ data: {
2526
+ profile: ctx.profile || config.profile || 'personal',
2527
+ runMode: ctx.runMode || config.runMode || 'thirdfy',
2528
+ custodyMode: ctx.custodyMode || config.custodyMode || defaultCustodyModeForRunMode(ctx.runMode || config.runMode || 'thirdfy'),
2529
+ auth: {
2530
+ source: auth.source || null,
2531
+ hasAuthToken: Boolean(auth.authToken),
2532
+ hasOwnerSessionToken: Boolean(auth.ownerSessionToken),
2533
+ ownerSessionToken: maskSecret(auth.ownerSessionToken),
2534
+ ownerSessionExpiresAt: auth.ownerSessionExpiresAt || null,
2535
+ },
2536
+ agent: {
2537
+ hasAgentApiKey: Boolean(agent.apiKey),
2538
+ agentApiKey: maskSecret(agent.apiKey),
2539
+ agentKey: agent.agentKey || null,
2540
+ },
2541
+ wallets: {
2542
+ primaryEvmWallet: wallets.primaryEvmWallet || null,
2543
+ evm: Array.isArray(wallets.evm) ? wallets.evm : [],
2544
+ solana: Array.isArray(wallets.solana) ? wallets.solana : [],
2545
+ },
2546
+ configPath: getProfileConfigPath(),
2547
+ nextSteps: buildOnboardingHelp(ctx).paths[0].commands,
2548
+ },
2549
+ meta: { apiBase: ctx.apiBase },
2550
+ });
2551
+ }
2552
+
2553
+ async function commandWalletList(ctx, _flags) {
2554
+ const config = loadProfileConfig();
2555
+ const wallets = config.wallets && typeof config.wallets === 'object' ? config.wallets : {};
2556
+ printEnvelope({
2557
+ success: true,
2558
+ code: 'WALLET_LIST_OK',
2559
+ message: 'Linked wallets from local Thirdfy CLI profile',
2560
+ data: {
2561
+ primaryEvmWallet: wallets.primaryEvmWallet || null,
2562
+ evm: Array.isArray(wallets.evm) ? wallets.evm : [],
2563
+ solana: Array.isArray(wallets.solana) ? wallets.solana : [],
2564
+ hint: 'Run `thirdfy-agent login email <email>` to refresh linked wallets from Privy-backed onboarding.',
2565
+ },
2566
+ meta: { apiBase: ctx.apiBase },
2567
+ });
2568
+ }
2569
+
2570
+ async function commandDoctorAuth(ctx, _flags) {
2571
+ const config = loadProfileConfig();
2572
+ const auth = config.auth && typeof config.auth === 'object' ? config.auth : {};
2573
+ const agent = config.agent && typeof config.agent === 'object' ? config.agent : {};
2574
+ const ownerSessionTokenFromEnv = String(process.env.THIRDFY_OWNER_SESSION_TOKEN || '').trim();
2575
+ const authTokenFromEnv = String(process.env.THIRDFY_AUTH_TOKEN || '').trim();
2576
+ const agentApiKeyFromEnv = String(process.env.THIRDFY_AGENT_API_KEY || '').trim();
2577
+ const checks = [
2578
+ {
2579
+ name: 'owner-auth',
2580
+ ok: Boolean(auth.ownerSessionToken || auth.authToken || ownerSessionTokenFromEnv || authTokenFromEnv),
2581
+ detail: auth.ownerSessionToken
2582
+ ? 'owner session stored'
2583
+ : auth.authToken
2584
+ ? 'auth token stored'
2585
+ : ownerSessionTokenFromEnv
2586
+ ? 'owner session from THIRDFY_OWNER_SESSION_TOKEN'
2587
+ : authTokenFromEnv
2588
+ ? 'auth token from THIRDFY_AUTH_TOKEN'
2589
+ : 'missing owner auth; run `thirdfy-agent login email <email>`',
2590
+ },
2591
+ {
2592
+ name: 'agent-api-key',
2593
+ optional: true,
2594
+ ok: Boolean(agent.apiKey || agentApiKeyFromEnv),
2595
+ detail: agent.apiKey
2596
+ ? 'agent API key stored'
2597
+ : agentApiKeyFromEnv
2598
+ ? 'agent API key from THIRDFY_AGENT_API_KEY'
2599
+ : 'not stored yet (optional until execution); complete onboarding or bootstrap to add one',
2600
+ },
2601
+ {
2602
+ name: 'wallet-profile',
2603
+ ok: Boolean(config.wallets?.primaryEvmWallet || (Array.isArray(config.wallets?.evm) && config.wallets.evm.length)),
2604
+ detail: config.wallets?.primaryEvmWallet
2605
+ || (Array.isArray(config.wallets?.evm) && config.wallets.evm.length
2606
+ ? `${config.wallets.evm.length} linked EVM wallet${config.wallets.evm.length === 1 ? '' : 's'}`
2607
+ : 'missing wallet profile; run email onboarding or wallet-sign proof'),
2608
+ },
2609
+ ];
2610
+ const ok = checks.every((entry) => entry.optional || entry.ok);
2611
+ printEnvelope({
2612
+ success: ok,
2613
+ code: ok ? 'DOCTOR_AUTH_OK' : 'DOCTOR_AUTH_NEEDS_SETUP',
2614
+ message: ok ? 'Auth profile is ready' : 'Auth profile needs setup',
2615
+ data: {
2616
+ checks,
2617
+ help: buildOnboardingHelp(ctx),
2618
+ },
2619
+ meta: { apiBase: ctx.apiBase },
2620
+ });
2621
+ if (!ok) process.exit(1);
2622
+ }
2623
+
2395
2624
  async function commandConfigSet(ctx, flags) {
2396
2625
  const key = String(flags.key || '').trim();
2397
2626
  const value = flags.value;
@@ -2542,6 +2771,149 @@ async function commandLogin(ctx, flags) {
2542
2771
  });
2543
2772
  }
2544
2773
 
2774
+ async function commandLoginEmail(ctx, flags) {
2775
+ const current = loadProfileConfig();
2776
+ const email = String(flags.address || flags.email || '').trim().toLowerCase();
2777
+ if (!email) {
2778
+ throw createCliError(
2779
+ 'LOGIN_EMAIL_REQUIRED',
2780
+ isNonInteractive(flags)
2781
+ ? 'Missing email address for non-interactive login. Use: thirdfy-agent login email user@example.com [--code <otp> --accept-terms]'
2782
+ : 'Missing email address. Use: thirdfy-agent login email user@example.com'
2783
+ );
2784
+ }
2785
+ if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
2786
+ throw createCliError('LOGIN_EMAIL_INVALID', 'Provide a valid email address for Thirdfy email login.');
2787
+ }
2788
+ const code = String(flags.code || flags.otp || '').trim();
2789
+ const keyName = String(flags.keyName || 'Thirdfy CLI').trim();
2790
+ if (!code) {
2791
+ const response = await apiPost(ctx, '/api/v1/agent/onboarding/cli/email/start', {
2792
+ email,
2793
+ keyName,
2794
+ requestedCapabilities: {
2795
+ walletApi: true,
2796
+ agentApi: true,
2797
+ evmWallet: true,
2798
+ solanaWallet: true,
2799
+ },
2800
+ });
2801
+ const data = response?.data || response || {};
2802
+ const next = {
2803
+ ...current,
2804
+ login: {
2805
+ ...(current.login && typeof current.login === 'object' ? current.login : {}),
2806
+ email: {
2807
+ lastEmail: email,
2808
+ lastAttemptId: data.loginAttemptId || null,
2809
+ sentAt: new Date().toISOString(),
2810
+ },
2811
+ },
2812
+ };
2813
+ persistProfileConfig(next);
2814
+ printEnvelope({
2815
+ success: true,
2816
+ code: 'LOGIN_EMAIL_CODE_SENT',
2817
+ message: response?.message || 'Verification code sent',
2818
+ data: {
2819
+ email,
2820
+ loginAttemptId: data.loginAttemptId || null,
2821
+ expiresAt: data.expiresAt || null,
2822
+ nextCommand: data.nextCommand || `thirdfy-agent login email ${email} --code <code> --accept-terms`,
2823
+ configPath: getProfileConfigPath(),
2824
+ },
2825
+ meta: { apiBase: ctx.apiBase },
2826
+ });
2827
+ return;
2828
+ }
2829
+
2830
+ if (!parseBooleanFlag(flags.acceptTerms, false)) {
2831
+ throw createCliError(
2832
+ 'LOGIN_EMAIL_TERMS_REQUIRED',
2833
+ 'Email login requires --accept-terms to complete first-run onboarding.'
2834
+ );
2835
+ }
2836
+
2837
+ const lastAttempt = current?.login?.email && typeof current.login.email === 'object' ? current.login.email : {};
2838
+ const response = await apiPost(ctx, '/api/v1/agent/onboarding/cli/email/complete', {
2839
+ email,
2840
+ code,
2841
+ loginAttemptId: String(flags.loginAttemptId || lastAttempt.lastAttemptId || '').trim() || undefined,
2842
+ acceptTerms: true,
2843
+ keyName,
2844
+ agentKey: String(flags.agentKey || '').trim() || undefined,
2845
+ runMode: normalizeRunMode(flags.runMode || current.runMode || process.env.THIRDFY_RUN_MODE || 'thirdfy'),
2846
+ custodyMode: normalizeCustodyMode(
2847
+ flags.custodyMode || current.custodyMode || process.env.THIRDFY_CUSTODY_MODE,
2848
+ normalizeRunMode(flags.runMode || current.runMode || process.env.THIRDFY_RUN_MODE || 'thirdfy')
2849
+ ),
2850
+ bootstrapAgent: !parseBooleanFlag(flags.noBootstrapAgent, false),
2851
+ rotateAgentKey: parseBooleanFlag(flags.rotateAgentKey, false),
2852
+ });
2853
+ const data = response?.data || response || {};
2854
+ const ownerSessionToken = String(data.ownerSessionToken || '').trim();
2855
+ const bootstrap = data.bootstrap && typeof data.bootstrap === 'object' ? data.bootstrap : {};
2856
+ const agentApiKey = String(bootstrap.agentApiKey || '').trim();
2857
+ const agentKey = String(bootstrap.agentKey || data.owner?.creatorWallet || '').trim();
2858
+ const wallets = data.wallets && typeof data.wallets === 'object' ? data.wallets : {};
2859
+ const primaryEvmWallet = String(wallets.primaryEvmWallet || data.owner?.creatorWallet || '').trim();
2860
+ const persistedWallets = {
2861
+ ...wallets,
2862
+ ...(primaryEvmWallet ? { primaryEvmWallet } : {}),
2863
+ };
2864
+ const runMode = normalizeRunMode(flags.runMode || current.runMode || process.env.THIRDFY_RUN_MODE || 'thirdfy');
2865
+ const custodyMode = normalizeCustodyMode(flags.custodyMode || current.custodyMode || process.env.THIRDFY_CUSTODY_MODE, runMode);
2866
+ if (!ownerSessionToken) {
2867
+ throw createCliError('LOGIN_EMAIL_OWNER_SESSION_MISSING', 'Email onboarding completed without an owner session token.');
2868
+ }
2869
+ const next = {
2870
+ ...current,
2871
+ runMode,
2872
+ custodyMode,
2873
+ auth: {
2874
+ ...(current.auth && typeof current.auth === 'object' ? current.auth : {}),
2875
+ lastLoginAt: new Date().toISOString(),
2876
+ source: 'email',
2877
+ ownerSessionToken,
2878
+ ownerSessionExpiresAt: data.ownerSessionExpiresAt || null,
2879
+ },
2880
+ wallets: persistedWallets,
2881
+ login: {
2882
+ ...(current.login && typeof current.login === 'object' ? current.login : {}),
2883
+ email: {
2884
+ lastEmail: email,
2885
+ completedAt: new Date().toISOString(),
2886
+ },
2887
+ },
2888
+ };
2889
+ delete next.auth.authToken;
2890
+ if (agentApiKey || agentKey) {
2891
+ next.agent = {
2892
+ ...(current.agent && typeof current.agent === 'object' ? current.agent : {}),
2893
+ ...(agentApiKey ? { apiKey: agentApiKey } : {}),
2894
+ ...(agentKey ? { agentKey } : {}),
2895
+ };
2896
+ }
2897
+ persistProfileConfig(next);
2898
+ printEnvelope({
2899
+ success: true,
2900
+ code: 'LOGIN_EMAIL_OK',
2901
+ message: response?.message || 'Email login completed',
2902
+ data: {
2903
+ email,
2904
+ hasOwnerSessionToken: Boolean(ownerSessionToken),
2905
+ hasAgentApiKey: Boolean(agentApiKey),
2906
+ agentKey: agentKey || null,
2907
+ evmWallets: Array.isArray(persistedWallets.evm) ? persistedWallets.evm : [],
2908
+ solanaWallets: Array.isArray(persistedWallets.solana) ? persistedWallets.solana : [],
2909
+ primaryEvmWallet: persistedWallets.primaryEvmWallet || null,
2910
+ configPath: getProfileConfigPath(),
2911
+ nextSteps: response?.nextSteps || [],
2912
+ },
2913
+ meta: { apiBase: ctx.apiBase },
2914
+ });
2915
+ }
2916
+
2545
2917
  async function commandLogout(ctx, flags) {
2546
2918
  const current = loadProfileConfig();
2547
2919
  const localOnly = flags.all ? false : true;
@@ -3516,6 +3888,7 @@ function inferActionProvider(action) {
3516
3888
  'deposit-hyperliquid-staking': 'hyperliquid',
3517
3889
  'deposit-hyperliquid-bridge': 'hyperliquid',
3518
3890
  'place-hyperliquid-perps-order': 'hyperliquid',
3891
+ 'cancel-hyperliquid-perps-order': 'hyperliquid',
3519
3892
  'get-perps-markets': 'hyperliquid',
3520
3893
  'get-perps-account': 'hyperliquid',
3521
3894
  'get-perps-position': 'hyperliquid',
@@ -3562,6 +3935,7 @@ function inferActionProvider(action) {
3562
3935
  };
3563
3936
  if (knownMap[key]) return knownMap[key];
3564
3937
  if (key.includes('hyperliquid')) return 'hyperliquid';
3938
+ if (key.includes('bitfinex')) return 'bitfinex';
3565
3939
  if (key.includes('polymarket') || key.includes('prediction')) return 'polymarket';
3566
3940
  if (key.includes('vaults-fyi')) return 'vaults-fyi';
3567
3941
  if (key.includes('yield-xyz')) return 'yield-xyz';
@@ -4247,8 +4621,10 @@ Core commands:
4247
4621
  thirdfy-agent profile show [--json]
4248
4622
  thirdfy-agent config set --key <path> --value <value> [--json]
4249
4623
  thirdfy-agent login [--auth-token <token> | --owner-session-token <token>] [--agent-api-key <key>] [--agent-key <key>] [--run-mode <mode>] [--custody-mode managed|external] [--wallet-address <addr>] [--json]
4624
+ thirdfy-agent login email <address> [--code <otp>] [--accept-terms] [--key-name <name>] [--agent-key <key>] [--not-interactive|--ni] [--json]
4250
4625
  thirdfy-agent logout [--all] [--clear-agent-key] [--json]
4251
4626
  thirdfy-agent whoami [--json]
4627
+ thirdfy-agent help onboarding [--json]
4252
4628
  thirdfy-agent catalogs list [--json]
4253
4629
  thirdfy-agent actions [--catalog <id>] [--provider <id>] [--agent-api-key <key>] [--chain-id <id>] [--run-mode <mode>] [--json]
4254
4630
  thirdfy-agent data summary --agent-key <key> [--chain-id <id>] [--network <id>] [--category trading|yield|prediction] [--limit <n>] [--json]
@@ -4271,6 +4647,7 @@ Core commands:
4271
4647
  thirdfy-agent managed wallet grant [--auth-token <token> | --owner-session-token <token>] --agent-key <key> --token-address <addr> --max-usd-per-day <usd> [--chain-id <id>] [--period-duration <sec>] [--expiry-seconds <sec>] [--json]
4272
4648
  thirdfy-agent managed setup [--auth-token <token> | --owner-session-token <token>] --agent-key <key> [--run-mode thirdfy|hybrid|agent_wallet] [--token-address <addr>] [--max-usd-per-day <usd>] [--chain-id <id>] [--json]
4273
4649
  thirdfy-agent wallet execute --agent-api-key <key> --user-did <did> --action <id> --params <json> [--chain-id <id>] [--idempotency-key <key>] [--wallet-address <expected>] [--allow-wallet-mismatch] [--json]
4650
+ thirdfy-agent wallet list [--json]
4274
4651
  thirdfy-agent wallet sign --agent-api-key <key> --action <id> --params <json> [--chain-id <id>] [--json]
4275
4652
  thirdfy-agent wallet submit --signed-tx-hex <hex> [--chain-id <id>] [--rpc-url <url>] [--json]
4276
4653
  thirdfy-agent agent run --agent-api-key <key> --action <id> --params <json> [--signer-method managed_wallet_server|byow|fanout_intent] [--strategy-intent single_wallet|fanout] [--run-mode <mode>] [--user-did <did>] [--json]
@@ -4290,6 +4667,7 @@ Core commands:
4290
4667
  thirdfy-agent run --agent-api-key <key> --action <id> --params <json> [--run-mode <mode>] [--broadcast] [--idempotency-key <key>] [--json]
4291
4668
  thirdfy-agent self-exec --agent-api-key <key> --action <id> --params <json> --wallet-address <addr> --ows-wallet-name <name> [--rpc-url <url>] [--effect-check strict|relaxed|off] [--json]
4292
4669
  thirdfy-agent doctor self [--wallet-address <addr>] [--ows-wallet-name <name>] [--rpc-url <url>] [--json]
4670
+ thirdfy-agent doctor auth [--json]
4293
4671
  thirdfy-agent jeff preflight --agent-api-key <key> --action <id> --params <json> [--run-mode <mode>] [--json]
4294
4672
  thirdfy-agent jeff trade --agent-api-key <key> --action <id> --params <json> [--run-mode <mode>] [--json]
4295
4673
  thirdfy-agent jeff status --intent-id <id> [--json]
@@ -4312,6 +4690,7 @@ Global flags:
4312
4690
  --allow-wallet-mismatch bypass strict funded-wallet vs execution-wallet guard
4313
4691
  --effect-check <mode> self-exec check mode: strict | relaxed | off (default: relaxed)
4314
4692
  --owner-session-token <token> owner session token (or THIRDFY_OWNER_SESSION_TOKEN)
4693
+ --not-interactive, --ni fail fast when required login input is missing
4315
4694
  --json stable machine-readable output
4316
4695
  --verbose print adapter diagnostics
4317
4696
  --version print version
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thirdfy/agent-cli",
3
- "version": "0.1.35",
3
+ "version": "0.1.37",
4
4
  "description": "Thirdfy Agent CLI for onboarding, governance preflight, execute-intent, and status polling.",
5
5
  "type": "module",
6
6
  "bin": {