@thirdfy/agent-cli 0.1.36 → 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 +15 -0
- package/README.md +9 -0
- package/bin/thirdfy-agent.mjs +343 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,21 @@ 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
|
+
|
|
7
22
|
## [0.1.36] - 2026-05-16
|
|
8
23
|
|
|
9
24
|
### Added
|
package/README.md
CHANGED
|
@@ -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
|
package/bin/thirdfy-agent.mjs
CHANGED
|
@@ -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
|
|
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()
|
|
@@ -2427,6 +2444,183 @@ async function commandProfileShow(ctx, _flags) {
|
|
|
2427
2444
|
});
|
|
2428
2445
|
}
|
|
2429
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
|
+
|
|
2430
2624
|
async function commandConfigSet(ctx, flags) {
|
|
2431
2625
|
const key = String(flags.key || '').trim();
|
|
2432
2626
|
const value = flags.value;
|
|
@@ -2577,6 +2771,149 @@ async function commandLogin(ctx, flags) {
|
|
|
2577
2771
|
});
|
|
2578
2772
|
}
|
|
2579
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
|
+
|
|
2580
2917
|
async function commandLogout(ctx, flags) {
|
|
2581
2918
|
const current = loadProfileConfig();
|
|
2582
2919
|
const localOnly = flags.all ? false : true;
|
|
@@ -4284,8 +4621,10 @@ Core commands:
|
|
|
4284
4621
|
thirdfy-agent profile show [--json]
|
|
4285
4622
|
thirdfy-agent config set --key <path> --value <value> [--json]
|
|
4286
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]
|
|
4287
4625
|
thirdfy-agent logout [--all] [--clear-agent-key] [--json]
|
|
4288
4626
|
thirdfy-agent whoami [--json]
|
|
4627
|
+
thirdfy-agent help onboarding [--json]
|
|
4289
4628
|
thirdfy-agent catalogs list [--json]
|
|
4290
4629
|
thirdfy-agent actions [--catalog <id>] [--provider <id>] [--agent-api-key <key>] [--chain-id <id>] [--run-mode <mode>] [--json]
|
|
4291
4630
|
thirdfy-agent data summary --agent-key <key> [--chain-id <id>] [--network <id>] [--category trading|yield|prediction] [--limit <n>] [--json]
|
|
@@ -4308,6 +4647,7 @@ Core commands:
|
|
|
4308
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]
|
|
4309
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]
|
|
4310
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]
|
|
4311
4651
|
thirdfy-agent wallet sign --agent-api-key <key> --action <id> --params <json> [--chain-id <id>] [--json]
|
|
4312
4652
|
thirdfy-agent wallet submit --signed-tx-hex <hex> [--chain-id <id>] [--rpc-url <url>] [--json]
|
|
4313
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]
|
|
@@ -4327,6 +4667,7 @@ Core commands:
|
|
|
4327
4667
|
thirdfy-agent run --agent-api-key <key> --action <id> --params <json> [--run-mode <mode>] [--broadcast] [--idempotency-key <key>] [--json]
|
|
4328
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]
|
|
4329
4669
|
thirdfy-agent doctor self [--wallet-address <addr>] [--ows-wallet-name <name>] [--rpc-url <url>] [--json]
|
|
4670
|
+
thirdfy-agent doctor auth [--json]
|
|
4330
4671
|
thirdfy-agent jeff preflight --agent-api-key <key> --action <id> --params <json> [--run-mode <mode>] [--json]
|
|
4331
4672
|
thirdfy-agent jeff trade --agent-api-key <key> --action <id> --params <json> [--run-mode <mode>] [--json]
|
|
4332
4673
|
thirdfy-agent jeff status --intent-id <id> [--json]
|
|
@@ -4349,6 +4690,7 @@ Global flags:
|
|
|
4349
4690
|
--allow-wallet-mismatch bypass strict funded-wallet vs execution-wallet guard
|
|
4350
4691
|
--effect-check <mode> self-exec check mode: strict | relaxed | off (default: relaxed)
|
|
4351
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
|
|
4352
4694
|
--json stable machine-readable output
|
|
4353
4695
|
--verbose print adapter diagnostics
|
|
4354
4696
|
--version print version
|