@thirdfy/agent-cli 0.1.36 → 0.1.38
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 +25 -0
- package/README.md +13 -3
- package/bin/thirdfy-agent.mjs +343 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,31 @@ All notable changes to `@thirdfy/agent-cli` are documented here. The format is b
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [0.1.38] - 2026-05-17
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- **npm registry README:** Patch republish so **npmjs.org** shows the same `README.md` as `main` (after 0.1.37 shipped, README/cursor-rule updates lived only on GitHub until this semver).
|
|
12
|
+
|
|
13
|
+
### Changed
|
|
14
|
+
|
|
15
|
+
- Align root `package-lock.json` semver metadata with `package.json` (root `version` fields were stale).
|
|
16
|
+
|
|
17
|
+
## [0.1.37] - 2026-05-17
|
|
18
|
+
|
|
19
|
+
### Added
|
|
20
|
+
|
|
21
|
+
- 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.
|
|
22
|
+
|
|
23
|
+
### Changed
|
|
24
|
+
|
|
25
|
+
- `whoami` now reports masked auth, agent, and wallet profile state instead of dumping raw local config secrets.
|
|
26
|
+
|
|
27
|
+
### Fixed
|
|
28
|
+
|
|
29
|
+
- 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.
|
|
30
|
+
- `doctor auth`: agent API key is diagnostic-only (optional until execution); missing key no longer fails the command while the detail calls it optional.
|
|
31
|
+
|
|
7
32
|
## [0.1.36] - 2026-05-16
|
|
8
33
|
|
|
9
34
|
### Added
|
package/README.md
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
`@thirdfy/agent-cli` is the standalone command-line product for Thirdfy agent operations.
|
|
4
4
|
Use it to:
|
|
5
5
|
|
|
6
|
+
- onboard with **Privy email OTP** (`login email`), onboarding help (`help onboarding`), auth diagnostics (`doctor auth`), and wallet discovery (`wallet list`)
|
|
6
7
|
- onboard and manage agent keys
|
|
7
8
|
- discover allowed actions (including policy-aware discovery)
|
|
8
9
|
- run governance preflight checks
|
|
@@ -40,7 +41,7 @@ npx @thirdfy/agent-cli --help
|
|
|
40
41
|
|
|
41
42
|
## Release notes
|
|
42
43
|
|
|
43
|
-
Version history and highlights: [CHANGELOG.md](./CHANGELOG.md). **v0.1.35**
|
|
44
|
+
Version history and highlights: [CHANGELOG.md](./CHANGELOG.md). **v0.1.38** is a **documentation and metadata patch**: it republishes to npm so the package homepage matches the updated `README.md` on `main`, and aligns root `package-lock.json` version fields with `package.json`. **v0.1.37** adds **email OTP** `thirdfy-agent login email` (send code, then complete with `--code --accept-terms`), **`help onboarding`**, **`doctor auth`**, **`wallet list`**, and **masked `whoami`** output so agents and operators get first-run guidance without dumping secrets. **v0.1.35** extended `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
45
|
|
|
45
46
|
**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
47
|
|
|
@@ -77,6 +78,13 @@ Execution-only workflows can run with only `AGENT_API_KEY` after onboarding is c
|
|
|
77
78
|
### Login / logout / config UX
|
|
78
79
|
|
|
79
80
|
```bash
|
|
81
|
+
# Email OTP first-run flow
|
|
82
|
+
thirdfy-agent login email you@example.com --json
|
|
83
|
+
thirdfy-agent login email you@example.com --code "<otp>" --accept-terms --key-name "My Agent" --json
|
|
84
|
+
thirdfy-agent whoami --json
|
|
85
|
+
thirdfy-agent wallet list --json
|
|
86
|
+
thirdfy-agent doctor auth --json
|
|
87
|
+
|
|
80
88
|
# Store auth defaults once
|
|
81
89
|
thirdfy-agent login --auth-token "$THIRDFY_AUTH_TOKEN" --agent-api-key "$AGENT_API_KEY" --json
|
|
82
90
|
|
|
@@ -95,6 +103,8 @@ thirdfy-agent logout --json
|
|
|
95
103
|
thirdfy-agent logout --all --json
|
|
96
104
|
```
|
|
97
105
|
|
|
106
|
+
`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 — place **`--ni` after the `login email` subcommand** (for example `thirdfy-agent login email you@example.com --code … --accept-terms --ni`); a leading `thirdfy-agent --ni login email …` is parsed incorrectly because `--ni` would consume the `login` token as its value.
|
|
107
|
+
|
|
98
108
|
`logout --all` is provider-agnostic: it attempts owner-session revocation when a session token exists, then always clears local credentials.
|
|
99
109
|
|
|
100
110
|
### Wallet-sign onboarding path
|
|
@@ -272,7 +282,7 @@ Provider and venue guides are kept outside `README` so this page stays stable as
|
|
|
272
282
|
- Execution: `preflight`, `run`, `intent-status`, `jeff preflight`, `jeff trade`, `jeff status`
|
|
273
283
|
- Managed signer execution: `wallet execute`, `wallet sign`, `wallet submit`, `agent run`
|
|
274
284
|
- Profiles: `profile init`, `profile use`, `profile show`, `whoami`
|
|
275
|
-
- Onboarding: `bootstrap begin`, `bootstrap complete`, `onboarding begin`, `onboarding complete`, `agent auth challenge`, `agent auth verify`, `agent register`, `agent key rotate`, `agent key revoke`, `developer bootstrap`
|
|
285
|
+
- Onboarding: `login email`, `help onboarding`, `doctor auth`, `wallet list`, `bootstrap begin`, `bootstrap complete`, `onboarding begin`, `onboarding complete`, `agent auth challenge`, `agent auth verify`, `agent register`, `agent key rotate`, `agent key revoke`, `developer bootstrap`
|
|
276
286
|
- Governance readiness: `delegation create`, `delegation activate`, `delegation status`, `credentials upsert`, `credentials status`
|
|
277
287
|
- Delegation operations: `delegation show`, `delegation inspect`, `delegation revoke`
|
|
278
288
|
- Delegation execution helpers: `delegation balance`, `delegation redeem`
|
|
@@ -406,7 +416,7 @@ npm run e2e:managed:delegation:parity
|
|
|
406
416
|
|
|
407
417
|
## Release
|
|
408
418
|
|
|
409
|
-
Version history and user-facing notes: [CHANGELOG.md](./CHANGELOG.md) (for example [v0.1.
|
|
419
|
+
Version history and user-facing notes: [CHANGELOG.md](./CHANGELOG.md) (for example [v0.1.38](https://github.com/thirdfy/agent-cli/releases/tag/v0.1.38) — npm README + lockfile metadata alignment; [v0.1.37](https://github.com/thirdfy/agent-cli/releases/tag/v0.1.37) — email OTP login, onboarding help, auth doctor, wallet list, masked `whoami`). **Before every version bump or npm publish:** update this **README** (release notes paragraph, quick start, and command groups) so the npm package page matches what ships; keep `CHANGELOG.md`, [command reference](./docs/command-reference.md), and MCP summaries aligned when discovery or onboarding surfaces change.
|
|
410
420
|
|
|
411
421
|
```bash
|
|
412
422
|
npm version patch
|
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
|