@thirdfy/agent-cli 0.4.1 → 0.4.3
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 +39 -0
- package/README.md +4 -7
- package/package.json +5 -3
- package/src/cli/help.mjs +1 -1
- package/src/cli/options/index.mjs +11 -11
- package/src/cli/options/onboarding/credentials.mjs +17 -0
- package/src/cli/program.mjs +1 -1
- package/src/commands/{agent.mjs → account/agent.mjs} +5 -5
- package/src/commands/{credits.mjs → account/credits.mjs} +13 -15
- package/src/commands/{wallet.mjs → account/wallet.mjs} +29 -33
- package/src/commands/{walletControl.mjs → account/walletControl.mjs} +3 -3
- package/src/commands/discovery.mjs +1 -1
- package/src/commands/doctor.mjs +5 -5
- package/src/commands/execute.mjs +4 -13
- package/src/commands/index.mjs +9 -9
- package/src/commands/{bootstrap.mjs → onboarding/bootstrap.mjs} +8 -8
- package/src/commands/{consoleMandate.mjs → onboarding/consoleMandate.mjs} +2 -2
- package/src/commands/{credentials.mjs → onboarding/credentials.mjs} +16 -29
- package/src/commands/{delegation.mjs → onboarding/delegation.mjs} +5 -5
- package/src/commands/{avantis.mjs → venues/avantis.mjs} +1 -1
- package/src/commands/{condor.mjs → venues/condor.mjs} +4 -4
- package/src/commands/{hummingbot.mjs → venues/hummingbot.mjs} +4 -4
- package/src/commands/{hyperliquid.mjs → venues/hyperliquid.mjs} +1 -1
- package/src/commands/{lighter.mjs → venues/lighter.mjs} +1 -1
- package/src/commands/{polymarket.mjs → venues/polymarket.mjs} +4 -4
- package/src/commands/{venue.mjs → venues/venue.mjs} +3 -3
- package/src/core/chainRegistry.mjs +11 -0
- package/src/core/constants.mjs +0 -19
- package/src/core/earnclawAttribution.mjs +1 -1
- package/src/core/{actionTimeouts.mjs → execution/actionTimeouts.mjs} +2 -2
- package/src/core/{walletExecuteChainId.mjs → execution/walletExecuteChainId.mjs} +1 -1
- package/src/core/http.mjs +1 -1
- package/src/runtime/actions/selection.mjs +0 -1
- package/src/runtime/execution/hostedRuntimeWriteGate.mjs +1 -1
- package/src/runtime/execution/payloads.mjs +4 -4
- package/src/runtime/execution/runners/agentWalletLane.mjs +109 -0
- package/src/runtime/execution/runners/createExecutionRunners.mjs +312 -0
- package/src/runtime/execution/runners/hybridLane.mjs +88 -0
- package/src/runtime/execution/runners/managedPreflight.mjs +253 -0
- package/src/runtime/execution/runners/managedResponse.mjs +119 -0
- package/src/runtime/execution/runners/selfLane.mjs +233 -0
- package/src/runtime/execution/runners/thirdfyLane.mjs +97 -0
- package/src/runtime/execution/svmLane.mjs +1 -1
- package/src/runtime/handlers.mjs +15 -20
- package/src/runtime/main.mjs +1 -11
- package/src/runtime/owsSigning.mjs +17 -16
- package/src/runtime/providerHints/cex.mjs +1 -52
- package/src/runtime/risk/notional.mjs +1 -1
- package/src/runtime/validation/actionParamsGate.mjs +1 -1
- package/src/cli/options/credentials.mjs +0 -15
- package/src/runtime/bitfinexProbe.mjs +0 -121
- package/src/runtime/execution/runners.mjs +0 -1134
- package/src/runtime/onboardingHints.mjs +0 -54
- /package/src/cli/options/{agent.mjs → account/agent.mjs} +0 -0
- /package/src/cli/options/{credits.mjs → account/credits.mjs} +0 -0
- /package/src/cli/options/{wallet.mjs → account/wallet.mjs} +0 -0
- /package/src/cli/options/{walletControl.mjs → account/walletControl.mjs} +0 -0
- /package/src/cli/options/{bootstrap.mjs → onboarding/bootstrap.mjs} +0 -0
- /package/src/cli/options/{delegation.mjs → onboarding/delegation.mjs} +0 -0
- /package/src/cli/options/{condor.mjs → venues/condor.mjs} +0 -0
- /package/src/cli/options/{hummingbot.mjs → venues/hummingbot.mjs} +0 -0
- /package/src/cli/options/{polymarket.mjs → venues/polymarket.mjs} +0 -0
- /package/src/cli/options/{venue.mjs → venues/venue.mjs} +0 -0
- /package/src/core/{executionContext.mjs → execution/executionContext.mjs} +0 -0
- /package/src/core/{fundingTokenIn.mjs → execution/fundingTokenIn.mjs} +0 -0
- /package/src/core/{hostedRuntimeIdempotency.mjs → execution/hostedRuntimeIdempotency.mjs} +0 -0
- /package/src/core/{readOnlyActions.mjs → execution/readOnlyActions.mjs} +0 -0
- /package/src/core/{signedTx.mjs → execution/signedTx.mjs} +0 -0
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { apiPost, responseHeadersFor } from '../../../core/http.mjs';
|
|
2
|
+
import { idempotentReplayFields } from '../../../core/execution/hostedRuntimeIdempotency.mjs';
|
|
3
|
+
import { withActionTimeout } from '../../../core/execution/actionTimeouts.mjs';
|
|
4
|
+
import { actionNameForApi } from '../../actions/selection.mjs';
|
|
5
|
+
import { isReadOnlyAction } from './managedResponse.mjs';
|
|
6
|
+
import { createRequire } from 'module';
|
|
7
|
+
|
|
8
|
+
const require = createRequire(import.meta.url);
|
|
9
|
+
const { normalizeIntentResponse } = require('../../../utils/cli/intentNormalization.cjs');
|
|
10
|
+
|
|
11
|
+
/** The thirdfy lane (execute intent), also the rail read-only actions use from the other lanes. */
|
|
12
|
+
export function createThirdfyLane(deps) {
|
|
13
|
+
const {
|
|
14
|
+
buildIntentPayload,
|
|
15
|
+
resolveEffectiveUserDid,
|
|
16
|
+
} = deps;
|
|
17
|
+
|
|
18
|
+
async function executeThirdfyRun(ctx, flags, resolved, options, runMode = 'thirdfy') {
|
|
19
|
+
const skipPreflight = Boolean(options?.skipPreflight);
|
|
20
|
+
const actionCtx = withActionTimeout(ctx, resolved);
|
|
21
|
+
if (!skipPreflight) {
|
|
22
|
+
const preflightPayload = buildIntentPayload(flags, {
|
|
23
|
+
validationOnly: true,
|
|
24
|
+
forceIdempotency: false,
|
|
25
|
+
resolvedAction: resolved.resolvedAction,
|
|
26
|
+
apiAction: actionNameForApi(resolved),
|
|
27
|
+
runMode,
|
|
28
|
+
mirrorOnly: false,
|
|
29
|
+
hybridWalletMode: options?.hybridWalletMode,
|
|
30
|
+
});
|
|
31
|
+
// validationOnly never writes, so a timed-out preflight is safe to resend.
|
|
32
|
+
const preflight = await apiPost(
|
|
33
|
+
{ ...actionCtx, writeAction: false },
|
|
34
|
+
'/api/v1/agent/execute-intent',
|
|
35
|
+
preflightPayload,
|
|
36
|
+
);
|
|
37
|
+
const preflightNormalized = normalizeIntentResponse(preflight);
|
|
38
|
+
const blockedCount = preflightNormalized.blocked || 0;
|
|
39
|
+
if (!preflightNormalized.success || blockedCount > 0) {
|
|
40
|
+
preflightNormalized.success = false;
|
|
41
|
+
preflightNormalized.error = preflightNormalized.error || 'Preflight blocked execution';
|
|
42
|
+
preflightNormalized.preflightBlocked = true;
|
|
43
|
+
return preflightNormalized;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
const payload = buildIntentPayload(flags, {
|
|
47
|
+
validationOnly: false,
|
|
48
|
+
forceIdempotency: true,
|
|
49
|
+
resolvedAction: resolved.resolvedAction,
|
|
50
|
+
apiAction: actionNameForApi(resolved),
|
|
51
|
+
runMode,
|
|
52
|
+
mirrorOnly: false,
|
|
53
|
+
hybridWalletMode: options?.hybridWalletMode,
|
|
54
|
+
});
|
|
55
|
+
const response = await apiPost(actionCtx, '/api/v1/agent/execute-intent', payload);
|
|
56
|
+
const normalized = normalizeIntentResponse(response);
|
|
57
|
+
normalized.idempotencyKey = payload.idempotencyKey;
|
|
58
|
+
if (!isReadOnlyAction(resolved)) {
|
|
59
|
+
Object.assign(normalized, idempotentReplayFields(response, responseHeadersFor(response), 'idempotencyKey'));
|
|
60
|
+
}
|
|
61
|
+
return normalized;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
async function runReadOnlyActionViaIntentRail(ctx, flags, resolved, options, preflight, reportedMode = 'agent_wallet') {
|
|
65
|
+
const userDid = resolveEffectiveUserDid(flags, {
|
|
66
|
+
runMode: 'agent_wallet',
|
|
67
|
+
hybridWalletMode: options?.hybridWalletMode,
|
|
68
|
+
preferDelegationIdentity: options?.preferDelegationIdentity,
|
|
69
|
+
});
|
|
70
|
+
const intentResult = await executeThirdfyRun(
|
|
71
|
+
ctx,
|
|
72
|
+
{
|
|
73
|
+
...flags,
|
|
74
|
+
chainId: Number(flags.chainId || 8453),
|
|
75
|
+
runMode: 'thirdfy',
|
|
76
|
+
userDid: userDid || flags.userDid,
|
|
77
|
+
executionScope: flags.executionScope || 'solo_owner_mirror',
|
|
78
|
+
},
|
|
79
|
+
resolved,
|
|
80
|
+
{ ...options, skipPreflight: true },
|
|
81
|
+
'thirdfy'
|
|
82
|
+
);
|
|
83
|
+
intentResult.mode = reportedMode;
|
|
84
|
+
intentResult.routeFallback = 'execute_intent_read_only';
|
|
85
|
+
if (preflight) {
|
|
86
|
+
intentResult.executionWalletPreflight = preflight;
|
|
87
|
+
intentResult.executionWalletAddress = preflight.executionWalletAddress || intentResult.executionWalletAddress || null;
|
|
88
|
+
}
|
|
89
|
+
intentResult.signerMethod = 'managed_wallet_server';
|
|
90
|
+
return intentResult;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return {
|
|
94
|
+
executeThirdfyRun,
|
|
95
|
+
runReadOnlyActionViaIntentRail,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
@@ -4,7 +4,7 @@ import { chainRegistrySource, configuredSvmChainIds, getChainInfo, isSvmChainId,
|
|
|
4
4
|
import {
|
|
5
5
|
inferChainIdFromCatalogRow,
|
|
6
6
|
resolveWalletExecuteChainId,
|
|
7
|
-
} from '../../core/walletExecuteChainId.mjs';
|
|
7
|
+
} from '../../core/execution/walletExecuteChainId.mjs';
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* Solana (svm) lane rules. Solana actions run only with run mode `agent_wallet` on the managed
|
package/src/runtime/handlers.mjs
CHANGED
|
@@ -1,27 +1,27 @@
|
|
|
1
1
|
import { negotiateCapabilities } from '../core/http.mjs';
|
|
2
|
-
import { createPolymarketCommands } from '../commands/polymarket.mjs';
|
|
3
|
-
import { createHummingbotCommands } from '../commands/hummingbot.mjs';
|
|
4
|
-
import { createCondorCommands } from '../commands/condor.mjs';
|
|
5
|
-
import { createHyperliquidHelpers } from '../commands/hyperliquid.mjs';
|
|
6
|
-
import { createLighterHelpers } from '../commands/lighter.mjs';
|
|
7
|
-
import { createAvantisHelpers } from '../commands/avantis.mjs';
|
|
2
|
+
import { createPolymarketCommands } from '../commands/venues/polymarket.mjs';
|
|
3
|
+
import { createHummingbotCommands } from '../commands/venues/hummingbot.mjs';
|
|
4
|
+
import { createCondorCommands } from '../commands/venues/condor.mjs';
|
|
5
|
+
import { createHyperliquidHelpers } from '../commands/venues/hyperliquid.mjs';
|
|
6
|
+
import { createLighterHelpers } from '../commands/venues/lighter.mjs';
|
|
7
|
+
import { createAvantisHelpers } from '../commands/venues/avantis.mjs';
|
|
8
8
|
import { createExecuteCommands } from '../commands/execute.mjs';
|
|
9
|
-
import { createWalletCommands } from '../commands/wallet.mjs';
|
|
10
|
-
import { createDelegationCommands } from '../commands/delegation.mjs';
|
|
11
|
-
import { createConsoleMandateCommands } from '../commands/consoleMandate.mjs';
|
|
12
|
-
import { createBootstrapCommands } from '../commands/bootstrap.mjs';
|
|
9
|
+
import { createWalletCommands } from '../commands/account/wallet.mjs';
|
|
10
|
+
import { createDelegationCommands } from '../commands/onboarding/delegation.mjs';
|
|
11
|
+
import { createConsoleMandateCommands } from '../commands/onboarding/consoleMandate.mjs';
|
|
12
|
+
import { createBootstrapCommands } from '../commands/onboarding/bootstrap.mjs';
|
|
13
13
|
import { createDiscoveryCommands } from '../commands/discovery.mjs';
|
|
14
14
|
import { createSignalsCommands } from '../commands/signals/command.mjs';
|
|
15
|
-
import { createCreditsCommands } from '../commands/credits.mjs';
|
|
15
|
+
import { createCreditsCommands } from '../commands/account/credits.mjs';
|
|
16
16
|
import { createChatCommands } from '../commands/chat.mjs';
|
|
17
17
|
import { createBrainCommands } from '../commands/brain.mjs';
|
|
18
18
|
import { createTelemetryCommands } from '../commands/telemetry.mjs';
|
|
19
|
-
import { createCredentialsCommands } from '../commands/credentials.mjs';
|
|
20
|
-
import { createAgentCommands } from '../commands/agent.mjs';
|
|
21
|
-
import { createWalletControlCommands } from '../commands/walletControl.mjs';
|
|
19
|
+
import { createCredentialsCommands } from '../commands/onboarding/credentials.mjs';
|
|
20
|
+
import { createAgentCommands } from '../commands/account/agent.mjs';
|
|
21
|
+
import { createWalletControlCommands } from '../commands/account/walletControl.mjs';
|
|
22
22
|
import { createDoctorCommands } from '../commands/doctor.mjs';
|
|
23
23
|
import { createPromptCommands } from '../commands/prompt.mjs';
|
|
24
|
-
import { createVenueCommands } from '../commands/venue.mjs';
|
|
24
|
+
import { createVenueCommands } from '../commands/venues/venue.mjs';
|
|
25
25
|
|
|
26
26
|
/**
|
|
27
27
|
* Wire command-module factories and local handlers for Commander manifest registration.
|
|
@@ -35,7 +35,6 @@ export function createRuntimeHandlers(deps) {
|
|
|
35
35
|
maskSecret,
|
|
36
36
|
getAuthToken,
|
|
37
37
|
buildOwnerAuthHeaders,
|
|
38
|
-
probeBitfinexPermissions,
|
|
39
38
|
applyActionFilters,
|
|
40
39
|
getProviderHintForActions,
|
|
41
40
|
resolveChainSupport,
|
|
@@ -51,7 +50,6 @@ export function createRuntimeHandlers(deps) {
|
|
|
51
50
|
submitSignedTx,
|
|
52
51
|
runPreflightByMode,
|
|
53
52
|
runExecutionByMode,
|
|
54
|
-
applyExecutionFallbackHints,
|
|
55
53
|
performSelfBroadcast,
|
|
56
54
|
resolveManagedExecutionPreflight,
|
|
57
55
|
resolveEffectiveUserDid,
|
|
@@ -66,7 +64,6 @@ export function createRuntimeHandlers(deps) {
|
|
|
66
64
|
applyActionFilters,
|
|
67
65
|
getProviderHintForActions,
|
|
68
66
|
resolveChainSupport,
|
|
69
|
-
getAuthToken,
|
|
70
67
|
buildOwnerAuthHeaders,
|
|
71
68
|
resolveAgentApiKey,
|
|
72
69
|
});
|
|
@@ -98,7 +95,6 @@ export function createRuntimeHandlers(deps) {
|
|
|
98
95
|
printEnvelope,
|
|
99
96
|
getAuthToken,
|
|
100
97
|
buildOwnerAuthHeaders,
|
|
101
|
-
probeBitfinexPermissions,
|
|
102
98
|
});
|
|
103
99
|
|
|
104
100
|
const __walletControl = createWalletControlCommands({ printEnvelope, buildOwnerAuthHeaders });
|
|
@@ -174,7 +170,6 @@ export function createRuntimeHandlers(deps) {
|
|
|
174
170
|
runPreflightByMode,
|
|
175
171
|
runExecutionByMode,
|
|
176
172
|
resolveManagedExecutionPreflight,
|
|
177
|
-
applyExecutionFallbackHints,
|
|
178
173
|
performSelfBroadcast,
|
|
179
174
|
executeManagedWalletRun,
|
|
180
175
|
executeSelfRun,
|
package/src/runtime/main.mjs
CHANGED
|
@@ -16,16 +16,14 @@ import { createInitialRuntimeContext, applyFlagsToRuntimeContext } from './conte
|
|
|
16
16
|
import { createProviderHints } from './providerHints/dispatch.mjs';
|
|
17
17
|
import { createRuntimeHandlers } from './handlers.mjs';
|
|
18
18
|
import { isCommanderCliError } from './errors.mjs';
|
|
19
|
-
import { createOnboardingHints } from './onboardingHints.mjs';
|
|
20
19
|
import { walletControlErrorFields } from './execution/walletControl.mjs';
|
|
21
20
|
import { createAgentExtract } from './agentExtract.mjs';
|
|
22
21
|
import { createAuthHelpers } from './authHelpers.mjs';
|
|
23
22
|
import { createExecutionAmounts } from './execution/amounts.mjs';
|
|
24
23
|
import { createExecutionLanes } from './execution/lanes.mjs';
|
|
25
24
|
import { createExecutionPayloads } from './execution/payloads.mjs';
|
|
26
|
-
import { createExecutionRunners } from './execution/runners.mjs';
|
|
25
|
+
import { createExecutionRunners } from './execution/runners/createExecutionRunners.mjs';
|
|
27
26
|
import { createActionSelection } from './actions/selection.mjs';
|
|
28
|
-
import { createBitfinexProbe } from './bitfinexProbe.mjs';
|
|
29
27
|
import { createOwsSigning } from './owsSigning.mjs';
|
|
30
28
|
import { enforceCatalogActionParams } from './validation/actionParamsGate.mjs';
|
|
31
29
|
|
|
@@ -66,7 +64,6 @@ export function createThirdfyAgentRuntime() {
|
|
|
66
64
|
providerHints;
|
|
67
65
|
|
|
68
66
|
const { extractAgentApiKey, extractAgentKey, maskSecret } = createAgentExtract();
|
|
69
|
-
const onboarding = createOnboardingHints();
|
|
70
67
|
const amounts = createExecutionAmounts();
|
|
71
68
|
const lanes = createExecutionLanes();
|
|
72
69
|
const auth = createAuthHelpers();
|
|
@@ -82,7 +79,6 @@ export function createThirdfyAgentRuntime() {
|
|
|
82
79
|
const catalogActionParamsGate = (resolved, flags) =>
|
|
83
80
|
enforceCatalogActionParams(resolved, flags, amounts.getPreparedParams);
|
|
84
81
|
const ows = createOwsSigning();
|
|
85
|
-
const bitfinex = createBitfinexProbe();
|
|
86
82
|
const runners = createExecutionRunners({
|
|
87
83
|
buildIntentPayload: payloads.buildIntentPayload,
|
|
88
84
|
buildBuildTxPayload: payloads.buildBuildTxPayload,
|
|
@@ -92,8 +88,6 @@ export function createThirdfyAgentRuntime() {
|
|
|
92
88
|
resolveTokenInForFunding: payloads.resolveTokenInForFunding,
|
|
93
89
|
shouldUseBuildTxPreflight: lanes.shouldUseBuildTxPreflight,
|
|
94
90
|
shouldUseBuildTxExecution: lanes.shouldUseBuildTxExecution,
|
|
95
|
-
buildBitfinexOnboardingCommand: onboarding.buildBitfinexOnboardingCommand,
|
|
96
|
-
inferBitfinexPermissionProfileFromAction: onboarding.inferBitfinexPermissionProfileFromAction,
|
|
97
91
|
resolveAgentApiKey: payloads.resolveAgentApiKey,
|
|
98
92
|
resolveAgentKey: payloads.resolveAgentKey,
|
|
99
93
|
loadEthers: ows.loadEthers,
|
|
@@ -111,7 +105,6 @@ export function createThirdfyAgentRuntime() {
|
|
|
111
105
|
maskSecret,
|
|
112
106
|
getAuthToken: auth.getAuthToken,
|
|
113
107
|
buildOwnerAuthHeaders: auth.buildOwnerAuthHeaders,
|
|
114
|
-
probeBitfinexPermissions: bitfinex.probeBitfinexPermissions,
|
|
115
108
|
applyActionFilters: actions.applyActionFilters,
|
|
116
109
|
getProviderHintForActions,
|
|
117
110
|
resolveChainSupport: lanes.resolveChainSupport,
|
|
@@ -127,7 +120,6 @@ export function createThirdfyAgentRuntime() {
|
|
|
127
120
|
submitSignedTx: runners.submitSignedTx,
|
|
128
121
|
runPreflightByMode: runners.runPreflightByMode,
|
|
129
122
|
runExecutionByMode: runners.runExecutionByMode,
|
|
130
|
-
applyExecutionFallbackHints: runners.applyExecutionFallbackHints,
|
|
131
123
|
performSelfBroadcast: runners.performSelfBroadcast,
|
|
132
124
|
resolveManagedExecutionPreflight: runners.resolveManagedExecutionPreflight,
|
|
133
125
|
resolveEffectiveUserDid: payloads.resolveEffectiveUserDid,
|
|
@@ -195,7 +187,6 @@ export function createThirdfyAgentRuntime() {
|
|
|
195
187
|
const blockedReason = String(payload.blockedReason || payload.error || '').trim() || undefined;
|
|
196
188
|
const blockedStage = String(payload.blockedStage || '').trim() || undefined;
|
|
197
189
|
const clientCode = String(payload.error || payload.blockedReason || '').trim() || undefined;
|
|
198
|
-
const onboardingHint = onboarding.buildTopLevelOnboardingHint(payload);
|
|
199
190
|
printEnvelope({
|
|
200
191
|
success: false,
|
|
201
192
|
// A missing or malformed flag is INVALID_ARGUMENTS at the top too, not an unhandled error.
|
|
@@ -209,7 +200,6 @@ export function createThirdfyAgentRuntime() {
|
|
|
209
200
|
field: payload.field || null,
|
|
210
201
|
resolvedAction: payload.resolvedAction || null,
|
|
211
202
|
requestedAction: payload.requestedAction || null,
|
|
212
|
-
onboardingHint,
|
|
213
203
|
// errorCode, retryable, retryAfterSeconds, nextAction, hint: as thirdfy-mcp's error envelope.
|
|
214
204
|
...describeCliError(error),
|
|
215
205
|
// API 4.0 wallet-control refusals: walletControl, nextAction, userMessage, retryable, hint.
|
|
@@ -1,19 +1,20 @@
|
|
|
1
1
|
import { spawnSync } from 'child_process';
|
|
2
2
|
import fs from 'fs';
|
|
3
|
+
import { createRequire } from 'module';
|
|
3
4
|
import os from 'os';
|
|
4
5
|
import path from 'path';
|
|
6
|
+
import { pinnedRpcProvider } from '../core/chainRegistry.mjs';
|
|
5
7
|
import { createCliError } from '../core/envelope.mjs';
|
|
8
|
+
|
|
9
|
+
const require = createRequire(import.meta.url);
|
|
6
10
|
export function createOwsSigning() {
|
|
11
|
+
// ethers is a dependency of this package; resolve it from here, never from the caller's directory.
|
|
7
12
|
async function loadEthers() {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
} catch {
|
|
13
|
-
// continue
|
|
14
|
-
}
|
|
13
|
+
try {
|
|
14
|
+
return require('ethers');
|
|
15
|
+
} catch {
|
|
16
|
+
throw new Error('Could not load the ethers dependency of @thirdfy/agent-cli; reinstall the package');
|
|
15
17
|
}
|
|
16
|
-
throw new Error('Could not load ethers dependency for self-exec');
|
|
17
18
|
}
|
|
18
19
|
|
|
19
20
|
function resolveOwsBinary() {
|
|
@@ -62,8 +63,8 @@ function signUnsignedTxWithOws(unsignedHex, walletName, chainId) {
|
|
|
62
63
|
|
|
63
64
|
async function signAndBroadcastWithOws({ txd, chainId, walletAddress, owsWalletName, rpcUrl }) {
|
|
64
65
|
const ethers = await loadEthers();
|
|
65
|
-
const {
|
|
66
|
-
const provider =
|
|
66
|
+
const { Transaction, Signature } = ethers;
|
|
67
|
+
const provider = pinnedRpcProvider(ethers, { rpcUrl, chainId });
|
|
67
68
|
const nonce = await provider.getTransactionCount(walletAddress, 'pending');
|
|
68
69
|
const fee = await provider.getFeeData();
|
|
69
70
|
const txFields = {
|
|
@@ -86,10 +87,10 @@ async function signAndBroadcastWithOws({ txd, chainId, walletAddress, owsWalletN
|
|
|
86
87
|
return { sent };
|
|
87
88
|
}
|
|
88
89
|
|
|
89
|
-
async function readTokenBalanceRaw(rpcUrl, walletAddress, tokenAddress) {
|
|
90
|
+
async function readTokenBalanceRaw(rpcUrl, walletAddress, tokenAddress, chainId) {
|
|
90
91
|
const ethers = await loadEthers();
|
|
91
|
-
const {
|
|
92
|
-
const provider =
|
|
92
|
+
const { Interface } = ethers;
|
|
93
|
+
const provider = pinnedRpcProvider(ethers, { rpcUrl, chainId });
|
|
93
94
|
const erc20 = new Interface(['function balanceOf(address owner) view returns (uint256)']);
|
|
94
95
|
const response = await provider.call({
|
|
95
96
|
to: tokenAddress,
|
|
@@ -99,10 +100,10 @@ async function readTokenBalanceRaw(rpcUrl, walletAddress, tokenAddress) {
|
|
|
99
100
|
return BigInt(decoded[0].toString()).toString();
|
|
100
101
|
}
|
|
101
102
|
|
|
102
|
-
async function readEffectBalances(rpcUrl, walletAddress, tokenIn, tokenOut) {
|
|
103
|
+
async function readEffectBalances(rpcUrl, walletAddress, tokenIn, tokenOut, chainId) {
|
|
103
104
|
const [inBal, outBal] = await Promise.all([
|
|
104
|
-
readTokenBalanceRaw(rpcUrl, walletAddress, tokenIn),
|
|
105
|
-
readTokenBalanceRaw(rpcUrl, walletAddress, tokenOut),
|
|
105
|
+
readTokenBalanceRaw(rpcUrl, walletAddress, tokenIn, chainId),
|
|
106
|
+
readTokenBalanceRaw(rpcUrl, walletAddress, tokenOut, chainId),
|
|
106
107
|
]);
|
|
107
108
|
return { tokenIn: inBal, tokenOut: outBal };
|
|
108
109
|
}
|
|
@@ -1,59 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Static provider hints. Centralized
|
|
2
|
+
* Static provider hints. Centralized exchange executors: Hummingbot and Condor.
|
|
3
3
|
* Each entry builds the hint for one provider id; `dispatch.mjs` picks the entry.
|
|
4
4
|
*/
|
|
5
5
|
export const CEX_PROVIDER_HINTS = Object.freeze({
|
|
6
|
-
bitfinex: ({ provider }) => ({
|
|
7
|
-
provider,
|
|
8
|
-
category: 'cex_funding_and_spot',
|
|
9
|
-
canonicalReadAction: 'get_bitfinex_funding_ticker',
|
|
10
|
-
canonicalWriteAction: 'submit_bitfinex_funding_offer',
|
|
11
|
-
readActions: [
|
|
12
|
-
'get_bitfinex_funding_stats_hist',
|
|
13
|
-
'get_bitfinex_funding_ticker',
|
|
14
|
-
'get_bitfinex_funding_book',
|
|
15
|
-
'get_bitfinex_wallets',
|
|
16
|
-
'list_bitfinex_funding_offers',
|
|
17
|
-
'list_bitfinex_funding_offers_hist',
|
|
18
|
-
'list_bitfinex_funding_credits',
|
|
19
|
-
'list_bitfinex_funding_loans',
|
|
20
|
-
'get_bitfinex_funding_info',
|
|
21
|
-
'get_bitfinex_ledgers_hist',
|
|
22
|
-
'get_bitfinex_orders',
|
|
23
|
-
],
|
|
24
|
-
orderManagementActions: [
|
|
25
|
-
'submit_bitfinex_funding_offer',
|
|
26
|
-
'cancel_bitfinex_funding_offer',
|
|
27
|
-
'transfer_bitfinex_between_wallets',
|
|
28
|
-
'get_bitfinex_orders',
|
|
29
|
-
'submit_bitfinex_order',
|
|
30
|
-
'update_bitfinex_order',
|
|
31
|
-
'cancel_bitfinex_order',
|
|
32
|
-
],
|
|
33
|
-
credentialType: 'api_key',
|
|
34
|
-
credentialRef: 'bitfinex:funding-primary',
|
|
35
|
-
credentialUx: 'Bitfinex credentials are owned by Thirdfy API encrypted delegation storage. CLI commands pass credentialRef only; never apiKey/apiSecret.',
|
|
36
|
-
laneCompatibility: 'Use `thirdfy-agent actions --provider bitfinex` for discovery, then `thirdfy-agent run --run-mode thirdfy` with credentialRef in params. The CLI does not store, log, or sign raw exchange credentials.',
|
|
37
|
-
example: 'thirdfy-agent actions --provider bitfinex && thirdfy-agent run --run-mode thirdfy --action submit_bitfinex_funding_offer --params \'{"symbol":"fUSD","amount":"100","rate":"0.0002","period":2,"credentialRef":"bitfinex:funding-primary"}\'',
|
|
38
|
-
credentialProfiles: [
|
|
39
|
-
{
|
|
40
|
-
id: 'bitfinex_funding_v1',
|
|
41
|
-
credentialRef: 'bitfinex:funding-primary',
|
|
42
|
-
required: ['funding.read', 'funding.write', 'wallets.read'],
|
|
43
|
-
},
|
|
44
|
-
{
|
|
45
|
-
id: 'bitfinex_spot_v1',
|
|
46
|
-
credentialRef: 'bitfinex:spot-primary',
|
|
47
|
-
required: ['orders.read', 'orders.write', 'wallets.read'],
|
|
48
|
-
},
|
|
49
|
-
],
|
|
50
|
-
setupBlockers: [
|
|
51
|
-
'CEX_CREDENTIAL_NOT_FOUND',
|
|
52
|
-
'BITFINEX_CREDENTIALS_NOT_IN_THIRDFY_DELEGATION_CONTEXT',
|
|
53
|
-
'BITFINEX_PERMISSION_SCOPE_MISSING',
|
|
54
|
-
'CEX_POLICY_ACTION_NOT_ALLOWED',
|
|
55
|
-
],
|
|
56
|
-
}),
|
|
57
6
|
hummingbot: ({ provider }) => ({
|
|
58
7
|
provider,
|
|
59
8
|
category: 'cex_executor',
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createCliError } from '../../core/envelope.mjs';
|
|
2
|
-
import { isReadOnlyResolvedAction } from '../../core/readOnlyActions.mjs';
|
|
2
|
+
import { isReadOnlyResolvedAction } from '../../core/execution/readOnlyActions.mjs';
|
|
3
3
|
import { resolveExecuteEstimatedAmountUsd } from '../execution/payloads.mjs';
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createCliError } from '../../core/envelope.mjs';
|
|
2
|
-
import { isReadOnlyResolvedAction } from '../../core/readOnlyActions.mjs';
|
|
2
|
+
import { isReadOnlyResolvedAction } from '../../core/execution/readOnlyActions.mjs';
|
|
3
3
|
import {
|
|
4
4
|
sanitizeParamsForSchemaValidation,
|
|
5
5
|
validateParamsSchema,
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
export function attachCredentialsOptions(cmd) {
|
|
2
|
-
return cmd
|
|
3
|
-
.option('--venue <id>', 'credential venue')
|
|
4
|
-
.option('--api-key <key>', 'exchange API key')
|
|
5
|
-
.option('--api-secret <secret>', 'exchange API secret')
|
|
6
|
-
.option('--permission-profile <profile>', 'funding|spot')
|
|
7
|
-
.option('--credential-ref <ref>', 'credential reference')
|
|
8
|
-
.option('--credential-type <type>', 'credential type')
|
|
9
|
-
.option('--verify-permissions', 'verify exchange permissions')
|
|
10
|
-
.option('--skip-permissions-probe', 'skip permissions probe')
|
|
11
|
-
.option('--bitfinex-base-url <url>', 'Bitfinex API base URL')
|
|
12
|
-
.option('--passphrase <value>', 'exchange passphrase')
|
|
13
|
-
.option('--metadata <json>', 'credential metadata JSON')
|
|
14
|
-
.option('--policy <json>', 'credential policy JSON');
|
|
15
|
-
}
|
|
@@ -1,121 +0,0 @@
|
|
|
1
|
-
import { createHmac } from 'crypto';
|
|
2
|
-
import { DEFAULT_BITFINEX_BASE_URL, BITFINEX_PERMISSION_PROFILES } from '../core/constants.mjs';
|
|
3
|
-
import { createCliError } from '../core/envelope.mjs';
|
|
4
|
-
import { requestJson, safeJsonParse } from '../core/http.mjs';
|
|
5
|
-
export function createBitfinexProbe() {
|
|
6
|
-
function parseBitfinexPermissionRows(rawData) {
|
|
7
|
-
const rows = Array.isArray(rawData) ? rawData : [];
|
|
8
|
-
const parsed = {};
|
|
9
|
-
for (const row of rows) {
|
|
10
|
-
if (!Array.isArray(row) || row.length < 3) continue;
|
|
11
|
-
const scope = String(row[0] || '').trim().toLowerCase();
|
|
12
|
-
if (!scope) continue;
|
|
13
|
-
const read = Number(row[1]) === 1 ? 1 : 0;
|
|
14
|
-
const write = Number(row[2]) === 1 ? 1 : 0;
|
|
15
|
-
parsed[scope] = { read, write };
|
|
16
|
-
}
|
|
17
|
-
return parsed;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
function getMissingBitfinexScopes(scopePermissions, permissionProfile = 'funding') {
|
|
21
|
-
const profile = BITFINEX_PERMISSION_PROFILES[permissionProfile] || BITFINEX_PERMISSION_PROFILES.funding;
|
|
22
|
-
const missing = [];
|
|
23
|
-
for (const [scope, required] of Object.entries(profile.required)) {
|
|
24
|
-
const actual = scopePermissions[scope] || { read: 0, write: 0 };
|
|
25
|
-
if (actual.read < required.read || actual.write < required.write) {
|
|
26
|
-
missing.push({
|
|
27
|
-
scope,
|
|
28
|
-
required,
|
|
29
|
-
actual,
|
|
30
|
-
});
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
return missing;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
async function probeBitfinexPermissions({ apiKey, apiSecret, permissionProfile = 'funding', timeoutMs, baseUrl }) {
|
|
37
|
-
const normalizedBaseUrl = String(baseUrl || DEFAULT_BITFINEX_BASE_URL).trim().replace(/\/+$/, '');
|
|
38
|
-
const apiPath = '/v2/auth/r/permissions';
|
|
39
|
-
const nonce = Date.now().toString();
|
|
40
|
-
const payload = '{}';
|
|
41
|
-
const signature = createHmac('sha384', String(apiSecret || ''))
|
|
42
|
-
.update(`/api${apiPath}${nonce}${payload}`)
|
|
43
|
-
.digest('hex');
|
|
44
|
-
const controller = new AbortController();
|
|
45
|
-
const timer = setTimeout(() => controller.abort(), Number(timeoutMs || DEFAULT_TIMEOUT_MS));
|
|
46
|
-
try {
|
|
47
|
-
const response = await fetch(`${normalizedBaseUrl}${apiPath}`, {
|
|
48
|
-
method: 'POST',
|
|
49
|
-
headers: {
|
|
50
|
-
'content-type': 'application/json',
|
|
51
|
-
'bfx-apikey': String(apiKey || ''),
|
|
52
|
-
'bfx-nonce': nonce,
|
|
53
|
-
'bfx-signature': signature,
|
|
54
|
-
},
|
|
55
|
-
body: payload,
|
|
56
|
-
signal: controller.signal,
|
|
57
|
-
});
|
|
58
|
-
const text = await response.text();
|
|
59
|
-
const parsed = text ? safeJsonParse(text) : {};
|
|
60
|
-
const bitfinexApiError =
|
|
61
|
-
Array.isArray(parsed) && String(parsed[0] || '').trim().toLowerCase() === 'error'
|
|
62
|
-
? {
|
|
63
|
-
code: Number(parsed[1]) || null,
|
|
64
|
-
message: String(parsed[2] || 'Unknown Bitfinex API error'),
|
|
65
|
-
}
|
|
66
|
-
: parsed && typeof parsed === 'object' && String(parsed.event || '').trim().toLowerCase() === 'error'
|
|
67
|
-
? {
|
|
68
|
-
code: Number(parsed.code) || null,
|
|
69
|
-
message: String(parsed.msg || parsed.message || 'Unknown Bitfinex API error'),
|
|
70
|
-
}
|
|
71
|
-
: null;
|
|
72
|
-
if (!response.ok) {
|
|
73
|
-
throw createCliError(
|
|
74
|
-
'BITFINEX_PERMISSIONS_PROBE_FAILED',
|
|
75
|
-
`Bitfinex permissions probe failed with HTTP ${response.status}. Verify API key/secret and key validity before onboarding.`,
|
|
76
|
-
{ statusCode: response.status }
|
|
77
|
-
);
|
|
78
|
-
}
|
|
79
|
-
if (bitfinexApiError) {
|
|
80
|
-
throw createCliError(
|
|
81
|
-
'BITFINEX_PERMISSIONS_PROBE_FAILED',
|
|
82
|
-
`Bitfinex permissions probe failed: ${bitfinexApiError.message}. Verify API key/secret and key validity before onboarding.`,
|
|
83
|
-
{ statusCode: response.status, bitfinexApiError }
|
|
84
|
-
);
|
|
85
|
-
}
|
|
86
|
-
const scopePermissions = parseBitfinexPermissionRows(parsed);
|
|
87
|
-
const missingScopes = getMissingBitfinexScopes(scopePermissions, permissionProfile);
|
|
88
|
-
if (missingScopes.length > 0) {
|
|
89
|
-
throw createCliError(
|
|
90
|
-
'BITFINEX_PERMISSION_SCOPE_MISSING',
|
|
91
|
-
`Bitfinex key is missing required scopes (${missingScopes
|
|
92
|
-
.map((entry) => entry.scope)
|
|
93
|
-
.join(', ')}). Enable the ${permissionProfile} profile scopes and retry onboarding.`,
|
|
94
|
-
{ missingScopes, scopePermissions, permissionProfile }
|
|
95
|
-
);
|
|
96
|
-
}
|
|
97
|
-
return {
|
|
98
|
-
venue: 'bitfinex',
|
|
99
|
-
permissionProfile,
|
|
100
|
-
checked: true,
|
|
101
|
-
scopes: scopePermissions,
|
|
102
|
-
missingScopes: [],
|
|
103
|
-
};
|
|
104
|
-
} catch (error) {
|
|
105
|
-
if (error?.payload) throw error;
|
|
106
|
-
throw createCliError(
|
|
107
|
-
'BITFINEX_PERMISSIONS_PROBE_FAILED',
|
|
108
|
-
`Could not verify Bitfinex key permissions. ${String(error?.message || 'Unknown probe error')}`,
|
|
109
|
-
{}
|
|
110
|
-
);
|
|
111
|
-
} finally {
|
|
112
|
-
clearTimeout(timer);
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
return {
|
|
117
|
-
parseBitfinexPermissionRows,
|
|
118
|
-
getMissingBitfinexScopes,
|
|
119
|
-
probeBitfinexPermissions,
|
|
120
|
-
};
|
|
121
|
-
}
|