@thirdfy/agent-cli 0.2.86 → 0.4.0
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 +116 -0
- package/README.md +61 -25
- package/package.json +2 -2
- package/scripts/validate-cli-contract-schemas.mjs +5 -0
- package/src/cli/errors.mjs +58 -8
- package/src/cli/help.mjs +0 -3
- package/src/cli/manifest.mjs +54 -27
- package/src/cli/options/bootstrap.mjs +15 -3
- package/src/cli/options/chains.mjs +5 -0
- package/src/cli/options/credits.mjs +9 -0
- package/src/cli/options/data.mjs +13 -1
- package/src/cli/options/index.mjs +10 -4
- package/src/cli/options/walletControl.mjs +8 -0
- package/src/commands/bootstrap.mjs +44 -5
- package/src/commands/chat.mjs +3 -0
- package/src/commands/credits.mjs +39 -4
- package/src/commands/delegation.mjs +3 -1
- package/src/commands/discovery.mjs +156 -13
- package/src/commands/doctor.mjs +5 -2
- package/src/commands/execute.mjs +96 -3
- package/src/commands/prompt.mjs +5 -4
- package/src/commands/wallet.mjs +119 -3
- package/src/commands/walletControl.mjs +66 -0
- package/src/contracts/cli/schemas/actions.response.schema.json +45 -0
- package/src/contracts/cli/schemas/chains-non-evm.response.schema.json +76 -0
- package/src/contracts/cli/schemas/chains.response.schema.json +151 -0
- package/src/contracts/cli/schemas/error.response.schema.json +61 -0
- package/src/contracts/cli/schemas/preview.response.schema.json +179 -0
- package/src/contracts/cli/schemas/readiness.response.schema.json +94 -0
- package/src/core/args.mjs +10 -2
- package/src/core/chainRegistry.mjs +223 -1
- package/src/core/context.mjs +37 -14
- package/src/core/errorEnvelope.mjs +209 -0
- package/src/core/fundingTokenIn.mjs +24 -1
- package/src/core/http.mjs +6 -1
- package/src/core/readOnlyActions.mjs +10 -0
- package/src/core/walletExecuteChainId.mjs +25 -0
- package/src/lib/creditsUsdSummary.mjs +48 -4
- package/src/runtime/actions/actionList.mjs +87 -0
- package/src/runtime/actions/selection.mjs +17 -22
- package/src/runtime/execution/actionPreflight.mjs +50 -14
- package/src/runtime/execution/payloads.mjs +36 -28
- package/src/runtime/execution/runners.mjs +75 -21
- package/src/runtime/execution/svmLane.mjs +106 -0
- package/src/runtime/execution/walletControl.mjs +104 -0
- package/src/runtime/handlers.mjs +16 -2
- package/src/runtime/index.mjs +1 -1
- package/src/runtime/main.mjs +21 -5
- package/src/runtime/providerHints/apps.mjs +36 -0
- package/src/runtime/providerHints/catalogHint.mjs +55 -0
- package/src/runtime/providerHints/cex.mjs +130 -0
- package/src/runtime/providerHints/data.mjs +144 -0
- package/src/runtime/providerHints/dispatch.mjs +177 -0
- package/src/runtime/providerHints/dogeos.mjs +38 -0
- package/src/runtime/providerHints/earn.mjs +201 -0
- package/src/runtime/providerHints/perps.mjs +346 -0
- package/src/runtime/providerHints/prediction.mjs +128 -0
- package/src/runtime/providerHints/solana.mjs +85 -0
- package/src/runtime/providerHints/trading.mjs +78 -0
- package/src/runtime/providerHints/walletControl.mjs +39 -0
- package/src/runtime/risk/notional.mjs +59 -0
- package/src/runtime/risk/orderPreview.mjs +154 -0
- package/src/runtime/risk/riskRefusal.mjs +74 -0
- package/src/runtime/validation/actionParamsGate.mjs +2 -0
- package/src/runtime/providerHints.mjs +0 -1017
|
@@ -1,10 +1,58 @@
|
|
|
1
1
|
import { requireFlag, parseJsonFlag } from '../core/args.mjs';
|
|
2
2
|
import { apiGet, apiPost } from '../core/http.mjs';
|
|
3
|
-
import { extractActions,
|
|
3
|
+
import { extractActions, findAgentApiKey, getCachedActionsCatalogSnapshot, loadProfileConfig } from '../core/context.mjs';
|
|
4
4
|
import { getEffectiveRunMode } from '../core/runMode.mjs';
|
|
5
|
-
import { formatCreditsBalanceSummary } from '../lib/creditsUsdSummary.mjs';
|
|
5
|
+
import { creditsFreeQuotaNote, formatCreditsBalanceSummary, stripUsdFields } from '../lib/creditsUsdSummary.mjs';
|
|
6
6
|
import { filterActionsByChain } from '../runtime/actions/chainFilter.mjs';
|
|
7
|
-
|
|
7
|
+
import { pageActionList, readActionListPaging } from '../runtime/actions/actionList.mjs';
|
|
8
|
+
import {
|
|
9
|
+
caip2ForChainId,
|
|
10
|
+
chainRegistrySource,
|
|
11
|
+
configuredEvmChainIds,
|
|
12
|
+
configuredSvmChainIds,
|
|
13
|
+
getApiChainRegistry,
|
|
14
|
+
isSvmChainId,
|
|
15
|
+
} from '../core/chainRegistry.mjs';
|
|
16
|
+
import { SOLANA_PROVIDER_HINTS } from '../runtime/providerHints/solana.mjs';
|
|
17
|
+
|
|
18
|
+
/** Execution lanes when the API's capabilities name none (thirdfy-mcp SUPPORTED_EXECUTION_LANES). */
|
|
19
|
+
const DEFAULT_EXECUTION_LANES = Object.freeze(['thirdfy', 'hybrid', 'self', 'agent_wallet', 'validation_only']);
|
|
20
|
+
|
|
21
|
+
/** Chain ids the catalog rows name (`supportedChains` and the like), EVM only. Same as thirdfy-mcp `inferCatalogEvmChainIds`. */
|
|
22
|
+
function catalogEvmChainIds(rows) {
|
|
23
|
+
const found = new Set();
|
|
24
|
+
const collect = (value) => {
|
|
25
|
+
if (typeof value === 'number' && Number.isInteger(value) && value > 0) found.add(value);
|
|
26
|
+
else if (typeof value === 'string' && /^\d+$/.test(value)) found.add(Number(value));
|
|
27
|
+
else if (Array.isArray(value)) value.forEach(collect);
|
|
28
|
+
};
|
|
29
|
+
for (const row of Array.isArray(rows) ? rows : []) {
|
|
30
|
+
for (const key of ['chainId', 'chainIds', 'supportedChainIds', 'supportedChains', 'chains', 'networks']) collect(row?.[key]);
|
|
31
|
+
}
|
|
32
|
+
return [...found].filter((chainId) => !isSvmChainId(chainId)).sort((a, b) => a - b);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** The Solana execution contract, as thirdfy-mcp `solanaExecutionContract` with CLI commands. */
|
|
36
|
+
function solanaExecutionContract() {
|
|
37
|
+
const chainIds = configuredSvmChainIds();
|
|
38
|
+
return {
|
|
39
|
+
enabled: chainIds.length > 0,
|
|
40
|
+
chainFamily: 'svm',
|
|
41
|
+
chainIds,
|
|
42
|
+
caip2: Object.fromEntries(chainIds.map((id) => [id, caip2ForChainId(id)]).filter(([, caip2]) => caip2)),
|
|
43
|
+
requiredRunMode: 'agent_wallet',
|
|
44
|
+
supportedExecutionLanes: ['agent_wallet'],
|
|
45
|
+
wallet: "The agent's managed Privy Solana wallet. executionWalletAddress is base58 and case-sensitive.",
|
|
46
|
+
commands: ['preview', 'run', 'wallet execute', 'agent run'],
|
|
47
|
+
evmOnlyCommands: ['self-exec', 'wallet sign', 'wallet submit'],
|
|
48
|
+
providers: Object.keys(SOLANA_PROVIDER_HINTS),
|
|
49
|
+
flow: '`thirdfy-agent login email` (executionWallets) or `preflight --chain-id 101 --run-mode agent_wallet` gives the base58 executionWalletAddress -> give the user that address (not the EVM one) and fund it with the token to spend -> `run --chain-id 101 --run-mode agent_wallet` with base58 params (for example jupiter_swap {inputMint, outputMint, amount}).',
|
|
50
|
+
gas: 'Thirdfy sponsors Solana network fees (the execution-wallet reply says gasSponsored): never ask the user to buy SOL for gas. The wallet pays only rent for new token accounts (about 0.002 SOL each) and SOL the action spends; a write short of it is refused with INSUFFICIENT_NATIVE_GAS.',
|
|
51
|
+
notes:
|
|
52
|
+
'Solana execution runs only with --run-mode agent_wallet on chain 101 (103 devnet where a provider supports it). self, hybrid and thirdfy stay EVM-only. Check `actions --chain-id 101` (supportedChains) for each action\'s chains.',
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
export function createDiscoveryCommands({ printEnvelope, applyActionFilters, getProviderHintForActions, resolveChainSupport, getAuthToken, resolveAgentApiKey }) {
|
|
8
56
|
async function commandCatalogsList(ctx, flags, capabilities) {
|
|
9
57
|
const response = await apiGet(ctx, '/api/v1/agent/actions/catalog');
|
|
10
58
|
const actions = extractActions(response);
|
|
@@ -32,20 +80,32 @@ async function commandCatalogsList(ctx, flags, capabilities) {
|
|
|
32
80
|
}
|
|
33
81
|
|
|
34
82
|
async function commandActions(ctx, flags, capabilities) {
|
|
35
|
-
|
|
83
|
+
// The agent's own list whenever execution would run with a key (flag, env or config), so `actions`
|
|
84
|
+
// lists what `run` / `preflight` accept, as thirdfy-mcp `getAgentActions` does.
|
|
85
|
+
const paging = readActionListPaging(flags);
|
|
86
|
+
const snapshot = await getCachedActionsCatalogSnapshot(ctx, flags);
|
|
36
87
|
const chainIdFilter = Number(flags.chainId || 0) || undefined;
|
|
37
|
-
const actions = filterActionsByChain(applyActionFilters(
|
|
38
|
-
const providerHint =
|
|
88
|
+
const actions = filterActionsByChain(applyActionFilters(snapshot.actions, flags), chainIdFilter);
|
|
89
|
+
const providerHint = getProviderHintForActions(flags.provider, actions);
|
|
90
|
+
// --search / --limit / --offset / --view: one page, as thirdfy-mcp getAgentActions answers.
|
|
91
|
+
const page = paging ? pageActionList(actions, paging) : null;
|
|
39
92
|
const effectiveRunMode = getEffectiveRunMode(ctx, flags);
|
|
40
93
|
const chainSupport = resolveChainSupport(capabilities, chainIdFilter, effectiveRunMode);
|
|
41
94
|
printEnvelope({
|
|
42
95
|
success: true,
|
|
43
96
|
code: 'OK',
|
|
44
97
|
message: 'Actions loaded',
|
|
45
|
-
data: {
|
|
98
|
+
data: {
|
|
99
|
+
...(page
|
|
100
|
+
? { actions: page.actions, pagination: page.pagination, view: page.view, ...(page.hint ? { hint: page.hint } : {}) }
|
|
101
|
+
: { actions }),
|
|
102
|
+
...(providerHint ? { providerHint } : {}),
|
|
103
|
+
},
|
|
46
104
|
meta: {
|
|
47
105
|
apiBase: ctx.apiBase,
|
|
48
|
-
|
|
106
|
+
// A key was sent; policyScoped: the API answered with the agent policy's allowlist.
|
|
107
|
+
policyAware: snapshot.keyed,
|
|
108
|
+
policyScoped: snapshot.policyScoped,
|
|
49
109
|
runMode: effectiveRunMode,
|
|
50
110
|
profile: ctx.profile || null,
|
|
51
111
|
chainCompatibility: chainSupport,
|
|
@@ -54,19 +114,101 @@ async function commandActions(ctx, flags, capabilities) {
|
|
|
54
114
|
});
|
|
55
115
|
}
|
|
56
116
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
117
|
+
/**
|
|
118
|
+
* `chains`: which chains Thirdfy acts on, from the API's capabilities and catalog (thirdfy-mcp
|
|
119
|
+
* `getChainCapabilities`). `--non-evm`: the Solana execution contract (`getNonEvmCapabilities`).
|
|
120
|
+
*/
|
|
121
|
+
async function commandChains(ctx, flags, capabilities) {
|
|
122
|
+
const svmChainIds = configuredSvmChainIds();
|
|
123
|
+
const nonEvmChains = svmChainIds.length > 0 ? ['solana'] : [];
|
|
124
|
+
const meta = { apiBase: ctx.apiBase, capabilitiesVersion: capabilities?.contractVersion || null };
|
|
125
|
+
if (flags.nonEvm) {
|
|
126
|
+
const executionEnabled = svmChainIds.length > 0;
|
|
127
|
+
printEnvelope({
|
|
128
|
+
success: true,
|
|
129
|
+
code: 'OK',
|
|
130
|
+
message: executionEnabled ? 'Solana execution is enabled' : 'Solana execution is not enabled on this API',
|
|
131
|
+
data: {
|
|
132
|
+
supportedChains: nonEvmChains,
|
|
133
|
+
executionEnabled,
|
|
134
|
+
contractVersion: 'v2',
|
|
135
|
+
chainRegistrySource: chainRegistrySource(),
|
|
136
|
+
solana: solanaExecutionContract(),
|
|
137
|
+
notes: executionEnabled
|
|
138
|
+
? 'Solana execution is enabled through `run` / `wallet execute` / `agent run` with --run-mode agent_wallet on chain 101 (the agent\'s managed Privy Solana wallet). self-exec, wallet sign and wallet submit stay EVM-only.'
|
|
139
|
+
: 'Solana execution is not enabled on this Thirdfy API: its capabilities list no svm chain. Chains 101/103 are refused with SOLANA_NOT_ENABLED.',
|
|
140
|
+
},
|
|
141
|
+
meta,
|
|
142
|
+
});
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
const configured = configuredEvmChainIds();
|
|
146
|
+
let catalogChainIds = [];
|
|
147
|
+
let catalogSource = 'live';
|
|
148
|
+
let catalogError;
|
|
149
|
+
try {
|
|
150
|
+
catalogChainIds = catalogEvmChainIds((await getCachedActionsCatalogSnapshot(ctx, flags)).actions);
|
|
151
|
+
} catch (error) {
|
|
152
|
+
catalogSource = 'unavailable';
|
|
153
|
+
catalogError = error?.message || String(error);
|
|
154
|
+
}
|
|
155
|
+
const apiChains = getApiChainRegistry()?.chains;
|
|
156
|
+
printEnvelope({
|
|
157
|
+
success: true,
|
|
158
|
+
code: 'OK',
|
|
159
|
+
message: 'Chain capabilities loaded',
|
|
160
|
+
data: {
|
|
161
|
+
supportedEvmChainIds: [...new Set([...configured, ...catalogChainIds])].sort((a, b) => a - b),
|
|
162
|
+
configuredEvmChainIds: configured,
|
|
163
|
+
catalogEvmChainIds: catalogChainIds,
|
|
164
|
+
catalogSource,
|
|
165
|
+
...(catalogError ? { catalogError } : {}),
|
|
166
|
+
supportedNonEvmChains: nonEvmChains,
|
|
167
|
+
supportedSvmChainIds: svmChainIds,
|
|
168
|
+
chainRegistrySource: chainRegistrySource(),
|
|
169
|
+
caip2: Object.fromEntries(
|
|
170
|
+
[...configured, ...svmChainIds].map((id) => [id, caip2ForChainId(id)]).filter(([, caip2]) => caip2),
|
|
171
|
+
),
|
|
172
|
+
supportedExecutionLanes: Array.isArray(capabilities?.runModes) && capabilities.runModes.length
|
|
173
|
+
? capabilities.runModes
|
|
174
|
+
: [...DEFAULT_EXECUTION_LANES],
|
|
175
|
+
defaultChainId: 8453,
|
|
176
|
+
defaultExecutionLane: getEffectiveRunMode(ctx, flags),
|
|
177
|
+
nonEvmExecutionContract: solanaExecutionContract(),
|
|
178
|
+
// Each chain the API lists, with its execution lanes (CLI only; the MCP has no per-chain lanes).
|
|
179
|
+
...(apiChains ? { chains: apiChains } : {}),
|
|
180
|
+
},
|
|
181
|
+
meta,
|
|
61
182
|
});
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* `credits balance`. Owner auth reads the owner's balance (`/credits/balance`). An explicit
|
|
187
|
+
* --agent-api-key, or no owner auth but an agent key in env or config, reads the agent's own ledger
|
|
188
|
+
* (`/agent/credits/balance`: its credits, free requests today and the owner-credits fallback), as
|
|
189
|
+
* thirdfy-mcp getCreditsBalance. Credits only, never dollars.
|
|
190
|
+
*/
|
|
191
|
+
async function commandCreditsBalance(ctx, flags, capabilities) {
|
|
192
|
+
const config = loadProfileConfig();
|
|
193
|
+
const ownerToken = String(flags.authToken || process.env.THIRDFY_AUTH_TOKEN || config?.auth?.authToken || '').trim();
|
|
194
|
+
const agentApiKey = findAgentApiKey(flags);
|
|
195
|
+
const agentLedger = Boolean(String(flags.agentApiKey || '').trim()) || (!ownerToken && Boolean(agentApiKey));
|
|
196
|
+
const response = agentLedger
|
|
197
|
+
? await apiGet(ctx, '/api/v1/agent/credits/balance', { 'x-agent-api-key': agentApiKey })
|
|
198
|
+
: await apiGet(ctx, '/api/v1/credits/balance', {
|
|
199
|
+
Authorization: `Bearer ${getAuthToken(flags, 'Missing --auth-token (or THIRDFY_AUTH_TOKEN), or an agent key for the agent\'s own credits, for credits balance')}`,
|
|
200
|
+
});
|
|
62
201
|
const summary = formatCreditsBalanceSummary(response);
|
|
202
|
+
const note = creditsFreeQuotaNote(response);
|
|
63
203
|
printEnvelope({
|
|
64
204
|
success: true,
|
|
65
205
|
code: 'OK',
|
|
66
206
|
message: flags.json ? 'Credits balance loaded' : `Credits balance loaded: ${summary}`,
|
|
67
|
-
|
|
207
|
+
// Credits only: no USD price per credit (thirdfy-mcp getCreditsBalance).
|
|
208
|
+
data: { ...stripUsdFields(response), ...(note ? { note } : {}) },
|
|
68
209
|
meta: {
|
|
69
210
|
apiBase: ctx.apiBase,
|
|
211
|
+
ledger: agentLedger ? 'agent' : 'owner',
|
|
70
212
|
capabilitiesVersion: capabilities?.contractVersion || null,
|
|
71
213
|
...(flags.json ? {} : { summary }),
|
|
72
214
|
},
|
|
@@ -76,6 +218,7 @@ async function commandCreditsBalance(ctx, flags, capabilities) {
|
|
|
76
218
|
return {
|
|
77
219
|
commandCatalogsList,
|
|
78
220
|
commandActions,
|
|
221
|
+
commandChains,
|
|
79
222
|
commandCreditsBalance,
|
|
80
223
|
};
|
|
81
224
|
}
|
package/src/commands/doctor.mjs
CHANGED
|
@@ -124,11 +124,14 @@ function buildOnboardingHelp(ctx, flags = {}) {
|
|
|
124
124
|
},
|
|
125
125
|
{
|
|
126
126
|
id: 'wallet_sign',
|
|
127
|
-
description: '
|
|
127
|
+
description: 'An agent with its own EVM key and no email: prove the key, then create the agent. The agent wallet owns the agent.',
|
|
128
128
|
commands: [
|
|
129
129
|
'thirdfy-agent agent auth challenge --agent-key 0xYOUR_AGENT_KEY --json',
|
|
130
|
-
'
|
|
130
|
+
'# sign data.message exactly as given with the private key of 0xYOUR_AGENT_KEY (EIP-191 personal_sign)',
|
|
131
|
+
'thirdfy-agent agent auth verify --agent-key 0xYOUR_AGENT_KEY --challenge-id <id> --signature <hex> --json',
|
|
132
|
+
'thirdfy-agent agent register --agent-key 0xYOUR_AGENT_KEY --owner-session-token <ownerSessionToken> --name "My Agent" --json',
|
|
131
133
|
],
|
|
134
|
+
note: 'verify returns an owner session; register with --name creates the agent and its API key. An existing agent logs in with `thirdfy-agent login --agent-key 0xYOUR_AGENT_KEY --challenge-id <id> --signature <hex>`. No owner session is stored until the challenge completes.',
|
|
132
135
|
},
|
|
133
136
|
{
|
|
134
137
|
id: 'existing_credentials',
|
package/src/commands/execute.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { requireFlag } from '../core/args.mjs';
|
|
1
|
+
import { parseJsonFlag, requireFlag } from '../core/args.mjs';
|
|
2
2
|
import { createCliError, exitAfterOutput } from '../core/envelope.mjs';
|
|
3
3
|
import {
|
|
4
4
|
getEffectiveRunMode,
|
|
@@ -7,6 +7,13 @@ import {
|
|
|
7
7
|
parseBooleanFlag,
|
|
8
8
|
} from '../core/runMode.mjs';
|
|
9
9
|
import { apiGet } from '../core/http.mjs';
|
|
10
|
+
import { applySvmLane } from '../runtime/execution/svmLane.mjs';
|
|
11
|
+
import { isReadOnlyResolvedAction } from '../core/readOnlyActions.mjs';
|
|
12
|
+
import { resolveWalletExecuteChainId } from '../core/walletExecuteChainId.mjs';
|
|
13
|
+
import { resolveExecuteEstimatedAmountUsd } from '../runtime/execution/payloads.mjs';
|
|
14
|
+
import { assertPreviewSized, buildPreviewOrder, shapeOrderPreview } from '../runtime/risk/orderPreview.mjs';
|
|
15
|
+
import { actionNameForApi } from '../runtime/actions/selection.mjs';
|
|
16
|
+
import { runNotionalWarnings } from '../runtime/risk/notional.mjs';
|
|
10
17
|
import { findTypedReading, formatTypedReadingLead } from './signals/typedReading.mjs';
|
|
11
18
|
import { createRequire } from 'module';
|
|
12
19
|
|
|
@@ -25,6 +32,7 @@ export function createExecuteCommands(deps) {
|
|
|
25
32
|
applyAvantisAgentWalletAddressDefault,
|
|
26
33
|
runPreflightByMode,
|
|
27
34
|
runExecutionByMode,
|
|
35
|
+
resolveManagedExecutionPreflight,
|
|
28
36
|
applyExecutionFallbackHints,
|
|
29
37
|
performSelfBroadcast,
|
|
30
38
|
executeManagedWalletRun,
|
|
@@ -39,7 +47,8 @@ async function commandPreflight(ctx, flags, capabilities) {
|
|
|
39
47
|
const resolved = await resolveActionSelection(ctx, flags);
|
|
40
48
|
prepareActionParamsForFlags(flags, resolved.resolvedAction);
|
|
41
49
|
enforceCatalogActionParams(resolved, flags);
|
|
42
|
-
|
|
50
|
+
// Solana-only rows default to chain 101; Solana runs only on agent_wallet.
|
|
51
|
+
const runMode = applySvmLane(flags, resolved, getEffectiveRunMode(ctx, flags));
|
|
43
52
|
enforceOffchainVenueLaneCompatibility(resolved.resolvedAction, runMode);
|
|
44
53
|
const hybridWalletMode = runMode === 'hybrid' ? normalizeHybridWalletMode(flags.hybridWalletMode) : null;
|
|
45
54
|
enforceChainCompatibility(capabilities, flags, runMode);
|
|
@@ -89,11 +98,88 @@ async function commandPreflight(ctx, flags, capabilities) {
|
|
|
89
98
|
}
|
|
90
99
|
|
|
91
100
|
|
|
101
|
+
/**
|
|
102
|
+
* `preview`: whether Thirdfy would sign a write, with its order and risk preview, before it is sent.
|
|
103
|
+
* Same verdict as thirdfy-mcp `previewOrder`. Read only: nothing is signed, billed or reserved. A
|
|
104
|
+
* blocked order is the answer (success, exit 0); `data.wouldSign` says whether `run` would sign it.
|
|
105
|
+
*/
|
|
106
|
+
async function commandPreview(ctx, flags, capabilities) {
|
|
107
|
+
const previewFlags = { ...flags, runMode: 'agent_wallet' };
|
|
108
|
+
const resolved = await resolveActionSelection(ctx, previewFlags);
|
|
109
|
+
prepareActionParamsForFlags(previewFlags, resolved.resolvedAction);
|
|
110
|
+
enforceCatalogActionParams(resolved, previewFlags);
|
|
111
|
+
if (isReadOnlyResolvedAction(resolved)) {
|
|
112
|
+
throw createCliError(
|
|
113
|
+
'PREVIEW_ORDER_WRITE_ONLY',
|
|
114
|
+
`${resolved.resolvedAction} is a read; preview previews writes. Run the read with \`thirdfy-agent run\`.`,
|
|
115
|
+
{ resolvedAction: resolved.resolvedAction, requestedAction: resolved.requestedAction },
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
applySvmLane(previewFlags, resolved, 'agent_wallet', { locked: true });
|
|
119
|
+
enforceChainCompatibility(capabilities, previewFlags, 'agent_wallet');
|
|
120
|
+
const managedPreflight =
|
|
121
|
+
(await applyHyperliquidAgentWalletAddressDefault(ctx, previewFlags, resolved, 'agent_wallet'))
|
|
122
|
+
|| (await applyLighterAgentWalletAddressDefault(ctx, previewFlags, resolved, 'agent_wallet'))
|
|
123
|
+
|| (await applyAvantisAgentWalletAddressDefault(ctx, previewFlags, resolved, 'agent_wallet'));
|
|
124
|
+
// The action-aware preflight, with no CLI funding or gas gate: the verdict reads the API's answer.
|
|
125
|
+
const preflight = await resolveManagedExecutionPreflight(ctx, previewFlags, resolved, {
|
|
126
|
+
runMode: 'agent_wallet',
|
|
127
|
+
requireFundingCheck: false,
|
|
128
|
+
preflightSeed: managedPreflight,
|
|
129
|
+
evaluateAction: true,
|
|
130
|
+
});
|
|
131
|
+
// Anything but an answer about the order (no identity, a wallet mismatch) is a failed command.
|
|
132
|
+
if (!preflight.success && preflight.preflightBlocked !== true) {
|
|
133
|
+
throw createCliError(preflight.blockedReason || 'PREVIEW_FAILED', preflight.error || 'Preview failed', {
|
|
134
|
+
blockedStage: preflight.blockedStage || 'routing',
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
// The catalog key drives the CLI's own rules; the verdict and the order name the action the API
|
|
138
|
+
// was asked about (the requested name), as thirdfy-mcp previewOrder does.
|
|
139
|
+
const action = resolved.resolvedAction;
|
|
140
|
+
const apiAction = actionNameForApi(resolved);
|
|
141
|
+
const chainId = Number(preflight.chainId) || resolveWalletExecuteChainId({
|
|
142
|
+
action,
|
|
143
|
+
chainId: previewFlags.chainId,
|
|
144
|
+
params: parseJsonFlag(previewFlags, 'params') || {},
|
|
145
|
+
});
|
|
146
|
+
const estimatedAmountUsd = resolveExecuteEstimatedAmountUsd(action, previewFlags);
|
|
147
|
+
const behavior = resolved.resolvedActionMeta?.behavior;
|
|
148
|
+
const shaped = shapeOrderPreview({ action: apiAction, chainId, preflight });
|
|
149
|
+
assertPreviewSized({ shaped, estimatedAmountUsd, behavior });
|
|
150
|
+
const order = buildPreviewOrder({
|
|
151
|
+
shaped,
|
|
152
|
+
params: parseJsonFlag(flags, 'params') || {},
|
|
153
|
+
estimatedAmountUsd,
|
|
154
|
+
paramsSchema: previewFlags.__actionParamsSchema,
|
|
155
|
+
});
|
|
156
|
+
printEnvelope({
|
|
157
|
+
success: true,
|
|
158
|
+
code: shaped.wouldSign ? 'PREVIEW_WOULD_SIGN' : 'PREVIEW_BLOCKED',
|
|
159
|
+
message: shaped.wouldSign
|
|
160
|
+
? 'Thirdfy would sign this order'
|
|
161
|
+
: shaped.message || `Thirdfy would not sign this order (${shaped.blockedReason || 'blocked'})`,
|
|
162
|
+
data: { ...shaped, ...(order ? { order } : {}) },
|
|
163
|
+
meta: {
|
|
164
|
+
apiBase: ctx.apiBase,
|
|
165
|
+
requestedAction: resolved.requestedAction,
|
|
166
|
+
resolvedAction: action,
|
|
167
|
+
runMode: 'agent_wallet',
|
|
168
|
+
executionWalletAddress: preflight.executionWalletAddress || null,
|
|
169
|
+
profile: ctx.profile || null,
|
|
170
|
+
swapInput: previewFlags.__swapInput || null,
|
|
171
|
+
capabilitiesVersion: capabilities?.contractVersion || null,
|
|
172
|
+
},
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
|
|
92
177
|
async function commandRun(ctx, flags, capabilities) {
|
|
93
178
|
const resolved = await resolveActionSelection(ctx, flags);
|
|
94
179
|
prepareActionParamsForFlags(flags, resolved.resolvedAction);
|
|
95
180
|
enforceCatalogActionParams(resolved, flags);
|
|
96
|
-
|
|
181
|
+
// Solana-only rows default to chain 101; Solana runs only on agent_wallet.
|
|
182
|
+
const runMode = applySvmLane(flags, resolved, getEffectiveRunMode(ctx, flags));
|
|
97
183
|
enforceOffchainVenueLaneCompatibility(resolved.resolvedAction, runMode);
|
|
98
184
|
const hybridWalletMode = runMode === 'hybrid' ? normalizeHybridWalletMode(flags.hybridWalletMode) : null;
|
|
99
185
|
enforceChainCompatibility(capabilities, flags, runMode);
|
|
@@ -102,6 +188,8 @@ async function commandRun(ctx, flags, capabilities) {
|
|
|
102
188
|
|| (await applyLighterAgentWalletAddressDefault(ctx, flags, resolved, runMode))
|
|
103
189
|
|| (await applyAvantisAgentWalletAddressDefault(ctx, flags, resolved, runMode));
|
|
104
190
|
const skipPreflight = Boolean(flags.skipPreflight);
|
|
191
|
+
// A write with no --estimated-amount-usd: a warning, or a refusal before anything is sent.
|
|
192
|
+
const warnings = runNotionalWarnings(resolved, flags, runMode);
|
|
105
193
|
const backendResult = await runExecutionByMode(runMode, ctx, flags, resolved, {
|
|
106
194
|
skipPreflight,
|
|
107
195
|
hybridWalletMode,
|
|
@@ -164,6 +252,9 @@ async function commandRun(ctx, flags, capabilities) {
|
|
|
164
252
|
skippedPreflight: skipPreflight,
|
|
165
253
|
swapInput: flags.__swapInput || null,
|
|
166
254
|
capabilitiesVersion: capabilities?.contractVersion || null,
|
|
255
|
+
...(flags.__runModeDefaultedForSvm ? { runModeDefaultedForSvm: true } : {}),
|
|
256
|
+
...(flags.__chainIdInferred ? { chainIdInferred: flags.__chainIdInferred } : {}),
|
|
257
|
+
...(warnings.length ? { warnings } : {}),
|
|
167
258
|
},
|
|
168
259
|
},
|
|
169
260
|
typedReading ? { humanLead: formatTypedReadingLead(typedReading) } : {},
|
|
@@ -238,6 +329,7 @@ async function commandSelfExec(ctx, flags, capabilities) {
|
|
|
238
329
|
const resolved = await resolveActionSelection(ctx, flags);
|
|
239
330
|
prepareActionParamsForFlags(flags, resolved.resolvedAction);
|
|
240
331
|
enforceCatalogActionParams(resolved, flags);
|
|
332
|
+
applySvmLane(flags, resolved, 'self', { locked: true });
|
|
241
333
|
enforceOffchainVenueLaneCompatibility(resolved.resolvedAction, 'self');
|
|
242
334
|
enforceChainCompatibility(capabilities, flags, 'self');
|
|
243
335
|
const selfResult = await executeSelfRun(ctx, flags, resolved, {
|
|
@@ -278,6 +370,7 @@ async function commandSelfExec(ctx, flags, capabilities) {
|
|
|
278
370
|
|
|
279
371
|
return {
|
|
280
372
|
commandPreflight,
|
|
373
|
+
commandPreview,
|
|
281
374
|
commandRun,
|
|
282
375
|
commandAgentRun,
|
|
283
376
|
commandIntentStatus,
|
package/src/commands/prompt.mjs
CHANGED
|
@@ -1,21 +1,22 @@
|
|
|
1
1
|
import { requireFlag } from '../core/args.mjs';
|
|
2
2
|
import { apiPost } from '../core/http.mjs';
|
|
3
|
+
import { getEffectiveRunMode } from '../core/runMode.mjs';
|
|
3
4
|
export function createPromptCommands({ printEnvelope }) {
|
|
4
5
|
async function commandPrompt(ctx, flags, capabilities, promptTokens) {
|
|
5
6
|
const prompt = String(flags.prompt || promptTokens.join(' ') || '').trim();
|
|
6
7
|
if (!prompt) {
|
|
7
8
|
throw new Error('Missing prompt text. Use: thirdfy-agent prompt "<text>"');
|
|
8
9
|
}
|
|
9
|
-
// Jeff rail: deterministic wrapper around existing run contract.
|
|
10
10
|
printEnvelope({
|
|
11
11
|
success: true,
|
|
12
12
|
code: 'PROMPT_RECEIVED',
|
|
13
|
-
message: 'Prompt captured;
|
|
13
|
+
message: 'Prompt captured; find the action with `actions`, check it with `preview`, then send it with `run`.',
|
|
14
14
|
data: {
|
|
15
15
|
prompt,
|
|
16
16
|
suggestedCommands: [
|
|
17
|
-
'thirdfy-agent
|
|
18
|
-
'thirdfy-agent
|
|
17
|
+
'thirdfy-agent actions --provider <provider> --json',
|
|
18
|
+
'thirdfy-agent preview --action <action> --params <json> --estimated-amount-usd <usd> --json',
|
|
19
|
+
'thirdfy-agent run --run-mode agent_wallet --action <action> --params <json> --estimated-amount-usd <usd> --json',
|
|
19
20
|
],
|
|
20
21
|
},
|
|
21
22
|
meta: {
|
package/src/commands/wallet.mjs
CHANGED
|
@@ -1,8 +1,14 @@
|
|
|
1
|
-
import { requireFlag } from '../core/args.mjs';
|
|
1
|
+
import { requireFlag, usageError } from '../core/args.mjs';
|
|
2
2
|
import { createCliError, exitAfterOutput } from '../core/envelope.mjs';
|
|
3
|
-
import { loadProfileConfig } from '../core/context.mjs';
|
|
4
|
-
import { DEFAULT_CHAIN_ID, parseChainIdFlag, resolveChainRpcUrl } from '../core/chainRegistry.mjs';
|
|
3
|
+
import { findAgentApiKey, loadProfileConfig } from '../core/context.mjs';
|
|
4
|
+
import { DEFAULT_CHAIN_ID, chainGasNote, isSvmChainId, parseChainIdFlag, resolveChainRpcUrl } from '../core/chainRegistry.mjs';
|
|
5
|
+
import { apiGet } from '../core/http.mjs';
|
|
6
|
+
import { getEffectiveRunMode } from '../core/runMode.mjs';
|
|
7
|
+
import { readWalletControl } from '../runtime/execution/walletControl.mjs';
|
|
8
|
+
import { assertSvmChainRunByApi } from '../runtime/execution/svmLane.mjs';
|
|
9
|
+
import { applySvmLane } from '../runtime/execution/svmLane.mjs';
|
|
5
10
|
import { readSignedTxChainId } from '../core/signedTx.mjs';
|
|
11
|
+
import { runNotionalWarnings } from '../runtime/risk/notional.mjs';
|
|
6
12
|
|
|
7
13
|
export function createWalletCommands(deps) {
|
|
8
14
|
const {
|
|
@@ -18,6 +24,9 @@ export function createWalletCommands(deps) {
|
|
|
18
24
|
executeSelfRun,
|
|
19
25
|
submitSignedTx,
|
|
20
26
|
printEnvelope,
|
|
27
|
+
resolveEffectiveUserDid,
|
|
28
|
+
resolveAgentKey,
|
|
29
|
+
buildOwnerAuthHeaders,
|
|
21
30
|
} = deps;
|
|
22
31
|
|
|
23
32
|
|
|
@@ -26,12 +35,15 @@ async function commandWalletExecute(ctx, flags, capabilities) {
|
|
|
26
35
|
const resolved = await resolveActionSelection(ctx, normalizedFlags);
|
|
27
36
|
prepareActionParamsForFlags(normalizedFlags, resolved.resolvedAction);
|
|
28
37
|
enforceCatalogActionParams(resolved, normalizedFlags);
|
|
38
|
+
applySvmLane(normalizedFlags, resolved, 'agent_wallet');
|
|
29
39
|
enforceChainCompatibility(capabilities, normalizedFlags, 'agent_wallet');
|
|
30
40
|
const managedPreflight =
|
|
31
41
|
(await applyHyperliquidAgentWalletAddressDefault(ctx, normalizedFlags, resolved, 'agent_wallet'))
|
|
32
42
|
|| (await applyLighterAgentWalletAddressDefault(ctx, normalizedFlags, resolved, 'agent_wallet'))
|
|
33
43
|
|| (await applyAvantisAgentWalletAddressDefault(ctx, normalizedFlags, resolved, 'agent_wallet'));
|
|
34
44
|
const skipPreflight = Boolean(normalizedFlags.skipPreflight);
|
|
45
|
+
// A write with no --estimated-amount-usd: a warning, or a refusal before anything is sent.
|
|
46
|
+
const warnings = runNotionalWarnings(resolved, normalizedFlags, 'agent_wallet');
|
|
35
47
|
const result = await executeManagedWalletRun(ctx, normalizedFlags, resolved, { skipPreflight, managedPreflight });
|
|
36
48
|
printEnvelope({
|
|
37
49
|
success: result.success,
|
|
@@ -47,6 +59,7 @@ async function commandWalletExecute(ctx, flags, capabilities) {
|
|
|
47
59
|
executionWalletAddress: result.executionWalletAddress || null,
|
|
48
60
|
profile: ctx.profile || null,
|
|
49
61
|
capabilitiesVersion: capabilities?.contractVersion || null,
|
|
62
|
+
...(warnings.length ? { warnings } : {}),
|
|
50
63
|
},
|
|
51
64
|
});
|
|
52
65
|
if (!result.success) await exitAfterOutput(1);
|
|
@@ -58,6 +71,7 @@ async function commandWalletSign(ctx, flags, capabilities) {
|
|
|
58
71
|
const resolved = await resolveActionSelection(ctx, normalizedFlags);
|
|
59
72
|
prepareActionParamsForFlags(normalizedFlags, resolved.resolvedAction);
|
|
60
73
|
enforceCatalogActionParams(resolved, normalizedFlags);
|
|
74
|
+
applySvmLane(normalizedFlags, resolved, 'self', { locked: true });
|
|
61
75
|
enforceOffchainVenueLaneCompatibility(resolved.resolvedAction, 'self');
|
|
62
76
|
enforceChainCompatibility(capabilities, normalizedFlags, 'self');
|
|
63
77
|
const selfResult = await executeSelfRun(ctx, normalizedFlags, resolved, {
|
|
@@ -91,6 +105,13 @@ async function commandWalletSubmit(ctx, flags, capabilities) {
|
|
|
91
105
|
if (Number.isNaN(flagChainId)) {
|
|
92
106
|
throw createCliError('INVALID_CHAIN_ID', `wallet submit --chain-id must be a positive integer (got ${flags.chainId})`);
|
|
93
107
|
}
|
|
108
|
+
if (isSvmChainId(flagChainId)) {
|
|
109
|
+
throw createCliError(
|
|
110
|
+
'SVM_REQUIRES_AGENT_WALLET',
|
|
111
|
+
`wallet submit broadcasts signed EVM transactions only. Solana (chain ${flagChainId}) actions run with --run-mode agent_wallet on the managed Solana agent wallet.`,
|
|
112
|
+
{ chainId: flagChainId, chainFamily: 'svm', recommendedRunModes: ['agent_wallet'] },
|
|
113
|
+
);
|
|
114
|
+
}
|
|
94
115
|
// The signed tx carries its chain id (EIP-155 / typed tx). Use it when --chain-id is absent,
|
|
95
116
|
// and refuse a mismatch so a DogeOS-signed tx is never sent to a Base RPC (or vice versa).
|
|
96
117
|
const txChainId = readSignedTxChainId(signedTxHex);
|
|
@@ -147,7 +168,102 @@ async function commandWalletList(ctx, _flags) {
|
|
|
147
168
|
});
|
|
148
169
|
}
|
|
149
170
|
|
|
171
|
+
/** The chain a readiness check uses, saying so when it assumed Base (thirdfy-mcp `resolveReadinessChain`). */
|
|
172
|
+
function resolveReadinessChain(flags) {
|
|
173
|
+
const chainId = parseChainIdFlag(flags.chainId);
|
|
174
|
+
// A bad --chain-id is an error, not a silent check of Base.
|
|
175
|
+
if (Number.isNaN(chainId)) throw usageError('--chain-id must be a positive integer chain id', 'chainId');
|
|
176
|
+
if (chainId) return { chainId };
|
|
177
|
+
return {
|
|
178
|
+
chainId: DEFAULT_CHAIN_ID,
|
|
179
|
+
chainIdAssumed: true,
|
|
180
|
+
chainIdNote: `--chain-id was not provided; assumed Base (${DEFAULT_CHAIN_ID}). Pass --chain-id for any other chain (for example DogeOS testnet 6281971, Solana 101).`,
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* `readiness`: one verdict on whether the agent can act, as thirdfy-mcp `getSelfReadiness`.
|
|
186
|
+
* - agent_wallet: the agent's execution wallet on the chain (`GET /agent/execution-wallet` with the
|
|
187
|
+
* agent's own key): its address, funding, gas, and wallet control (API 4.0), with `ready`,
|
|
188
|
+
* `walletControl` and `nextAction` at the top. No owner auth.
|
|
189
|
+
* - self / hybrid / thirdfy: the delegation check (`GET /agent/delegation/check`, owner auth).
|
|
190
|
+
*/
|
|
191
|
+
async function commandReadiness(ctx, flags, capabilities) {
|
|
192
|
+
const runMode = getEffectiveRunMode(ctx, flags);
|
|
193
|
+
const chain = resolveReadinessChain(flags);
|
|
194
|
+
const meta = { apiBase: ctx.apiBase, runMode, capabilitiesVersion: capabilities?.contractVersion || null };
|
|
195
|
+
const echo = {
|
|
196
|
+
runMode,
|
|
197
|
+
custodyMode: ctx.custodyMode || null,
|
|
198
|
+
chainId: chain.chainId,
|
|
199
|
+
...(chain.chainIdAssumed ? { chainIdAssumed: true, chainIdNote: chain.chainIdNote } : {}),
|
|
200
|
+
walletAddress: flags.walletAddress ? String(flags.walletAddress) : null,
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
if (runMode !== 'agent_wallet') {
|
|
204
|
+
const headers = buildOwnerAuthHeaders(flags, `readiness with --run-mode ${runMode} needs owner auth (--auth-token or --owner-session-token).`);
|
|
205
|
+
const agentKey = requireFlag({ agentKey: resolveAgentKey(flags) }, 'agentKey', 'Missing --agent-key (or THIRDFY_AGENT_KEY / config.agent.agentKey)');
|
|
206
|
+
const query = new URLSearchParams({ agentKey, chainId: String(chain.chainId) });
|
|
207
|
+
const readiness = await apiGet(ctx, `/api/v1/agent/delegation/check?${query.toString()}`, headers);
|
|
208
|
+
printEnvelope({
|
|
209
|
+
success: true,
|
|
210
|
+
code: 'READINESS_OK',
|
|
211
|
+
message: `Delegation readiness for ${runMode}`,
|
|
212
|
+
data: { ...echo, ...(chainGasNote(chain.chainId) ? { gas: chainGasNote(chain.chainId) } : {}), source: '/api/v1/agent/delegation/check', readiness },
|
|
213
|
+
meta,
|
|
214
|
+
});
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// A Solana chain the API does not run is refused as `run` refuses it (thirdfy-mcp unsupportedChainFields).
|
|
219
|
+
assertSvmChainRunByApi(chain.chainId);
|
|
220
|
+
// Only the agent's own key: readiness acts for the caller's agent.
|
|
221
|
+
const agentApiKey = findAgentApiKey(flags);
|
|
222
|
+
if (!agentApiKey) {
|
|
223
|
+
throw createCliError(
|
|
224
|
+
'AGENT_API_KEY_REQUIRED',
|
|
225
|
+
"readiness with --run-mode agent_wallet needs the agent's own API key (--agent-api-key, THIRDFY_AGENT_API_KEY or config).",
|
|
226
|
+
{ blockedStage: 'auth', runMode, chainId: chain.chainId },
|
|
227
|
+
);
|
|
228
|
+
}
|
|
229
|
+
const userDid = resolveEffectiveUserDid(flags, { runMode: 'agent_wallet' });
|
|
230
|
+
const query = new URLSearchParams({ chainId: String(chain.chainId), runMode: 'agent_wallet' });
|
|
231
|
+
if (userDid) query.set('userDid', userDid);
|
|
232
|
+
const data = await apiGet(ctx, `/api/v1/agent/execution-wallet?${query.toString()}`, { 'x-agent-api-key': agentApiKey });
|
|
233
|
+
// The API says per chain whether it sponsors the fee (Solana `gasSponsored`); that answer wins.
|
|
234
|
+
const gas = chainGasNote(chain.chainId, { gasSponsored: data?.gasSponsored });
|
|
235
|
+
const control = readWalletControl(data, { svm: isSvmChainId(chain.chainId) });
|
|
236
|
+
const raw = String(data?.fundingTokenBalance?.raw ?? '').trim();
|
|
237
|
+
const fundingReady = /^(0x[0-9a-fA-F]+|[0-9]+)$/.test(raw) ? BigInt(raw) > 0n : undefined;
|
|
238
|
+
const ready = Boolean(data?.executionWalletAddress) && data?.success !== false && !control.walletControlBlocked;
|
|
239
|
+
printEnvelope({
|
|
240
|
+
success: true,
|
|
241
|
+
code: ready ? 'READY' : 'NOT_READY',
|
|
242
|
+
message: ready ? 'The agent can act on this chain' : control.userMessage || 'The agent is not ready on this chain',
|
|
243
|
+
data: {
|
|
244
|
+
...echo,
|
|
245
|
+
...(gas ? { gas } : {}),
|
|
246
|
+
source: '/api/v1/agent/execution-wallet',
|
|
247
|
+
ready,
|
|
248
|
+
// `ready` says the wallet can sign; whether it holds the funding token is this separate field.
|
|
249
|
+
...(fundingReady !== undefined ? { fundingReady } : {}),
|
|
250
|
+
...(control.walletControl ? { walletControl: control.walletControl } : {}),
|
|
251
|
+
...(control.nextAction ? { nextAction: control.nextAction, userMessage: control.userMessage } : {}),
|
|
252
|
+
readiness: {
|
|
253
|
+
executionWalletAddress: data?.executionWalletAddress || null,
|
|
254
|
+
userDid: userDid || data?.userDid || null,
|
|
255
|
+
ready,
|
|
256
|
+
...(fundingReady !== undefined ? { fundingReady } : {}),
|
|
257
|
+
...(control.walletControlBlocked ? { blockedBy: 'wallet_control', blocker: control.walletControlBlocker, signerActive: false } : {}),
|
|
258
|
+
data,
|
|
259
|
+
},
|
|
260
|
+
},
|
|
261
|
+
meta,
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
|
|
150
265
|
return {
|
|
266
|
+
commandReadiness,
|
|
151
267
|
commandWalletExecute,
|
|
152
268
|
commandWalletSign,
|
|
153
269
|
commandWalletSubmit,
|