@thirdfy/agent-cli 0.1.22 → 0.1.23
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/bin/thirdfy-agent.mjs +232 -5
- 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.23] - 2026-05-02
|
|
8
|
+
|
|
9
|
+
**npm:** [`@thirdfy/agent-cli@0.1.23`](https://www.npmjs.com/package/@thirdfy/agent-cli/v/0.1.23)
|
|
10
|
+
**Git tag:** `v0.1.23`
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- Provider discovery hints for `earn`, `morpho`, `curve`, and generic `prediction` so agents can filter action catalogs by domain without hard-coded protocol-specific flows.
|
|
15
|
+
- Curve/Base 4pool write hints surfaced through the actions provider filter for earn flows.
|
|
16
|
+
|
|
17
|
+
### Fixed
|
|
18
|
+
|
|
19
|
+
- **Provider filter:** Case-insensitive matching for earn-related action keys when filtering catalogs by provider.
|
|
20
|
+
- **Self custody:** `preflight` and self-exec paths reject offchain venue order actions before `build-tx`, aligned with venue execution guardrails.
|
|
21
|
+
|
|
7
22
|
## [0.1.22] - 2026-04-28
|
|
8
23
|
|
|
9
24
|
**npm:** [`@thirdfy/agent-cli@0.1.22`](https://www.npmjs.com/package/@thirdfy/agent-cli/v/0.1.22)
|
package/bin/thirdfy-agent.mjs
CHANGED
|
@@ -374,6 +374,212 @@ function shouldApplySwapAmountContract(actionName) {
|
|
|
374
374
|
return action === 'swap' || /(^|:|-)swap$/.test(action);
|
|
375
375
|
}
|
|
376
376
|
|
|
377
|
+
function getTradingProviderHint(providerId) {
|
|
378
|
+
const provider = String(providerId || '').trim().toLowerCase();
|
|
379
|
+
const manifestHint = getCachedProviderHintFromCapabilities(provider);
|
|
380
|
+
if (manifestHint) return manifestHint;
|
|
381
|
+
if (provider === 'trading') {
|
|
382
|
+
return {
|
|
383
|
+
provider,
|
|
384
|
+
category: 'onchain_spot',
|
|
385
|
+
canonicalWriteAction: 'swap',
|
|
386
|
+
credentialUx: 'Use normal Thirdfy login/delegation; do not use this provider for Hyperliquid or Polymarket.',
|
|
387
|
+
};
|
|
388
|
+
}
|
|
389
|
+
if (provider === 'hyperliquid') {
|
|
390
|
+
return {
|
|
391
|
+
provider,
|
|
392
|
+
category: 'leveraged_perps',
|
|
393
|
+
canonicalWriteAction: 'place_hyperliquid_perps_order',
|
|
394
|
+
readActions: [
|
|
395
|
+
'get_hyperliquid_perps_meta',
|
|
396
|
+
'get_hyperliquid_all_mids',
|
|
397
|
+
'get_hyperliquid_l2_book',
|
|
398
|
+
'get_hyperliquid_candles',
|
|
399
|
+
'get_hyperliquid_user_state',
|
|
400
|
+
'get_hyperliquid_open_orders',
|
|
401
|
+
'get_hyperliquid_onboarding_plan',
|
|
402
|
+
'get_hyperliquid_referral_status',
|
|
403
|
+
'get_hyperliquid_builder_fee_status',
|
|
404
|
+
],
|
|
405
|
+
orderManagementActions: [
|
|
406
|
+
'place_hyperliquid_perps_order',
|
|
407
|
+
'get_hyperliquid_open_orders',
|
|
408
|
+
],
|
|
409
|
+
setupActions: [
|
|
410
|
+
'get_hyperliquid_setup_status',
|
|
411
|
+
'get_hyperliquid_onboarding_plan',
|
|
412
|
+
'prepare_hyperliquid_onboarding',
|
|
413
|
+
'get_bridge_quote',
|
|
414
|
+
'execute_bridge',
|
|
415
|
+
'approve_hyperliquid_agent',
|
|
416
|
+
'get_hyperliquid_builder_fee_status',
|
|
417
|
+
'approve_hyperliquid_builder_fee',
|
|
418
|
+
'set_hyperliquid_referrer',
|
|
419
|
+
'deposit_hyperliquid_bridge',
|
|
420
|
+
'deposit_hyperliquid_staking',
|
|
421
|
+
'claim_hyperliquid_testnet_faucet',
|
|
422
|
+
],
|
|
423
|
+
statusActions: ['get_hyperliquid_setup_status', 'get_hyperliquid_builder_fee_status', 'get_hyperliquid_referral_status'],
|
|
424
|
+
credentialType: 'hyperliquid_api_wallet',
|
|
425
|
+
credentialUx:
|
|
426
|
+
'Thirdfy manages Hyperliquid API-wallet setup for normal users. Delegated writes require encrypted privateKey-backed hyperliquid_api_wallet credentials; walletAddress-only records are rejected until Privy walletId-backed Hyperliquid signing ships.',
|
|
427
|
+
recommendedFundingChainId: 42161,
|
|
428
|
+
supportedFundingSourceChains: [
|
|
429
|
+
{ chainId: 42161, name: 'Arbitrum One', path: 'native USDC -> Bridge2' },
|
|
430
|
+
{ chainId: 8453, name: 'Base', path: 'Li.Fi -> Arbitrum native USDC -> Bridge2' },
|
|
431
|
+
{ chainId: 1, name: 'Ethereum', path: 'Li.Fi -> Arbitrum native USDC -> Bridge2' },
|
|
432
|
+
{ chainId: 137, name: 'Polygon', path: 'Li.Fi -> Arbitrum native USDC -> Bridge2' },
|
|
433
|
+
],
|
|
434
|
+
setupBlockers: [
|
|
435
|
+
'HYPERLIQUID_SOURCE_FUNDS_REQUIRED',
|
|
436
|
+
'HYPERLIQUID_ARBITRUM_USDC_REQUIRED',
|
|
437
|
+
'HYPERLIQUID_BRIDGE2_DEPOSIT_REQUIRED',
|
|
438
|
+
'HYPERLIQUID_API_WALLET_APPROVAL_REQUIRED',
|
|
439
|
+
'HYPERLIQUID_BUILDER_APPROVAL_REQUIRED',
|
|
440
|
+
'HYPERLIQUID_POLICY_REQUIRED',
|
|
441
|
+
'HYPERLIQUID_CREDITS_REQUIRED',
|
|
442
|
+
'HYPERLIQUID_API_WALLET_SIGNER_REQUIRED',
|
|
443
|
+
],
|
|
444
|
+
sourceBridgeProvider: 'lifi',
|
|
445
|
+
funding:
|
|
446
|
+
'Best path: Base/Ethereum/Polygon USDC -> Li.Fi bridge to native Arbitrum USDC -> Hyperliquid Bridge2. If the user already has native Circle USDC on Arbitrum One (42161), skip Li.Fi and call deposit_hyperliquid_bridge. Minimum 5 USDC; Bridge2 credits the sender/source wallet.',
|
|
447
|
+
laneCompatibility:
|
|
448
|
+
'Use self only for setup signatures or Arbitrum funding transactions. Perps orders should use thirdfy, hybrid, or agent_wallet.',
|
|
449
|
+
setupExample:
|
|
450
|
+
'actions --provider hyperliquid -> get_hyperliquid_onboarding_plan -> prepare_hyperliquid_onboarding -> get_bridge_quote/execute_bridge if source is Base -> deposit_hyperliquid_bridge on 42161 -> approve_hyperliquid_agent -> approve_hyperliquid_builder_fee if required -> get_hyperliquid_setup_status',
|
|
451
|
+
example:
|
|
452
|
+
'thirdfy-agent actions --provider hyperliquid && thirdfy-agent preflight --action place_hyperliquid_perps_order --params \'{"coin":"ETH","isBuy":true,"size":0.01,"limitPx":3500,"orderType":"market"}\'',
|
|
453
|
+
};
|
|
454
|
+
}
|
|
455
|
+
if (provider === 'polymarket') {
|
|
456
|
+
return {
|
|
457
|
+
provider,
|
|
458
|
+
category: 'prediction_market',
|
|
459
|
+
canonicalWriteAction: 'place_polymarket_order',
|
|
460
|
+
readActions: [
|
|
461
|
+
'get_polymarket_markets',
|
|
462
|
+
'get_polymarket_market',
|
|
463
|
+
'get_polymarket_order_book',
|
|
464
|
+
'get_polymarket_prices',
|
|
465
|
+
'get_polymarket_price_history',
|
|
466
|
+
'get_polymarket_setup_status',
|
|
467
|
+
'get_polymarket_order',
|
|
468
|
+
'get_polymarket_user_orders',
|
|
469
|
+
],
|
|
470
|
+
orderManagementActions: [
|
|
471
|
+
'place_polymarket_order',
|
|
472
|
+
'get_polymarket_order',
|
|
473
|
+
'get_polymarket_user_orders',
|
|
474
|
+
'cancel_polymarket_order',
|
|
475
|
+
],
|
|
476
|
+
setupActions: [
|
|
477
|
+
'get_polymarket_setup_status',
|
|
478
|
+
'agent_wallet_setup:ensure_agent_wallet_safe',
|
|
479
|
+
'agent_wallet_setup:deploy_safe_and_set_approvals',
|
|
480
|
+
'agent_wallet_setup:create_or_derive_clob_credentials',
|
|
481
|
+
'browser_setup:add_privy_signer',
|
|
482
|
+
'browser_setup:create_or_derive_clob_credentials',
|
|
483
|
+
'browser_setup:deploy_safe_and_set_approvals',
|
|
484
|
+
],
|
|
485
|
+
statusActions: ['get_polymarket_setup_status'],
|
|
486
|
+
credentialType: 'polymarket_clob',
|
|
487
|
+
credentialUx:
|
|
488
|
+
'Autonomous agents use an agent-owned Privy wallet/Safe first: Thirdfy creates the agent wallet, deploys/approves the Safe, and stores encrypted CLOB credentials once Polymarket key creation is available. User-owned browser setup remains optional for interactive capital.',
|
|
489
|
+
funding:
|
|
490
|
+
'Fund the agent-owned Polymarket Safe with Polygon USDC/pUSD collateral after Safe deployment and approvals.',
|
|
491
|
+
laneCompatibility:
|
|
492
|
+
'Use agent_wallet as the preferred autonomous-agent path. Use self only for user-owned setup/funding signatures. CLOB orders should use thirdfy, hybrid, or agent_wallet after polymarket_clob credentials are stored.',
|
|
493
|
+
setupBlockers: [
|
|
494
|
+
'POLYMARKET_AGENT_WALLET_SAFE_REQUIRED',
|
|
495
|
+
'POLYMARKET_CLOB_CREDENTIALS_REQUIRED',
|
|
496
|
+
'POLYMARKET_SAFE_DEPLOYMENT_REQUIRED',
|
|
497
|
+
'POLYMARKET_SAFE_APPROVALS_REQUIRED',
|
|
498
|
+
'POLYMARKET_USDC_REQUIRED',
|
|
499
|
+
'POLYMARKET_CREDITS_REQUIRED',
|
|
500
|
+
'POLYMARKET_RELAYER_UNAVAILABLE',
|
|
501
|
+
'POLYMARKET_CLOB_KEY_CREATION_BLOCKED_UNTIL_POLYMARKET_FIX',
|
|
502
|
+
],
|
|
503
|
+
example:
|
|
504
|
+
'thirdfy-agent actions --provider polymarket && thirdfy-agent preflight --action place_polymarket_order --params \'{"tokenID":"...","price":0.42,"size":10,"side":"BUY"}\'',
|
|
505
|
+
};
|
|
506
|
+
}
|
|
507
|
+
if (provider === 'prediction') {
|
|
508
|
+
return {
|
|
509
|
+
provider,
|
|
510
|
+
category: 'prediction',
|
|
511
|
+
canonicalWriteAction: 'place_prediction_order',
|
|
512
|
+
readActions: ['get_prediction_markets', 'get_prediction_market', 'get_prediction_order_book'],
|
|
513
|
+
orderManagementActions: ['place_prediction_order', 'cancel_prediction_order'],
|
|
514
|
+
compatibilityProvider: 'polymarket',
|
|
515
|
+
};
|
|
516
|
+
}
|
|
517
|
+
if (provider === 'earn') {
|
|
518
|
+
return {
|
|
519
|
+
provider,
|
|
520
|
+
category: 'earn',
|
|
521
|
+
canonicalReadAction: 'get_earn_opportunities',
|
|
522
|
+
canonicalWriteAction: 'deposit_earn_position',
|
|
523
|
+
readActions: ['get_earn_opportunities', 'get_earn_provider', 'get_earn_position'],
|
|
524
|
+
orderManagementActions: ['deposit_earn_position', 'withdraw_earn_position'],
|
|
525
|
+
laneCompatibility:
|
|
526
|
+
'Generic earn writes use deposit_earn_position / withdraw_earn_position on normal Thirdfy policy/delegation rails; routing selects Morpho vault calldata or the validated Curve Base 4pool adapter on Base (8453) per opportunity.',
|
|
527
|
+
};
|
|
528
|
+
}
|
|
529
|
+
if (provider === 'morpho') {
|
|
530
|
+
return {
|
|
531
|
+
provider,
|
|
532
|
+
category: 'earn',
|
|
533
|
+
canonicalReadAction: 'get_morpho_vaults',
|
|
534
|
+
canonicalWriteAction: 'deposit_earn_position',
|
|
535
|
+
readActions: ['get_morpho_vaults', 'get_earn_provider', 'get_earn_position'],
|
|
536
|
+
orderManagementActions: ['deposit_earn_position', 'withdraw_earn_position'],
|
|
537
|
+
supportedChains: [8453, 84532],
|
|
538
|
+
laneCompatibility: 'Morpho deposits require ERC-20 approvals to the vault/market routes surfaced by the catalog before deposit_earn_position.',
|
|
539
|
+
example:
|
|
540
|
+
'thirdfy-agent actions --provider morpho && thirdfy-agent run --action deposit_earn_position --params \'{"providerId":"morpho","vaultAddress":"0x...","tokenAddress":"0x...","amount":"10","chainId":8453}\'',
|
|
541
|
+
};
|
|
542
|
+
}
|
|
543
|
+
if (provider === 'curve') {
|
|
544
|
+
return {
|
|
545
|
+
provider,
|
|
546
|
+
category: 'earn',
|
|
547
|
+
canonicalReadAction: 'get_curve_pools',
|
|
548
|
+
canonicalWriteAction: 'deposit_earn_position',
|
|
549
|
+
readActions: ['get_curve_pools', 'get_earn_opportunities', 'get_earn_provider', 'get_earn_position'],
|
|
550
|
+
orderManagementActions: ['deposit_earn_position', 'withdraw_earn_position'],
|
|
551
|
+
supportedChains: [8453],
|
|
552
|
+
laneCompatibility:
|
|
553
|
+
'Curve EVM writes are limited to the validated Base mainnet 4pool adapter; other pools remain discovery-only until per-pool calldata is validated.',
|
|
554
|
+
};
|
|
555
|
+
}
|
|
556
|
+
return null;
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
function getCachedProviderHintFromCapabilities(_provider) {
|
|
560
|
+
return null;
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
const OFFCHAIN_VENUE_ORDER_ACTIONS = new Set([
|
|
564
|
+
'place_hyperliquid_perps_order',
|
|
565
|
+
'place-hyperliquid-perps-order',
|
|
566
|
+
'place_polymarket_order',
|
|
567
|
+
'place-polymarket-order',
|
|
568
|
+
]);
|
|
569
|
+
|
|
570
|
+
function enforceOffchainVenueLaneCompatibility(action, runMode) {
|
|
571
|
+
const normalizedAction = String(action || '').trim().toLowerCase();
|
|
572
|
+
if (runMode !== 'self' || !OFFCHAIN_VENUE_ORDER_ACTIONS.has(normalizedAction)) return;
|
|
573
|
+
throw createCliError(
|
|
574
|
+
'OFFCHAIN_VENUE_ORDER_SELF_UNSUPPORTED',
|
|
575
|
+
'Polymarket CLOB and Hyperliquid perps orders are offchain signed API actions, not EVM build-tx actions. Use self for setup/funding signatures, then run venue orders with --run-mode thirdfy, --run-mode hybrid, or --run-mode agent_wallet.',
|
|
576
|
+
{
|
|
577
|
+
recommendedRunModes: ['thirdfy', 'hybrid', 'agent_wallet'],
|
|
578
|
+
setupOnlyRunMode: 'self',
|
|
579
|
+
}
|
|
580
|
+
);
|
|
581
|
+
}
|
|
582
|
+
|
|
377
583
|
function createCliError(code, message, payload = {}) {
|
|
378
584
|
const error = new Error(message);
|
|
379
585
|
error.payload = {
|
|
@@ -630,6 +836,7 @@ async function commandTrackAction(ctx, flags, capabilities) {
|
|
|
630
836
|
async function commandActions(ctx, flags, capabilities) {
|
|
631
837
|
const policyAware = Boolean(flags.agentApiKey);
|
|
632
838
|
const actions = applyActionFilters(await getCachedActionsCatalog(ctx, flags), flags);
|
|
839
|
+
const providerHint = getTradingProviderHint(flags.provider);
|
|
633
840
|
const effectiveRunMode = getEffectiveRunMode(ctx, flags);
|
|
634
841
|
const chainIdFilter = Number(flags.chainId || 0) || undefined;
|
|
635
842
|
const chainSupport = resolveChainSupport(capabilities, chainIdFilter, effectiveRunMode);
|
|
@@ -637,7 +844,7 @@ async function commandActions(ctx, flags, capabilities) {
|
|
|
637
844
|
success: true,
|
|
638
845
|
code: 'OK',
|
|
639
846
|
message: 'Actions loaded',
|
|
640
|
-
data: { actions },
|
|
847
|
+
data: { actions, ...(providerHint ? { providerHint } : {}) },
|
|
641
848
|
meta: {
|
|
642
849
|
apiBase: ctx.apiBase,
|
|
643
850
|
policyAware,
|
|
@@ -653,6 +860,7 @@ async function commandPreflight(ctx, flags, capabilities) {
|
|
|
653
860
|
const resolved = await resolveActionSelection(ctx, flags);
|
|
654
861
|
prepareActionParamsForFlags(flags, resolved.resolvedAction);
|
|
655
862
|
const runMode = getEffectiveRunMode(ctx, flags);
|
|
863
|
+
enforceOffchainVenueLaneCompatibility(resolved.resolvedAction, runMode);
|
|
656
864
|
const hybridWalletMode = runMode === 'hybrid' ? normalizeHybridWalletMode(flags.hybridWalletMode) : null;
|
|
657
865
|
enforceChainCompatibility(capabilities, flags, runMode);
|
|
658
866
|
const backendResult = await runPreflightByMode(runMode, ctx, flags, resolved, { hybridWalletMode });
|
|
@@ -687,6 +895,7 @@ async function commandRun(ctx, flags, capabilities) {
|
|
|
687
895
|
const resolved = await resolveActionSelection(ctx, flags);
|
|
688
896
|
prepareActionParamsForFlags(flags, resolved.resolvedAction);
|
|
689
897
|
const runMode = getEffectiveRunMode(ctx, flags);
|
|
898
|
+
enforceOffchainVenueLaneCompatibility(resolved.resolvedAction, runMode);
|
|
690
899
|
const hybridWalletMode = runMode === 'hybrid' ? normalizeHybridWalletMode(flags.hybridWalletMode) : null;
|
|
691
900
|
enforceChainCompatibility(capabilities, flags, runMode);
|
|
692
901
|
const skipPreflight = Boolean(flags.skipPreflight);
|
|
@@ -812,6 +1021,7 @@ async function commandWalletSign(ctx, flags, capabilities) {
|
|
|
812
1021
|
const normalizedFlags = { ...flags, runMode: 'self' };
|
|
813
1022
|
const resolved = await resolveActionSelection(ctx, normalizedFlags);
|
|
814
1023
|
prepareActionParamsForFlags(normalizedFlags, resolved.resolvedAction);
|
|
1024
|
+
enforceOffchainVenueLaneCompatibility(resolved.resolvedAction, 'self');
|
|
815
1025
|
enforceChainCompatibility(capabilities, normalizedFlags, 'self');
|
|
816
1026
|
const selfResult = await executeSelfRun(ctx, normalizedFlags, resolved, {
|
|
817
1027
|
skipPreflight: Boolean(normalizedFlags.skipPreflight),
|
|
@@ -1682,6 +1892,7 @@ async function commandPrompt(ctx, flags, capabilities, promptTokens) {
|
|
|
1682
1892
|
async function commandSelfExec(ctx, flags, capabilities) {
|
|
1683
1893
|
const resolved = await resolveActionSelection(ctx, flags);
|
|
1684
1894
|
prepareActionParamsForFlags(flags, resolved.resolvedAction);
|
|
1895
|
+
enforceOffchainVenueLaneCompatibility(resolved.resolvedAction, 'self');
|
|
1685
1896
|
enforceChainCompatibility(capabilities, flags, 'self');
|
|
1686
1897
|
const selfResult = await executeSelfRun(ctx, flags, resolved, {
|
|
1687
1898
|
skipPreflight: Boolean(flags.skipPreflight),
|
|
@@ -2969,11 +3180,22 @@ function applyActionFilters(actions, flags) {
|
|
|
2969
3180
|
}
|
|
2970
3181
|
if (flags.provider) {
|
|
2971
3182
|
const expectedProvider = String(flags.provider).trim().toLowerCase();
|
|
2972
|
-
filtered = filtered.filter((a) =>
|
|
3183
|
+
filtered = filtered.filter((a) => {
|
|
3184
|
+
const provider = getActionProvider(a);
|
|
3185
|
+
const actionKey = getActionKey(a).replace(/-/g, '_').toLowerCase();
|
|
3186
|
+
if (provider === expectedProvider) return true;
|
|
3187
|
+
if (expectedProvider === 'earn') return provider === 'morpho' || provider === 'curve' || actionKey.includes('_earn_');
|
|
3188
|
+
if (expectedProvider === 'prediction') return provider === 'polymarket' || actionKey.includes('_prediction_');
|
|
3189
|
+
return false;
|
|
3190
|
+
});
|
|
2973
3191
|
}
|
|
2974
3192
|
return filtered;
|
|
2975
3193
|
}
|
|
2976
3194
|
|
|
3195
|
+
function getActionProvider(action) {
|
|
3196
|
+
return String(action?.provider || action?.providerId || action?.provider_id || action?.catalog || '').trim().toLowerCase();
|
|
3197
|
+
}
|
|
3198
|
+
|
|
2977
3199
|
async function resolveActionSelection(ctx, flags) {
|
|
2978
3200
|
const requestedAction = requireFlag(flags, 'action', 'Missing --action');
|
|
2979
3201
|
const requestedLower = requestedAction.toLowerCase();
|
|
@@ -3009,10 +3231,11 @@ async function resolveActionSelection(ctx, flags) {
|
|
|
3009
3231
|
return { requestedAction, resolvedAction: exactAlias[0].key, resolvedActionMeta: exactAlias[0].action || null };
|
|
3010
3232
|
}
|
|
3011
3233
|
if (exactAlias.length > 1) {
|
|
3234
|
+
const providerHint = flags.provider ? getTradingProviderHint(flags.provider) : null;
|
|
3012
3235
|
throw new Error(
|
|
3013
3236
|
`Ambiguous --action "${requestedAction}". Alias matches multiple actions: ${formatActionList(
|
|
3014
3237
|
exactAlias.map((v) => v.key)
|
|
3015
|
-
)}.`
|
|
3238
|
+
)}.${providerHint ? ` Provider ${providerHint.provider} expects ${providerHint.canonicalWriteAction}.` : ''}`
|
|
3016
3239
|
);
|
|
3017
3240
|
}
|
|
3018
3241
|
|
|
@@ -3025,14 +3248,18 @@ async function resolveActionSelection(ctx, flags) {
|
|
|
3025
3248
|
return { requestedAction, resolvedAction: fuzzy[0].key, resolvedActionMeta: fuzzy[0].action || null };
|
|
3026
3249
|
}
|
|
3027
3250
|
if (fuzzy.length > 1) {
|
|
3251
|
+
const providerHint = flags.provider ? getTradingProviderHint(flags.provider) : null;
|
|
3028
3252
|
throw new Error(
|
|
3029
3253
|
`Ambiguous --action "${requestedAction}". Did you mean one of: ${formatActionList(
|
|
3030
3254
|
fuzzy.slice(0, 10).map((v) => v.key)
|
|
3031
|
-
)}
|
|
3255
|
+
)}?${providerHint ? ` Provider ${providerHint.provider} expects ${providerHint.canonicalWriteAction}.` : ''}`
|
|
3032
3256
|
);
|
|
3033
3257
|
}
|
|
3258
|
+
const providerHint = flags.provider ? getTradingProviderHint(flags.provider) : null;
|
|
3034
3259
|
throw new Error(
|
|
3035
|
-
`Unknown --action "${requestedAction}". No compatible action found in current catalog/provider scope
|
|
3260
|
+
`Unknown --action "${requestedAction}". No compatible action found in current catalog/provider scope.${
|
|
3261
|
+
providerHint ? ` Provider ${providerHint.provider} expects ${providerHint.canonicalWriteAction}; run actions --provider ${providerHint.provider} first.` : ''
|
|
3262
|
+
}`
|
|
3036
3263
|
);
|
|
3037
3264
|
}
|
|
3038
3265
|
|