@thirdfy/agent-cli 0.2.31 → 0.2.33
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 +12 -0
- package/README.md +2 -3
- package/package.json +1 -1
- package/src/commands/bootstrap.mjs +34 -5
- package/src/runtime/certification/executionCertify.mjs +13 -8
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,18 @@ All notable changes to `@thirdfy/agent-cli` are documented here. The format is b
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [0.2.33] - 2026-07-24
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- `login email` always prints `executionWallets` / `executionWalletsError` and fails closed with `LOGIN_EMAIL_INCOMPLETE` when `runMode=agent_wallet` and execution wallets are missing or errored. Fund the managed execution wallet, not the owner embed.
|
|
12
|
+
|
|
13
|
+
## [0.2.32] - 2026-07-23
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
|
|
17
|
+
- `doctor certify execution` now keys live catalog and fixture rows by normalized action id (kebab/snake). Catalog `place-hyperliquid-perps-order` (and siblings) overwrite fixtures so required write actions resolve `source=catalog` instead of failing with `CATALOG_SCHEMA_UNAVAILABLE` / write-policy mismatch.
|
|
18
|
+
|
|
7
19
|
## [0.2.31] - 2026-07-23
|
|
8
20
|
|
|
9
21
|
### Added
|
package/README.md
CHANGED
|
@@ -40,10 +40,9 @@ Run without global install:
|
|
|
40
40
|
npx @thirdfy/agent-cli --help
|
|
41
41
|
```
|
|
42
42
|
|
|
43
|
-
## What's new in v0.2.
|
|
43
|
+
## What's new in v0.2.33
|
|
44
44
|
|
|
45
|
-
-
|
|
46
|
-
- Adds `doctor certify execution` for a no-write certification report (CLI version, command availability, venue schema matrix, authority inputs). EarnClaw passes `--no-write`, `--run-mode`, `--schema-digest`, and `--required-actions` for fail-closed pack certification.
|
|
45
|
+
- `login email` always surfaces `executionWallets` (and `executionWalletsError` when present) and fails closed for `agent_wallet` when execution wallets are missing. Fund the printed execution address, not the owner login wallet.
|
|
47
46
|
|
|
48
47
|
Older versions: see [CHANGELOG.md](./CHANGELOG.md) and [GitHub Releases](https://github.com/thirdfy/agent-cli/releases).
|
|
49
48
|
|
package/package.json
CHANGED
|
@@ -450,13 +450,18 @@ async function commandLoginEmail(ctx, flags) {
|
|
|
450
450
|
const agentKey = extractAgentKey(data);
|
|
451
451
|
const wallets = data.wallets && typeof data.wallets === 'object' ? data.wallets : {};
|
|
452
452
|
const executionWallets = data.executionWallets && typeof data.executionWallets === 'object' ? data.executionWallets : {};
|
|
453
|
+
const executionWalletsError = data.executionWalletsError
|
|
454
|
+
? String(data.executionWalletsError)
|
|
455
|
+
: data.execution_wallets_error
|
|
456
|
+
? String(data.execution_wallets_error)
|
|
457
|
+
: null;
|
|
453
458
|
const userDid = String(data.owner?.creatorDid || wallets.userDid || '').trim();
|
|
454
459
|
const primaryEvmWallet = String(wallets.primaryEvmWallet || data.owner?.creatorWallet || '').trim();
|
|
455
460
|
const persistedWallets = {
|
|
456
461
|
...wallets,
|
|
457
462
|
...(userDid ? { userDid } : {}),
|
|
458
463
|
...(primaryEvmWallet ? { primaryEvmWallet } : {}),
|
|
459
|
-
|
|
464
|
+
executionWallets,
|
|
460
465
|
};
|
|
461
466
|
const runMode = normalizeRunMode(flags.runMode || current.runMode || process.env.THIRDFY_RUN_MODE || 'agent_wallet');
|
|
462
467
|
const custodyMode = normalizeCustodyMode(flags.custodyMode || current.custodyMode || process.env.THIRDFY_CUSTODY_MODE, runMode);
|
|
@@ -491,7 +496,27 @@ async function commandLoginEmail(ctx, flags) {
|
|
|
491
496
|
...(agentKey ? { agentKey } : {}),
|
|
492
497
|
};
|
|
493
498
|
}
|
|
499
|
+
// Persist first so operators keep session tokens even when wallet provisioning failed.
|
|
494
500
|
persistProfileConfig(next);
|
|
501
|
+
|
|
502
|
+
const executionWalletAddresses = Object.values(executionWallets)
|
|
503
|
+
.map((row) => (row && typeof row === 'object' ? String(row.walletAddress || '').trim() : ''))
|
|
504
|
+
.filter((addr) => /^0x[a-fA-F0-9]{40}$/.test(addr));
|
|
505
|
+
const hasExecutionWallet = executionWalletAddresses.length > 0;
|
|
506
|
+
if (runMode === 'agent_wallet' && (executionWalletsError || !hasExecutionWallet)) {
|
|
507
|
+
const fundHint = executionWalletAddresses[0] || 'the managed execution wallet address for each chain';
|
|
508
|
+
throw createCliError(
|
|
509
|
+
'LOGIN_EMAIL_INCOMPLETE',
|
|
510
|
+
executionWalletsError
|
|
511
|
+
? `Email login completed but execution wallets failed: ${executionWalletsError}. Fund the managed execution wallet (${fundHint}), not the owner embedded login wallet.`
|
|
512
|
+
: `Email login completed without execution wallets. Fund the managed execution wallet for agent_wallet trading (${fundHint}), not the owner embedded login wallet. Re-run login email or call /api/v1/agent/onboarding/cli/email/complete and confirm executionWallets is returned.`,
|
|
513
|
+
{
|
|
514
|
+
executionWallets,
|
|
515
|
+
executionWalletsError: executionWalletsError || null,
|
|
516
|
+
}
|
|
517
|
+
);
|
|
518
|
+
}
|
|
519
|
+
|
|
495
520
|
printEnvelope({
|
|
496
521
|
success: true,
|
|
497
522
|
code: 'LOGIN_EMAIL_OK',
|
|
@@ -504,13 +529,17 @@ async function commandLoginEmail(ctx, flags) {
|
|
|
504
529
|
evmWallets: Array.isArray(persistedWallets.evm) ? persistedWallets.evm : [],
|
|
505
530
|
solanaWallets: Array.isArray(persistedWallets.solana) ? persistedWallets.solana : [],
|
|
506
531
|
primaryEvmWallet: persistedWallets.primaryEvmWallet || null,
|
|
507
|
-
executionWallets
|
|
508
|
-
|
|
509
|
-
? persistedWallets.executionWallets
|
|
510
|
-
: {},
|
|
532
|
+
executionWallets,
|
|
533
|
+
executionWalletsError: executionWalletsError || null,
|
|
511
534
|
ownerLinkedWallet: data.ownerLinkedWallet || null,
|
|
512
535
|
configPath: getProfileConfigPath(),
|
|
513
536
|
nextSteps: response?.nextSteps || [],
|
|
537
|
+
...(runMode === 'agent_wallet'
|
|
538
|
+
? {
|
|
539
|
+
fundingReminder:
|
|
540
|
+
'Fund executionWallets addresses for agent_wallet. Do not fund the owner embedded wallet for Path A trading.',
|
|
541
|
+
}
|
|
542
|
+
: {}),
|
|
514
543
|
},
|
|
515
544
|
meta: { apiBase: ctx.apiBase },
|
|
516
545
|
});
|
|
@@ -76,9 +76,10 @@ export function computeActionSchemaDigest(actions) {
|
|
|
76
76
|
}
|
|
77
77
|
|
|
78
78
|
function mergeCatalogWithFixtures(liveActions) {
|
|
79
|
+
// Key by normalized action id so catalog kebab-case overwrites fixture snake_case.
|
|
79
80
|
const byAction = new Map();
|
|
80
81
|
for (const fixture of VENUE_WRITE_ACTION_FIXTURES) {
|
|
81
|
-
byAction.set(fixture.action, {
|
|
82
|
+
byAction.set(normalizeActionKey(fixture.action), {
|
|
82
83
|
action: fixture.action,
|
|
83
84
|
aliases: fixture.aliases,
|
|
84
85
|
provider: fixture.provider,
|
|
@@ -90,10 +91,12 @@ function mergeCatalogWithFixtures(liveActions) {
|
|
|
90
91
|
for (const row of liveActions || []) {
|
|
91
92
|
const action = String(row?.action || '').trim();
|
|
92
93
|
if (!action) continue;
|
|
93
|
-
const
|
|
94
|
-
byAction.
|
|
94
|
+
const key = normalizeActionKey(action);
|
|
95
|
+
const existing = byAction.get(key);
|
|
96
|
+
const aliases = Array.isArray(row.aliases) ? row.aliases : existing?.aliases || [];
|
|
97
|
+
byAction.set(key, {
|
|
95
98
|
action,
|
|
96
|
-
aliases
|
|
99
|
+
aliases,
|
|
97
100
|
provider: row.provider || row.providerId || existing?.provider || null,
|
|
98
101
|
paramsSchema: row.paramsSchema || existing?.paramsSchema || null,
|
|
99
102
|
requiredParams: Array.isArray(row.requiredParams) ? row.requiredParams : existing?.requiredParams || [],
|
|
@@ -104,9 +107,9 @@ function mergeCatalogWithFixtures(liveActions) {
|
|
|
104
107
|
}
|
|
105
108
|
|
|
106
109
|
function resolveCatalogRow(catalogByAction, action) {
|
|
107
|
-
const direct = catalogByAction.get(action);
|
|
108
|
-
if (direct) return direct;
|
|
109
110
|
const normalized = normalizeActionKey(action);
|
|
111
|
+
const direct = catalogByAction.get(normalized) || catalogByAction.get(action);
|
|
112
|
+
if (direct) return direct;
|
|
110
113
|
for (const row of catalogByAction.values()) {
|
|
111
114
|
if (normalizeActionKey(row.action) === normalized) return row;
|
|
112
115
|
for (const alias of row.aliases || []) {
|
|
@@ -117,9 +120,11 @@ function resolveCatalogRow(catalogByAction, action) {
|
|
|
117
120
|
}
|
|
118
121
|
|
|
119
122
|
function runSchemaCompatibilityChecks(catalogByAction, actionsToCheck, { requireLiveCatalog = false } = {}) {
|
|
120
|
-
const fixturesByAction = new Map(
|
|
123
|
+
const fixturesByAction = new Map(
|
|
124
|
+
VENUE_WRITE_ACTION_FIXTURES.map((fixture) => [normalizeActionKey(fixture.action), fixture]),
|
|
125
|
+
);
|
|
121
126
|
return actionsToCheck.map((actionName) => {
|
|
122
|
-
const fixture = fixturesByAction.get(actionName) || null;
|
|
127
|
+
const fixture = fixturesByAction.get(normalizeActionKey(actionName)) || null;
|
|
123
128
|
const catalogRow = resolveCatalogRow(catalogByAction, actionName);
|
|
124
129
|
const liveSchema = catalogRow?.source === 'catalog' ? catalogRow.paramsSchema : null;
|
|
125
130
|
const schema = requireLiveCatalog ? liveSchema : catalogRow?.paramsSchema || fixture?.paramsSchema || null;
|