@thirdfy/agent-cli 0.1.18 → 0.1.19
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/README.md +17 -1
- package/bin/thirdfy-agent.mjs +366 -40
- package/package.json +1 -1
- package/src/utils/cli/intentNormalization.cjs +1 -0
package/README.md
CHANGED
|
@@ -271,6 +271,7 @@ Provider and venue guides are kept outside `README` so this page stays stable as
|
|
|
271
271
|
|
|
272
272
|
- Discovery: `catalogs list`, `actions`
|
|
273
273
|
- Execution: `preflight`, `run`, `intent-status`, `jeff preflight`, `jeff trade`, `jeff status`
|
|
274
|
+
- Managed signer execution: `wallet execute`, `wallet sign`, `wallet submit`, `agent run`
|
|
274
275
|
- Profiles: `profile init`, `profile use`, `profile show`, `whoami`
|
|
275
276
|
- 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`
|
|
276
277
|
- Governance readiness: `delegation create`, `delegation activate`, `delegation status`, `credentials upsert`, `credentials status`
|
|
@@ -369,9 +370,24 @@ thirdfy-agent managed wallet init --auth-token "$THIRDFY_AUTH_TOKEN" --json
|
|
|
369
370
|
# or: --owner-session-token "$THIRDFY_OWNER_SESSION_TOKEN"
|
|
370
371
|
thirdfy-agent managed wallet grant --auth-token "$THIRDFY_AUTH_TOKEN" --agent-key "0xYOUR_AGENT_KEY" --token-address "0x..." --max-usd-per-day 250 --json
|
|
371
372
|
# or: --owner-session-token "$THIRDFY_OWNER_SESSION_TOKEN"
|
|
372
|
-
thirdfy-agent
|
|
373
|
+
thirdfy-agent agent run --agent-api-key "$AGENT_API_KEY" --signer-method fanout_intent --action swap --params '{"tokenIn":"0x...","tokenOut":"0x...","amountInRaw":"1000000"}' --json
|
|
373
374
|
```
|
|
374
375
|
|
|
376
|
+
### Signer command model (managed + BYOW)
|
|
377
|
+
|
|
378
|
+
The CLI now supports the same signer primitives used by the MCP:
|
|
379
|
+
|
|
380
|
+
- `wallet execute` -> direct managed self-wallet execution (`/api/v1/agent/execute`)
|
|
381
|
+
- `wallet sign` -> unsigned tx packaging for BYOW signing flow
|
|
382
|
+
- `wallet submit` -> signed tx broadcast
|
|
383
|
+
- `agent run` -> signer router (`managed_wallet_server`, `fanout_intent`, `byow`)
|
|
384
|
+
|
|
385
|
+
Managed-self execution performs strict wallet preflight and returns deterministic blockers:
|
|
386
|
+
|
|
387
|
+
- `blockedReason`
|
|
388
|
+
- `blockedStage`
|
|
389
|
+
- `executionWalletAddress`
|
|
390
|
+
|
|
375
391
|
## More docs
|
|
376
392
|
|
|
377
393
|
- OpenClaw CLI onboarding flow:
|
package/bin/thirdfy-agent.mjs
CHANGED
|
@@ -155,8 +155,8 @@ async function main() {
|
|
|
155
155
|
const capabilities = await negotiateCapabilities(context);
|
|
156
156
|
|
|
157
157
|
switch (commandKey) {
|
|
158
|
-
case '
|
|
159
|
-
await
|
|
158
|
+
case 'agent run':
|
|
159
|
+
await commandAgentRun(context, subFlags, capabilities);
|
|
160
160
|
return;
|
|
161
161
|
case 'managed setup':
|
|
162
162
|
await commandManagedSetup(context, subFlags, capabilities);
|
|
@@ -253,6 +253,18 @@ async function main() {
|
|
|
253
253
|
await commandDelegationCustodialGrant(context, subFlags, capabilities);
|
|
254
254
|
return;
|
|
255
255
|
}
|
|
256
|
+
if (commandKey3 === 'wallet execute') {
|
|
257
|
+
await commandWalletExecute(context, subFlags, capabilities);
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
if (commandKey3 === 'wallet sign') {
|
|
261
|
+
await commandWalletSign(context, subFlags, capabilities);
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
if (commandKey3 === 'wallet submit') {
|
|
265
|
+
await commandWalletSubmit(context, subFlags, capabilities);
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
256
268
|
|
|
257
269
|
const rootCommand = parsed.positionals[0];
|
|
258
270
|
switch (rootCommand) {
|
|
@@ -640,6 +652,129 @@ async function commandRun(ctx, flags, capabilities) {
|
|
|
640
652
|
if (!normalized.success) process.exit(1);
|
|
641
653
|
}
|
|
642
654
|
|
|
655
|
+
async function commandAgentRun(ctx, flags, capabilities) {
|
|
656
|
+
const signerMethod = String(flags.signerMethod || '').trim().toLowerCase();
|
|
657
|
+
const strategyIntent = String(flags.strategyIntent || '').trim().toLowerCase();
|
|
658
|
+
const requestedRunMode = String(flags.runMode || '').trim().toLowerCase();
|
|
659
|
+
if (signerMethod && !['managed_wallet_server', 'byow', 'fanout_intent'].includes(signerMethod)) {
|
|
660
|
+
throw createCliError(
|
|
661
|
+
'SIGNER_METHOD_INVALID',
|
|
662
|
+
`Unsupported --signer-method "${signerMethod}". Use managed_wallet_server, fanout_intent, or byow.`
|
|
663
|
+
);
|
|
664
|
+
}
|
|
665
|
+
if (signerMethod === 'byow' || requestedRunMode === 'self') {
|
|
666
|
+
await commandWalletSign(ctx, { ...flags, runMode: 'self' }, capabilities);
|
|
667
|
+
return;
|
|
668
|
+
}
|
|
669
|
+
if (signerMethod === 'fanout_intent') {
|
|
670
|
+
await commandRun(ctx, { ...flags, runMode: 'thirdfy' }, capabilities);
|
|
671
|
+
return;
|
|
672
|
+
}
|
|
673
|
+
if (signerMethod === 'managed_wallet_server') {
|
|
674
|
+
await commandWalletExecute(ctx, { ...flags, runMode: 'agent_wallet' }, capabilities);
|
|
675
|
+
return;
|
|
676
|
+
}
|
|
677
|
+
if (requestedRunMode) {
|
|
678
|
+
const runMode = normalizeRunMode(requestedRunMode);
|
|
679
|
+
if (runMode === 'self') {
|
|
680
|
+
await commandWalletSign(ctx, { ...flags, runMode }, capabilities);
|
|
681
|
+
return;
|
|
682
|
+
}
|
|
683
|
+
if (runMode === 'thirdfy' || runMode === 'hybrid') {
|
|
684
|
+
await commandRun(ctx, { ...flags, runMode }, capabilities);
|
|
685
|
+
return;
|
|
686
|
+
}
|
|
687
|
+
await commandWalletExecute(ctx, { ...flags, runMode: 'agent_wallet' }, capabilities);
|
|
688
|
+
return;
|
|
689
|
+
}
|
|
690
|
+
if (strategyIntent === 'fanout') {
|
|
691
|
+
await commandRun(ctx, { ...flags, runMode: 'thirdfy' }, capabilities);
|
|
692
|
+
return;
|
|
693
|
+
}
|
|
694
|
+
await commandWalletExecute(ctx, { ...flags, runMode: 'agent_wallet' }, capabilities);
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
async function commandWalletExecute(ctx, flags, capabilities) {
|
|
698
|
+
const normalizedFlags = { ...flags, runMode: 'agent_wallet' };
|
|
699
|
+
const resolved = await resolveActionSelection(ctx, normalizedFlags);
|
|
700
|
+
prepareActionParamsForFlags(normalizedFlags, resolved.resolvedAction);
|
|
701
|
+
enforceChainCompatibility(capabilities, normalizedFlags, 'agent_wallet');
|
|
702
|
+
const skipPreflight = Boolean(normalizedFlags.skipPreflight);
|
|
703
|
+
const result = await executeManagedWalletRun(ctx, normalizedFlags, resolved, { skipPreflight });
|
|
704
|
+
printEnvelope({
|
|
705
|
+
success: result.success,
|
|
706
|
+
code: result.success ? 'WALLET_EXECUTED' : result.preflightBlocked ? 'PREFLIGHT_BLOCKED' : 'RUN_FAILED',
|
|
707
|
+
message: result.success ? 'Managed wallet execution completed' : result.error || 'Managed wallet execution failed',
|
|
708
|
+
data: result,
|
|
709
|
+
meta: {
|
|
710
|
+
apiBase: ctx.apiBase,
|
|
711
|
+
requestedAction: resolved.requestedAction,
|
|
712
|
+
resolvedAction: resolved.resolvedAction,
|
|
713
|
+
runMode: 'agent_wallet',
|
|
714
|
+
idempotencyKey: result.idempotencyKey || null,
|
|
715
|
+
executionWalletAddress: result.executionWalletAddress || null,
|
|
716
|
+
profile: ctx.profile || null,
|
|
717
|
+
capabilitiesVersion: capabilities?.contractVersion || null,
|
|
718
|
+
},
|
|
719
|
+
});
|
|
720
|
+
if (!result.success) process.exit(1);
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
async function commandWalletSign(ctx, flags, capabilities) {
|
|
724
|
+
const normalizedFlags = { ...flags, runMode: 'self' };
|
|
725
|
+
const resolved = await resolveActionSelection(ctx, normalizedFlags);
|
|
726
|
+
prepareActionParamsForFlags(normalizedFlags, resolved.resolvedAction);
|
|
727
|
+
enforceChainCompatibility(capabilities, normalizedFlags, 'self');
|
|
728
|
+
const selfResult = await executeSelfRun(ctx, normalizedFlags, resolved, {
|
|
729
|
+
skipPreflight: Boolean(normalizedFlags.skipPreflight),
|
|
730
|
+
runMode: 'self',
|
|
731
|
+
});
|
|
732
|
+
printEnvelope({
|
|
733
|
+
success: selfResult.success,
|
|
734
|
+
code: selfResult.success ? 'WALLET_SIGN_READY' : selfResult.preflightBlocked ? 'PREFLIGHT_BLOCKED' : 'WALLET_SIGN_FAILED',
|
|
735
|
+
message: selfResult.success ? 'Unsigned transaction prepared for BYOW signing' : selfResult.error || 'wallet sign failed',
|
|
736
|
+
data: selfResult,
|
|
737
|
+
meta: {
|
|
738
|
+
apiBase: ctx.apiBase,
|
|
739
|
+
requestedAction: resolved.requestedAction,
|
|
740
|
+
resolvedAction: resolved.resolvedAction,
|
|
741
|
+
runMode: 'self',
|
|
742
|
+
profile: ctx.profile || null,
|
|
743
|
+
capabilitiesVersion: capabilities?.contractVersion || null,
|
|
744
|
+
},
|
|
745
|
+
});
|
|
746
|
+
if (!selfResult.success) process.exit(1);
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
async function commandWalletSubmit(ctx, flags, capabilities) {
|
|
750
|
+
const signedTxHex = String(flags.signedTxHex || flags.signedTx || '').trim();
|
|
751
|
+
if (!signedTxHex) {
|
|
752
|
+
throw createCliError('MISSING_SIGNED_TX', 'wallet submit requires --signed-tx-hex');
|
|
753
|
+
}
|
|
754
|
+
const chainId = Number(flags.chainId || 8453);
|
|
755
|
+
const rpcUrl = String(
|
|
756
|
+
flags.rpcUrl || process.env.BASE_RPC_URL || process.env.BASE_RPC_HTTP_URL || process.env.RPC_URL || 'https://mainnet.base.org'
|
|
757
|
+
).trim();
|
|
758
|
+
const txHash = await submitSignedTx({ rpcUrl, chainId, signedTxHex });
|
|
759
|
+
printEnvelope({
|
|
760
|
+
success: true,
|
|
761
|
+
code: 'WALLET_SUBMITTED',
|
|
762
|
+
message: 'Signed transaction broadcasted',
|
|
763
|
+
data: {
|
|
764
|
+
txHash,
|
|
765
|
+
chainId,
|
|
766
|
+
signerMethod: 'byow',
|
|
767
|
+
executionMode: 'byow_submit',
|
|
768
|
+
},
|
|
769
|
+
meta: {
|
|
770
|
+
apiBase: ctx.apiBase,
|
|
771
|
+
rpcUrl,
|
|
772
|
+
profile: ctx.profile || null,
|
|
773
|
+
capabilitiesVersion: capabilities?.contractVersion || null,
|
|
774
|
+
},
|
|
775
|
+
});
|
|
776
|
+
}
|
|
777
|
+
|
|
643
778
|
async function commandIntentStatus(ctx, flags, capabilities) {
|
|
644
779
|
const intentId = requireFlag(flags, 'intentId', 'Missing --intent-id');
|
|
645
780
|
const response = await apiGet(ctx, `/api/v1/agent/execute-intent/status?intentId=${encodeURIComponent(intentId)}`);
|
|
@@ -1565,41 +1700,6 @@ async function commandDoctorSelf(ctx, flags) {
|
|
|
1565
1700
|
if (!ok) process.exit(1);
|
|
1566
1701
|
}
|
|
1567
1702
|
|
|
1568
|
-
async function commandManagedRun(ctx, flags, capabilities) {
|
|
1569
|
-
const mode = normalizeManagedRunMode(flags.runMode || 'thirdfy');
|
|
1570
|
-
const managedFlags = { ...flags, runMode: mode };
|
|
1571
|
-
const resolved = await resolveActionSelection(ctx, managedFlags);
|
|
1572
|
-
prepareActionParamsForFlags(managedFlags, resolved.resolvedAction);
|
|
1573
|
-
enforceChainCompatibility(capabilities, managedFlags, mode);
|
|
1574
|
-
const skipPreflight = Boolean(managedFlags.skipPreflight);
|
|
1575
|
-
const backendResult = await runExecutionByMode(mode, ctx, managedFlags, resolved, {
|
|
1576
|
-
skipPreflight,
|
|
1577
|
-
});
|
|
1578
|
-
const normalized = backendResult.normalized;
|
|
1579
|
-
printEnvelope({
|
|
1580
|
-
success: normalized.success,
|
|
1581
|
-
code: normalized.success ? 'MANAGED_RUN_QUEUED' : backendResult.code,
|
|
1582
|
-
message: normalized.success
|
|
1583
|
-
? mode === 'hybrid'
|
|
1584
|
-
? 'Managed easy-wallet hybrid execution prepared'
|
|
1585
|
-
: 'Managed easy-wallet execution queued'
|
|
1586
|
-
: backendResult.message,
|
|
1587
|
-
data: normalized,
|
|
1588
|
-
meta: {
|
|
1589
|
-
apiBase: ctx.apiBase,
|
|
1590
|
-
requestedAction: resolved.requestedAction,
|
|
1591
|
-
resolvedAction: resolved.resolvedAction,
|
|
1592
|
-
lane: 'managed_easy_wallet',
|
|
1593
|
-
runMode: mode,
|
|
1594
|
-
skippedPreflight: skipPreflight,
|
|
1595
|
-
profile: ctx.profile || null,
|
|
1596
|
-
swapInput: managedFlags.__swapInput || null,
|
|
1597
|
-
capabilitiesVersion: capabilities?.contractVersion || null,
|
|
1598
|
-
},
|
|
1599
|
-
});
|
|
1600
|
-
if (!normalized.success) process.exit(1);
|
|
1601
|
-
}
|
|
1602
|
-
|
|
1603
1703
|
async function commandManagedSetup(ctx, flags, capabilities) {
|
|
1604
1704
|
const mode = normalizeManagedRunMode(flags.runMode || 'agent_wallet');
|
|
1605
1705
|
const authHeaders = buildOwnerAuthHeaders(
|
|
@@ -2189,6 +2289,34 @@ function shouldUseBuildTxPreflight(runMode, resolved) {
|
|
|
2189
2289
|
|
|
2190
2290
|
async function runPreflightByMode(runMode, ctx, flags, resolved, options = {}) {
|
|
2191
2291
|
const resolvedAction = resolved.resolvedAction;
|
|
2292
|
+
if (runMode === 'agent_wallet') {
|
|
2293
|
+
const preflight = await resolveManagedExecutionPreflight(ctx, flags, resolved, {
|
|
2294
|
+
runMode,
|
|
2295
|
+
requireFundingCheck: true,
|
|
2296
|
+
});
|
|
2297
|
+
const normalized = normalizeIntentResponse({
|
|
2298
|
+
success: preflight.success,
|
|
2299
|
+
status: preflight.success ? 'ready' : 'failed',
|
|
2300
|
+
mode: 'agent_wallet',
|
|
2301
|
+
executionWalletAddress: preflight.executionWalletAddress || null,
|
|
2302
|
+
signerMethod: preflight.signerMethod || 'managed_wallet_server',
|
|
2303
|
+
blockedReason: preflight.blockedReason || null,
|
|
2304
|
+
blockedStage: preflight.blockedStage || null,
|
|
2305
|
+
error: preflight.success ? null : preflight.error || 'Managed wallet preflight failed',
|
|
2306
|
+
preflightBlocked: !preflight.success,
|
|
2307
|
+
fundingTokenBalance: preflight.fundingTokenBalance || null,
|
|
2308
|
+
executionWalletPreflight: preflight,
|
|
2309
|
+
});
|
|
2310
|
+
normalized.executionWalletAddress = preflight.executionWalletAddress || null;
|
|
2311
|
+
normalized.signerMethod = preflight.signerMethod || 'managed_wallet_server';
|
|
2312
|
+
normalized.executionWalletPreflight = preflight;
|
|
2313
|
+
normalized.fundingTokenBalance = preflight.fundingTokenBalance || null;
|
|
2314
|
+
return {
|
|
2315
|
+
message: preflight.success ? 'Managed wallet preflight completed' : 'Managed wallet preflight failed',
|
|
2316
|
+
route: 'execution_wallet_preflight',
|
|
2317
|
+
normalized,
|
|
2318
|
+
};
|
|
2319
|
+
}
|
|
2192
2320
|
if (runMode === 'self') {
|
|
2193
2321
|
const route = shouldUseBuildTxPreflight(runMode, resolved) ? 'build_tx' : 'execute_intent_validation';
|
|
2194
2322
|
const normalized = await executeSelfRun(
|
|
@@ -2269,7 +2397,7 @@ async function runPreflightByMode(runMode, ctx, flags, resolved, options = {}) {
|
|
|
2269
2397
|
}
|
|
2270
2398
|
|
|
2271
2399
|
async function runExecutionByMode(runMode, ctx, flags, resolved, options) {
|
|
2272
|
-
if (runMode === 'thirdfy'
|
|
2400
|
+
if (runMode === 'thirdfy') {
|
|
2273
2401
|
const normalized = await executeThirdfyRun(ctx, flags, resolved, options, runMode);
|
|
2274
2402
|
const preflightBlocked = !normalized.success && Boolean(normalized.preflightBlocked);
|
|
2275
2403
|
return {
|
|
@@ -2283,6 +2411,20 @@ async function runExecutionByMode(runMode, ctx, flags, resolved, options) {
|
|
|
2283
2411
|
normalized,
|
|
2284
2412
|
};
|
|
2285
2413
|
}
|
|
2414
|
+
if (runMode === 'agent_wallet') {
|
|
2415
|
+
const normalized = await executeManagedWalletRun(ctx, flags, resolved, options);
|
|
2416
|
+
const preflightBlocked = !normalized.success && Boolean(normalized.preflightBlocked);
|
|
2417
|
+
return {
|
|
2418
|
+
code: normalized.success ? 'WALLET_EXECUTED' : preflightBlocked ? 'PREFLIGHT_BLOCKED' : 'RUN_FAILED',
|
|
2419
|
+
message: normalized.success
|
|
2420
|
+
? 'Managed wallet execution completed'
|
|
2421
|
+
: preflightBlocked
|
|
2422
|
+
? 'Managed wallet preflight blocked execution'
|
|
2423
|
+
: 'Managed wallet execution failed',
|
|
2424
|
+
idempotencyKey: normalized.idempotencyKey || null,
|
|
2425
|
+
normalized,
|
|
2426
|
+
};
|
|
2427
|
+
}
|
|
2286
2428
|
if (runMode === 'self') {
|
|
2287
2429
|
const normalized = await executeSelfRun(ctx, flags, resolved, options);
|
|
2288
2430
|
const preflightBlocked = !normalized.success && Boolean(normalized.preflightBlocked);
|
|
@@ -2355,6 +2497,61 @@ async function executeThirdfyRun(ctx, flags, resolved, options, runMode = 'third
|
|
|
2355
2497
|
return normalized;
|
|
2356
2498
|
}
|
|
2357
2499
|
|
|
2500
|
+
async function executeManagedWalletRun(ctx, flags, resolved, options) {
|
|
2501
|
+
const skipPreflight = Boolean(options?.skipPreflight);
|
|
2502
|
+
let preflight = null;
|
|
2503
|
+
if (!skipPreflight) {
|
|
2504
|
+
preflight = await resolveManagedExecutionPreflight(ctx, flags, resolved, {
|
|
2505
|
+
runMode: 'agent_wallet',
|
|
2506
|
+
requireFundingCheck: true,
|
|
2507
|
+
});
|
|
2508
|
+
if (!preflight.success) {
|
|
2509
|
+
const normalized = normalizeIntentResponse({
|
|
2510
|
+
success: false,
|
|
2511
|
+
status: 'failed',
|
|
2512
|
+
mode: 'agent_wallet',
|
|
2513
|
+
blockedReason: preflight.blockedReason || 'PRECHECK_FAILED',
|
|
2514
|
+
blockedStage: preflight.blockedStage || 'routing',
|
|
2515
|
+
error: preflight.error || 'Managed wallet preflight failed',
|
|
2516
|
+
preflightBlocked: true,
|
|
2517
|
+
executionWalletAddress: preflight.executionWalletAddress || null,
|
|
2518
|
+
executionWalletPreflight: preflight,
|
|
2519
|
+
});
|
|
2520
|
+
normalized.executionWalletAddress = preflight.executionWalletAddress || null;
|
|
2521
|
+
normalized.executionWalletPreflight = preflight;
|
|
2522
|
+
return normalized;
|
|
2523
|
+
}
|
|
2524
|
+
}
|
|
2525
|
+
const payload = buildManagedExecutePayload(flags, {
|
|
2526
|
+
resolvedAction: resolved.resolvedAction,
|
|
2527
|
+
runMode: 'agent_wallet',
|
|
2528
|
+
forceIdempotency: true,
|
|
2529
|
+
});
|
|
2530
|
+
const response = await apiPost(ctx, '/api/v1/agent/execute', payload);
|
|
2531
|
+
const normalized = normalizeIntentResponse({
|
|
2532
|
+
success: Boolean(response?.success),
|
|
2533
|
+
status: response?.success ? 'completed' : 'failed',
|
|
2534
|
+
mode: 'agent_wallet',
|
|
2535
|
+
txHash: response?.txHash || null,
|
|
2536
|
+
blockedReason: response?.blockedReason || response?.data?.blockedReason || null,
|
|
2537
|
+
blockedStage: response?.blockedStage || response?.data?.blockedStage || null,
|
|
2538
|
+
error: response?.error || null,
|
|
2539
|
+
executionWalletAddress: response?.executionWalletAddress || response?.data?.executionWalletAddress || null,
|
|
2540
|
+
signerMethod: 'managed_wallet_server',
|
|
2541
|
+
idempotencyKey: payload.executionIdempotencyKey || null,
|
|
2542
|
+
executionWalletPreflight: preflight,
|
|
2543
|
+
amountNormalization: response?.amountNormalization || response?.data?.amountNormalization || null,
|
|
2544
|
+
raw: response,
|
|
2545
|
+
});
|
|
2546
|
+
normalized.executionWalletAddress = response?.executionWalletAddress || response?.data?.executionWalletAddress || null;
|
|
2547
|
+
normalized.signerMethod = 'managed_wallet_server';
|
|
2548
|
+
normalized.executionWalletPreflight = preflight;
|
|
2549
|
+
normalized.amountNormalization = response?.amountNormalization || response?.data?.amountNormalization || null;
|
|
2550
|
+
normalized.raw = response;
|
|
2551
|
+
normalized.idempotencyKey = payload.executionIdempotencyKey || null;
|
|
2552
|
+
return normalized;
|
|
2553
|
+
}
|
|
2554
|
+
|
|
2358
2555
|
async function executeSelfRun(ctx, flags, resolved, options) {
|
|
2359
2556
|
const skipPreflight = Boolean(options?.skipPreflight);
|
|
2360
2557
|
const blockedReasonCode = String(options?.blockedReasonCode || 'SELF_MODE_REQUIRES_BUILD_TX');
|
|
@@ -2401,7 +2598,7 @@ async function executeSelfRun(ctx, flags, resolved, options) {
|
|
|
2401
2598
|
async function executeHybridRun(ctx, flags, resolved, options) {
|
|
2402
2599
|
const hybridWalletMode = normalizeHybridWalletMode(options?.hybridWalletMode || flags.hybridWalletMode || 'self');
|
|
2403
2600
|
if (hybridWalletMode === 'agent_wallet') {
|
|
2404
|
-
const managedResult = await
|
|
2601
|
+
const managedResult = await executeManagedWalletRun(ctx, { ...flags, runMode: 'agent_wallet' }, resolved, options);
|
|
2405
2602
|
if (!managedResult.success) {
|
|
2406
2603
|
return normalizeIntentResponse({
|
|
2407
2604
|
...managedResult,
|
|
@@ -2468,6 +2665,122 @@ function buildBuildTxPayload(flags, resolvedAction) {
|
|
|
2468
2665
|
return payload;
|
|
2469
2666
|
}
|
|
2470
2667
|
|
|
2668
|
+
function buildManagedExecutePayload(flags, options) {
|
|
2669
|
+
const runMode = String(options.runMode || flags.runMode || 'agent_wallet').trim().toLowerCase();
|
|
2670
|
+
const payload = {
|
|
2671
|
+
agentApiKey: resolveAgentApiKey(flags, runMode),
|
|
2672
|
+
action: String(options.resolvedAction || requireFlag(flags, 'action', 'Missing --action')).trim(),
|
|
2673
|
+
userDid: String(flags.userDid || process.env.THIRDFY_USER_DID || '').trim(),
|
|
2674
|
+
params: getPreparedParams(flags),
|
|
2675
|
+
chainId: Number(flags.chainId || 8453),
|
|
2676
|
+
executionStrategy: {
|
|
2677
|
+
runMode,
|
|
2678
|
+
mirrorOnly: false,
|
|
2679
|
+
},
|
|
2680
|
+
};
|
|
2681
|
+
if (!payload.userDid) {
|
|
2682
|
+
throw createCliError(
|
|
2683
|
+
'MISSING_USER_DID',
|
|
2684
|
+
'Managed wallet execution requires --user-did (or THIRDFY_USER_DID).'
|
|
2685
|
+
);
|
|
2686
|
+
}
|
|
2687
|
+
if (flags.estimatedAmountUsd) payload.estimatedAmountUsd = Number(flags.estimatedAmountUsd);
|
|
2688
|
+
if (options.forceIdempotency) {
|
|
2689
|
+
payload.executionIdempotencyKey = String(
|
|
2690
|
+
flags.idempotencyKey || `cli:walletExecute:${payload.action}:${Date.now()}:${randomUUID()}`
|
|
2691
|
+
);
|
|
2692
|
+
} else if (flags.idempotencyKey) {
|
|
2693
|
+
payload.executionIdempotencyKey = String(flags.idempotencyKey);
|
|
2694
|
+
}
|
|
2695
|
+
return payload;
|
|
2696
|
+
}
|
|
2697
|
+
|
|
2698
|
+
function resolveTokenInForFunding(params) {
|
|
2699
|
+
if (!params || typeof params !== 'object') return '';
|
|
2700
|
+
const tokenIn = String(params.tokenIn || params.tokenAddress || params.token || '').trim();
|
|
2701
|
+
return tokenIn;
|
|
2702
|
+
}
|
|
2703
|
+
|
|
2704
|
+
async function resolveManagedExecutionPreflight(ctx, flags, resolved, options = {}) {
|
|
2705
|
+
const chainId = Number(flags.chainId || 8453);
|
|
2706
|
+
const runMode = String(options.runMode || 'agent_wallet').trim().toLowerCase();
|
|
2707
|
+
const agentApiKey = resolveAgentApiKey(flags, runMode);
|
|
2708
|
+
const userDid = String(flags.userDid || process.env.THIRDFY_USER_DID || '').trim();
|
|
2709
|
+
if (!userDid) {
|
|
2710
|
+
return {
|
|
2711
|
+
success: false,
|
|
2712
|
+
blockedReason: 'MISSING_USER_DID',
|
|
2713
|
+
blockedStage: 'routing',
|
|
2714
|
+
error: 'Managed wallet execution requires --user-did (or THIRDFY_USER_DID).',
|
|
2715
|
+
signerMethod: 'managed_wallet_server',
|
|
2716
|
+
};
|
|
2717
|
+
}
|
|
2718
|
+
const tokenIn = resolveTokenInForFunding(getPreparedParams(flags));
|
|
2719
|
+
const payload = {
|
|
2720
|
+
agentApiKey,
|
|
2721
|
+
userDid,
|
|
2722
|
+
chainId,
|
|
2723
|
+
runMode,
|
|
2724
|
+
...(tokenIn ? { tokenIn } : {}),
|
|
2725
|
+
};
|
|
2726
|
+
const response = await apiPost(ctx, '/api/v1/agent/execution-wallet', payload);
|
|
2727
|
+
const executionWalletAddress = String(response?.executionWalletAddress || '').trim().toLowerCase();
|
|
2728
|
+
const signerMethod = String(response?.signerMethod || 'managed_wallet_server').trim();
|
|
2729
|
+
const rawBalance = String(response?.fundingTokenBalance?.raw || '').trim();
|
|
2730
|
+
let parsedRawBalance = null;
|
|
2731
|
+
if (rawBalance) {
|
|
2732
|
+
try {
|
|
2733
|
+
parsedRawBalance = BigInt(rawBalance);
|
|
2734
|
+
} catch {
|
|
2735
|
+
return {
|
|
2736
|
+
success: false,
|
|
2737
|
+
blockedReason: 'INVALID_FUNDING_BALANCE',
|
|
2738
|
+
blockedStage: 'sizing',
|
|
2739
|
+
error: `Execution wallet ${executionWalletAddress || 'unknown'} returned invalid funding token balance.`,
|
|
2740
|
+
executionWalletAddress,
|
|
2741
|
+
signerMethod,
|
|
2742
|
+
fundingTokenBalance: response?.fundingTokenBalance || null,
|
|
2743
|
+
};
|
|
2744
|
+
}
|
|
2745
|
+
}
|
|
2746
|
+
const expectedWallet = String(flags.walletAddress || flags.executionWalletAddress || '').trim().toLowerCase();
|
|
2747
|
+
const allowWalletMismatch = parseBooleanFlag(flags.allowWalletMismatch, false);
|
|
2748
|
+
|
|
2749
|
+
if (expectedWallet && executionWalletAddress && expectedWallet !== executionWalletAddress && !allowWalletMismatch) {
|
|
2750
|
+
return {
|
|
2751
|
+
success: false,
|
|
2752
|
+
blockedReason: 'EXECUTION_WALLET_MISMATCH',
|
|
2753
|
+
blockedStage: 'routing',
|
|
2754
|
+
error: `Funded wallet (${expectedWallet}) does not match execution wallet (${executionWalletAddress}). Use --allow-wallet-mismatch to override.`,
|
|
2755
|
+
executionWalletAddress,
|
|
2756
|
+
signerMethod,
|
|
2757
|
+
fundingTokenBalance: response?.fundingTokenBalance || null,
|
|
2758
|
+
};
|
|
2759
|
+
}
|
|
2760
|
+
|
|
2761
|
+
if (options.requireFundingCheck && parsedRawBalance !== null && parsedRawBalance <= 0n) {
|
|
2762
|
+
return {
|
|
2763
|
+
success: false,
|
|
2764
|
+
blockedReason: 'INSUFFICIENT_FUNDS',
|
|
2765
|
+
blockedStage: 'sizing',
|
|
2766
|
+
error: `Execution wallet ${executionWalletAddress || 'unknown'} has insufficient tokenIn balance.`,
|
|
2767
|
+
executionWalletAddress,
|
|
2768
|
+
signerMethod,
|
|
2769
|
+
fundingTokenBalance: response?.fundingTokenBalance || null,
|
|
2770
|
+
};
|
|
2771
|
+
}
|
|
2772
|
+
|
|
2773
|
+
return {
|
|
2774
|
+
success: true,
|
|
2775
|
+
executionWalletAddress,
|
|
2776
|
+
signerMethod,
|
|
2777
|
+
fundingTokenBalance: response?.fundingTokenBalance || null,
|
|
2778
|
+
userDid,
|
|
2779
|
+
chainId,
|
|
2780
|
+
tokenIn: tokenIn || null,
|
|
2781
|
+
};
|
|
2782
|
+
}
|
|
2783
|
+
|
|
2471
2784
|
function buildIntentPayload(flags, options) {
|
|
2472
2785
|
const runMode = String(options.runMode || flags.runMode || '').trim().toLowerCase() || 'thirdfy';
|
|
2473
2786
|
const payload = {
|
|
@@ -2938,6 +3251,14 @@ async function signAndBroadcastWithOws({ txd, chainId, walletAddress, owsWalletN
|
|
|
2938
3251
|
return { sent };
|
|
2939
3252
|
}
|
|
2940
3253
|
|
|
3254
|
+
async function submitSignedTx({ rpcUrl, chainId, signedTxHex }) {
|
|
3255
|
+
const ethers = await loadEthers();
|
|
3256
|
+
const { JsonRpcProvider } = ethers;
|
|
3257
|
+
const provider = new JsonRpcProvider(rpcUrl, chainId);
|
|
3258
|
+
const sent = await provider.broadcastTransaction(String(signedTxHex));
|
|
3259
|
+
return sent?.hash || null;
|
|
3260
|
+
}
|
|
3261
|
+
|
|
2941
3262
|
async function readTokenBalanceRaw(rpcUrl, walletAddress, tokenAddress) {
|
|
2942
3263
|
const ethers = await loadEthers();
|
|
2943
3264
|
const { JsonRpcProvider, Interface } = ethers;
|
|
@@ -3170,7 +3491,10 @@ Core commands:
|
|
|
3170
3491
|
thirdfy-agent managed wallet init [--auth-token <token> | --owner-session-token <token>] [--chain-id <id>] [--json]
|
|
3171
3492
|
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]
|
|
3172
3493
|
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]
|
|
3173
|
-
thirdfy-agent
|
|
3494
|
+
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]
|
|
3495
|
+
thirdfy-agent wallet sign --agent-api-key <key> --action <id> --params <json> [--chain-id <id>] [--json]
|
|
3496
|
+
thirdfy-agent wallet submit --signed-tx-hex <hex> [--chain-id <id>] [--rpc-url <url>] [--json]
|
|
3497
|
+
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]
|
|
3174
3498
|
thirdfy-agent credentials upsert --auth-token <token> --venue <id> --api-key <key> --api-secret <secret> [--verify-permissions] [--skip-permissions-probe] [--bitfinex-base-url <url>] [--json]
|
|
3175
3499
|
thirdfy-agent credentials status --auth-token <token> [--venue <id>] [--json]
|
|
3176
3500
|
thirdfy-agent agent auth challenge --agent-key <key> [--wallet-address <addr>] [--chain-id <id>] [--json]
|
|
@@ -3205,6 +3529,8 @@ Global flags:
|
|
|
3205
3529
|
--timeout <ms> request timeout
|
|
3206
3530
|
--params <json> for swap: exactly one of {amountInRaw} or {amountInHuman + tokenInDecimals}
|
|
3207
3531
|
--broadcast with run --run-mode self, sign and broadcast using OWS
|
|
3532
|
+
--user-did <did> required for managed wallet server execution paths
|
|
3533
|
+
--allow-wallet-mismatch bypass strict funded-wallet vs execution-wallet guard
|
|
3208
3534
|
--effect-check <mode> self-exec check mode: strict | relaxed | off (default: relaxed)
|
|
3209
3535
|
--owner-session-token <token> owner session token (or THIRDFY_OWNER_SESSION_TOKEN)
|
|
3210
3536
|
--json stable machine-readable output
|
package/package.json
CHANGED
|
@@ -54,6 +54,7 @@ function normalizeIntentResponse(response) {
|
|
|
54
54
|
mode: response?.mode || null,
|
|
55
55
|
unsignedTx: response?.unsignedTx || null,
|
|
56
56
|
estimatedGas: response?.estimatedGas || null,
|
|
57
|
+
txHash: response?.txHash || null,
|
|
57
58
|
idempotencyKey: response?.idempotencyKey || null,
|
|
58
59
|
mirror: response?.mirror || null,
|
|
59
60
|
self: response?.self || null,
|