@pgnio/helios-mcp 0.2.9 → 0.5.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/README.md +19 -2
- package/dist/chunk-XM257Y63.mjs +4307 -0
- package/dist/cli.js +2284 -1358
- package/dist/cli.mjs +8 -4
- package/dist/index.d.mts +1032 -134
- package/dist/index.d.ts +1032 -134
- package/dist/index.js +2250 -987
- package/dist/index.mjs +79 -3
- package/docs/ai-agent-mcp/04_trading/01_tools.txt +2 -1
- package/package.json +9 -16
- package/dist/chunk-7TMDGB6C.mjs +0 -3084
- package/dist/chunk-LMZEWQVW.mjs +0 -3084
package/dist/index.js
CHANGED
|
@@ -32,6 +32,8 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
|
|
|
32
32
|
// src/index.ts
|
|
33
33
|
var index_exports = {};
|
|
34
34
|
__export(index_exports, {
|
|
35
|
+
DEFAULT_HELIOS_BASE_URL: () => DEFAULT_HELIOS_BASE_URL,
|
|
36
|
+
DEFAULT_HELIOS_SESSION_ISSUER: () => DEFAULT_HELIOS_SESSION_ISSUER,
|
|
35
37
|
DEFAULT_SNAPSHOT_FIELDS: () => DEFAULT_SNAPSHOT_FIELDS,
|
|
36
38
|
DEFAULT_SNAPSHOT_TOP: () => DEFAULT_SNAPSHOT_TOP,
|
|
37
39
|
FileAgentSessionStore: () => FileAgentSessionStore,
|
|
@@ -43,7 +45,15 @@ __export(index_exports, {
|
|
|
43
45
|
HeliosAgentMcpMarketService: () => HeliosAgentMcpMarketService,
|
|
44
46
|
HeliosAgentMcpProviderService: () => HeliosAgentMcpProviderService,
|
|
45
47
|
HeliosAgentMcpTradingService: () => HeliosAgentMcpTradingService,
|
|
48
|
+
MAX_TOOL_INPUT_ELEMENTS: () => MAX_TOOL_INPUT_ELEMENTS,
|
|
49
|
+
MCP_PROFILES: () => MCP_PROFILES,
|
|
50
|
+
MIN_ORDER_USD: () => MIN_ORDER_USD,
|
|
46
51
|
MemoryAgentMcpSessionStore: () => MemoryAgentMcpSessionStore,
|
|
52
|
+
TRADE_KEY_WINDOW_SECONDS: () => TRADE_KEY_WINDOW_SECONDS,
|
|
53
|
+
accountInputSchema: () => accountInputSchema,
|
|
54
|
+
ago: () => ago,
|
|
55
|
+
assertHeliosBaseUrl: () => assertHeliosBaseUrl,
|
|
56
|
+
candlesInputSchema: () => candlesInputSchema,
|
|
47
57
|
compactSymbolDetail: () => compactSymbolDetail,
|
|
48
58
|
createHeliosAccountToolDefinitions: () => createHeliosAccountToolDefinitions,
|
|
49
59
|
createHeliosAgentMcpServer: () => createHeliosAgentMcpServer,
|
|
@@ -53,15 +63,43 @@ __export(index_exports, {
|
|
|
53
63
|
createHeliosMarketToolDefinitions: () => createHeliosMarketToolDefinitions,
|
|
54
64
|
createHeliosProviderToolDefinitions: () => createHeliosProviderToolDefinitions,
|
|
55
65
|
createHeliosTradingToolDefinitions: () => createHeliosTradingToolDefinitions,
|
|
66
|
+
createLeanToolDefinitions: () => createLeanToolDefinitions,
|
|
67
|
+
entryText: () => entryText,
|
|
56
68
|
formatHeliosLoginPrompt: () => formatHeliosLoginPrompt,
|
|
57
69
|
getDefaultHeliosAgentMcpDocsRoot: () => getDefaultHeliosAgentMcpDocsRoot,
|
|
58
70
|
getDefaultHeliosAgentMcpSessionPath: () => getDefaultHeliosAgentMcpSessionPath,
|
|
59
|
-
|
|
71
|
+
isSameHeliosIssuer: () => isSameHeliosIssuer,
|
|
72
|
+
leanToolsFor: () => leanToolsFor,
|
|
73
|
+
marketInputSchema: () => marketInputSchema,
|
|
74
|
+
moveText: () => moveText,
|
|
75
|
+
pct: () => pct,
|
|
76
|
+
pendingAgentLoginSchema: () => pendingAgentLoginSchema2,
|
|
77
|
+
positionLine: () => positionLine,
|
|
78
|
+
practiceOnly: () => practiceOnly,
|
|
79
|
+
price: () => price,
|
|
80
|
+
protectInputSchema: () => protectInputSchema,
|
|
81
|
+
renderAccount: () => renderAccount,
|
|
82
|
+
renderBoard: () => renderBoard,
|
|
83
|
+
renderCandles: () => renderCandles,
|
|
84
|
+
renderCoin: () => renderCoin,
|
|
85
|
+
renderMarket: () => renderMarket,
|
|
86
|
+
renderMoves: () => renderMoves,
|
|
87
|
+
renderTrades: () => renderTrades,
|
|
60
88
|
retryAfterUnauthorizedRefresh: () => retryAfterUnauthorizedRefresh,
|
|
89
|
+
smBoardInputSchema: () => smBoardInputSchema,
|
|
90
|
+
smCoinInputSchema: () => smCoinInputSchema,
|
|
91
|
+
smMovesInputSchema: () => smMovesInputSchema,
|
|
61
92
|
toolAnnotations: () => toolAnnotations,
|
|
62
93
|
topOfSnapshot: () => topOfSnapshot,
|
|
94
|
+
tradeInputSchema: () => tradeInputSchema,
|
|
95
|
+
tradeKeyFor: () => tradeKeyFor,
|
|
96
|
+
trader: () => trader,
|
|
97
|
+
tradesInputSchema: () => tradesInputSchema,
|
|
98
|
+
tradingLine: () => tradingLine,
|
|
99
|
+
usd: () => usd,
|
|
63
100
|
withDataText: () => withDataText,
|
|
64
|
-
withSnapshotDefaults: () => withSnapshotDefaults
|
|
101
|
+
withSnapshotDefaults: () => withSnapshotDefaults,
|
|
102
|
+
wrongSideTpSl: () => wrongSideTpSl
|
|
65
103
|
});
|
|
66
104
|
module.exports = __toCommonJS(index_exports);
|
|
67
105
|
|
|
@@ -69,9 +107,55 @@ module.exports = __toCommonJS(index_exports);
|
|
|
69
107
|
var import_zod = require("zod");
|
|
70
108
|
var import_zod2 = require("zod");
|
|
71
109
|
var import_zod3 = require("zod");
|
|
110
|
+
var import_zod4 = require("zod");
|
|
72
111
|
var __defProp2 = Object.defineProperty;
|
|
73
112
|
var __defNormalProp2 = (obj, key, value) => key in obj ? __defProp2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
74
113
|
var __publicField2 = (obj, key, value) => __defNormalProp2(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
114
|
+
var VERIFIED_AGENT_APPS = [
|
|
115
|
+
{ name: "Claude", redirects: ["https://claude.ai/api/mcp/auth_callback", "https://claude.com/api/mcp/auth_callback"] },
|
|
116
|
+
{ name: "ChatGPT", redirects: ["https://chatgpt.com/connector_platform_oauth_redirect"] },
|
|
117
|
+
{ name: "Cursor", redirects: ["https://www.cursor.com/agents/mcp/oauth/callback"] }
|
|
118
|
+
];
|
|
119
|
+
var RESERVED_AGENT_APP_NAMES = [
|
|
120
|
+
...VERIFIED_AGENT_APPS.map((app) => app.name),
|
|
121
|
+
"VS Code",
|
|
122
|
+
"Helios",
|
|
123
|
+
"Wagmi",
|
|
124
|
+
"Ares",
|
|
125
|
+
"OpenAI",
|
|
126
|
+
"Anthropic",
|
|
127
|
+
"Grok"
|
|
128
|
+
];
|
|
129
|
+
var heliosAgentErrorResponseSchema = import_zod2.z.object({
|
|
130
|
+
error: import_zod2.z.string().optional(),
|
|
131
|
+
errorDescription: import_zod2.z.string().optional(),
|
|
132
|
+
message: import_zod2.z.string().optional()
|
|
133
|
+
});
|
|
134
|
+
var HeliosAgentApiError = class extends Error {
|
|
135
|
+
constructor(options) {
|
|
136
|
+
super(options.message);
|
|
137
|
+
__publicField2(this, "status");
|
|
138
|
+
__publicField2(this, "code");
|
|
139
|
+
__publicField2(this, "errorDescription");
|
|
140
|
+
__publicField2(this, "payload");
|
|
141
|
+
this.name = "HeliosAgentApiError";
|
|
142
|
+
this.status = options.status;
|
|
143
|
+
this.code = options.code;
|
|
144
|
+
this.errorDescription = options.errorDescription;
|
|
145
|
+
this.payload = options.payload;
|
|
146
|
+
if (options.cause !== void 0) {
|
|
147
|
+
Object.defineProperty(this, "cause", {
|
|
148
|
+
configurable: true,
|
|
149
|
+
enumerable: false,
|
|
150
|
+
value: options.cause,
|
|
151
|
+
writable: true
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
function isHeliosAgentApiError(error) {
|
|
157
|
+
return error instanceof HeliosAgentApiError;
|
|
158
|
+
}
|
|
75
159
|
var AGENT_SCOPES = {
|
|
76
160
|
ACCOUNT_READ: "account:read",
|
|
77
161
|
MARKET_READ: "market:read",
|
|
@@ -84,433 +168,976 @@ var AGENT_SCOPE_VALUES = [
|
|
|
84
168
|
AGENT_SCOPES.ORDERS_WRITE,
|
|
85
169
|
AGENT_SCOPES.ORDERS_CANCEL
|
|
86
170
|
];
|
|
87
|
-
var agentScopeSchema =
|
|
88
|
-
var
|
|
89
|
-
var
|
|
90
|
-
var
|
|
91
|
-
var
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
171
|
+
var agentScopeSchema = import_zod3.z.enum(AGENT_SCOPE_VALUES);
|
|
172
|
+
var AGENT_TRADING_SCOPES = [AGENT_SCOPES.ORDERS_WRITE, AGENT_SCOPES.ORDERS_CANCEL];
|
|
173
|
+
var agentGrantStatusSchema = import_zod3.z.enum(["active", "revoked"]);
|
|
174
|
+
var agentDeviceRequestStatusSchema = import_zod3.z.enum(["pending", "approved", "rejected", "exchanged", "expired"]);
|
|
175
|
+
var AGENT_CONSTRAINTS_MAX_KEYS = 16;
|
|
176
|
+
var agentConstraintsSchema = import_zod3.z.record(import_zod3.z.string().max(64), import_zod3.z.unknown()).refine((value) => Object.keys(value).length <= AGENT_CONSTRAINTS_MAX_KEYS, {
|
|
177
|
+
message: `At most ${AGENT_CONSTRAINTS_MAX_KEYS} constraints`
|
|
178
|
+
}).default({});
|
|
179
|
+
var agentGrantMetadataSchema = import_zod3.z.object({
|
|
180
|
+
id: import_zod3.z.string(),
|
|
181
|
+
userId: import_zod3.z.string(),
|
|
182
|
+
clientName: import_zod3.z.string(),
|
|
95
183
|
status: agentGrantStatusSchema,
|
|
96
|
-
scope:
|
|
184
|
+
scope: import_zod3.z.array(agentScopeSchema).min(1),
|
|
97
185
|
constraints: agentConstraintsSchema,
|
|
98
|
-
tokenVersion:
|
|
99
|
-
createdAt:
|
|
100
|
-
approvedAt:
|
|
101
|
-
lastUsedAt:
|
|
102
|
-
revokedAt:
|
|
103
|
-
});
|
|
104
|
-
var agentDeviceStartInputSchema = import_zod.z.object({
|
|
105
|
-
clientName: import_zod.z.string().min(1).max(64),
|
|
106
|
-
scope: import_zod.z.array(agentScopeSchema).min(1),
|
|
107
|
-
constraints: agentConstraintsSchema.optional()
|
|
186
|
+
tokenVersion: import_zod3.z.number().int().nonnegative(),
|
|
187
|
+
createdAt: import_zod3.z.string(),
|
|
188
|
+
approvedAt: import_zod3.z.string().optional(),
|
|
189
|
+
lastUsedAt: import_zod3.z.string().optional(),
|
|
190
|
+
revokedAt: import_zod3.z.string().optional()
|
|
108
191
|
});
|
|
109
|
-
var
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
192
|
+
var AGENT_START_SECRET_HEADER = "x-helios-agent-start-secret";
|
|
193
|
+
var AGENT_CLIENT_IP_HEADER = "x-helios-client-ip";
|
|
194
|
+
var agentDeviceStartInputSchema = import_zod3.z.object({
|
|
195
|
+
clientName: import_zod3.z.string().trim().min(1).max(64),
|
|
196
|
+
scope: import_zod3.z.array(agentScopeSchema).min(1),
|
|
197
|
+
constraints: agentConstraintsSchema.optional(),
|
|
198
|
+
/** The app was matched to a known client by the hosted MCP. Ignored unless the start carries the start secret. */
|
|
199
|
+
verified: import_zod3.z.boolean().optional(),
|
|
200
|
+
/** The approval answers a one-time proof the device must send with the token exchange (the hosted MCP's redirect). */
|
|
201
|
+
requireApprovalProof: import_zod3.z.boolean().optional()
|
|
116
202
|
});
|
|
117
|
-
var
|
|
118
|
-
deviceCode:
|
|
203
|
+
var agentDeviceStartOutputSchema = import_zod3.z.object({
|
|
204
|
+
deviceCode: import_zod3.z.string(),
|
|
205
|
+
userCode: import_zod3.z.string(),
|
|
206
|
+
verificationUri: import_zod3.z.string().url(),
|
|
207
|
+
verificationUriComplete: import_zod3.z.string().url(),
|
|
208
|
+
expiresInSeconds: import_zod3.z.number().int().positive(),
|
|
209
|
+
pollIntervalSeconds: import_zod3.z.number().int().positive()
|
|
119
210
|
});
|
|
120
|
-
var
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
211
|
+
var agentDeviceTokenInputSchema = import_zod3.z.object({
|
|
212
|
+
deviceCode: import_zod3.z.string().min(1),
|
|
213
|
+
/** Required when the login was started with requireApprovalProof: the proof the approval answered with. */
|
|
214
|
+
approvalProof: import_zod3.z.string().min(1).max(128).optional()
|
|
215
|
+
});
|
|
216
|
+
var agentDeviceRequestViewSchema = import_zod3.z.object({
|
|
217
|
+
userCode: import_zod3.z.string(),
|
|
218
|
+
clientName: import_zod3.z.string(),
|
|
219
|
+
scope: import_zod3.z.array(agentScopeSchema).min(1),
|
|
124
220
|
constraints: agentConstraintsSchema,
|
|
125
221
|
status: agentDeviceRequestStatusSchema,
|
|
126
|
-
expiresAt:
|
|
222
|
+
expiresAt: import_zod3.z.string(),
|
|
223
|
+
/** False when the app's name wasn't checked by Helios (anyone can start a login under any name). */
|
|
224
|
+
verified: import_zod3.z.boolean().optional(),
|
|
225
|
+
/** The signed-in viewer already has an active connection under this name, which approving would replace. */
|
|
226
|
+
replacesExisting: import_zod3.z.boolean().optional(),
|
|
227
|
+
/** Where the login was started, as identity saw the device/start call. Absent from older identity builds. */
|
|
228
|
+
startedFrom: import_zod3.z.object({
|
|
229
|
+
ip: import_zod3.z.string().nullable(),
|
|
230
|
+
userAgent: import_zod3.z.string().nullable(),
|
|
231
|
+
startedAt: import_zod3.z.string()
|
|
232
|
+
}).optional()
|
|
127
233
|
});
|
|
128
|
-
var
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
234
|
+
var agentDeviceApproveInputSchema = import_zod3.z.object({
|
|
235
|
+
/** The person confirmed replacing their existing connection of the same name. */
|
|
236
|
+
replaceExisting: import_zod3.z.boolean().optional(),
|
|
237
|
+
/**
|
|
238
|
+
* The scopes the person allowed: a non-empty subset of the request's scope (identity answers 400 invalid_scope
|
|
239
|
+
* otherwise). The grant and its tokens get exactly these. Absent: everything the request asked for.
|
|
240
|
+
*/
|
|
241
|
+
scope: import_zod3.z.array(agentScopeSchema).min(1).max(AGENT_SCOPE_VALUES.length).optional(),
|
|
242
|
+
/**
|
|
243
|
+
* The person confirmed they trust an unverified app to trade. Required (else 409 trading_acknowledgement_required)
|
|
244
|
+
* when the login is unverified and the approved scope includes a trading scope (AGENT_TRADING_SCOPES).
|
|
245
|
+
*/
|
|
246
|
+
acknowledgeTrading: import_zod3.z.boolean().optional()
|
|
247
|
+
});
|
|
248
|
+
var agentTokenResponseSchema = import_zod3.z.object({
|
|
249
|
+
accessToken: import_zod3.z.string(),
|
|
250
|
+
refreshToken: import_zod3.z.string(),
|
|
251
|
+
tokenType: import_zod3.z.literal("Bearer"),
|
|
252
|
+
expiresInSeconds: import_zod3.z.number().int().positive(),
|
|
253
|
+
scope: import_zod3.z.array(agentScopeSchema).min(1),
|
|
254
|
+
ownerUserId: import_zod3.z.string(),
|
|
255
|
+
agentGrantId: import_zod3.z.string()
|
|
136
256
|
});
|
|
137
|
-
var agentRefreshInputSchema =
|
|
138
|
-
refreshToken:
|
|
257
|
+
var agentRefreshInputSchema = import_zod3.z.object({
|
|
258
|
+
refreshToken: import_zod3.z.string().min(1)
|
|
139
259
|
});
|
|
140
|
-
var agentApprovalResponseSchema =
|
|
260
|
+
var agentApprovalResponseSchema = import_zod3.z.object({
|
|
141
261
|
request: agentDeviceRequestViewSchema,
|
|
142
|
-
grant: agentGrantMetadataSchema
|
|
262
|
+
grant: agentGrantMetadataSchema,
|
|
263
|
+
/** Present when the login asked for one: hand it back to the device, which sends it with the token exchange. */
|
|
264
|
+
approvalProof: import_zod3.z.string().optional()
|
|
143
265
|
});
|
|
144
|
-
var agentSessionStateSchema =
|
|
145
|
-
status:
|
|
266
|
+
var agentSessionStateSchema = import_zod3.z.object({
|
|
267
|
+
status: import_zod3.z.enum(["pending", "authorized", "rejected", "revoked"]),
|
|
146
268
|
request: agentDeviceRequestViewSchema.optional(),
|
|
147
269
|
grant: agentGrantMetadataSchema.optional()
|
|
148
270
|
});
|
|
149
|
-
var providerSchema =
|
|
150
|
-
var walletStateSchema =
|
|
151
|
-
provider:
|
|
152
|
-
accountValue:
|
|
153
|
-
availableBalance:
|
|
154
|
-
withdrawable:
|
|
155
|
-
totalMarginUsed:
|
|
156
|
-
totalNotionalPosition:
|
|
157
|
-
crossMarginAccountValue:
|
|
158
|
-
isActivated:
|
|
159
|
-
fetchedAt:
|
|
160
|
-
});
|
|
161
|
-
var openPositionSchema =
|
|
162
|
-
symbol:
|
|
163
|
-
side:
|
|
164
|
-
size:
|
|
165
|
-
entryPrice:
|
|
166
|
-
markPrice:
|
|
167
|
-
unrealizedPnl:
|
|
168
|
-
margin:
|
|
169
|
-
leverage:
|
|
170
|
-
liquidationPrice:
|
|
171
|
-
});
|
|
172
|
-
var openPositionsSnapshotSchema =
|
|
173
|
-
provider:
|
|
174
|
-
fetchedAt:
|
|
175
|
-
totalUnrealizedPnl:
|
|
176
|
-
positions:
|
|
177
|
-
});
|
|
178
|
-
var protectiveOrderKindSchema =
|
|
179
|
-
var protectiveOrderSideSchema =
|
|
180
|
-
var protectiveOrderPositionSideSchema =
|
|
181
|
-
var protectiveOrderSchema =
|
|
271
|
+
var providerSchema = import_zod3.z.enum(["hyperliquid"]);
|
|
272
|
+
var walletStateSchema = import_zod3.z.object({
|
|
273
|
+
provider: import_zod3.z.literal("hyperliquid"),
|
|
274
|
+
accountValue: import_zod3.z.number(),
|
|
275
|
+
availableBalance: import_zod3.z.number(),
|
|
276
|
+
withdrawable: import_zod3.z.number(),
|
|
277
|
+
totalMarginUsed: import_zod3.z.number(),
|
|
278
|
+
totalNotionalPosition: import_zod3.z.number(),
|
|
279
|
+
crossMarginAccountValue: import_zod3.z.number(),
|
|
280
|
+
isActivated: import_zod3.z.boolean(),
|
|
281
|
+
fetchedAt: import_zod3.z.string().datetime()
|
|
282
|
+
});
|
|
283
|
+
var openPositionSchema = import_zod3.z.object({
|
|
284
|
+
symbol: import_zod3.z.string(),
|
|
285
|
+
side: import_zod3.z.enum(["LONG", "SHORT"]),
|
|
286
|
+
size: import_zod3.z.number(),
|
|
287
|
+
entryPrice: import_zod3.z.number(),
|
|
288
|
+
markPrice: import_zod3.z.number(),
|
|
289
|
+
unrealizedPnl: import_zod3.z.number(),
|
|
290
|
+
margin: import_zod3.z.number(),
|
|
291
|
+
leverage: import_zod3.z.number(),
|
|
292
|
+
liquidationPrice: import_zod3.z.number().optional()
|
|
293
|
+
});
|
|
294
|
+
var openPositionsSnapshotSchema = import_zod3.z.object({
|
|
295
|
+
provider: import_zod3.z.literal("hyperliquid"),
|
|
296
|
+
fetchedAt: import_zod3.z.string().datetime(),
|
|
297
|
+
totalUnrealizedPnl: import_zod3.z.number(),
|
|
298
|
+
positions: import_zod3.z.array(openPositionSchema)
|
|
299
|
+
});
|
|
300
|
+
var protectiveOrderKindSchema = import_zod3.z.enum(["STOP_LOSS", "TAKE_PROFIT"]);
|
|
301
|
+
var protectiveOrderSideSchema = import_zod3.z.enum(["BUY", "SELL"]);
|
|
302
|
+
var protectiveOrderPositionSideSchema = import_zod3.z.enum(["BOTH", "LONG", "SHORT"]);
|
|
303
|
+
var protectiveOrderSchema = import_zod3.z.object({
|
|
182
304
|
/** Helios's record id: what helios_positions_delete_tp_sl takes (the Hyperliquid order id works there too). */
|
|
183
|
-
id:
|
|
305
|
+
id: import_zod3.z.string(),
|
|
184
306
|
/** The Hyperliquid order id (named after the retired Binance field). */
|
|
185
|
-
binanceOrderId:
|
|
186
|
-
clientOrderId:
|
|
187
|
-
symbol:
|
|
307
|
+
binanceOrderId: import_zod3.z.number().int().nonnegative().nullish(),
|
|
308
|
+
clientOrderId: import_zod3.z.string().nullish(),
|
|
309
|
+
symbol: import_zod3.z.string(),
|
|
188
310
|
side: protectiveOrderSideSchema,
|
|
189
311
|
positionSide: protectiveOrderPositionSideSchema.nullish().transform((value) => value ?? "BOTH"),
|
|
190
|
-
status:
|
|
312
|
+
status: import_zod3.z.string(),
|
|
191
313
|
kind: protectiveOrderKindSchema,
|
|
192
|
-
groupId:
|
|
193
|
-
stopPrice:
|
|
194
|
-
parentClientOrderId:
|
|
195
|
-
assetId:
|
|
196
|
-
createdAt:
|
|
197
|
-
});
|
|
198
|
-
var
|
|
199
|
-
provider:
|
|
200
|
-
totalBalance:
|
|
201
|
-
availableBalance:
|
|
202
|
-
totalMargin:
|
|
203
|
-
unrealizedPnl:
|
|
204
|
-
positions:
|
|
314
|
+
groupId: import_zod3.z.string().nullish(),
|
|
315
|
+
stopPrice: import_zod3.z.union([import_zod3.z.string(), import_zod3.z.number()]).nullish().transform((value) => value == null ? value : String(value)),
|
|
316
|
+
parentClientOrderId: import_zod3.z.string().nullish(),
|
|
317
|
+
assetId: import_zod3.z.number().int().nonnegative().nullish(),
|
|
318
|
+
createdAt: import_zod3.z.string().nullish()
|
|
319
|
+
});
|
|
320
|
+
var portfolioSnapshotSchema = import_zod3.z.object({
|
|
321
|
+
provider: import_zod3.z.literal("hyperliquid"),
|
|
322
|
+
totalBalance: import_zod3.z.number(),
|
|
323
|
+
availableBalance: import_zod3.z.number(),
|
|
324
|
+
totalMargin: import_zod3.z.number(),
|
|
325
|
+
unrealizedPnl: import_zod3.z.number(),
|
|
326
|
+
positions: import_zod3.z.array(openPositionSchema),
|
|
205
327
|
/** Unreadable entries are dropped (see readProtectiveOrders) rather than failing the read. */
|
|
206
|
-
protectiveOrders:
|
|
328
|
+
protectiveOrders: import_zod3.z.array(protectiveOrderSchema)
|
|
207
329
|
});
|
|
208
|
-
var portfolioInputSchema =
|
|
330
|
+
var portfolioInputSchema = import_zod3.z.object({
|
|
209
331
|
provider: providerSchema
|
|
210
332
|
});
|
|
211
|
-
var
|
|
212
|
-
var
|
|
213
|
-
var ledgerFillsInputSchema = import_zod.z.object({
|
|
333
|
+
var ledgerWindowSchema = import_zod3.z.enum(["24h", "7d", "30d"]);
|
|
334
|
+
var ledgerFillsInputSchema = import_zod3.z.object({
|
|
214
335
|
window: ledgerWindowSchema.optional(),
|
|
215
|
-
limit:
|
|
216
|
-
before:
|
|
217
|
-
});
|
|
218
|
-
var ledgerFillSchema =
|
|
219
|
-
coin:
|
|
220
|
-
px:
|
|
221
|
-
sz:
|
|
222
|
-
side:
|
|
223
|
-
time:
|
|
224
|
-
dir:
|
|
225
|
-
closedPnl:
|
|
226
|
-
fee:
|
|
227
|
-
crossed:
|
|
228
|
-
oid:
|
|
229
|
-
tid:
|
|
230
|
-
});
|
|
231
|
-
var ledgerFillsPageSchema =
|
|
336
|
+
limit: import_zod3.z.number().int().positive().max(200).optional(),
|
|
337
|
+
before: import_zod3.z.number().int().nonnegative().optional()
|
|
338
|
+
});
|
|
339
|
+
var ledgerFillSchema = import_zod3.z.object({
|
|
340
|
+
coin: import_zod3.z.string(),
|
|
341
|
+
px: import_zod3.z.number(),
|
|
342
|
+
sz: import_zod3.z.number(),
|
|
343
|
+
side: import_zod3.z.enum(["B", "A"]),
|
|
344
|
+
time: import_zod3.z.number().int().nonnegative(),
|
|
345
|
+
dir: import_zod3.z.string(),
|
|
346
|
+
closedPnl: import_zod3.z.number(),
|
|
347
|
+
fee: import_zod3.z.number(),
|
|
348
|
+
crossed: import_zod3.z.boolean(),
|
|
349
|
+
oid: import_zod3.z.number().int().nonnegative(),
|
|
350
|
+
tid: import_zod3.z.number().int().nonnegative()
|
|
351
|
+
});
|
|
352
|
+
var ledgerFillsPageSchema = import_zod3.z.object({
|
|
232
353
|
window: ledgerWindowSchema,
|
|
233
|
-
limit:
|
|
234
|
-
before:
|
|
235
|
-
hasMore:
|
|
236
|
-
nextBefore:
|
|
237
|
-
fetchedAt:
|
|
238
|
-
fills:
|
|
239
|
-
});
|
|
240
|
-
var ledgerPeriodSummarySchema =
|
|
241
|
-
realizedPnl:
|
|
242
|
-
totalFees:
|
|
243
|
-
fillCount:
|
|
244
|
-
closeCount:
|
|
245
|
-
winCount:
|
|
246
|
-
lossCount:
|
|
247
|
-
averageWin:
|
|
248
|
-
averageLoss:
|
|
249
|
-
});
|
|
250
|
-
var ledgerSummaryInputSchema =
|
|
354
|
+
limit: import_zod3.z.number().int().positive(),
|
|
355
|
+
before: import_zod3.z.number().int().nonnegative().optional(),
|
|
356
|
+
hasMore: import_zod3.z.boolean(),
|
|
357
|
+
nextBefore: import_zod3.z.number().int().nonnegative().optional(),
|
|
358
|
+
fetchedAt: import_zod3.z.string().datetime(),
|
|
359
|
+
fills: import_zod3.z.array(ledgerFillSchema)
|
|
360
|
+
});
|
|
361
|
+
var ledgerPeriodSummarySchema = import_zod3.z.object({
|
|
362
|
+
realizedPnl: import_zod3.z.number(),
|
|
363
|
+
totalFees: import_zod3.z.number(),
|
|
364
|
+
fillCount: import_zod3.z.number().int().nonnegative(),
|
|
365
|
+
closeCount: import_zod3.z.number().int().nonnegative(),
|
|
366
|
+
winCount: import_zod3.z.number().int().nonnegative(),
|
|
367
|
+
lossCount: import_zod3.z.number().int().nonnegative(),
|
|
368
|
+
averageWin: import_zod3.z.number(),
|
|
369
|
+
averageLoss: import_zod3.z.number()
|
|
370
|
+
});
|
|
371
|
+
var ledgerSummaryInputSchema = import_zod3.z.object({
|
|
251
372
|
window: ledgerWindowSchema.optional()
|
|
252
373
|
});
|
|
253
374
|
var ledgerSummarySchema = ledgerPeriodSummarySchema.extend({
|
|
254
375
|
window: ledgerWindowSchema,
|
|
255
|
-
generatedAt:
|
|
256
|
-
periods:
|
|
376
|
+
generatedAt: import_zod3.z.string().datetime(),
|
|
377
|
+
periods: import_zod3.z.object({
|
|
257
378
|
"24h": ledgerPeriodSummarySchema,
|
|
258
379
|
"7d": ledgerPeriodSummarySchema
|
|
259
380
|
})
|
|
260
381
|
});
|
|
261
|
-
var marketSnapshotScopeSchema =
|
|
262
|
-
var marketSnapshotResolvedScopeSchema =
|
|
263
|
-
var marketSnapshotValueSchema =
|
|
264
|
-
var marketSnapshotInputSchema =
|
|
382
|
+
var marketSnapshotScopeSchema = import_zod3.z.enum(["selected", "all"]);
|
|
383
|
+
var marketSnapshotResolvedScopeSchema = import_zod3.z.enum(["selected", "all", "explicit"]);
|
|
384
|
+
var marketSnapshotValueSchema = import_zod3.z.union([import_zod3.z.string(), import_zod3.z.number(), import_zod3.z.boolean(), import_zod3.z.null()]);
|
|
385
|
+
var marketSnapshotInputSchema = import_zod3.z.object({
|
|
265
386
|
scope: marketSnapshotScopeSchema.optional(),
|
|
266
|
-
symbols:
|
|
267
|
-
fields:
|
|
268
|
-
sort:
|
|
387
|
+
symbols: import_zod3.z.array(import_zod3.z.string().min(1)).min(1).optional(),
|
|
388
|
+
fields: import_zod3.z.array(import_zod3.z.string().min(1)).min(1).optional().describe("Table columns to return; sym and base always come back. Ids: rank, lp, bv, qv, ts, r1m, r3m, r5m, r15m, r30m, r1h, r2h, r4h, r6h, r8h, r12h, r24h, r3d, r7d, hmi, hmiv, reg, ob, de, hfi, ar, ms, vcc, vcp, obosd, obosp, vwap, vwz, poc, pocd, vap, macdh, macds, macdd, srs, srr, fh24, fl24, fh7, fl7, sr, az, hfiw, trv, tri, trh, hs."),
|
|
389
|
+
sort: import_zod3.z.enum(["rank"]).optional().describe('"rank" orders rows by market cap rank, unranked last.')
|
|
269
390
|
});
|
|
270
391
|
var marketSnapshotChunkInputSchema = marketSnapshotInputSchema.extend({
|
|
271
|
-
offset:
|
|
272
|
-
limit:
|
|
273
|
-
});
|
|
274
|
-
var marketFieldBlockSchema =
|
|
275
|
-
fieldIds:
|
|
276
|
-
values:
|
|
277
|
-
symbolLists:
|
|
278
|
-
});
|
|
279
|
-
var marketMatrixSchema =
|
|
280
|
-
fieldIds:
|
|
281
|
-
rows:
|
|
282
|
-
});
|
|
283
|
-
var marketFieldSemanticsSchema =
|
|
284
|
-
nullMeans:
|
|
285
|
-
percentScale:
|
|
286
|
-
percentFields:
|
|
287
|
-
changeMapPercentKeys:
|
|
288
|
-
});
|
|
289
|
-
var marketSnapshotSchema =
|
|
290
|
-
schema:
|
|
291
|
-
view:
|
|
392
|
+
offset: import_zod3.z.number().int().nonnegative().optional(),
|
|
393
|
+
limit: import_zod3.z.number().int().positive().max(100).optional()
|
|
394
|
+
});
|
|
395
|
+
var marketFieldBlockSchema = import_zod3.z.object({
|
|
396
|
+
fieldIds: import_zod3.z.array(import_zod3.z.string()).min(1),
|
|
397
|
+
values: import_zod3.z.array(marketSnapshotValueSchema),
|
|
398
|
+
symbolLists: import_zod3.z.record(import_zod3.z.string(), import_zod3.z.array(import_zod3.z.string())).optional()
|
|
399
|
+
});
|
|
400
|
+
var marketMatrixSchema = import_zod3.z.object({
|
|
401
|
+
fieldIds: import_zod3.z.array(import_zod3.z.string()).min(1),
|
|
402
|
+
rows: import_zod3.z.array(import_zod3.z.array(marketSnapshotValueSchema))
|
|
403
|
+
});
|
|
404
|
+
var marketFieldSemanticsSchema = import_zod3.z.object({
|
|
405
|
+
nullMeans: import_zod3.z.literal("unavailable"),
|
|
406
|
+
percentScale: import_zod3.z.literal("already_percent"),
|
|
407
|
+
percentFields: import_zod3.z.array(import_zod3.z.string()).min(1),
|
|
408
|
+
changeMapPercentKeys: import_zod3.z.array(import_zod3.z.string()).min(1)
|
|
409
|
+
});
|
|
410
|
+
var marketSnapshotSchema = import_zod3.z.object({
|
|
411
|
+
schema: import_zod3.z.literal("helios.market_snapshot.v1"),
|
|
412
|
+
view: import_zod3.z.literal("market_overview"),
|
|
292
413
|
scope: marketSnapshotResolvedScopeSchema,
|
|
293
|
-
symbolCount:
|
|
294
|
-
generatedAt:
|
|
295
|
-
returnedSymbols:
|
|
414
|
+
symbolCount: import_zod3.z.number().int().nonnegative(),
|
|
415
|
+
generatedAt: import_zod3.z.string().datetime(),
|
|
416
|
+
returnedSymbols: import_zod3.z.array(import_zod3.z.string()),
|
|
296
417
|
semantics: marketFieldSemanticsSchema,
|
|
297
418
|
market: marketFieldBlockSchema,
|
|
298
419
|
table: marketMatrixSchema,
|
|
299
|
-
legend:
|
|
420
|
+
legend: import_zod3.z.record(import_zod3.z.string(), import_zod3.z.union([import_zod3.z.string(), import_zod3.z.array(import_zod3.z.string())]))
|
|
300
421
|
});
|
|
301
|
-
var marketSnapshotChunkSchema =
|
|
302
|
-
schema:
|
|
303
|
-
view:
|
|
422
|
+
var marketSnapshotChunkSchema = import_zod3.z.object({
|
|
423
|
+
schema: import_zod3.z.literal("helios.market_snapshot_chunk.v1"),
|
|
424
|
+
view: import_zod3.z.literal("market_overview_chunk"),
|
|
304
425
|
scope: marketSnapshotResolvedScopeSchema,
|
|
305
|
-
totalSymbolCount:
|
|
306
|
-
generatedAt:
|
|
307
|
-
offset:
|
|
308
|
-
limit:
|
|
309
|
-
chunkSymbolCount:
|
|
310
|
-
hasMore:
|
|
311
|
-
nextOffset:
|
|
312
|
-
returnedSymbols:
|
|
426
|
+
totalSymbolCount: import_zod3.z.number().int().nonnegative(),
|
|
427
|
+
generatedAt: import_zod3.z.string().datetime(),
|
|
428
|
+
offset: import_zod3.z.number().int().nonnegative(),
|
|
429
|
+
limit: import_zod3.z.number().int().positive(),
|
|
430
|
+
chunkSymbolCount: import_zod3.z.number().int().nonnegative(),
|
|
431
|
+
hasMore: import_zod3.z.boolean(),
|
|
432
|
+
nextOffset: import_zod3.z.number().int().nonnegative().optional(),
|
|
433
|
+
returnedSymbols: import_zod3.z.array(import_zod3.z.string()),
|
|
313
434
|
semantics: marketFieldSemanticsSchema,
|
|
314
435
|
market: marketFieldBlockSchema,
|
|
315
436
|
table: marketMatrixSchema,
|
|
316
|
-
legend:
|
|
437
|
+
legend: import_zod3.z.record(import_zod3.z.string(), import_zod3.z.union([import_zod3.z.string(), import_zod3.z.array(import_zod3.z.string())]))
|
|
317
438
|
});
|
|
318
|
-
var marketSymbolDetailInputSchema =
|
|
319
|
-
symbol:
|
|
320
|
-
full:
|
|
439
|
+
var marketSymbolDetailInputSchema = import_zod3.z.object({
|
|
440
|
+
symbol: import_zod3.z.string().min(1),
|
|
441
|
+
full: import_zod3.z.boolean().optional().describe("true adds every raw indicator and the price history (large, ~25 KB). Omit for the compact row and market context.")
|
|
321
442
|
});
|
|
322
|
-
var marketSymbolDetailSchema =
|
|
323
|
-
schema:
|
|
324
|
-
generatedAt:
|
|
325
|
-
symbol:
|
|
443
|
+
var marketSymbolDetailSchema = import_zod3.z.object({
|
|
444
|
+
schema: import_zod3.z.literal("helios.market_symbol_detail.v1"),
|
|
445
|
+
generatedAt: import_zod3.z.string().datetime(),
|
|
446
|
+
symbol: import_zod3.z.string(),
|
|
326
447
|
semantics: marketFieldSemanticsSchema,
|
|
327
448
|
market: marketFieldBlockSchema,
|
|
328
|
-
row:
|
|
329
|
-
symbolCtx:
|
|
330
|
-
changeMap:
|
|
331
|
-
metrics:
|
|
332
|
-
legend:
|
|
333
|
-
});
|
|
334
|
-
var tradingDirectionSchema =
|
|
335
|
-
var tradingExecutionStyleSchema =
|
|
336
|
-
var tradingSizeSchema =
|
|
337
|
-
mode:
|
|
338
|
-
value:
|
|
339
|
-
});
|
|
340
|
-
var tradingProtectionLegSchema =
|
|
341
|
-
kind:
|
|
342
|
-
triggerPrice:
|
|
343
|
-
limitPrice:
|
|
344
|
-
});
|
|
345
|
-
var tradingProtectionSchema =
|
|
449
|
+
row: import_zod3.z.record(import_zod3.z.string(), marketSnapshotValueSchema),
|
|
450
|
+
symbolCtx: import_zod3.z.record(import_zod3.z.string(), marketSnapshotValueSchema),
|
|
451
|
+
changeMap: import_zod3.z.record(import_zod3.z.string(), import_zod3.z.number().nullable()),
|
|
452
|
+
metrics: import_zod3.z.record(import_zod3.z.string(), import_zod3.z.unknown()).nullable(),
|
|
453
|
+
legend: import_zod3.z.record(import_zod3.z.string(), import_zod3.z.union([import_zod3.z.string(), import_zod3.z.array(import_zod3.z.string())]))
|
|
454
|
+
});
|
|
455
|
+
var tradingDirectionSchema = import_zod3.z.enum(["LONG", "SHORT"]);
|
|
456
|
+
var tradingExecutionStyleSchema = import_zod3.z.enum(["MARKET", "LIMIT_TAKER", "LIMIT_MAKER", "LIMIT_GTC"]);
|
|
457
|
+
var tradingSizeSchema = import_zod3.z.object({
|
|
458
|
+
mode: import_zod3.z.enum(["MARGIN_USD", "BASE_QUANTITY"]),
|
|
459
|
+
value: import_zod3.z.number().positive()
|
|
460
|
+
});
|
|
461
|
+
var tradingProtectionLegSchema = import_zod3.z.object({
|
|
462
|
+
kind: import_zod3.z.enum(["LIMIT", "MARKET"]),
|
|
463
|
+
triggerPrice: import_zod3.z.number().positive(),
|
|
464
|
+
limitPrice: import_zod3.z.number().positive().optional()
|
|
465
|
+
});
|
|
466
|
+
var tradingProtectionSchema = import_zod3.z.object({
|
|
346
467
|
takeProfit: tradingProtectionLegSchema.optional(),
|
|
347
468
|
stopLoss: tradingProtectionLegSchema.optional()
|
|
348
469
|
});
|
|
349
|
-
var
|
|
350
|
-
var
|
|
470
|
+
var tradingSymbolSchema = import_zod3.z.string().regex(/^(?:[a-zA-Z0-9]{1,10}:)?[a-zA-Z0-9]+(?:USDT)?$/);
|
|
471
|
+
var tradeIntentTypeSchema = import_zod3.z.enum(["OPEN", "DCA", "REVERSE", "CLOSE"]);
|
|
472
|
+
var submitTradeIntentInputSchema = import_zod3.z.object({
|
|
351
473
|
provider: providerSchema.optional(),
|
|
352
|
-
symbol:
|
|
474
|
+
symbol: tradingSymbolSchema,
|
|
353
475
|
intentType: tradeIntentTypeSchema,
|
|
354
476
|
direction: tradingDirectionSchema,
|
|
355
477
|
size: tradingSizeSchema,
|
|
356
|
-
execution:
|
|
478
|
+
execution: import_zod3.z.object({
|
|
357
479
|
style: tradingExecutionStyleSchema,
|
|
358
|
-
timeInForce:
|
|
359
|
-
limitPrice:
|
|
480
|
+
timeInForce: import_zod3.z.enum(["GTC", "IOC", "FOK", "GTX"]).optional(),
|
|
481
|
+
limitPrice: import_zod3.z.number().positive().optional()
|
|
360
482
|
}),
|
|
361
|
-
maxSlippageBps:
|
|
362
|
-
leverageOverride:
|
|
363
|
-
marginModeOverride:
|
|
364
|
-
reduceOnly:
|
|
483
|
+
maxSlippageBps: import_zod3.z.number().int().nonnegative().max(1e3).optional(),
|
|
484
|
+
leverageOverride: import_zod3.z.number().positive().optional(),
|
|
485
|
+
marginModeOverride: import_zod3.z.enum(["ISOLATED", "CROSS"]).optional(),
|
|
486
|
+
reduceOnly: import_zod3.z.boolean().optional(),
|
|
365
487
|
protection: tradingProtectionSchema.optional(),
|
|
366
488
|
/** One per user action, reused on retries: the data-bridge never places a second order for the same key. */
|
|
367
|
-
idempotencyKey:
|
|
489
|
+
idempotencyKey: import_zod3.z.string().regex(/^[A-Za-z0-9_-]{8,64}$/).optional(),
|
|
490
|
+
/** With it, idempotencyKey is the order's fingerprint: repeats within this many seconds of the first are one order. */
|
|
491
|
+
idempotencyWindowSeconds: import_zod3.z.number().int().min(10).max(600).optional()
|
|
368
492
|
});
|
|
369
|
-
var submitTradeIntentResultSchema =
|
|
370
|
-
status:
|
|
493
|
+
var submitTradeIntentResultSchema = import_zod3.z.object({
|
|
494
|
+
status: import_zod3.z.literal("submitted"),
|
|
371
495
|
/** `replayed`: an earlier attempt of this action already placed the order; nothing new was sent. */
|
|
372
|
-
outcome:
|
|
373
|
-
clientOrderId:
|
|
374
|
-
orderStatus:
|
|
496
|
+
outcome: import_zod3.z.enum(["placed", "replayed"]).optional(),
|
|
497
|
+
clientOrderId: import_zod3.z.string().optional(),
|
|
498
|
+
orderStatus: import_zod3.z.string().optional(),
|
|
375
499
|
/** TP/SL legs Hyperliquid refused while the entry went through: the position is open without them. */
|
|
376
|
-
rejectedProtection:
|
|
500
|
+
rejectedProtection: import_zod3.z.array(import_zod3.z.object({ kind: import_zod3.z.string(), reason: import_zod3.z.string() })).optional(),
|
|
377
501
|
provider: providerSchema.optional(),
|
|
378
|
-
symbol:
|
|
502
|
+
symbol: import_zod3.z.string(),
|
|
379
503
|
intentType: tradeIntentTypeSchema,
|
|
380
504
|
direction: tradingDirectionSchema,
|
|
381
505
|
size: tradingSizeSchema,
|
|
382
|
-
execution:
|
|
506
|
+
execution: import_zod3.z.object({
|
|
383
507
|
style: tradingExecutionStyleSchema,
|
|
384
|
-
timeInForce:
|
|
385
|
-
limitPrice:
|
|
508
|
+
timeInForce: import_zod3.z.enum(["GTC", "IOC", "FOK", "GTX"]).optional(),
|
|
509
|
+
limitPrice: import_zod3.z.number().positive().optional()
|
|
386
510
|
}),
|
|
387
|
-
maxSlippageBps:
|
|
388
|
-
leverageOverride:
|
|
389
|
-
marginModeOverride:
|
|
390
|
-
reduceOnly:
|
|
391
|
-
hasProtection:
|
|
511
|
+
maxSlippageBps: import_zod3.z.number().int().nonnegative().max(1e3).optional(),
|
|
512
|
+
leverageOverride: import_zod3.z.number().positive().optional(),
|
|
513
|
+
marginModeOverride: import_zod3.z.enum(["ISOLATED", "CROSS"]).optional(),
|
|
514
|
+
reduceOnly: import_zod3.z.boolean(),
|
|
515
|
+
hasProtection: import_zod3.z.boolean()
|
|
392
516
|
});
|
|
393
|
-
var closePositionInputSchema =
|
|
517
|
+
var closePositionInputSchema = import_zod3.z.object({
|
|
394
518
|
provider: providerSchema.optional(),
|
|
395
|
-
symbol:
|
|
396
|
-
maxSlippageBps:
|
|
519
|
+
symbol: tradingSymbolSchema,
|
|
520
|
+
maxSlippageBps: import_zod3.z.number().int().nonnegative().max(1e3).optional(),
|
|
521
|
+
/** The share of the position to close (0 < fraction < 1 closes part of it); omitted or 1 closes it all. */
|
|
522
|
+
fraction: import_zod3.z.number().positive().max(1).optional()
|
|
397
523
|
});
|
|
398
|
-
var closePositionResultSchema =
|
|
399
|
-
status:
|
|
524
|
+
var closePositionResultSchema = import_zod3.z.object({
|
|
525
|
+
status: import_zod3.z.literal("submitted"),
|
|
400
526
|
/** `no_position`: nothing was open on this coin, so no order was sent. Absent from older data-bridges. */
|
|
401
|
-
outcome:
|
|
527
|
+
outcome: import_zod3.z.enum(["closed", "no_position"]).optional(),
|
|
402
528
|
provider: providerSchema.optional(),
|
|
403
|
-
symbol:
|
|
404
|
-
mode:
|
|
405
|
-
|
|
529
|
+
symbol: import_zod3.z.string(),
|
|
530
|
+
mode: import_zod3.z.enum(["FULL", "PARTIAL"]),
|
|
531
|
+
fraction: import_zod3.z.number().positive().max(1).optional(),
|
|
532
|
+
maxSlippageBps: import_zod3.z.number().int().nonnegative().max(1e3).optional()
|
|
406
533
|
});
|
|
407
|
-
var setTpSlInputSchema =
|
|
534
|
+
var setTpSlInputSchema = import_zod3.z.object({
|
|
408
535
|
provider: providerSchema.optional(),
|
|
409
|
-
symbol:
|
|
410
|
-
applyTo:
|
|
411
|
-
quantity:
|
|
412
|
-
parentClientOrderId:
|
|
536
|
+
symbol: tradingSymbolSchema,
|
|
537
|
+
applyTo: import_zod3.z.enum(["FULL_POSITION", "CUSTOM_QUANTITY"]),
|
|
538
|
+
quantity: import_zod3.z.number().positive().optional(),
|
|
539
|
+
parentClientOrderId: import_zod3.z.string(),
|
|
413
540
|
takeProfit: tradingProtectionLegSchema.optional(),
|
|
414
541
|
stopLoss: tradingProtectionLegSchema.optional()
|
|
415
542
|
});
|
|
416
|
-
var setTpSlResultSchema =
|
|
417
|
-
status:
|
|
543
|
+
var setTpSlResultSchema = import_zod3.z.object({
|
|
544
|
+
status: import_zod3.z.literal("submitted"),
|
|
418
545
|
provider: providerSchema.optional(),
|
|
419
|
-
symbol:
|
|
420
|
-
applyTo:
|
|
421
|
-
quantity:
|
|
422
|
-
parentClientOrderId:
|
|
423
|
-
hasTakeProfit:
|
|
424
|
-
hasStopLoss:
|
|
425
|
-
});
|
|
426
|
-
var deleteTpSlInputSchema =
|
|
546
|
+
symbol: import_zod3.z.string(),
|
|
547
|
+
applyTo: import_zod3.z.enum(["FULL_POSITION", "CUSTOM_QUANTITY"]),
|
|
548
|
+
quantity: import_zod3.z.number().positive().optional(),
|
|
549
|
+
parentClientOrderId: import_zod3.z.string(),
|
|
550
|
+
hasTakeProfit: import_zod3.z.boolean(),
|
|
551
|
+
hasStopLoss: import_zod3.z.boolean()
|
|
552
|
+
});
|
|
553
|
+
var deleteTpSlInputSchema = import_zod3.z.object({
|
|
427
554
|
provider: providerSchema.optional(),
|
|
428
|
-
symbol:
|
|
429
|
-
orderId:
|
|
555
|
+
symbol: tradingSymbolSchema,
|
|
556
|
+
orderId: import_zod3.z.string()
|
|
430
557
|
});
|
|
431
|
-
var deleteTpSlResultSchema =
|
|
432
|
-
status:
|
|
558
|
+
var deleteTpSlResultSchema = import_zod3.z.object({
|
|
559
|
+
status: import_zod3.z.literal("deleted"),
|
|
433
560
|
provider: providerSchema.optional(),
|
|
434
|
-
symbol:
|
|
435
|
-
orderId:
|
|
436
|
-
});
|
|
437
|
-
var openOrderSchema = import_zod.z.object({
|
|
438
|
-
coin: import_zod.z.string(),
|
|
439
|
-
side: import_zod.z.enum(["B", "A"]),
|
|
440
|
-
limitPx: import_zod.z.string(),
|
|
441
|
-
sz: import_zod.z.string(),
|
|
442
|
-
oid: import_zod.z.number().int().nonnegative(),
|
|
443
|
-
timestamp: import_zod.z.number().int().nonnegative(),
|
|
444
|
-
origSz: import_zod.z.string(),
|
|
445
|
-
orderType: import_zod.z.string(),
|
|
446
|
-
tif: import_zod.z.string().nullable(),
|
|
447
|
-
reduceOnly: import_zod.z.boolean(),
|
|
448
|
-
isTrigger: import_zod.z.boolean(),
|
|
449
|
-
triggerPx: import_zod.z.string(),
|
|
450
|
-
cloid: import_zod.z.string().nullable(),
|
|
451
|
-
assetId: import_zod.z.number().int().nonnegative().optional()
|
|
452
|
-
});
|
|
453
|
-
var cancelOrderInputSchema = import_zod.z.object({
|
|
454
|
-
symbol: import_zod.z.string().regex(/^[a-zA-Z0-9]+(?:USDT)?$/),
|
|
455
|
-
oid: import_zod.z.number().int().nonnegative()
|
|
456
|
-
});
|
|
457
|
-
var cancelOrderResultSchema = import_zod.z.object({
|
|
458
|
-
status: import_zod.z.literal("cancelled"),
|
|
459
|
-
symbol: import_zod.z.string(),
|
|
460
|
-
oid: import_zod.z.number().int().nonnegative()
|
|
561
|
+
symbol: import_zod3.z.string(),
|
|
562
|
+
orderId: import_zod3.z.string()
|
|
461
563
|
});
|
|
462
|
-
var
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
564
|
+
var openOrderSchema = import_zod3.z.object({
|
|
565
|
+
coin: import_zod3.z.string(),
|
|
566
|
+
side: import_zod3.z.enum(["B", "A"]),
|
|
567
|
+
limitPx: import_zod3.z.string(),
|
|
568
|
+
sz: import_zod3.z.string(),
|
|
569
|
+
oid: import_zod3.z.number().int().nonnegative(),
|
|
570
|
+
timestamp: import_zod3.z.number().int().nonnegative(),
|
|
571
|
+
origSz: import_zod3.z.string(),
|
|
572
|
+
orderType: import_zod3.z.string(),
|
|
573
|
+
tif: import_zod3.z.string().nullable(),
|
|
574
|
+
reduceOnly: import_zod3.z.boolean(),
|
|
575
|
+
isTrigger: import_zod3.z.boolean(),
|
|
576
|
+
triggerPx: import_zod3.z.string(),
|
|
577
|
+
cloid: import_zod3.z.string().nullable(),
|
|
578
|
+
assetId: import_zod3.z.number().int().nonnegative().optional()
|
|
466
579
|
});
|
|
467
|
-
var
|
|
580
|
+
var cancelOrderInputSchema = import_zod3.z.object({
|
|
581
|
+
symbol: tradingSymbolSchema,
|
|
582
|
+
oid: import_zod3.z.number().int().nonnegative()
|
|
583
|
+
});
|
|
584
|
+
var cancelOrderResultSchema = import_zod3.z.object({
|
|
585
|
+
status: import_zod3.z.literal("cancelled"),
|
|
586
|
+
symbol: import_zod3.z.string(),
|
|
587
|
+
oid: import_zod3.z.number().int().nonnegative()
|
|
588
|
+
});
|
|
589
|
+
var modifyOrderInputSchema = import_zod3.z.object({
|
|
590
|
+
symbol: tradingSymbolSchema,
|
|
591
|
+
oid: import_zod3.z.number().int().nonnegative(),
|
|
592
|
+
price: import_zod3.z.number().positive().finite(),
|
|
593
|
+
/** Size in the coin (base quantity). */
|
|
594
|
+
size: import_zod3.z.number().positive().finite()
|
|
595
|
+
});
|
|
596
|
+
var modifyOrderResultSchema = import_zod3.z.object({
|
|
597
|
+
status: import_zod3.z.literal("modified"),
|
|
598
|
+
symbol: import_zod3.z.string(),
|
|
599
|
+
oid: import_zod3.z.number().int().nonnegative(),
|
|
600
|
+
price: import_zod3.z.number(),
|
|
601
|
+
size: import_zod3.z.number()
|
|
602
|
+
});
|
|
603
|
+
var AGENT_PAPER_ACCOUNT = "agent";
|
|
604
|
+
var paperFillSchema = import_zod3.z.object({
|
|
605
|
+
ts: import_zod3.z.number(),
|
|
606
|
+
coin: import_zod3.z.string(),
|
|
607
|
+
kind: import_zod3.z.string(),
|
|
608
|
+
side: import_zod3.z.enum(["BUY", "SELL"]).nullable(),
|
|
609
|
+
size: import_zod3.z.number(),
|
|
610
|
+
price: import_zod3.z.number(),
|
|
611
|
+
fee: import_zod3.z.number(),
|
|
612
|
+
pnl: import_zod3.z.number()
|
|
613
|
+
});
|
|
614
|
+
var paperPortfolioSchema = portfolioSnapshotSchema.extend({
|
|
615
|
+
cash: import_zod3.z.number().optional(),
|
|
616
|
+
realizedPnl: import_zod3.z.number().optional(),
|
|
617
|
+
feesPaid: import_zod3.z.number().optional(),
|
|
618
|
+
fundingPaid: import_zod3.z.number().optional(),
|
|
619
|
+
liquidations: import_zod3.z.number().optional()
|
|
620
|
+
});
|
|
621
|
+
var agentSetupSchema = import_zod3.z.enum(["turning up from breakdown", "falling, no turn yet", "stretched up", "stretched down", "neutral"]);
|
|
622
|
+
var smSideNameSchema = import_zod3.z.enum(["long", "short"]);
|
|
623
|
+
var agentMarketInputSchema = import_zod3.z.object({
|
|
624
|
+
coins: import_zod3.z.array(import_zod3.z.string().min(1)).min(1).max(50).optional(),
|
|
625
|
+
limit: import_zod3.z.number().int().min(1).max(300).optional(),
|
|
626
|
+
offset: import_zod3.z.number().int().nonnegative().optional(),
|
|
627
|
+
sort: import_zod3.z.enum(["volume", "rank", "move1h", "move24h", "funding", "lean", "edge"]).optional(),
|
|
628
|
+
filter: import_zod3.z.enum(["turning", "falling", "stretched_up", "stretched_down", "sm_long", "sm_short", "discount"]).optional()
|
|
629
|
+
});
|
|
630
|
+
var agentMarketRowSchema = import_zod3.z.object({
|
|
631
|
+
coin: import_zod3.z.string(),
|
|
632
|
+
rank: import_zod3.z.number().nullable(),
|
|
633
|
+
px: import_zod3.z.number().nullable(),
|
|
634
|
+
r15m: import_zod3.z.number().nullable(),
|
|
635
|
+
r1h: import_zod3.z.number().nullable(),
|
|
636
|
+
r24h: import_zod3.z.number().nullable(),
|
|
637
|
+
volUsd: import_zod3.z.number().nullable(),
|
|
638
|
+
fundAprPct: import_zod3.z.number().nullable(),
|
|
639
|
+
oiUsd: import_zod3.z.number().nullable(),
|
|
640
|
+
setup: agentSetupSchema.nullable(),
|
|
641
|
+
/** Coin limits and the book; absent from older data-bridges. */
|
|
642
|
+
maxLev: import_zod3.z.number().nullish(),
|
|
643
|
+
spreadBps: import_zod3.z.number().nullish(),
|
|
644
|
+
slip10kBps: import_zod3.z.number().nullish(),
|
|
645
|
+
slip100kBps: import_zod3.z.number().nullish(),
|
|
646
|
+
depth1pctUsd: import_zod3.z.number().nullish()
|
|
647
|
+
});
|
|
648
|
+
var agentMarketSchema = import_zod3.z.object({
|
|
649
|
+
asOf: import_zod3.z.string().nullable(),
|
|
650
|
+
smAsOf: import_zod3.z.number().nullable(),
|
|
651
|
+
market: import_zod3.z.object({ os: import_zod3.z.number().nullable(), mti: import_zod3.z.number().nullable(), Mti: import_zod3.z.number().nullable() }),
|
|
652
|
+
total: import_zod3.z.number().int().nonnegative(),
|
|
653
|
+
offset: import_zod3.z.number().int().nonnegative(),
|
|
654
|
+
rows: import_zod3.z.array(
|
|
655
|
+
agentMarketRowSchema.extend({
|
|
656
|
+
sm: import_zod3.z.object({
|
|
657
|
+
side: smSideNameSchema.nullable(),
|
|
658
|
+
share: import_zod3.z.number(),
|
|
659
|
+
edgePct: import_zod3.z.number().nullable(),
|
|
660
|
+
discount: import_zod3.z.enum(["strict", "fill"]).nullable()
|
|
661
|
+
}).nullable()
|
|
662
|
+
})
|
|
663
|
+
),
|
|
664
|
+
next: import_zod3.z.number().int().nonnegative().nullable(),
|
|
665
|
+
unknown: import_zod3.z.array(import_zod3.z.string())
|
|
666
|
+
});
|
|
667
|
+
var smPileInSchema = import_zod3.z.object({
|
|
668
|
+
coin: import_zod3.z.string(),
|
|
669
|
+
side: smSideNameSchema,
|
|
670
|
+
dir: import_zod3.z.enum(["in", "out"]),
|
|
671
|
+
traders: import_zod3.z.number(),
|
|
672
|
+
totalUsd: import_zod3.z.number(),
|
|
673
|
+
weight: import_zod3.z.number(),
|
|
674
|
+
names: import_zod3.z.array(import_zod3.z.string()),
|
|
675
|
+
first: import_zod3.z.number(),
|
|
676
|
+
last: import_zod3.z.number()
|
|
677
|
+
});
|
|
678
|
+
var smartMoneyBoardInputSchema = import_zod3.z.object({
|
|
679
|
+
view: import_zod3.z.enum(["conviction", "discount", "flows", "pileins"]).optional(),
|
|
680
|
+
side: import_zod3.z.enum(["long", "short", "any"]).optional(),
|
|
681
|
+
limit: import_zod3.z.number().int().min(1).max(25).optional()
|
|
682
|
+
});
|
|
683
|
+
var smartMoneyBoardSchema = import_zod3.z.object({
|
|
684
|
+
asOf: import_zod3.z.number().nullable(),
|
|
685
|
+
view: import_zod3.z.enum(["conviction", "discount", "flows", "pileins"]),
|
|
686
|
+
wallets: import_zod3.z.number(),
|
|
687
|
+
known: import_zod3.z.number(),
|
|
688
|
+
rows: import_zod3.z.array(
|
|
689
|
+
import_zod3.z.object({
|
|
690
|
+
coin: import_zod3.z.string(),
|
|
691
|
+
side: smSideNameSchema.nullable(),
|
|
692
|
+
share: import_zod3.z.number(),
|
|
693
|
+
lean: import_zod3.z.number(),
|
|
694
|
+
sideUsd: import_zod3.z.number(),
|
|
695
|
+
heads: import_zod3.z.number(),
|
|
696
|
+
traders: import_zod3.z.number(),
|
|
697
|
+
entry: import_zod3.z.number().nullable(),
|
|
698
|
+
mark: import_zod3.z.number().nullable(),
|
|
699
|
+
edgePct: import_zod3.z.number().nullable(),
|
|
700
|
+
flow1hUsd: import_zod3.z.number(),
|
|
701
|
+
discount: import_zod3.z.enum(["strict", "fill"]).nullable(),
|
|
702
|
+
setup: agentSetupSchema.nullable(),
|
|
703
|
+
r24h: import_zod3.z.number().nullable()
|
|
704
|
+
})
|
|
705
|
+
),
|
|
706
|
+
pileIns: import_zod3.z.array(smPileInSchema),
|
|
707
|
+
note: import_zod3.z.string().nullable()
|
|
708
|
+
});
|
|
709
|
+
var smartMoneyMovesInputSchema = import_zod3.z.object({
|
|
710
|
+
since: import_zod3.z.string().regex(/^\d{1,16}-\d{1,16}$/).optional(),
|
|
711
|
+
coin: import_zod3.z.string().min(1).optional(),
|
|
712
|
+
kind: import_zod3.z.enum(["entries", "exits", "all"]).optional(),
|
|
713
|
+
minUsd: import_zod3.z.number().nonnegative().optional(),
|
|
714
|
+
limit: import_zod3.z.number().int().min(1).max(50).optional()
|
|
715
|
+
});
|
|
716
|
+
var smMoveSchema = import_zod3.z.object({
|
|
717
|
+
id: import_zod3.z.string(),
|
|
718
|
+
t: import_zod3.z.number(),
|
|
719
|
+
coin: import_zod3.z.string(),
|
|
720
|
+
kind: import_zod3.z.enum(["open", "add", "reduce", "close", "flip"]),
|
|
721
|
+
side: smSideNameSchema,
|
|
722
|
+
usd: import_zod3.z.number(),
|
|
723
|
+
px: import_zod3.z.number(),
|
|
724
|
+
lev: import_zod3.z.number().nullable(),
|
|
725
|
+
pnl: import_zod3.z.number().nullable(),
|
|
726
|
+
pnlPct: import_zod3.z.number().nullable(),
|
|
727
|
+
nick: import_zod3.z.string(),
|
|
728
|
+
rank: import_zod3.z.number(),
|
|
729
|
+
style: import_zod3.z.string(),
|
|
730
|
+
win: import_zod3.z.number().nullable(),
|
|
731
|
+
w: import_zod3.z.number()
|
|
732
|
+
});
|
|
733
|
+
var smartMoneyMovesSchema = import_zod3.z.object({
|
|
734
|
+
order: import_zod3.z.enum(["newest", "oldest"]),
|
|
735
|
+
moves: import_zod3.z.array(smMoveSchema),
|
|
736
|
+
next: import_zod3.z.string().nullable(),
|
|
737
|
+
more: import_zod3.z.boolean(),
|
|
738
|
+
gap: import_zod3.z.boolean(),
|
|
739
|
+
note: import_zod3.z.string().nullable()
|
|
740
|
+
});
|
|
741
|
+
var smBookSideSchema = import_zod3.z.object({ usd: import_zod3.z.number(), heads: import_zod3.z.number(), traders: import_zod3.z.number(), entry: import_zod3.z.number().nullable() });
|
|
742
|
+
var smartMoneyCoinSchema = import_zod3.z.object({
|
|
743
|
+
coin: import_zod3.z.string(),
|
|
744
|
+
asOf: import_zod3.z.number().nullable(),
|
|
745
|
+
sm: import_zod3.z.object({
|
|
746
|
+
coin: import_zod3.z.string(),
|
|
747
|
+
mark: import_zod3.z.number().nullable(),
|
|
748
|
+
long: smBookSideSchema,
|
|
749
|
+
short: smBookSideSchema,
|
|
750
|
+
lean: import_zod3.z.number(),
|
|
751
|
+
side: smSideNameSchema.nullable(),
|
|
752
|
+
share: import_zod3.z.number(),
|
|
753
|
+
edgePct: import_zod3.z.number().nullable(),
|
|
754
|
+
flow1hUsd: import_zod3.z.number(),
|
|
755
|
+
conviction: import_zod3.z.number().nullable(),
|
|
756
|
+
discount: import_zod3.z.enum(["strict", "fill"]).nullable(),
|
|
757
|
+
pileIn: smPileInSchema.nullable()
|
|
758
|
+
}).nullable(),
|
|
759
|
+
market: agentMarketRowSchema.nullable(),
|
|
760
|
+
moves: import_zod3.z.array(smMoveSchema),
|
|
761
|
+
note: import_zod3.z.string().nullable()
|
|
762
|
+
});
|
|
763
|
+
var marketCandlesInputSchema = import_zod3.z.object({
|
|
764
|
+
symbol: import_zod3.z.string().regex(/^[A-Za-z0-9]{1,20}$/),
|
|
765
|
+
interval: import_zod3.z.enum(["1m", "3m", "5m", "15m", "30m", "1h", "2h", "4h", "6h", "8h", "12h", "1d", "1w", "1M"]),
|
|
766
|
+
endTime: import_zod3.z.number().int().positive().optional(),
|
|
767
|
+
limit: import_zod3.z.number().int().min(1).max(10080).optional()
|
|
768
|
+
});
|
|
769
|
+
var marketCandlesSchema = import_zod3.z.object({
|
|
770
|
+
candles: import_zod3.z.array(import_zod3.z.object({ t: import_zod3.z.number(), o: import_zod3.z.number(), h: import_zod3.z.number(), l: import_zod3.z.number(), c: import_zod3.z.number(), v: import_zod3.z.number() })),
|
|
771
|
+
complete: import_zod3.z.boolean()
|
|
772
|
+
});
|
|
773
|
+
var DEFAULT_AGENT_REFRESH_WINDOW_MS = 60 * 1e3;
|
|
774
|
+
var agentConnectionStatusSchema = import_zod4.z.enum([
|
|
775
|
+
"disconnected",
|
|
776
|
+
"pending",
|
|
777
|
+
"connected",
|
|
778
|
+
"rejected",
|
|
779
|
+
"expired",
|
|
780
|
+
"failed"
|
|
781
|
+
]);
|
|
782
|
+
var agentSessionIssuerSchema = import_zod4.z.object({
|
|
783
|
+
identityServiceUrl: import_zod4.z.string(),
|
|
784
|
+
dataBridgeUrl: import_zod4.z.string()
|
|
785
|
+
});
|
|
786
|
+
var storedAgentSessionSchema = import_zod4.z.object({
|
|
787
|
+
accessToken: import_zod4.z.string(),
|
|
788
|
+
refreshToken: import_zod4.z.string(),
|
|
789
|
+
tokenType: agentTokenResponseSchema.shape.tokenType,
|
|
790
|
+
ownerUserId: import_zod4.z.string(),
|
|
791
|
+
agentGrantId: import_zod4.z.string(),
|
|
792
|
+
scope: import_zod4.z.array(agentScopeSchema).min(1),
|
|
793
|
+
accessTokenExpiresAt: import_zod4.z.string().datetime(),
|
|
794
|
+
createdAt: import_zod4.z.string().datetime(),
|
|
795
|
+
lastRefreshedAt: import_zod4.z.string().datetime(),
|
|
796
|
+
issuer: agentSessionIssuerSchema.nullish()
|
|
797
|
+
});
|
|
798
|
+
var agentSessionViewSchema = import_zod4.z.object({
|
|
799
|
+
connected: import_zod4.z.boolean(),
|
|
800
|
+
status: agentConnectionStatusSchema,
|
|
801
|
+
ownerUserId: import_zod4.z.string().optional(),
|
|
802
|
+
agentGrantId: import_zod4.z.string().optional(),
|
|
803
|
+
scope: import_zod4.z.array(agentScopeSchema).optional(),
|
|
804
|
+
accessTokenExpiresAt: import_zod4.z.string().datetime().optional(),
|
|
805
|
+
lastRefreshedAt: import_zod4.z.string().datetime().optional()
|
|
806
|
+
});
|
|
807
|
+
function createStoredAgentSession(tokenResponse, now = /* @__PURE__ */ new Date()) {
|
|
808
|
+
const createdAt = now.toISOString();
|
|
809
|
+
return {
|
|
810
|
+
accessToken: tokenResponse.accessToken,
|
|
811
|
+
refreshToken: tokenResponse.refreshToken,
|
|
812
|
+
tokenType: tokenResponse.tokenType,
|
|
813
|
+
ownerUserId: tokenResponse.ownerUserId,
|
|
814
|
+
agentGrantId: tokenResponse.agentGrantId,
|
|
815
|
+
scope: tokenResponse.scope,
|
|
816
|
+
accessTokenExpiresAt: new Date(now.getTime() + tokenResponse.expiresInSeconds * 1e3).toISOString(),
|
|
817
|
+
createdAt,
|
|
818
|
+
lastRefreshedAt: createdAt
|
|
819
|
+
};
|
|
820
|
+
}
|
|
821
|
+
function sanitizeAgentSession(session, status = session ? "connected" : "disconnected") {
|
|
822
|
+
if (!session) {
|
|
823
|
+
return {
|
|
824
|
+
connected: false,
|
|
825
|
+
status: status === "connected" ? "disconnected" : status
|
|
826
|
+
};
|
|
827
|
+
}
|
|
828
|
+
return {
|
|
829
|
+
connected: true,
|
|
830
|
+
status,
|
|
831
|
+
ownerUserId: session.ownerUserId,
|
|
832
|
+
agentGrantId: session.agentGrantId,
|
|
833
|
+
scope: session.scope,
|
|
834
|
+
accessTokenExpiresAt: session.accessTokenExpiresAt,
|
|
835
|
+
lastRefreshedAt: session.lastRefreshedAt
|
|
836
|
+
};
|
|
837
|
+
}
|
|
838
|
+
function shouldRefreshAgentSession(session, now = /* @__PURE__ */ new Date(), refreshWindowMs = DEFAULT_AGENT_REFRESH_WINDOW_MS) {
|
|
839
|
+
return new Date(session.accessTokenExpiresAt).getTime() - now.getTime() <= refreshWindowMs;
|
|
840
|
+
}
|
|
841
|
+
var pendingAgentLoginSchema = import_zod.z.object({
|
|
842
|
+
status: import_zod.z.literal("pending"),
|
|
843
|
+
deviceCode: import_zod.z.string(),
|
|
844
|
+
userCode: import_zod.z.string(),
|
|
845
|
+
verificationUri: import_zod.z.string().url(),
|
|
846
|
+
verificationUriComplete: import_zod.z.string().url(),
|
|
847
|
+
expiresInSeconds: import_zod.z.number().int().positive(),
|
|
848
|
+
pollIntervalSeconds: import_zod.z.number().int().positive(),
|
|
849
|
+
requestedScope: import_zod.z.array(agentScopeSchema).min(1),
|
|
850
|
+
startedAt: import_zod.z.string().datetime()
|
|
851
|
+
});
|
|
852
|
+
var DEFAULT_SCOPE = [AGENT_SCOPES.ACCOUNT_READ, AGENT_SCOPES.MARKET_READ];
|
|
853
|
+
var AgentAuthService = class {
|
|
468
854
|
constructor(options) {
|
|
469
|
-
|
|
470
|
-
__publicField2(this, "
|
|
471
|
-
__publicField2(this, "
|
|
472
|
-
__publicField2(this, "
|
|
473
|
-
__publicField2(this, "
|
|
474
|
-
this
|
|
475
|
-
this
|
|
476
|
-
this
|
|
477
|
-
this
|
|
478
|
-
this
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
855
|
+
__publicField2(this, "client");
|
|
856
|
+
__publicField2(this, "sessionStore");
|
|
857
|
+
__publicField2(this, "messages");
|
|
858
|
+
__publicField2(this, "refreshWindowMs");
|
|
859
|
+
__publicField2(this, "now");
|
|
860
|
+
__publicField2(this, "defaultClientName");
|
|
861
|
+
__publicField2(this, "deviceLogin");
|
|
862
|
+
__publicField2(this, "reloginForMissingScope");
|
|
863
|
+
__publicField2(this, "refreshInFlight", null);
|
|
864
|
+
__publicField2(this, "exchangeInFlight", null);
|
|
865
|
+
this.client = options.client;
|
|
866
|
+
this.sessionStore = options.sessionStore;
|
|
867
|
+
this.messages = options.messages;
|
|
868
|
+
this.refreshWindowMs = options.refreshWindowMs ?? DEFAULT_AGENT_REFRESH_WINDOW_MS;
|
|
869
|
+
this.now = options.now ?? (() => /* @__PURE__ */ new Date());
|
|
870
|
+
this.defaultClientName = options.defaultClientName;
|
|
871
|
+
this.deviceLogin = options.deviceLogin ?? true;
|
|
872
|
+
this.reloginForMissingScope = options.reloginForMissingScope ?? false;
|
|
873
|
+
}
|
|
874
|
+
async startLogin(input) {
|
|
875
|
+
if (!this.deviceLogin) {
|
|
876
|
+
throw new Error(this.messages.reconnect);
|
|
877
|
+
}
|
|
878
|
+
const response = await this.client.startLogin(input);
|
|
879
|
+
const result = {
|
|
880
|
+
status: "pending",
|
|
881
|
+
...response,
|
|
882
|
+
requestedScope: input.scope,
|
|
883
|
+
startedAt: this.now().toISOString()
|
|
884
|
+
};
|
|
885
|
+
await this.sessionStore.setPendingLogin(result);
|
|
886
|
+
return result;
|
|
887
|
+
}
|
|
888
|
+
async resetSessionAndStartLogin(input) {
|
|
889
|
+
await this.clearSessionState();
|
|
890
|
+
return this.startLogin(input);
|
|
891
|
+
}
|
|
892
|
+
async startRelogin(options = {}) {
|
|
893
|
+
const started = await this.resetSessionAndStartLogin(this.loginInput(options));
|
|
894
|
+
return this.pendingResult(started);
|
|
895
|
+
}
|
|
896
|
+
async clearSessionState() {
|
|
897
|
+
await this.sessionStore.clearSession();
|
|
898
|
+
await this.sessionStore.clearPendingLogin();
|
|
899
|
+
}
|
|
900
|
+
/** After a 401 from Helios: drops the session and starts a fresh login. Null for any other error. */
|
|
901
|
+
async invalidateIfUnauthorized(error, options = {}) {
|
|
902
|
+
if (!isHeliosAgentApiError(error) || error.status !== 401) {
|
|
903
|
+
return null;
|
|
904
|
+
}
|
|
905
|
+
return this.startRelogin(options);
|
|
906
|
+
}
|
|
907
|
+
/** After a 401 from Helios: refreshes the session even if it isn't near expiry. Null for any other error. */
|
|
908
|
+
async refreshSessionAfterUnauthorized(error) {
|
|
909
|
+
if (!isHeliosAgentApiError(error) || error.status !== 401) {
|
|
910
|
+
return null;
|
|
911
|
+
}
|
|
912
|
+
const session = await this.sessionStore.getSession();
|
|
913
|
+
return session ? this.refreshStoredSession(session, true) : null;
|
|
914
|
+
}
|
|
915
|
+
async ensureLogin(options = {}) {
|
|
916
|
+
const requestedScope = options.scope ?? DEFAULT_SCOPE;
|
|
917
|
+
const requestedConstraints = options.constraints ?? {};
|
|
918
|
+
const session = await this.getValidSession();
|
|
919
|
+
if (session) {
|
|
920
|
+
if (!this.reloginForMissingScope || hasScopes(session, requestedScope)) {
|
|
921
|
+
return connectedResult(session);
|
|
922
|
+
}
|
|
923
|
+
await this.sessionStore.clearSession();
|
|
924
|
+
}
|
|
925
|
+
const pendingLogin = await this.getPendingLogin(requestedScope, requestedConstraints);
|
|
926
|
+
if (pendingLogin) {
|
|
927
|
+
const polled = await this.exchangePendingLogin(pendingLogin.deviceCode).catch(() => null);
|
|
928
|
+
if (polled?.status === "connected") return polled;
|
|
929
|
+
const stillPending = await this.getPendingLogin(requestedScope, requestedConstraints);
|
|
930
|
+
if (stillPending) return this.pendingResult(stillPending);
|
|
931
|
+
}
|
|
932
|
+
return this.pendingResult(await this.startLogin(this.loginInput(options)));
|
|
933
|
+
}
|
|
934
|
+
/**
|
|
935
|
+
* Polls the stored pending login. A code, when given, must be that login's device code or the user code shown to the
|
|
936
|
+
* person: a code from anywhere else (a prompt injection carrying a login someone else approved) is never exchanged, so
|
|
937
|
+
* it can't swap the stored session for another account's.
|
|
938
|
+
*/
|
|
939
|
+
async pollLogin(code) {
|
|
940
|
+
const pending = await this.sessionStore.getPendingLogin();
|
|
941
|
+
if (!pending) {
|
|
942
|
+
const session = await this.getValidSession();
|
|
943
|
+
if (session) return connectedResult(session);
|
|
944
|
+
return { status: "failed", connected: false, message: this.messages.noLoginInProgress };
|
|
945
|
+
}
|
|
946
|
+
const given = code?.trim();
|
|
947
|
+
if (given && given !== pending.deviceCode && given.toUpperCase() !== pending.userCode.toUpperCase()) {
|
|
948
|
+
return { status: "failed", connected: false, message: this.messages.wrongCode };
|
|
949
|
+
}
|
|
950
|
+
return this.exchangePendingLogin(pending.deviceCode);
|
|
951
|
+
}
|
|
952
|
+
async whoami() {
|
|
953
|
+
return sanitizeAgentSession(await this.getValidSession());
|
|
954
|
+
}
|
|
955
|
+
async getAccessToken() {
|
|
956
|
+
return (await this.getValidSession())?.accessToken ?? null;
|
|
957
|
+
}
|
|
958
|
+
async getPendingLoginState() {
|
|
959
|
+
return this.sessionStore.getPendingLogin();
|
|
960
|
+
}
|
|
961
|
+
async requireSession() {
|
|
962
|
+
const session = await this.getValidSession();
|
|
963
|
+
if (!session) {
|
|
964
|
+
throw new Error(this.deviceLogin ? "No Helios delegated agent session is connected" : this.messages.reconnect);
|
|
965
|
+
}
|
|
966
|
+
return session;
|
|
967
|
+
}
|
|
968
|
+
loginInput(options) {
|
|
969
|
+
return {
|
|
970
|
+
clientName: options.clientName ?? this.defaultClientName,
|
|
971
|
+
scope: options.scope ?? DEFAULT_SCOPE,
|
|
972
|
+
constraints: options.constraints
|
|
973
|
+
};
|
|
974
|
+
}
|
|
975
|
+
pendingResult(started) {
|
|
976
|
+
return { ...started, connected: false, message: this.messages.loginPrompt(started) };
|
|
977
|
+
}
|
|
978
|
+
async getValidSession() {
|
|
979
|
+
const session = await this.sessionStore.getSession();
|
|
980
|
+
if (!session) {
|
|
981
|
+
return null;
|
|
982
|
+
}
|
|
983
|
+
if (!shouldRefreshAgentSession(session, this.now(), this.refreshWindowMs)) {
|
|
984
|
+
return session;
|
|
985
|
+
}
|
|
986
|
+
return this.refreshStoredSession(session, false);
|
|
987
|
+
}
|
|
988
|
+
/**
|
|
989
|
+
* Refreshes `seen` (the session the caller read), joining a refresh already in progress. `force` refreshes even a
|
|
990
|
+
* session that isn't near expiry (Helios just answered 401 to it).
|
|
991
|
+
*/
|
|
992
|
+
refreshStoredSession(seen, force) {
|
|
993
|
+
this.refreshInFlight ?? (this.refreshInFlight = this.refreshOnce(seen, force).finally(() => {
|
|
994
|
+
this.refreshInFlight = null;
|
|
995
|
+
}));
|
|
996
|
+
return this.refreshInFlight;
|
|
997
|
+
}
|
|
998
|
+
async refreshOnce(seen, force) {
|
|
999
|
+
const latest = await this.sessionStore.getSession();
|
|
1000
|
+
if (!latest) {
|
|
1001
|
+
return null;
|
|
1002
|
+
}
|
|
1003
|
+
const rotatedElsewhere = latest.refreshToken !== seen.refreshToken;
|
|
1004
|
+
if ((rotatedElsewhere || !force) && !shouldRefreshAgentSession(latest, this.now(), this.refreshWindowMs)) {
|
|
1005
|
+
return latest;
|
|
1006
|
+
}
|
|
1007
|
+
if (!latest.refreshToken) {
|
|
1008
|
+
return null;
|
|
1009
|
+
}
|
|
1010
|
+
try {
|
|
1011
|
+
const refreshed = await this.client.refreshSession(latest.refreshToken);
|
|
1012
|
+
const nextSession = createStoredAgentSession(refreshed, this.now());
|
|
1013
|
+
await this.sessionStore.setSession(nextSession);
|
|
1014
|
+
return nextSession;
|
|
1015
|
+
} catch (error) {
|
|
1016
|
+
const stored = await this.sessionStore.getSession().catch(() => null);
|
|
1017
|
+
if (stored && stored.refreshToken !== latest.refreshToken) {
|
|
1018
|
+
return stored;
|
|
1019
|
+
}
|
|
1020
|
+
if (isHeliosAgentApiError(error) && error.code === "invalid_grant") {
|
|
1021
|
+
await this.clearSessionState();
|
|
1022
|
+
return null;
|
|
1023
|
+
}
|
|
1024
|
+
throw error;
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
1027
|
+
exchangePendingLogin(deviceCode) {
|
|
1028
|
+
if (this.exchangeInFlight?.deviceCode !== deviceCode) {
|
|
1029
|
+
const flight = {
|
|
1030
|
+
deviceCode,
|
|
1031
|
+
promise: this.exchangeOnce(deviceCode).finally(() => {
|
|
1032
|
+
if (this.exchangeInFlight === flight) this.exchangeInFlight = null;
|
|
1033
|
+
})
|
|
1034
|
+
};
|
|
1035
|
+
this.exchangeInFlight = flight;
|
|
1036
|
+
}
|
|
1037
|
+
return this.exchangeInFlight.promise;
|
|
1038
|
+
}
|
|
1039
|
+
async exchangeOnce(deviceCode) {
|
|
1040
|
+
try {
|
|
1041
|
+
const response = await this.client.exchangeDeviceCode(deviceCode);
|
|
1042
|
+
const session = createStoredAgentSession(response, this.now());
|
|
1043
|
+
await this.sessionStore.setSession(session);
|
|
1044
|
+
await this.sessionStore.clearPendingLogin();
|
|
1045
|
+
return connectedResult(session);
|
|
1046
|
+
} catch (error) {
|
|
1047
|
+
if (!isHeliosAgentApiError(error)) {
|
|
1048
|
+
return {
|
|
1049
|
+
status: "failed",
|
|
1050
|
+
connected: false,
|
|
1051
|
+
message: error instanceof Error ? error.message : "Unexpected Helios login error"
|
|
1052
|
+
};
|
|
1053
|
+
}
|
|
1054
|
+
switch (error.code) {
|
|
1055
|
+
case "authorization_pending":
|
|
1056
|
+
return { status: "pending", connected: false, message: error.message };
|
|
1057
|
+
case "access_denied":
|
|
1058
|
+
await this.clearPendingLoginIfMatches(deviceCode);
|
|
1059
|
+
return { status: "rejected", connected: false, message: error.message };
|
|
1060
|
+
case "expired_token":
|
|
1061
|
+
await this.clearPendingLoginIfMatches(deviceCode);
|
|
1062
|
+
return { status: "expired", connected: false, message: error.message };
|
|
1063
|
+
case "invalid_grant":
|
|
1064
|
+
await this.clearPendingLoginIfMatches(deviceCode);
|
|
1065
|
+
return { status: "failed", connected: false, message: this.messages.staleLogin };
|
|
1066
|
+
default:
|
|
1067
|
+
return { status: "failed", connected: false, message: error.message };
|
|
1068
|
+
}
|
|
1069
|
+
}
|
|
1070
|
+
}
|
|
1071
|
+
async getPendingLogin(requestedScope, requestedConstraints) {
|
|
1072
|
+
const pendingLogin = await this.sessionStore.getPendingLogin();
|
|
1073
|
+
if (!pendingLogin) {
|
|
1074
|
+
return null;
|
|
1075
|
+
}
|
|
1076
|
+
const expiresAt = new Date(pendingLogin.startedAt).getTime() + pendingLogin.expiresInSeconds * 1e3;
|
|
1077
|
+
if (this.now().getTime() >= expiresAt) {
|
|
1078
|
+
await this.sessionStore.clearPendingLogin();
|
|
1079
|
+
return null;
|
|
1080
|
+
}
|
|
1081
|
+
const pendingScope = new Set(pendingLogin.requestedScope);
|
|
1082
|
+
if (!requestedScope.every((scope) => pendingScope.has(scope))) {
|
|
1083
|
+
return null;
|
|
1084
|
+
}
|
|
1085
|
+
return Object.keys(requestedConstraints ?? {}).length === 0 ? pendingLogin : null;
|
|
1086
|
+
}
|
|
1087
|
+
async clearPendingLoginIfMatches(deviceCode) {
|
|
1088
|
+
const pendingLogin = await this.sessionStore.getPendingLogin();
|
|
1089
|
+
if (pendingLogin?.deviceCode === deviceCode) {
|
|
1090
|
+
await this.sessionStore.clearPendingLogin();
|
|
486
1091
|
}
|
|
487
1092
|
}
|
|
488
1093
|
};
|
|
489
|
-
function
|
|
490
|
-
|
|
1094
|
+
async function retryAfterUnauthorizedRefresh(options) {
|
|
1095
|
+
const refreshedSession = await options.authService.refreshSessionAfterUnauthorized(options.error);
|
|
1096
|
+
if (!refreshedSession) {
|
|
1097
|
+
return options.authService.invalidateIfUnauthorized(options.error, options.loginOptions);
|
|
1098
|
+
}
|
|
1099
|
+
try {
|
|
1100
|
+
return await options.retry(refreshedSession.accessToken);
|
|
1101
|
+
} catch (retryError) {
|
|
1102
|
+
const relogin = await options.authService.invalidateIfUnauthorized(retryError, options.loginOptions);
|
|
1103
|
+
if (relogin) {
|
|
1104
|
+
return relogin;
|
|
1105
|
+
}
|
|
1106
|
+
throw retryError;
|
|
1107
|
+
}
|
|
1108
|
+
}
|
|
1109
|
+
function connectedResult(session) {
|
|
1110
|
+
return { ...sanitizeAgentSession(session), connected: true, status: "connected" };
|
|
1111
|
+
}
|
|
1112
|
+
function hasScopes(session, requestedScope) {
|
|
1113
|
+
const granted = new Set(session.scope);
|
|
1114
|
+
return requestedScope.every((scope) => granted.has(scope));
|
|
491
1115
|
}
|
|
492
1116
|
var HeliosAgentAuthClient = class {
|
|
493
1117
|
constructor(options) {
|
|
494
1118
|
__publicField2(this, "baseUrl");
|
|
495
1119
|
__publicField2(this, "fetchImpl");
|
|
496
1120
|
__publicField2(this, "headers");
|
|
1121
|
+
__publicField2(this, "startSecret");
|
|
497
1122
|
this.baseUrl = options.baseUrl.replace(/\/+$/, "");
|
|
498
1123
|
this.fetchImpl = options.fetch ?? fetch;
|
|
499
1124
|
this.headers = options.headers ?? {};
|
|
1125
|
+
this.startSecret = options.startSecret || void 0;
|
|
500
1126
|
}
|
|
501
|
-
async startLogin(input) {
|
|
1127
|
+
async startLogin(input, options = {}) {
|
|
502
1128
|
const parsed = agentDeviceStartInputSchema.parse(input);
|
|
503
|
-
|
|
1129
|
+
const headers = this.startSecret ? { [AGENT_START_SECRET_HEADER]: this.startSecret, ...options.clientIp ? { [AGENT_CLIENT_IP_HEADER]: options.clientIp } : {} } : void 0;
|
|
1130
|
+
return this.post("/api/agents/device/start", parsed, agentDeviceStartOutputSchema, headers);
|
|
504
1131
|
}
|
|
505
|
-
async exchangeDeviceCode(deviceCode) {
|
|
506
|
-
const parsed =
|
|
507
|
-
return this.post("/api/agents/device/token",
|
|
1132
|
+
async exchangeDeviceCode(deviceCode, options = {}) {
|
|
1133
|
+
const parsed = agentDeviceTokenInputSchema.parse({ deviceCode, approvalProof: options.approvalProof });
|
|
1134
|
+
return this.post("/api/agents/device/token", parsed, agentTokenResponseSchema);
|
|
508
1135
|
}
|
|
509
1136
|
async refreshSession(refreshToken) {
|
|
510
1137
|
const parsed = agentRefreshInputSchema.parse({ refreshToken });
|
|
511
1138
|
return this.post("/api/agents/auth/refresh", parsed, agentTokenResponseSchema);
|
|
512
1139
|
}
|
|
513
|
-
async post(path3, body, schema) {
|
|
1140
|
+
async post(path3, body, schema, extraHeaders) {
|
|
514
1141
|
let response;
|
|
515
1142
|
try {
|
|
516
1143
|
response = await this.fetchImpl(`${this.baseUrl}${path3}`, {
|
|
@@ -518,7 +1145,8 @@ var HeliosAgentAuthClient = class {
|
|
|
518
1145
|
headers: {
|
|
519
1146
|
Accept: "application/json",
|
|
520
1147
|
"Content-Type": "application/json",
|
|
521
|
-
...this.headers
|
|
1148
|
+
...this.headers,
|
|
1149
|
+
...extraHeaders
|
|
522
1150
|
},
|
|
523
1151
|
body: JSON.stringify(body)
|
|
524
1152
|
});
|
|
@@ -544,12 +1172,12 @@ var HeliosAgentAuthClient = class {
|
|
|
544
1172
|
return schema.parse(payload);
|
|
545
1173
|
}
|
|
546
1174
|
async parseJson(response) {
|
|
547
|
-
const
|
|
548
|
-
if (!
|
|
1175
|
+
const text2 = await response.text();
|
|
1176
|
+
if (!text2) {
|
|
549
1177
|
return null;
|
|
550
1178
|
}
|
|
551
1179
|
try {
|
|
552
|
-
return JSON.parse(
|
|
1180
|
+
return JSON.parse(text2);
|
|
553
1181
|
} catch (error) {
|
|
554
1182
|
throw new HeliosAgentApiError({
|
|
555
1183
|
status: response.status,
|
|
@@ -618,6 +1246,30 @@ var HeliosAgentDataBridgeClient = class {
|
|
|
618
1246
|
const payload = await this.request(accessToken, "market.getSymbolDetail", parsedInput);
|
|
619
1247
|
return marketSymbolDetailSchema.parse(this.unwrapResult(payload));
|
|
620
1248
|
}
|
|
1249
|
+
/** The lean market table: price, two returns, volume, funding, open interest, setup label and the smart money lean. */
|
|
1250
|
+
async getAgentMarket(accessToken, input = {}) {
|
|
1251
|
+
const payload = await this.request(accessToken, "market.getAgentTable", agentMarketInputSchema.parse(input));
|
|
1252
|
+
return agentMarketSchema.parse(this.unwrapResult(payload));
|
|
1253
|
+
}
|
|
1254
|
+
async getMarketCandles(accessToken, input) {
|
|
1255
|
+
const payload = await this.request(accessToken, "market.getCandles", marketCandlesInputSchema.parse(input));
|
|
1256
|
+
return marketCandlesSchema.parse(this.unwrapResult(payload));
|
|
1257
|
+
}
|
|
1258
|
+
/** The research wallets' smart money board (conviction, discount, flows or pile-ins). */
|
|
1259
|
+
async getSmartMoneyBoard(accessToken, input = {}) {
|
|
1260
|
+
const payload = await this.request(accessToken, "smartMoney.board", smartMoneyBoardInputSchema.parse(input));
|
|
1261
|
+
return smartMoneyBoardSchema.parse(this.unwrapResult(payload));
|
|
1262
|
+
}
|
|
1263
|
+
/** Their trades by nickname: the latest, or only what came after an earlier call's `next`. */
|
|
1264
|
+
async getSmartMoneyMoves(accessToken, input = {}) {
|
|
1265
|
+
const payload = await this.request(accessToken, "smartMoney.moves", smartMoneyMovesInputSchema.parse(input));
|
|
1266
|
+
return smartMoneyMovesSchema.parse(this.unwrapResult(payload));
|
|
1267
|
+
}
|
|
1268
|
+
/** One coin: their book, its market row and their latest trades on it. */
|
|
1269
|
+
async getSmartMoneyCoin(accessToken, coin2) {
|
|
1270
|
+
const payload = await this.request(accessToken, "smartMoney.coin", { coin: coin2 });
|
|
1271
|
+
return smartMoneyCoinSchema.parse(this.unwrapResult(payload));
|
|
1272
|
+
}
|
|
621
1273
|
async submitTradeIntent(accessToken, input) {
|
|
622
1274
|
const parsedInput = submitTradeIntentInputSchema.parse(input);
|
|
623
1275
|
const payload = await this.request(accessToken, "trading.submitIntent", parsedInput, "POST");
|
|
@@ -643,13 +1295,15 @@ var HeliosAgentDataBridgeClient = class {
|
|
|
643
1295
|
}
|
|
644
1296
|
async closePosition(accessToken, input) {
|
|
645
1297
|
const parsedInput = closePositionInputSchema.parse(input);
|
|
1298
|
+
const partial = parsedInput.fraction !== void 0 && parsedInput.fraction < 1;
|
|
646
1299
|
const payload = await this.request(
|
|
647
1300
|
accessToken,
|
|
648
1301
|
"positions.close",
|
|
649
1302
|
{
|
|
650
1303
|
...parsedInput.provider !== void 0 ? { provider: parsedInput.provider } : {},
|
|
651
1304
|
symbol: parsedInput.symbol,
|
|
652
|
-
mode: "FULL",
|
|
1305
|
+
mode: partial ? "PARTIAL" : "FULL",
|
|
1306
|
+
...partial ? { fraction: parsedInput.fraction } : {},
|
|
653
1307
|
...parsedInput.maxSlippageBps !== void 0 ? { maxSlippageBps: parsedInput.maxSlippageBps } : {}
|
|
654
1308
|
},
|
|
655
1309
|
"POST"
|
|
@@ -660,7 +1314,8 @@ var HeliosAgentDataBridgeClient = class {
|
|
|
660
1314
|
...outcome === "closed" || outcome === "no_position" ? { outcome } : {},
|
|
661
1315
|
provider: parsedInput.provider,
|
|
662
1316
|
symbol: parsedInput.symbol,
|
|
663
|
-
mode: "FULL",
|
|
1317
|
+
mode: partial ? "PARTIAL" : "FULL",
|
|
1318
|
+
...partial ? { fraction: parsedInput.fraction } : {},
|
|
664
1319
|
maxSlippageBps: parsedInput.maxSlippageBps
|
|
665
1320
|
});
|
|
666
1321
|
}
|
|
@@ -701,6 +1356,62 @@ var HeliosAgentDataBridgeClient = class {
|
|
|
701
1356
|
oid: parsedInput.oid
|
|
702
1357
|
});
|
|
703
1358
|
}
|
|
1359
|
+
async modifyOrder(accessToken, input) {
|
|
1360
|
+
const parsedInput = modifyOrderInputSchema.parse(input);
|
|
1361
|
+
await this.request(accessToken, "positions.modifyOrder", parsedInput, "POST");
|
|
1362
|
+
return modifyOrderResultSchema.parse({ status: "modified", ...parsedInput });
|
|
1363
|
+
}
|
|
1364
|
+
// ---------------------------------------------------------------- paper (AGENT_PAPER_ACCOUNT unless told otherwise)
|
|
1365
|
+
async getPaperPortfolio(accessToken, accountId = AGENT_PAPER_ACCOUNT) {
|
|
1366
|
+
const payload = await this.request(accessToken, "paper.getPortfolio", { accountId, fills: 0 });
|
|
1367
|
+
return paperPortfolioSchema.parse(this.unwrapResult(payload));
|
|
1368
|
+
}
|
|
1369
|
+
/** A paper market order; answers with its fills (none when nothing filled). */
|
|
1370
|
+
async submitPaperIntent(accessToken, input, accountId = AGENT_PAPER_ACCOUNT) {
|
|
1371
|
+
const parsedInput = submitTradeIntentInputSchema.parse(input);
|
|
1372
|
+
const payload = await this.request(accessToken, "paper.submitIntent", { ...parsedInput, accountId }, "POST");
|
|
1373
|
+
return paperFillSchema.array().parse(this.tryUnwrapResult(payload) ?? []);
|
|
1374
|
+
}
|
|
1375
|
+
/** Closes all or part of a paper position; no fills when there was none. */
|
|
1376
|
+
async closePaperPosition(accessToken, input, accountId = AGENT_PAPER_ACCOUNT) {
|
|
1377
|
+
const parsedInput = closePositionInputSchema.parse(input);
|
|
1378
|
+
const partial = parsedInput.fraction !== void 0 && parsedInput.fraction < 1;
|
|
1379
|
+
const payload = await this.request(
|
|
1380
|
+
accessToken,
|
|
1381
|
+
"paper.close",
|
|
1382
|
+
{ symbol: parsedInput.symbol, mode: partial ? "PARTIAL" : "FULL", ...partial ? { fraction: parsedInput.fraction } : {}, accountId },
|
|
1383
|
+
"POST"
|
|
1384
|
+
);
|
|
1385
|
+
return paperFillSchema.array().parse(this.tryUnwrapResult(payload) ?? []);
|
|
1386
|
+
}
|
|
1387
|
+
async setPaperTpSl(accessToken, input, accountId = AGENT_PAPER_ACCOUNT) {
|
|
1388
|
+
const parsedInput = setTpSlInputSchema.parse(input);
|
|
1389
|
+
await this.request(accessToken, "paper.setTpSl", { ...parsedInput, accountId }, "POST");
|
|
1390
|
+
return setTpSlResultSchema.parse({
|
|
1391
|
+
status: "submitted",
|
|
1392
|
+
provider: parsedInput.provider,
|
|
1393
|
+
symbol: parsedInput.symbol,
|
|
1394
|
+
applyTo: parsedInput.applyTo,
|
|
1395
|
+
quantity: parsedInput.quantity,
|
|
1396
|
+
parentClientOrderId: parsedInput.parentClientOrderId,
|
|
1397
|
+
hasTakeProfit: Boolean(parsedInput.takeProfit),
|
|
1398
|
+
hasStopLoss: Boolean(parsedInput.stopLoss)
|
|
1399
|
+
});
|
|
1400
|
+
}
|
|
1401
|
+
async deletePaperTpSl(accessToken, input, accountId = AGENT_PAPER_ACCOUNT) {
|
|
1402
|
+
const parsedInput = deleteTpSlInputSchema.parse(input);
|
|
1403
|
+
await this.request(accessToken, "paper.deleteTpSl", { ...parsedInput, accountId }, "POST");
|
|
1404
|
+
return deleteTpSlResultSchema.parse({ status: "deleted", provider: parsedInput.provider, symbol: parsedInput.symbol, orderId: parsedInput.orderId });
|
|
1405
|
+
}
|
|
1406
|
+
async getPaperLedgerFills(accessToken, input = {}, accountId = AGENT_PAPER_ACCOUNT) {
|
|
1407
|
+
const parsedInput = ledgerFillsInputSchema.parse(input);
|
|
1408
|
+
const payload = await this.request(accessToken, "paper.getLedgerFills", { ...parsedInput, accountId });
|
|
1409
|
+
return ledgerFillsPageSchema.parse(this.unwrapResult(payload));
|
|
1410
|
+
}
|
|
1411
|
+
/** Starts the paper account over with `startingBalance` USD (data-bridge's default when omitted). */
|
|
1412
|
+
async resetPaperAccount(accessToken, startingBalance, accountId = AGENT_PAPER_ACCOUNT) {
|
|
1413
|
+
await this.request(accessToken, "paper.reset", { accountId, ...startingBalance ? { startingBalance } : {} }, "POST");
|
|
1414
|
+
}
|
|
704
1415
|
async request(accessToken, procedure, input, method = "GET") {
|
|
705
1416
|
let response;
|
|
706
1417
|
try {
|
|
@@ -740,12 +1451,12 @@ var HeliosAgentDataBridgeClient = class {
|
|
|
740
1451
|
return url.toString();
|
|
741
1452
|
}
|
|
742
1453
|
async parseJson(response) {
|
|
743
|
-
const
|
|
744
|
-
if (!
|
|
1454
|
+
const text2 = await response.text();
|
|
1455
|
+
if (!text2) {
|
|
745
1456
|
return null;
|
|
746
1457
|
}
|
|
747
1458
|
try {
|
|
748
|
-
return JSON.parse(
|
|
1459
|
+
return JSON.parse(text2);
|
|
749
1460
|
} catch (error) {
|
|
750
1461
|
throw new HeliosAgentApiError({
|
|
751
1462
|
status: response.status,
|
|
@@ -783,373 +1494,71 @@ var HeliosAgentDataBridgeClient = class {
|
|
|
783
1494
|
}
|
|
784
1495
|
const data = result.data;
|
|
785
1496
|
if (data && typeof data === "object" && "json" in data) {
|
|
786
|
-
return data.json;
|
|
787
|
-
}
|
|
788
|
-
return data;
|
|
789
|
-
}
|
|
790
|
-
extractErrorCode(payload) {
|
|
791
|
-
if (payload && typeof payload === "object") {
|
|
792
|
-
const errorShape = this.unwrapTrpcError(payload);
|
|
793
|
-
if (errorShape?.data?.code) {
|
|
794
|
-
return errorShape.data.code;
|
|
795
|
-
}
|
|
796
|
-
}
|
|
797
|
-
return "request_failed";
|
|
798
|
-
}
|
|
799
|
-
extractErrorMessage(payload, status) {
|
|
800
|
-
if (payload && typeof payload === "object") {
|
|
801
|
-
const errorShape = this.unwrapTrpcError(payload);
|
|
802
|
-
if (errorShape?.message) {
|
|
803
|
-
return errorShape.message;
|
|
804
|
-
}
|
|
805
|
-
}
|
|
806
|
-
return `Request failed with status ${status}`;
|
|
807
|
-
}
|
|
808
|
-
/**
|
|
809
|
-
* Unwrap a tRPC error envelope, handling the superjson `{ json: ... }` wrapper
|
|
810
|
-
* that the data-bridge applies via its transformer configuration.
|
|
811
|
-
*/
|
|
812
|
-
unwrapTrpcError(payload) {
|
|
813
|
-
const envelope = payload.error;
|
|
814
|
-
if (!envelope || typeof envelope !== "object") {
|
|
815
|
-
return void 0;
|
|
816
|
-
}
|
|
817
|
-
if ("json" in envelope) {
|
|
818
|
-
const inner = envelope.json;
|
|
819
|
-
if (inner && typeof inner === "object") {
|
|
820
|
-
return inner;
|
|
821
|
-
}
|
|
822
|
-
}
|
|
823
|
-
return envelope;
|
|
824
|
-
}
|
|
825
|
-
};
|
|
826
|
-
function readProtectiveOrders(value) {
|
|
827
|
-
if (!Array.isArray(value)) return [];
|
|
828
|
-
return value.flatMap((entry) => {
|
|
829
|
-
const parsed = protectiveOrderSchema.safeParse(entry);
|
|
830
|
-
return parsed.success ? [parsed.data] : [];
|
|
831
|
-
});
|
|
832
|
-
}
|
|
833
|
-
var DEFAULT_AGENT_REFRESH_WINDOW_MS = 60 * 1e3;
|
|
834
|
-
var agentConnectionStatusSchema = import_zod3.z.enum([
|
|
835
|
-
"disconnected",
|
|
836
|
-
"pending",
|
|
837
|
-
"connected",
|
|
838
|
-
"rejected",
|
|
839
|
-
"expired",
|
|
840
|
-
"failed"
|
|
841
|
-
]);
|
|
842
|
-
var storedAgentSessionSchema = import_zod3.z.object({
|
|
843
|
-
accessToken: import_zod3.z.string(),
|
|
844
|
-
refreshToken: import_zod3.z.string(),
|
|
845
|
-
tokenType: agentTokenResponseSchema.shape.tokenType,
|
|
846
|
-
ownerUserId: import_zod3.z.string(),
|
|
847
|
-
agentGrantId: import_zod3.z.string(),
|
|
848
|
-
scope: import_zod3.z.array(agentScopeSchema).min(1),
|
|
849
|
-
accessTokenExpiresAt: import_zod3.z.string().datetime(),
|
|
850
|
-
createdAt: import_zod3.z.string().datetime(),
|
|
851
|
-
lastRefreshedAt: import_zod3.z.string().datetime()
|
|
852
|
-
});
|
|
853
|
-
var agentSessionViewSchema = import_zod3.z.object({
|
|
854
|
-
connected: import_zod3.z.boolean(),
|
|
855
|
-
status: agentConnectionStatusSchema,
|
|
856
|
-
ownerUserId: import_zod3.z.string().optional(),
|
|
857
|
-
agentGrantId: import_zod3.z.string().optional(),
|
|
858
|
-
scope: import_zod3.z.array(agentScopeSchema).optional(),
|
|
859
|
-
accessTokenExpiresAt: import_zod3.z.string().datetime().optional(),
|
|
860
|
-
lastRefreshedAt: import_zod3.z.string().datetime().optional()
|
|
861
|
-
});
|
|
862
|
-
function createStoredAgentSession(tokenResponse, now = /* @__PURE__ */ new Date()) {
|
|
863
|
-
const createdAt = now.toISOString();
|
|
864
|
-
return {
|
|
865
|
-
accessToken: tokenResponse.accessToken,
|
|
866
|
-
refreshToken: tokenResponse.refreshToken,
|
|
867
|
-
tokenType: tokenResponse.tokenType,
|
|
868
|
-
ownerUserId: tokenResponse.ownerUserId,
|
|
869
|
-
agentGrantId: tokenResponse.agentGrantId,
|
|
870
|
-
scope: tokenResponse.scope,
|
|
871
|
-
accessTokenExpiresAt: new Date(now.getTime() + tokenResponse.expiresInSeconds * 1e3).toISOString(),
|
|
872
|
-
createdAt,
|
|
873
|
-
lastRefreshedAt: createdAt
|
|
874
|
-
};
|
|
875
|
-
}
|
|
876
|
-
function sanitizeAgentSession(session, status = session ? "connected" : "disconnected") {
|
|
877
|
-
if (!session) {
|
|
878
|
-
return {
|
|
879
|
-
connected: false,
|
|
880
|
-
status: status === "connected" ? "disconnected" : status
|
|
881
|
-
};
|
|
882
|
-
}
|
|
883
|
-
return {
|
|
884
|
-
connected: true,
|
|
885
|
-
status,
|
|
886
|
-
ownerUserId: session.ownerUserId,
|
|
887
|
-
agentGrantId: session.agentGrantId,
|
|
888
|
-
scope: session.scope,
|
|
889
|
-
accessTokenExpiresAt: session.accessTokenExpiresAt,
|
|
890
|
-
lastRefreshedAt: session.lastRefreshedAt
|
|
891
|
-
};
|
|
892
|
-
}
|
|
893
|
-
function shouldRefreshAgentSession(session, now = /* @__PURE__ */ new Date(), refreshWindowMs = DEFAULT_AGENT_REFRESH_WINDOW_MS) {
|
|
894
|
-
return new Date(session.accessTokenExpiresAt).getTime() - now.getTime() <= refreshWindowMs;
|
|
895
|
-
}
|
|
896
|
-
|
|
897
|
-
// src/auth-service.ts
|
|
898
|
-
var HOSTED_RECONNECT_MESSAGE = "Helios no longer accepts this connection (it expired or was revoked). Reconnect Helios in your AI app to continue.";
|
|
899
|
-
var HeliosAgentMcpAuthService = class {
|
|
900
|
-
constructor(options) {
|
|
901
|
-
__publicField(this, "client");
|
|
902
|
-
__publicField(this, "sessionStore");
|
|
903
|
-
__publicField(this, "refreshWindowMs");
|
|
904
|
-
__publicField(this, "now");
|
|
905
|
-
__publicField(this, "deviceLogin");
|
|
906
|
-
this.client = options.client;
|
|
907
|
-
this.sessionStore = options.sessionStore;
|
|
908
|
-
this.refreshWindowMs = options.refreshWindowMs ?? 60 * 1e3;
|
|
909
|
-
this.now = options.now ?? (() => /* @__PURE__ */ new Date());
|
|
910
|
-
this.deviceLogin = options.deviceLogin ?? true;
|
|
911
|
-
}
|
|
912
|
-
async startLogin(input) {
|
|
913
|
-
const response = await this.startDeviceLogin(input);
|
|
914
|
-
const result = this.buildStartLoginResult(response, input.scope);
|
|
915
|
-
await this.sessionStore.setPendingLogin(result);
|
|
916
|
-
return result;
|
|
917
|
-
}
|
|
918
|
-
async resetSessionAndStartLogin(input) {
|
|
919
|
-
await this.sessionStore.clearSession();
|
|
920
|
-
await this.sessionStore.clearPendingLogin();
|
|
921
|
-
const response = await this.startDeviceLogin(input);
|
|
922
|
-
const result = this.buildStartLoginResult(response, input.scope);
|
|
923
|
-
await this.sessionStore.setPendingLogin(result);
|
|
924
|
-
return result;
|
|
925
|
-
}
|
|
926
|
-
async startDeviceLogin(input) {
|
|
927
|
-
if (!this.deviceLogin) {
|
|
928
|
-
throw new Error(HOSTED_RECONNECT_MESSAGE);
|
|
929
|
-
}
|
|
930
|
-
return this.client.startLogin(input);
|
|
931
|
-
}
|
|
932
|
-
buildStartLoginResult(response, requestedScope) {
|
|
933
|
-
return {
|
|
934
|
-
status: "pending",
|
|
935
|
-
...response,
|
|
936
|
-
requestedScope,
|
|
937
|
-
startedAt: this.now().toISOString()
|
|
938
|
-
};
|
|
939
|
-
}
|
|
940
|
-
async startRelogin(options = {}) {
|
|
941
|
-
const requestedScope = options.scope ?? [AGENT_SCOPES.ACCOUNT_READ, AGENT_SCOPES.MARKET_READ];
|
|
942
|
-
const started = await this.resetSessionAndStartLogin({
|
|
943
|
-
clientName: options.clientName ?? "Helios MCP",
|
|
944
|
-
scope: requestedScope,
|
|
945
|
-
constraints: options.constraints
|
|
946
|
-
});
|
|
947
|
-
return {
|
|
948
|
-
...started,
|
|
949
|
-
connected: false,
|
|
950
|
-
message: formatHeliosLoginPrompt(started)
|
|
951
|
-
};
|
|
952
|
-
}
|
|
953
|
-
async clearSessionState() {
|
|
954
|
-
await this.sessionStore.clearSession();
|
|
955
|
-
await this.sessionStore.clearPendingLogin();
|
|
956
|
-
}
|
|
957
|
-
async invalidateIfUnauthorized(error, options = {}) {
|
|
958
|
-
if (!isHeliosAgentApiError(error) || error.status !== 401) {
|
|
959
|
-
return null;
|
|
960
|
-
}
|
|
961
|
-
return this.startRelogin(options);
|
|
962
|
-
}
|
|
963
|
-
async refreshSessionAfterUnauthorized(error) {
|
|
964
|
-
if (!isHeliosAgentApiError(error) || error.status !== 401) {
|
|
965
|
-
return null;
|
|
966
|
-
}
|
|
967
|
-
const session = await this.sessionStore.getSession();
|
|
968
|
-
if (!session) {
|
|
969
|
-
return null;
|
|
970
|
-
}
|
|
971
|
-
return this.refreshStoredSession(session);
|
|
972
|
-
}
|
|
973
|
-
async ensureLogin(options = {}) {
|
|
974
|
-
const requestedScope = options.scope ?? [AGENT_SCOPES.ACCOUNT_READ, AGENT_SCOPES.MARKET_READ];
|
|
975
|
-
const requestedConstraints = options.constraints ?? {};
|
|
976
|
-
const session = await this.getValidSession();
|
|
977
|
-
if (session) {
|
|
978
|
-
return {
|
|
979
|
-
...sanitizeAgentSession(session),
|
|
980
|
-
connected: true,
|
|
981
|
-
status: "connected"
|
|
982
|
-
};
|
|
983
|
-
}
|
|
984
|
-
const pendingLogin = await this.getPendingLogin({
|
|
985
|
-
requestedScope,
|
|
986
|
-
requestedConstraints
|
|
987
|
-
});
|
|
988
|
-
if (pendingLogin) {
|
|
989
|
-
const polled = await this.pollLogin(pendingLogin.deviceCode).catch(() => null);
|
|
990
|
-
if (polled?.status === "connected") return polled;
|
|
991
|
-
if (polled?.status !== "rejected" && polled?.status !== "expired") {
|
|
992
|
-
return {
|
|
993
|
-
...pendingLogin,
|
|
994
|
-
connected: false,
|
|
995
|
-
message: formatHeliosLoginPrompt(pendingLogin)
|
|
996
|
-
};
|
|
997
|
-
}
|
|
998
|
-
}
|
|
999
|
-
const started = await this.startLogin({
|
|
1000
|
-
clientName: options.clientName ?? "Helios MCP",
|
|
1001
|
-
scope: requestedScope,
|
|
1002
|
-
constraints: options.constraints
|
|
1003
|
-
});
|
|
1004
|
-
return {
|
|
1005
|
-
...started,
|
|
1006
|
-
connected: false,
|
|
1007
|
-
message: formatHeliosLoginPrompt(started)
|
|
1008
|
-
};
|
|
1009
|
-
}
|
|
1010
|
-
/** Polls the given login, or the stored pending one when no device code is given. */
|
|
1011
|
-
async pollLogin(deviceCode) {
|
|
1012
|
-
if (!deviceCode) {
|
|
1013
|
-
const pending = await this.sessionStore.getPendingLogin();
|
|
1014
|
-
if (!pending) {
|
|
1015
|
-
const session = await this.getValidSession();
|
|
1016
|
-
if (session) return { ...sanitizeAgentSession(session), connected: true, status: "connected" };
|
|
1017
|
-
return {
|
|
1018
|
-
status: "failed",
|
|
1019
|
-
connected: false,
|
|
1020
|
-
message: "No Helios login is in progress. Call helios_auth_ensure_login to start one."
|
|
1021
|
-
};
|
|
1022
|
-
}
|
|
1023
|
-
deviceCode = pending.deviceCode;
|
|
1024
|
-
} else {
|
|
1025
|
-
const pending = await this.sessionStore.getPendingLogin();
|
|
1026
|
-
if (pending && pending.userCode.toUpperCase() === deviceCode.trim().toUpperCase()) deviceCode = pending.deviceCode;
|
|
1027
|
-
}
|
|
1028
|
-
try {
|
|
1029
|
-
const response = await this.client.exchangeDeviceCode(deviceCode);
|
|
1030
|
-
const session = createStoredAgentSession(response, this.now());
|
|
1031
|
-
await this.sessionStore.setSession(session);
|
|
1032
|
-
await this.sessionStore.clearPendingLogin();
|
|
1033
|
-
return {
|
|
1034
|
-
...sanitizeAgentSession(session),
|
|
1035
|
-
connected: true,
|
|
1036
|
-
status: "connected"
|
|
1037
|
-
};
|
|
1038
|
-
} catch (error) {
|
|
1039
|
-
if (isHeliosAgentApiError(error)) {
|
|
1040
|
-
if (error.code === "authorization_pending") {
|
|
1041
|
-
return {
|
|
1042
|
-
status: "pending",
|
|
1043
|
-
connected: false,
|
|
1044
|
-
message: error.message
|
|
1045
|
-
};
|
|
1046
|
-
}
|
|
1047
|
-
if (error.code === "access_denied") {
|
|
1048
|
-
await this.clearPendingLoginIfMatches(deviceCode);
|
|
1049
|
-
return {
|
|
1050
|
-
status: "rejected",
|
|
1051
|
-
connected: false,
|
|
1052
|
-
message: error.message
|
|
1053
|
-
};
|
|
1054
|
-
}
|
|
1055
|
-
if (error.code === "expired_token") {
|
|
1056
|
-
await this.clearPendingLoginIfMatches(deviceCode);
|
|
1057
|
-
return {
|
|
1058
|
-
status: "expired",
|
|
1059
|
-
connected: false,
|
|
1060
|
-
message: error.message
|
|
1061
|
-
};
|
|
1062
|
-
}
|
|
1063
|
-
return {
|
|
1064
|
-
status: "failed",
|
|
1065
|
-
connected: false,
|
|
1066
|
-
message: error.message
|
|
1067
|
-
};
|
|
1068
|
-
}
|
|
1069
|
-
return {
|
|
1070
|
-
status: "failed",
|
|
1071
|
-
connected: false,
|
|
1072
|
-
message: error instanceof Error ? error.message : "Unexpected Helios login error"
|
|
1073
|
-
};
|
|
1074
|
-
}
|
|
1075
|
-
}
|
|
1076
|
-
async whoami() {
|
|
1077
|
-
const session = await this.getValidSession();
|
|
1078
|
-
return sanitizeAgentSession(session);
|
|
1079
|
-
}
|
|
1080
|
-
async getAccessToken() {
|
|
1081
|
-
const session = await this.getValidSession();
|
|
1082
|
-
return session?.accessToken ?? null;
|
|
1083
|
-
}
|
|
1084
|
-
async requireSession() {
|
|
1085
|
-
const session = await this.getValidSession();
|
|
1086
|
-
if (!session) {
|
|
1087
|
-
throw new Error("No Helios delegated agent session is connected");
|
|
1088
|
-
}
|
|
1089
|
-
return session;
|
|
1090
|
-
}
|
|
1091
|
-
async getValidSession() {
|
|
1092
|
-
const session = await this.sessionStore.getSession();
|
|
1093
|
-
if (!session) {
|
|
1094
|
-
return null;
|
|
1095
|
-
}
|
|
1096
|
-
if (!shouldRefreshAgentSession(session, this.now(), this.refreshWindowMs)) {
|
|
1097
|
-
return session;
|
|
1497
|
+
return data.json;
|
|
1098
1498
|
}
|
|
1099
|
-
return
|
|
1499
|
+
return data;
|
|
1100
1500
|
}
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
const
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
return nextSession;
|
|
1107
|
-
} catch (error) {
|
|
1108
|
-
if (isHeliosAgentApiError(error) && error.code === "invalid_grant") {
|
|
1109
|
-
await this.sessionStore.clearSession();
|
|
1110
|
-
await this.sessionStore.clearPendingLogin();
|
|
1111
|
-
return null;
|
|
1501
|
+
extractErrorCode(payload) {
|
|
1502
|
+
if (payload && typeof payload === "object") {
|
|
1503
|
+
const errorShape = this.unwrapTrpcError(payload);
|
|
1504
|
+
if (errorShape?.data?.code) {
|
|
1505
|
+
return errorShape.data.code;
|
|
1112
1506
|
}
|
|
1113
|
-
throw error;
|
|
1114
1507
|
}
|
|
1508
|
+
return "request_failed";
|
|
1115
1509
|
}
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
if (this.now().getTime() >= expiresAt) {
|
|
1123
|
-
await this.sessionStore.clearPendingLogin();
|
|
1124
|
-
return null;
|
|
1125
|
-
}
|
|
1126
|
-
if (!isPendingLoginCompatible(pendingLogin, options.requestedScope, options.requestedConstraints)) {
|
|
1127
|
-
return null;
|
|
1510
|
+
extractErrorMessage(payload, status) {
|
|
1511
|
+
if (payload && typeof payload === "object") {
|
|
1512
|
+
const errorShape = this.unwrapTrpcError(payload);
|
|
1513
|
+
if (errorShape?.message) {
|
|
1514
|
+
return errorShape.message;
|
|
1515
|
+
}
|
|
1128
1516
|
}
|
|
1129
|
-
return
|
|
1517
|
+
return `Request failed with status ${status}`;
|
|
1130
1518
|
}
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1519
|
+
/**
|
|
1520
|
+
* Unwrap a tRPC error envelope, handling the superjson `{ json: ... }` wrapper
|
|
1521
|
+
* that the data-bridge applies via its transformer configuration.
|
|
1522
|
+
*/
|
|
1523
|
+
unwrapTrpcError(payload) {
|
|
1524
|
+
const envelope = payload.error;
|
|
1525
|
+
if (!envelope || typeof envelope !== "object") {
|
|
1526
|
+
return void 0;
|
|
1135
1527
|
}
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
return options.authService.invalidateIfUnauthorized(options.error, options.loginOptions);
|
|
1142
|
-
}
|
|
1143
|
-
try {
|
|
1144
|
-
return await options.retry(refreshedSession.accessToken);
|
|
1145
|
-
} catch (retryError) {
|
|
1146
|
-
const relogin = await options.authService.invalidateIfUnauthorized(retryError, options.loginOptions);
|
|
1147
|
-
if (relogin) {
|
|
1148
|
-
return relogin;
|
|
1528
|
+
if ("json" in envelope) {
|
|
1529
|
+
const inner = envelope.json;
|
|
1530
|
+
if (inner && typeof inner === "object") {
|
|
1531
|
+
return inner;
|
|
1532
|
+
}
|
|
1149
1533
|
}
|
|
1150
|
-
|
|
1534
|
+
return envelope;
|
|
1151
1535
|
}
|
|
1536
|
+
};
|
|
1537
|
+
function readProtectiveOrders(value) {
|
|
1538
|
+
if (!Array.isArray(value)) return [];
|
|
1539
|
+
return value.flatMap((entry) => {
|
|
1540
|
+
const parsed = protectiveOrderSchema.safeParse(entry);
|
|
1541
|
+
return parsed.success ? [parsed.data] : [];
|
|
1542
|
+
});
|
|
1152
1543
|
}
|
|
1544
|
+
|
|
1545
|
+
// src/auth-service.ts
|
|
1546
|
+
var HOSTED_RECONNECT_MESSAGE = "Helios no longer accepts this connection (it expired or was revoked). Reconnect Helios in your AI app to continue.";
|
|
1547
|
+
var HeliosAgentMcpAuthService = class extends AgentAuthService {
|
|
1548
|
+
constructor(options) {
|
|
1549
|
+
super({
|
|
1550
|
+
...options,
|
|
1551
|
+
defaultClientName: "Helios MCP",
|
|
1552
|
+
messages: {
|
|
1553
|
+
loginPrompt: formatHeliosLoginPrompt,
|
|
1554
|
+
noLoginInProgress: "No Helios login is in progress. Call helios_auth_ensure_login to start one.",
|
|
1555
|
+
wrongCode: "That code is not the Helios login in progress. Call helios_auth_poll_login without a code to check it.",
|
|
1556
|
+
staleLogin: "Helios no longer knows this login request. Call helios_auth_ensure_login to start a fresh one.",
|
|
1557
|
+
reconnect: HOSTED_RECONNECT_MESSAGE
|
|
1558
|
+
}
|
|
1559
|
+
});
|
|
1560
|
+
}
|
|
1561
|
+
};
|
|
1153
1562
|
function formatHeliosLoginPrompt(result) {
|
|
1154
1563
|
return `Log in to Helios by opening this URL in your browser:
|
|
1155
1564
|
|
|
@@ -1161,14 +1570,6 @@ Requested scopes: ${result.requestedScope.join(", ")}
|
|
|
1161
1570
|
|
|
1162
1571
|
After approving access, call helios_auth_ensure_login (or helios_auth_poll_login) again to finish connecting.`;
|
|
1163
1572
|
}
|
|
1164
|
-
function isPendingLoginCompatible(pendingLogin, requestedScope, requestedConstraints) {
|
|
1165
|
-
const pendingScope = new Set(pendingLogin.requestedScope);
|
|
1166
|
-
const scopeCompatible = requestedScope.every((scope) => pendingScope.has(scope));
|
|
1167
|
-
if (!scopeCompatible) {
|
|
1168
|
-
return false;
|
|
1169
|
-
}
|
|
1170
|
-
return Object.keys(requestedConstraints ?? {}).length === 0;
|
|
1171
|
-
}
|
|
1172
1573
|
|
|
1173
1574
|
// src/account-service.ts
|
|
1174
1575
|
var HeliosAgentMcpAccountService = class {
|
|
@@ -1366,6 +1767,41 @@ function toPosixPath(value) {
|
|
|
1366
1767
|
return value.split(import_node_path.default.sep).join("/");
|
|
1367
1768
|
}
|
|
1368
1769
|
|
|
1770
|
+
// src/endpoints.ts
|
|
1771
|
+
var DEFAULT_HELIOS_BASE_URL = "https://helios.pgn.io";
|
|
1772
|
+
var DEFAULT_HELIOS_SESSION_ISSUER = {
|
|
1773
|
+
identityServiceUrl: DEFAULT_HELIOS_BASE_URL,
|
|
1774
|
+
dataBridgeUrl: DEFAULT_HELIOS_BASE_URL
|
|
1775
|
+
};
|
|
1776
|
+
var LOOPBACK_HOSTNAMES = /* @__PURE__ */ new Set(["localhost", "127.0.0.1", "[::1]"]);
|
|
1777
|
+
function assertHeliosBaseUrl(name, value) {
|
|
1778
|
+
let url;
|
|
1779
|
+
try {
|
|
1780
|
+
url = new URL(value);
|
|
1781
|
+
} catch {
|
|
1782
|
+
throw new Error(`${name} must be a URL, got ${JSON.stringify(value)}`);
|
|
1783
|
+
}
|
|
1784
|
+
if (url.protocol === "https:") {
|
|
1785
|
+
return value;
|
|
1786
|
+
}
|
|
1787
|
+
if (url.protocol === "http:" && LOOPBACK_HOSTNAMES.has(url.hostname)) {
|
|
1788
|
+
return value;
|
|
1789
|
+
}
|
|
1790
|
+
throw new Error(`${name} must use https (plain http only for localhost, 127.0.0.1 or [::1]), got ${url.origin}`);
|
|
1791
|
+
}
|
|
1792
|
+
function isSameHeliosIssuer(stored, configured) {
|
|
1793
|
+
const issuer = stored ?? DEFAULT_HELIOS_SESSION_ISSUER;
|
|
1794
|
+
return normalizeBaseUrl(issuer.identityServiceUrl) === normalizeBaseUrl(configured.identityServiceUrl) && normalizeBaseUrl(issuer.dataBridgeUrl) === normalizeBaseUrl(configured.dataBridgeUrl);
|
|
1795
|
+
}
|
|
1796
|
+
function normalizeBaseUrl(value) {
|
|
1797
|
+
try {
|
|
1798
|
+
const url = new URL(value);
|
|
1799
|
+
return `${url.origin}${url.pathname.replace(/\/+$/, "")}`;
|
|
1800
|
+
} catch {
|
|
1801
|
+
return value.replace(/\/+$/, "");
|
|
1802
|
+
}
|
|
1803
|
+
}
|
|
1804
|
+
|
|
1369
1805
|
// src/ledger-service.ts
|
|
1370
1806
|
var HeliosAgentMcpLedgerService = class {
|
|
1371
1807
|
constructor(options) {
|
|
@@ -1711,149 +2147,150 @@ var HeliosAgentMcpProviderService = class {
|
|
|
1711
2147
|
};
|
|
1712
2148
|
|
|
1713
2149
|
// src/session-store.ts
|
|
2150
|
+
var import_node_crypto = require("crypto");
|
|
2151
|
+
var import_node_fs2 = require("fs");
|
|
1714
2152
|
var import_promises = require("fs/promises");
|
|
1715
2153
|
var import_node_os = require("os");
|
|
1716
2154
|
var import_node_path2 = __toESM(require("path"));
|
|
1717
|
-
var
|
|
2155
|
+
var import_zod5 = require("zod");
|
|
1718
2156
|
function getDefaultHeliosAgentMcpSessionPath() {
|
|
1719
2157
|
return import_node_path2.default.join((0, import_node_os.homedir)(), ".helios-agent-mcp", "session.json");
|
|
1720
2158
|
}
|
|
1721
|
-
var
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
verificationUriComplete: import_zod4.z.string().url(),
|
|
1727
|
-
expiresInSeconds: import_zod4.z.number().int().positive(),
|
|
1728
|
-
pollIntervalSeconds: import_zod4.z.number().int().positive(),
|
|
1729
|
-
requestedScope: import_zod4.z.array(agentScopeSchema).min(1),
|
|
1730
|
-
startedAt: import_zod4.z.string().datetime()
|
|
1731
|
-
});
|
|
1732
|
-
var mcpSessionStateSchema = import_zod4.z.object({
|
|
2159
|
+
var pendingAgentLoginSchema2 = pendingAgentLoginSchema.extend({
|
|
2160
|
+
/** The hosts the login was started with (FileAgentSessionStoreOptions.issuer); its device code is only polled there. */
|
|
2161
|
+
issuer: agentSessionIssuerSchema.nullish()
|
|
2162
|
+
});
|
|
2163
|
+
var mcpSessionStateSchema = import_zod5.z.object({
|
|
1733
2164
|
session: storedAgentSessionSchema.nullish(),
|
|
1734
|
-
pendingLogin:
|
|
2165
|
+
pendingLogin: pendingAgentLoginSchema2.nullish(),
|
|
1735
2166
|
// A provider pinned before Binance was retired ('binance') reads as unpinned, so the agent must pick again, rather than
|
|
1736
2167
|
// failing the whole file (which would drop the session) or silently switching it to Hyperliquid.
|
|
1737
2168
|
activeProvider: providerSchema.nullish().catch(null)
|
|
1738
2169
|
});
|
|
2170
|
+
var EMPTY_STATE = { session: null, pendingLogin: null, activeProvider: null };
|
|
1739
2171
|
var FileAgentSessionStore = class {
|
|
1740
|
-
constructor(filePath = getDefaultHeliosAgentMcpSessionPath()) {
|
|
1741
|
-
this
|
|
2172
|
+
constructor(filePath = getDefaultHeliosAgentMcpSessionPath(), options = {}) {
|
|
2173
|
+
__publicField(this, "filePath", filePath);
|
|
2174
|
+
__publicField(this, "issuer");
|
|
2175
|
+
this.issuer = options.issuer ?? null;
|
|
1742
2176
|
}
|
|
1743
2177
|
async getSession() {
|
|
1744
|
-
|
|
2178
|
+
const { session } = await this.readState();
|
|
2179
|
+
if (!session || !this.isFromIssuer(session.issuer)) return null;
|
|
2180
|
+
const { issuer: _issuer, ...rest } = session;
|
|
2181
|
+
return rest;
|
|
1745
2182
|
}
|
|
1746
2183
|
async getPendingLogin() {
|
|
1747
|
-
|
|
2184
|
+
const { pendingLogin } = await this.readState();
|
|
2185
|
+
if (!pendingLogin || !this.isFromIssuer(pendingLogin.issuer)) return null;
|
|
2186
|
+
const { issuer: _issuer, ...rest } = pendingLogin;
|
|
2187
|
+
return rest;
|
|
1748
2188
|
}
|
|
1749
2189
|
async getActiveProvider() {
|
|
1750
2190
|
return (await this.readState()).activeProvider;
|
|
1751
2191
|
}
|
|
1752
2192
|
async setSession(session) {
|
|
1753
|
-
|
|
1754
|
-
await this.writeState({
|
|
2193
|
+
await this.update((state) => ({
|
|
1755
2194
|
...state,
|
|
1756
|
-
session
|
|
1757
|
-
});
|
|
2195
|
+
session: this.issuer ? { ...session, issuer: this.issuer } : session
|
|
2196
|
+
}));
|
|
1758
2197
|
}
|
|
1759
2198
|
async setPendingLogin(pendingLogin) {
|
|
1760
|
-
|
|
1761
|
-
await this.writeState({
|
|
2199
|
+
await this.update((state) => ({
|
|
1762
2200
|
...state,
|
|
1763
|
-
pendingLogin
|
|
1764
|
-
});
|
|
2201
|
+
pendingLogin: this.issuer ? { ...pendingLogin, issuer: this.issuer } : pendingLogin
|
|
2202
|
+
}));
|
|
1765
2203
|
}
|
|
1766
2204
|
async setActiveProvider(activeProvider) {
|
|
1767
|
-
|
|
1768
|
-
await this.writeState({
|
|
2205
|
+
await this.update((state) => ({
|
|
1769
2206
|
...state,
|
|
1770
2207
|
activeProvider
|
|
1771
|
-
});
|
|
2208
|
+
}));
|
|
1772
2209
|
}
|
|
1773
2210
|
async clearSession() {
|
|
1774
|
-
|
|
1775
|
-
await this.writeStateOrDelete({
|
|
1776
|
-
...state,
|
|
1777
|
-
session: null
|
|
1778
|
-
});
|
|
2211
|
+
await this.update((state) => ({ ...state, session: null }), { deleteWhenEmpty: true });
|
|
1779
2212
|
}
|
|
1780
2213
|
async clearPendingLogin() {
|
|
1781
|
-
|
|
1782
|
-
await this.writeStateOrDelete({
|
|
1783
|
-
...state,
|
|
1784
|
-
pendingLogin: null
|
|
1785
|
-
});
|
|
2214
|
+
await this.update((state) => ({ ...state, pendingLogin: null }), { deleteWhenEmpty: true });
|
|
1786
2215
|
}
|
|
1787
2216
|
async clearActiveProvider() {
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
2217
|
+
await this.update((state) => ({ ...state, activeProvider: null }), { deleteWhenEmpty: true });
|
|
2218
|
+
}
|
|
2219
|
+
isFromIssuer(issuer) {
|
|
2220
|
+
return !this.issuer || isSameHeliosIssuer(issuer, this.issuer);
|
|
2221
|
+
}
|
|
2222
|
+
/**
|
|
2223
|
+
* Read-modify-write under the file's lock: tool calls run concurrently and several MCP processes can share the file,
|
|
2224
|
+
* so without it one write could drop another's (a refreshed session, a pending login).
|
|
2225
|
+
*/
|
|
2226
|
+
async update(change, options = {}) {
|
|
2227
|
+
await withSessionFileLock(this.filePath, async () => {
|
|
2228
|
+
const state = change(await this.readState());
|
|
2229
|
+
if (options.deleteWhenEmpty && !state.session && !state.pendingLogin && !state.activeProvider) {
|
|
2230
|
+
await this.deleteFile();
|
|
2231
|
+
return;
|
|
2232
|
+
}
|
|
2233
|
+
await this.writeState(state);
|
|
1792
2234
|
});
|
|
1793
2235
|
}
|
|
1794
2236
|
async readState() {
|
|
2237
|
+
let parsed;
|
|
1795
2238
|
try {
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
if (
|
|
1799
|
-
|
|
1800
|
-
if (envelope.success) {
|
|
1801
|
-
return {
|
|
1802
|
-
session: envelope.data.session ?? null,
|
|
1803
|
-
pendingLogin: envelope.data.pendingLogin ?? null,
|
|
1804
|
-
activeProvider: envelope.data.activeProvider ?? null
|
|
1805
|
-
};
|
|
1806
|
-
}
|
|
2239
|
+
parsed = JSON.parse(await readFileNoFollow(this.filePath));
|
|
2240
|
+
} catch (error) {
|
|
2241
|
+
if (isErrnoException(error, "ENOENT") || isErrnoException(error, "ELOOP") || error instanceof SyntaxError) {
|
|
2242
|
+
return { ...EMPTY_STATE };
|
|
1807
2243
|
}
|
|
1808
|
-
|
|
1809
|
-
|
|
2244
|
+
throw error;
|
|
2245
|
+
}
|
|
2246
|
+
if (parsed && typeof parsed === "object" && ("session" in parsed || "pendingLogin" in parsed || "activeProvider" in parsed)) {
|
|
2247
|
+
const envelope = mcpSessionStateSchema.safeParse(parsed);
|
|
2248
|
+
if (envelope.success) {
|
|
1810
2249
|
return {
|
|
1811
|
-
session:
|
|
1812
|
-
pendingLogin: null,
|
|
1813
|
-
activeProvider: null
|
|
2250
|
+
session: envelope.data.session ?? null,
|
|
2251
|
+
pendingLogin: envelope.data.pendingLogin ?? null,
|
|
2252
|
+
activeProvider: envelope.data.activeProvider ?? null
|
|
1814
2253
|
};
|
|
1815
2254
|
}
|
|
2255
|
+
}
|
|
2256
|
+
const legacySession = storedAgentSessionSchema.safeParse(parsed);
|
|
2257
|
+
if (legacySession.success) {
|
|
1816
2258
|
return {
|
|
1817
|
-
session:
|
|
2259
|
+
session: legacySession.data,
|
|
1818
2260
|
pendingLogin: null,
|
|
1819
2261
|
activeProvider: null
|
|
1820
2262
|
};
|
|
1821
|
-
} catch (error) {
|
|
1822
|
-
if (isMissingFileError(error)) {
|
|
1823
|
-
return {
|
|
1824
|
-
session: null,
|
|
1825
|
-
pendingLogin: null,
|
|
1826
|
-
activeProvider: null
|
|
1827
|
-
};
|
|
1828
|
-
}
|
|
1829
|
-
if (error instanceof Error && error.name === "ZodError") {
|
|
1830
|
-
return {
|
|
1831
|
-
session: null,
|
|
1832
|
-
pendingLogin: null,
|
|
1833
|
-
activeProvider: null
|
|
1834
|
-
};
|
|
1835
|
-
}
|
|
1836
|
-
throw error;
|
|
1837
2263
|
}
|
|
2264
|
+
return { ...EMPTY_STATE };
|
|
1838
2265
|
}
|
|
1839
2266
|
async writeState(state) {
|
|
1840
|
-
|
|
1841
|
-
await (0, import_promises.
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
2267
|
+
const directory = import_node_path2.default.dirname(this.filePath);
|
|
2268
|
+
await (0, import_promises.mkdir)(directory, { recursive: true, mode: 448 });
|
|
2269
|
+
if (import_node_path2.default.resolve(directory) === import_node_path2.default.dirname(getDefaultHeliosAgentMcpSessionPath())) {
|
|
2270
|
+
await (0, import_promises.chmod)(directory, 448).catch(() => void 0);
|
|
2271
|
+
}
|
|
2272
|
+
const tempPath = `${this.filePath}.${process.pid}.${(0, import_node_crypto.randomBytes)(6).toString("hex")}.tmp`;
|
|
2273
|
+
const handle = await (0, import_promises.open)(tempPath, "wx", 384);
|
|
2274
|
+
try {
|
|
2275
|
+
try {
|
|
2276
|
+
await handle.writeFile(`${JSON.stringify(state, null, 2)}
|
|
2277
|
+
`, "utf8");
|
|
2278
|
+
await handle.chmod(384);
|
|
2279
|
+
await handle.sync();
|
|
2280
|
+
} finally {
|
|
2281
|
+
await handle.close();
|
|
2282
|
+
}
|
|
2283
|
+
await (0, import_promises.rename)(tempPath, this.filePath);
|
|
2284
|
+
} catch (error) {
|
|
2285
|
+
await (0, import_promises.rm)(tempPath, { force: true }).catch(() => void 0);
|
|
2286
|
+
throw error;
|
|
1849
2287
|
}
|
|
1850
|
-
await this.writeState(state);
|
|
1851
2288
|
}
|
|
1852
2289
|
async deleteFile() {
|
|
1853
2290
|
try {
|
|
1854
2291
|
await (0, import_promises.rm)(this.filePath, { force: true });
|
|
1855
2292
|
} catch (error) {
|
|
1856
|
-
if (!
|
|
2293
|
+
if (!isErrnoException(error, "ENOENT")) {
|
|
1857
2294
|
throw error;
|
|
1858
2295
|
}
|
|
1859
2296
|
}
|
|
@@ -1861,8 +2298,8 @@ var FileAgentSessionStore = class {
|
|
|
1861
2298
|
};
|
|
1862
2299
|
var MemoryAgentMcpSessionStore = class {
|
|
1863
2300
|
constructor(session = null, activeProvider = "hyperliquid") {
|
|
1864
|
-
this
|
|
1865
|
-
this
|
|
2301
|
+
__publicField(this, "session", session);
|
|
2302
|
+
__publicField(this, "activeProvider", activeProvider);
|
|
1866
2303
|
__publicField(this, "pendingLogin", null);
|
|
1867
2304
|
}
|
|
1868
2305
|
async getSession() {
|
|
@@ -1893,12 +2330,62 @@ var MemoryAgentMcpSessionStore = class {
|
|
|
1893
2330
|
this.pendingLogin = null;
|
|
1894
2331
|
}
|
|
1895
2332
|
};
|
|
1896
|
-
function
|
|
1897
|
-
return error instanceof Error && "code" in error && error.code ===
|
|
2333
|
+
function isErrnoException(error, code) {
|
|
2334
|
+
return error instanceof Error && "code" in error && error.code === code;
|
|
2335
|
+
}
|
|
2336
|
+
async function readFileNoFollow(filePath) {
|
|
2337
|
+
const flags = process.platform === "win32" || import_node_fs2.constants.O_NOFOLLOW === void 0 ? "r" : import_node_fs2.constants.O_RDONLY | import_node_fs2.constants.O_NOFOLLOW;
|
|
2338
|
+
const handle = await (0, import_promises.open)(filePath, flags);
|
|
2339
|
+
try {
|
|
2340
|
+
return await handle.readFile("utf8");
|
|
2341
|
+
} finally {
|
|
2342
|
+
await handle.close();
|
|
2343
|
+
}
|
|
2344
|
+
}
|
|
2345
|
+
var sessionFileQueues = /* @__PURE__ */ new Map();
|
|
2346
|
+
var LOCK_FILE_STALE_MS = 1e4;
|
|
2347
|
+
var LOCK_FILE_WAIT_MS = 2e3;
|
|
2348
|
+
async function withSessionFileLock(filePath, task) {
|
|
2349
|
+
const key = import_node_path2.default.resolve(filePath);
|
|
2350
|
+
const previous = sessionFileQueues.get(key) ?? Promise.resolve();
|
|
2351
|
+
let release;
|
|
2352
|
+
const done = new Promise((resolve) => release = resolve);
|
|
2353
|
+
const queued = previous.then(() => done);
|
|
2354
|
+
sessionFileQueues.set(key, queued);
|
|
2355
|
+
await previous;
|
|
2356
|
+
try {
|
|
2357
|
+
const locked = await acquireLockFile(`${key}.lock`);
|
|
2358
|
+
try {
|
|
2359
|
+
return await task();
|
|
2360
|
+
} finally {
|
|
2361
|
+
if (locked) await (0, import_promises.rm)(`${key}.lock`, { force: true }).catch(() => void 0);
|
|
2362
|
+
}
|
|
2363
|
+
} finally {
|
|
2364
|
+
release();
|
|
2365
|
+
if (sessionFileQueues.get(key) === queued) sessionFileQueues.delete(key);
|
|
2366
|
+
}
|
|
2367
|
+
}
|
|
2368
|
+
async function acquireLockFile(lockPath) {
|
|
2369
|
+
const deadline = Date.now() + LOCK_FILE_WAIT_MS;
|
|
2370
|
+
for (; ; ) {
|
|
2371
|
+
try {
|
|
2372
|
+
await (await (0, import_promises.open)(lockPath, "wx", 384)).close();
|
|
2373
|
+
return true;
|
|
2374
|
+
} catch (error) {
|
|
2375
|
+
if (!isErrnoException(error, "EEXIST")) return false;
|
|
2376
|
+
}
|
|
2377
|
+
const stats = await (0, import_promises.lstat)(lockPath).catch(() => null);
|
|
2378
|
+
if (stats && Date.now() - stats.mtimeMs > LOCK_FILE_STALE_MS) {
|
|
2379
|
+
await (0, import_promises.rm)(lockPath, { force: true }).catch(() => void 0);
|
|
2380
|
+
continue;
|
|
2381
|
+
}
|
|
2382
|
+
if (Date.now() >= deadline) return false;
|
|
2383
|
+
await new Promise((resolve) => setTimeout(resolve, 20 + Math.floor(Math.random() * 30)));
|
|
2384
|
+
}
|
|
1898
2385
|
}
|
|
1899
2386
|
|
|
1900
2387
|
// src/trading-service.ts
|
|
1901
|
-
var
|
|
2388
|
+
var import_node_crypto2 = require("crypto");
|
|
1902
2389
|
var HeliosAgentMcpTradingService = class {
|
|
1903
2390
|
constructor(options) {
|
|
1904
2391
|
__publicField(this, "authService");
|
|
@@ -2136,7 +2623,7 @@ var HeliosAgentMcpTradingService = class {
|
|
|
2136
2623
|
return asPending2(login);
|
|
2137
2624
|
}
|
|
2138
2625
|
const session = await this.authService.requireSession();
|
|
2139
|
-
const idempotencyKey = input.idempotencyKey ?? (0,
|
|
2626
|
+
const idempotencyKey = input.idempotencyKey ?? (0, import_node_crypto2.randomUUID)().replace(/-/g, "");
|
|
2140
2627
|
try {
|
|
2141
2628
|
const provider = await this.providerService.resolveProvider(input.provider);
|
|
2142
2629
|
const submission = await this.dataBridgeClient.submitTradeIntent(session.accessToken, {
|
|
@@ -2185,251 +2672,984 @@ function asPending2(login) {
|
|
|
2185
2672
|
};
|
|
2186
2673
|
}
|
|
2187
2674
|
|
|
2675
|
+
// src/lean/tools.ts
|
|
2676
|
+
var import_node_crypto3 = require("crypto");
|
|
2677
|
+
var import_zod6 = require("zod");
|
|
2678
|
+
|
|
2679
|
+
// src/lean/format.ts
|
|
2680
|
+
var usd = (v, signed = false) => {
|
|
2681
|
+
if (v === null || v === void 0 || !Number.isFinite(v)) return "n/a";
|
|
2682
|
+
const sign = v < 0 ? "-" : signed && v > 0 ? "+" : "";
|
|
2683
|
+
const a = Math.abs(v);
|
|
2684
|
+
const body = a >= 1e9 ? `${trim(a / 1e9, 2)}B` : a >= 1e6 ? `${trim(a / 1e6, 2)}M` : a >= 1e4 ? `${Math.round(a / 1e3)}k` : a >= 1e3 ? `${trim(a / 1e3, 1)}k` : `${Math.round(a)}`;
|
|
2685
|
+
return `${sign}$${body}`;
|
|
2686
|
+
};
|
|
2687
|
+
var trim = (v, places) => String(Number(v.toFixed(places)));
|
|
2688
|
+
var pct = (v, places = 1) => v === null || v === void 0 || !Number.isFinite(v) ? "n/a" : `${v > 0 ? "+" : ""}${v.toFixed(places)}%`;
|
|
2689
|
+
var price = (v) => {
|
|
2690
|
+
if (v === null || v === void 0 || !Number.isFinite(v)) return "n/a";
|
|
2691
|
+
const a = Math.abs(v);
|
|
2692
|
+
if (a >= 1e3) return v.toLocaleString("en-US", { maximumFractionDigits: a >= 1e4 ? 0 : 2 });
|
|
2693
|
+
if (a >= 1) return v.toFixed(a >= 100 ? 2 : a >= 10 ? 2 : 3);
|
|
2694
|
+
return v.toPrecision(4);
|
|
2695
|
+
};
|
|
2696
|
+
var ago = (t, now) => {
|
|
2697
|
+
if (t === null || t === void 0) return "n/a";
|
|
2698
|
+
const s = Math.max(0, Math.round((now - t) / 1e3));
|
|
2699
|
+
if (s < 60) return `${s}s`;
|
|
2700
|
+
if (s < 3600) return `${Math.round(s / 60)}m`;
|
|
2701
|
+
if (s < 172800) return `${Math.round(s / 3600)}h`;
|
|
2702
|
+
return `${Math.round(s / 86400)}d`;
|
|
2703
|
+
};
|
|
2704
|
+
var STYLE = {
|
|
2705
|
+
scalper: "scalper",
|
|
2706
|
+
"intraday trader": "intraday",
|
|
2707
|
+
"swing trader": "swing",
|
|
2708
|
+
"position trader": "position"
|
|
2709
|
+
};
|
|
2710
|
+
var trader = (m) => {
|
|
2711
|
+
const tags = [STYLE[m.style], m.win === null ? null : `${Math.round(m.win)}% win`].filter(Boolean);
|
|
2712
|
+
return `${m.nick} #${m.rank}${tags.length ? ` (${tags.join(", ")})` : ""}`;
|
|
2713
|
+
};
|
|
2714
|
+
var VERB = { open: "opened", add: "added", reduce: "trimmed", close: "closed", flip: "flipped to" };
|
|
2715
|
+
var moveText = (m) => {
|
|
2716
|
+
const pnl = m.pnl === null ? "" : ` ${usd(m.pnl, true)}${m.pnlPct === null ? "" : ` (${pct(m.pnlPct)})`}`;
|
|
2717
|
+
const lev = m.lev && (m.kind === "open" || m.kind === "add" || m.kind === "flip") ? ` ${trim(m.lev, 1)}x` : "";
|
|
2718
|
+
return `${VERB[m.kind]} ${m.side} ${m.coin} ${usd(m.usd)} @ ${price(m.px)}${lev}${pnl}`;
|
|
2719
|
+
};
|
|
2720
|
+
var entryText = (side, edgePct) => {
|
|
2721
|
+
if (!side || edgePct === null) return null;
|
|
2722
|
+
const below = side === "long" ? edgePct >= 0 : edgePct < 0;
|
|
2723
|
+
return `${Math.abs(edgePct).toFixed(1)}% ${below ? "under" : "over"} their entry`;
|
|
2724
|
+
};
|
|
2725
|
+
function renderMarket(m, now = Date.now()) {
|
|
2726
|
+
const head = [`market \xB7 ${m.rows.length} of ${m.total}${m.offset ? ` from #${m.offset + 1}` : ""}`];
|
|
2727
|
+
if (m.market.os !== null) head.push(`${m.market.os} of 23 large coins down >7% in 24h`);
|
|
2728
|
+
if (m.market.Mti !== null) head.push(`majors trend ${pct(m.market.Mti, 2)}`);
|
|
2729
|
+
if (m.asOf) head.push(`as of ${ago(Date.parse(m.asOf), now)} ago`);
|
|
2730
|
+
const lines = [head.join(" \xB7 ")];
|
|
2731
|
+
if (m.rows.length) {
|
|
2732
|
+
lines.push("coin | price | 1h | 24h | vol 24h | funding APR | open interest | setup | top traders");
|
|
2733
|
+
for (const r of m.rows) {
|
|
2734
|
+
const sm = r.sm?.side ? [`${r.sm.side} ${Math.round(r.sm.share * 100)}%`, entryText(r.sm.side, r.sm.edgePct), r.sm.discount === "strict" ? "discount" : null].filter(Boolean).join(", ") : "none";
|
|
2735
|
+
lines.push(
|
|
2736
|
+
[r.coin, price(r.px), pct(r.r1h), pct(r.r24h), usd(r.volUsd), pct(r.fundAprPct, 0), usd(r.oiUsd), r.setup ?? "n/a", sm].join(
|
|
2737
|
+
" | "
|
|
2738
|
+
)
|
|
2739
|
+
);
|
|
2740
|
+
}
|
|
2741
|
+
} else lines.push("No coins match.");
|
|
2742
|
+
if (m.unknown.length) lines.push(`Not listed on Hyperliquid: ${m.unknown.join(", ")}`);
|
|
2743
|
+
if (m.next !== null) lines.push(`More: call again with offset=${m.next}.`);
|
|
2744
|
+
return lines.join("\n");
|
|
2745
|
+
}
|
|
2746
|
+
var pileText = (p, now) => `${p.coin} | ${p.traders} traders ${p.dir === "in" ? "into" : "out of"} ${p.side} | ${usd(p.totalUsd)} | ${ago(p.first, now)} to ${ago(p.last, now)} ago | ${p.names.join(", ")}`;
|
|
2747
|
+
function renderBoard(b, now = Date.now()) {
|
|
2748
|
+
const title = {
|
|
2749
|
+
conviction: "where the top traders agree (weighted by track record)",
|
|
2750
|
+
discount: "coins below the top traders\u2019 entry (longs) or above it (shorts)",
|
|
2751
|
+
flows: "biggest net flows of the last hour",
|
|
2752
|
+
pileins: "pile-ins: 3+ top traders moving the same way within 45 min, last 2h"
|
|
2753
|
+
};
|
|
2754
|
+
const lines = [`smart money \xB7 ${title[b.view]}${b.asOf ? ` \xB7 as of ${ago(b.asOf, now)} ago` : ""}`];
|
|
2755
|
+
if (b.view === "pileins") {
|
|
2756
|
+
if (b.pileIns.length) lines.push("coin | who | size | when | biggest movers", ...b.pileIns.map((p) => pileText(p, now)));
|
|
2757
|
+
else lines.push("No pile-ins in the last 2 hours.");
|
|
2758
|
+
} else if (b.rows.length) {
|
|
2759
|
+
lines.push("coin | side | share | size | traders | their entry | price | vs entry | 1h flow | setup | 24h");
|
|
2760
|
+
for (const r of b.rows)
|
|
2761
|
+
lines.push(
|
|
2762
|
+
[
|
|
2763
|
+
r.coin,
|
|
2764
|
+
r.side ?? "n/a",
|
|
2765
|
+
`${Math.round(r.share * 100)}%`,
|
|
2766
|
+
usd(r.sideUsd),
|
|
2767
|
+
`${r.traders} (${trim(r.heads, 1)} weighted)`,
|
|
2768
|
+
price(r.entry),
|
|
2769
|
+
price(r.mark),
|
|
2770
|
+
entryText(r.side, r.edgePct) ?? "n/a",
|
|
2771
|
+
usd(r.flow1hUsd, true),
|
|
2772
|
+
r.setup ?? "n/a",
|
|
2773
|
+
pct(r.r24h)
|
|
2774
|
+
].join(" | ")
|
|
2775
|
+
);
|
|
2776
|
+
} else lines.push("No coins on this board right now.");
|
|
2777
|
+
if (b.note) lines.push(b.note);
|
|
2778
|
+
return lines.join("\n");
|
|
2779
|
+
}
|
|
2780
|
+
function renderMoves(m, now = Date.now()) {
|
|
2781
|
+
const lines = [];
|
|
2782
|
+
if (m.order === "oldest") lines.push(m.moves.length ? `${m.moves.length} new move${m.moves.length === 1 ? "" : "s"}, oldest first` : "No new moves.");
|
|
2783
|
+
else lines.push(m.moves.length ? `latest ${m.moves.length} moves, newest first` : "No moves match.");
|
|
2784
|
+
for (const mv of m.moves) lines.push(`${ago(mv.t, now)} ago | ${trader(mv)} | ${moveText(mv)}`);
|
|
2785
|
+
if (m.gap) lines.push("Some moves before these were missed: the cursor was older than the moves kept.");
|
|
2786
|
+
if (m.more) lines.push(`More waiting: call again with since=${m.next}.`);
|
|
2787
|
+
else if (m.next) lines.push(`next=${m.next} (pass as since to get only newer moves)`);
|
|
2788
|
+
if (m.note) lines.push(m.note);
|
|
2789
|
+
return lines.join("\n");
|
|
2790
|
+
}
|
|
2791
|
+
function renderCoin(c, you, now = Date.now()) {
|
|
2792
|
+
const lines = [];
|
|
2793
|
+
const sm = c.sm;
|
|
2794
|
+
const mk = c.market;
|
|
2795
|
+
if (sm && sm.side) {
|
|
2796
|
+
const ours = sm.side === "long" ? sm.long : sm.short;
|
|
2797
|
+
const theirs = sm.side === "long" ? sm.short : sm.long;
|
|
2798
|
+
const tags = [sm.discount === "strict" ? "on the discount board" : null, sm.conviction !== null ? "on the conviction board" : null].filter(Boolean);
|
|
2799
|
+
lines.push(
|
|
2800
|
+
`${c.coin}: top traders lean ${sm.side.toUpperCase()} ${Math.round(sm.share * 100)}% (${usd(ours.usd)} vs ${usd(theirs.usd)})${tags.length ? ` \xB7 ${tags.join(", ")}` : ""}`
|
|
2801
|
+
);
|
|
2802
|
+
lines.push(
|
|
2803
|
+
`book | long ${sm.long.traders} traders (${trim(sm.long.heads, 1)} weighted), avg entry ${price(sm.long.entry)} | short ${sm.short.traders} traders (${trim(sm.short.heads, 1)} weighted), avg entry ${price(sm.short.entry)}`
|
|
2804
|
+
);
|
|
2805
|
+
} else lines.push(`${c.coin}: no top-trader positions right now`);
|
|
2806
|
+
if (mk) {
|
|
2807
|
+
const vs = sm?.side ? entryText(sm.side, sm.edgePct) : null;
|
|
2808
|
+
lines.push(`price | ${price(mk.px)}${vs ? ` (${vs})` : ""} \xB7 15m ${pct(mk.r15m)} \xB7 1h ${pct(mk.r1h)} \xB7 24h ${pct(mk.r24h)}`);
|
|
2809
|
+
lines.push(`setup | ${mk.setup ?? "n/a"} \xB7 funding ${pct(mk.fundAprPct, 0)} APR \xB7 open interest ${usd(mk.oiUsd)} \xB7 vol ${usd(mk.volUsd)}`);
|
|
2810
|
+
lines.push(tradingLine(mk, now));
|
|
2811
|
+
} else lines.push("price | no market data for this coin");
|
|
2812
|
+
if (sm && sm.flow1hUsd !== 0) lines.push(`flow | ${usd(Math.abs(sm.flow1hUsd))} net toward ${sm.flow1hUsd > 0 ? "long" : "short"} in the last hour`);
|
|
2813
|
+
if (sm?.pileIn) lines.push(`pile-in | ${pileText(sm.pileIn, now)}`);
|
|
2814
|
+
if (c.moves.length) lines.push(...c.moves.map((m) => `move | ${ago(m.t, now)} ago | ${trader(m)} | ${moveText(m)}`));
|
|
2815
|
+
else lines.push("move | none of $10k+ recently");
|
|
2816
|
+
if (you) {
|
|
2817
|
+
const p = you.position;
|
|
2818
|
+
lines.push(
|
|
2819
|
+
p ? `you | ${p.side.toLowerCase()} ${trim(Math.abs(p.size), 6)} @ ${price(p.entryPrice)}, open PnL ${usd(p.unrealizedPnl, true)}, ${trim(p.leverage, 1)}x \xB7 ${usd(you.availableBalance)} free` : `you | no ${c.coin} position \xB7 ${usd(you.availableBalance)} free`
|
|
2820
|
+
);
|
|
2821
|
+
}
|
|
2822
|
+
if (c.note) lines.push(c.note);
|
|
2823
|
+
return lines.join("\n");
|
|
2824
|
+
}
|
|
2825
|
+
var MIN_ORDER_USD = 10;
|
|
2826
|
+
var bps = (v) => v === null || v === void 0 || !Number.isFinite(v) ? "n/a" : `${trim(v, 2)} bps`;
|
|
2827
|
+
function tradingLine(mk, now = Date.now()) {
|
|
2828
|
+
const parts = [`max ${mk.maxLev ? `${trim(mk.maxLev, 0)}x` : "n/a"}`, `min order $${MIN_ORDER_USD}`];
|
|
2829
|
+
if (mk.spreadBps !== null && mk.spreadBps !== void 0) parts.push(`spread ${bps(mk.spreadBps)}`);
|
|
2830
|
+
if (mk.slip10kBps !== null && mk.slip10kBps !== void 0) parts.push(`$10k market order slips ${bps(mk.slip10kBps)}, $100k ${bps(mk.slip100kBps)}`);
|
|
2831
|
+
if (mk.depth1pctUsd !== null && mk.depth1pctUsd !== void 0) parts.push(`${usd(mk.depth1pctUsd)} within 1%`);
|
|
2832
|
+
if (mk.fundAprPct !== null) {
|
|
2833
|
+
const hourly = mk.fundAprPct / (24 * 365);
|
|
2834
|
+
const mins = Math.max(1, Math.ceil((36e5 - now % 36e5) / 6e4));
|
|
2835
|
+
parts.push(`funding ${hourly > 0 ? "+" : ""}${hourly.toFixed(4)}%/h (${hourly >= 0 ? "longs pay" : "shorts pay"}), next in ${mins}m`);
|
|
2836
|
+
}
|
|
2837
|
+
return `trading | ${parts.join(" \xB7 ")}`;
|
|
2838
|
+
}
|
|
2839
|
+
function renderAccount(p, orders, summary) {
|
|
2840
|
+
const lines = [
|
|
2841
|
+
`account \xB7 value ${usd(p.totalBalance)} \xB7 free ${usd(p.availableBalance)} \xB7 margin used ${usd(p.totalMargin)} \xB7 open PnL ${usd(p.unrealizedPnl, true)}`
|
|
2842
|
+
];
|
|
2843
|
+
if (p.positions.length) {
|
|
2844
|
+
lines.push("positions: coin | side | size @ entry | now | open PnL | leverage | liquidation | TP/SL (id)");
|
|
2845
|
+
for (const pos of p.positions) {
|
|
2846
|
+
const prot = p.protectiveOrders.filter((o) => o.symbol.replace(/USDT$/, "").toUpperCase() === pos.symbol.replace(/USDT$/, "").toUpperCase()).map((o) => `${o.kind === "TAKE_PROFIT" ? "TP" : "SL"} ${o.stopPrice ?? "n/a"} (${o.id})`);
|
|
2847
|
+
lines.push(
|
|
2848
|
+
[
|
|
2849
|
+
pos.symbol,
|
|
2850
|
+
pos.side.toLowerCase(),
|
|
2851
|
+
`${trim(Math.abs(pos.size), 6)} @ ${price(pos.entryPrice)}`,
|
|
2852
|
+
price(pos.markPrice),
|
|
2853
|
+
usd(pos.unrealizedPnl, true),
|
|
2854
|
+
`${trim(pos.leverage, 1)}x`,
|
|
2855
|
+
price(pos.liquidationPrice ?? null),
|
|
2856
|
+
prot.length ? prot.join(", ") : "none"
|
|
2857
|
+
].join(" | ")
|
|
2858
|
+
);
|
|
2859
|
+
}
|
|
2860
|
+
} else lines.push("positions: none");
|
|
2861
|
+
if (orders) {
|
|
2862
|
+
const working = orders.filter((o) => !o.isTrigger);
|
|
2863
|
+
if (working.length) {
|
|
2864
|
+
lines.push("open orders: coin | side | size @ price | type | oid");
|
|
2865
|
+
for (const o of working) lines.push([o.coin, o.side === "B" ? "buy" : "sell", `${o.sz} @ ${o.limitPx}`, `${o.orderType}${o.tif ? ` ${o.tif}` : ""}${o.reduceOnly ? " reduce-only" : ""}`, o.oid].join(" | "));
|
|
2866
|
+
} else lines.push("open orders: none");
|
|
2867
|
+
}
|
|
2868
|
+
if (summary)
|
|
2869
|
+
lines.push(
|
|
2870
|
+
`${summary.window}: realized ${usd(summary.realizedPnl, true)} \xB7 fees ${usd(summary.totalFees)} \xB7 ${summary.winCount} wins / ${summary.lossCount} losses`
|
|
2871
|
+
);
|
|
2872
|
+
return lines.join("\n");
|
|
2873
|
+
}
|
|
2874
|
+
function positionLine(pos) {
|
|
2875
|
+
return [
|
|
2876
|
+
`${pos.side.toLowerCase()} ${trim(Math.abs(pos.size), 6)} ${pos.symbol.replace(/USDT$/, "")} @ ${price(pos.entryPrice)}`,
|
|
2877
|
+
`mark ${price(pos.markPrice)}`,
|
|
2878
|
+
`open PnL ${usd(pos.unrealizedPnl, true)}`,
|
|
2879
|
+
`${trim(pos.leverage, 1)}x`,
|
|
2880
|
+
`liquidation ${price(pos.liquidationPrice ?? null)}`
|
|
2881
|
+
].join(" \xB7 ");
|
|
2882
|
+
}
|
|
2883
|
+
var cents = (v, signed = false) => Math.abs(v) >= 1e3 ? usd(v, signed) : `${v < 0 ? "-" : signed && v > 0 ? "+" : ""}$${Math.abs(v).toFixed(2)}`;
|
|
2884
|
+
function renderTrades(page, paper2, coin2, now = Date.now()) {
|
|
2885
|
+
const f = page.fills;
|
|
2886
|
+
const closes = f.filter((x) => x.closedPnl !== 0);
|
|
2887
|
+
const pnl = f.reduce((sum, x) => sum + x.closedPnl, 0);
|
|
2888
|
+
const fees = f.reduce((sum, x) => sum + x.fee, 0);
|
|
2889
|
+
const wins = closes.filter((x) => x.closedPnl > 0).length;
|
|
2890
|
+
const head = `${paper2 ? "PRACTICE " : ""}trades ${page.window}${coin2 ? ` ${coin2}` : ""} \xB7 ${f.length} fills \xB7 realized ${cents(pnl, true)} \xB7 fees ${cents(fees)} \xB7 net ${cents(pnl - fees, true)} \xB7 ${wins} wins / ${closes.length - wins} losses`;
|
|
2891
|
+
if (!f.length) return `${head}
|
|
2892
|
+
no fills in this window`;
|
|
2893
|
+
const lines = [head, "ago | coin | what | size @ price | realized PnL | fee"];
|
|
2894
|
+
for (const x of f)
|
|
2895
|
+
lines.push([ago(x.time, now), x.coin, x.dir, `${x.sz} @ ${price(x.px)}`, x.closedPnl === 0 ? "-" : cents(x.closedPnl, true), cents(x.fee)].join(" | "));
|
|
2896
|
+
if (page.hasMore) lines.push("more fills are older: widen window or raise limit");
|
|
2897
|
+
return lines.join("\n");
|
|
2898
|
+
}
|
|
2899
|
+
function renderCandles(coin2, interval, c) {
|
|
2900
|
+
const iso = (t) => new Date(t).toISOString().slice(0, 16).replace("T", " ");
|
|
2901
|
+
const lines = [`${coin2} ${interval} candles \xB7 ${c.candles.length}, oldest first${c.complete ? "" : " \xB7 some missing minutes left gaps"}`, "time UTC,open,high,low,close,volume"];
|
|
2902
|
+
for (const k of c.candles) lines.push([iso(k.t), k.o, k.h, k.l, k.c, k.v].join(","));
|
|
2903
|
+
return lines.join("\n");
|
|
2904
|
+
}
|
|
2905
|
+
|
|
2906
|
+
// src/lean/tools.ts
|
|
2907
|
+
var text = (body, data) => ({
|
|
2908
|
+
content: [{ type: "text", text: body }],
|
|
2909
|
+
structuredContent: data
|
|
2910
|
+
});
|
|
2911
|
+
var loginText = (login) => `Helios is not connected yet. Open this link to approve access, then call this tool again:
|
|
2912
|
+
${login.verificationUriComplete}
|
|
2913
|
+
User code: ${login.userCode}`;
|
|
2914
|
+
var pendingResult = (login) => text(loginText(login), {
|
|
2915
|
+
status: "pending",
|
|
2916
|
+
connected: false,
|
|
2917
|
+
actionRequired: true,
|
|
2918
|
+
loginUrl: login.verificationUriComplete,
|
|
2919
|
+
userCode: login.userCode
|
|
2920
|
+
});
|
|
2921
|
+
async function withSession(auth, scope, call) {
|
|
2922
|
+
const loginOptions = { clientName: "Helios MCP", scope };
|
|
2923
|
+
const login = await auth.ensureLogin(loginOptions);
|
|
2924
|
+
if (login.status !== "connected") return { ok: false, pending: login };
|
|
2925
|
+
const session = await auth.requireSession();
|
|
2926
|
+
try {
|
|
2927
|
+
return { ok: true, value: await call(session.accessToken) };
|
|
2928
|
+
} catch (error) {
|
|
2929
|
+
const recovery = await retryAfterUnauthorizedRefresh({ authService: auth, error, loginOptions, retry: call });
|
|
2930
|
+
if (recovery === null) throw error;
|
|
2931
|
+
if (isPending(recovery)) return { ok: false, pending: recovery };
|
|
2932
|
+
return { ok: true, value: recovery };
|
|
2933
|
+
}
|
|
2934
|
+
}
|
|
2935
|
+
function isPending(value) {
|
|
2936
|
+
return Boolean(value && typeof value === "object" && value.connected === false && "verificationUriComplete" in value);
|
|
2937
|
+
}
|
|
2938
|
+
async function sessionScopes(auth) {
|
|
2939
|
+
const session = await auth.requireSession().catch(() => null);
|
|
2940
|
+
return new Set(session?.scope ?? []);
|
|
2941
|
+
}
|
|
2942
|
+
var coin = import_zod6.z.string().trim().min(1).max(36).describe("e.g. BTC, kPEPE, xyz:TSLA");
|
|
2943
|
+
var smBoardInputSchema = import_zod6.z.object({
|
|
2944
|
+
view: import_zod6.z.enum(["conviction", "discount", "flows", "pileins"]).optional().describe("conviction (default): where they agree; discount: price better than their entry; flows: last hour; pileins: 3+ moving together"),
|
|
2945
|
+
side: import_zod6.z.enum(["long", "short", "any"]).optional(),
|
|
2946
|
+
limit: import_zod6.z.number().int().min(1).max(25).optional().describe("Default 8")
|
|
2947
|
+
});
|
|
2948
|
+
var smMovesInputSchema = import_zod6.z.object({
|
|
2949
|
+
since: import_zod6.z.string().regex(/^\d{1,16}-\d{1,16}$/).optional().describe("next from your last call: only newer moves"),
|
|
2950
|
+
coin: coin.optional(),
|
|
2951
|
+
kind: import_zod6.z.enum(["entries", "exits", "all"]).optional().describe("entries = opens, adds; exits = trims, closes"),
|
|
2952
|
+
min_usd: import_zod6.z.number().nonnegative().optional().describe("Default 25000"),
|
|
2953
|
+
limit: import_zod6.z.number().int().min(1).max(50).optional().describe("Default 15")
|
|
2954
|
+
});
|
|
2955
|
+
var smCoinInputSchema = import_zod6.z.object({ coin });
|
|
2956
|
+
var marketInputSchema = import_zod6.z.object({
|
|
2957
|
+
coins: import_zod6.z.string().max(400).optional().describe('Comma list, e.g. "BTC,HYPE"; omit for all'),
|
|
2958
|
+
limit: import_zod6.z.number().int().min(1).max(300).optional().describe("Default 20"),
|
|
2959
|
+
offset: import_zod6.z.number().int().nonnegative().optional(),
|
|
2960
|
+
sort: import_zod6.z.enum(["volume", "rank", "move1h", "move24h", "funding", "lean", "edge"]).optional(),
|
|
2961
|
+
filter: import_zod6.z.enum(["turning", "falling", "stretched_up", "stretched_down", "sm_long", "sm_short", "discount"]).optional().describe("turning = turning up from breakdown (held up in backtests); discount = better than their entry")
|
|
2962
|
+
});
|
|
2963
|
+
var paper = import_zod6.z.boolean().optional().describe("true = your practice account: live prices, simulated fills, no real money (market orders only)");
|
|
2964
|
+
var accountInputSchema = import_zod6.z.object({ paper });
|
|
2965
|
+
var tradeInputSchema = import_zod6.z.object({
|
|
2966
|
+
coin,
|
|
2967
|
+
action: import_zod6.z.enum(["open", "add", "close", "reverse", "reset"]).describe("reset = start the practice account over (paper only; margin_usd = starting balance, default $10k)"),
|
|
2968
|
+
side: import_zod6.z.enum(["long", "short"]).optional().describe("For open, add, reverse"),
|
|
2969
|
+
// Only obviously absurd values are cut here; Hyperliquid's margin check and the person's approval do the rest.
|
|
2970
|
+
margin_usd: import_zod6.z.number().positive().max(1e6).optional().describe("Size = margin x leverage"),
|
|
2971
|
+
leverage: import_zod6.z.number().positive().max(100).optional(),
|
|
2972
|
+
sl_pct: import_zod6.z.number().positive().max(95).optional().describe("Stop, % from entry"),
|
|
2973
|
+
tp_pct: import_zod6.z.number().positive().max(1e3).optional().describe("Take profit, % from entry"),
|
|
2974
|
+
limit_px: import_zod6.z.number().positive().optional().describe("Omit for market"),
|
|
2975
|
+
slippage_bps: import_zod6.z.number().int().min(1).max(1e3).optional().describe("Market only: worst fill vs price, bps. Default 300 (3%)"),
|
|
2976
|
+
fraction: import_zod6.z.number().positive().max(1).optional().describe("close only: share to close, e.g. 0.5 = half. Default 1 = all"),
|
|
2977
|
+
paper,
|
|
2978
|
+
key: import_zod6.z.string().regex(/^[A-Za-z0-9_-]{8,64}$/).optional().describe("Same key on retry = never placed twice. Omitted: repeats of the same call within 60s of the first are placed once")
|
|
2979
|
+
});
|
|
2980
|
+
var TRADE_KEY_WINDOW_SECONDS = 60;
|
|
2981
|
+
function tradeKeyFor(input, account) {
|
|
2982
|
+
const order = [
|
|
2983
|
+
account,
|
|
2984
|
+
input.coin.toUpperCase(),
|
|
2985
|
+
input.action,
|
|
2986
|
+
input.side ?? "",
|
|
2987
|
+
input.margin_usd ?? "",
|
|
2988
|
+
input.leverage ?? "",
|
|
2989
|
+
input.sl_pct ?? "",
|
|
2990
|
+
input.tp_pct ?? "",
|
|
2991
|
+
input.limit_px ?? "",
|
|
2992
|
+
input.slippage_bps ?? "",
|
|
2993
|
+
input.fraction ?? ""
|
|
2994
|
+
];
|
|
2995
|
+
return `mcpw-${(0, import_node_crypto3.createHash)("sha256").update(`trade:${order.join("|")}`).digest("base64url")}`;
|
|
2996
|
+
}
|
|
2997
|
+
var protectInputSchema = import_zod6.z.object({
|
|
2998
|
+
coin,
|
|
2999
|
+
op: import_zod6.z.enum(["set", "remove", "cancel", "move"]).describe("set TP/SL; remove a TP/SL; cancel an order; move an open order to px"),
|
|
3000
|
+
tp_px: import_zod6.z.number().positive().optional(),
|
|
3001
|
+
sl_px: import_zod6.z.number().positive().optional(),
|
|
3002
|
+
order_id: import_zod6.z.string().max(64).optional().describe("From account: TP/SL id or order oid"),
|
|
3003
|
+
px: import_zod6.z.number().positive().optional().describe("move only: the new limit (or trigger) price"),
|
|
3004
|
+
paper
|
|
3005
|
+
});
|
|
3006
|
+
var tradesInputSchema = import_zod6.z.object({
|
|
3007
|
+
window: import_zod6.z.enum(["24h", "7d", "30d"]).optional().describe("Default 7d"),
|
|
3008
|
+
coin: coin.optional(),
|
|
3009
|
+
limit: import_zod6.z.number().int().min(1).max(100).optional().describe("Default 20"),
|
|
3010
|
+
paper
|
|
3011
|
+
});
|
|
3012
|
+
var candlesInputSchema = import_zod6.z.object({
|
|
3013
|
+
coin: import_zod6.z.string().regex(/^[A-Za-z0-9]{1,20}$/).describe("e.g. BTC, kPEPE"),
|
|
3014
|
+
interval: import_zod6.z.enum(["1m", "5m", "15m", "1h", "4h", "1d"]),
|
|
3015
|
+
limit: import_zod6.z.number().int().min(1).max(1e3).optional().describe("Default 100")
|
|
3016
|
+
});
|
|
3017
|
+
var READ = [AGENT_SCOPES.MARKET_READ];
|
|
3018
|
+
var sameCoinName = (a, b) => a.replace(/USDT$/, "").toUpperCase() === b.replace(/USDT$/, "").toUpperCase();
|
|
3019
|
+
var sameCoin = (pos, coin2) => sameCoinName(pos.symbol, coin2);
|
|
3020
|
+
var paperTotals = (p) => p.realizedPnl === void 0 ? "" : `
|
|
3021
|
+
practice totals: realized ${p.realizedPnl >= 0 ? "+" : "-"}$${Math.abs(p.realizedPnl).toFixed(2)} \xB7 fees $${(p.feesPaid ?? 0).toFixed(2)} \xB7 funding $${(p.fundingPaid ?? 0).toFixed(2)} \xB7 liquidations ${p.liquidations ?? 0}`;
|
|
3022
|
+
function wrongSideTpSl(side, mark, tp, sl) {
|
|
3023
|
+
const long = side === "LONG";
|
|
3024
|
+
const at = `${side.toLowerCase()} (price ${price(mark)})`;
|
|
3025
|
+
if (sl !== void 0 && (long ? sl >= mark : sl <= mark)) return `Stop ${price(sl)} must be ${long ? "under" : "over"} the price for a ${at}`;
|
|
3026
|
+
if (tp !== void 0 && (long ? tp <= mark : tp >= mark)) return `Take profit ${price(tp)} must be ${long ? "over" : "under"} the price for a ${at}`;
|
|
3027
|
+
return null;
|
|
3028
|
+
}
|
|
3029
|
+
function createLeanToolDefinitions(services) {
|
|
3030
|
+
const { auth, dataBridge } = services;
|
|
3031
|
+
const smBoard = {
|
|
3032
|
+
name: "sm_board",
|
|
3033
|
+
title: "Smart Money Board",
|
|
3034
|
+
description: "Where the top Hyperliquid traders (ranked on a year of results) are positioned: side, size, entry vs price.",
|
|
3035
|
+
inputSchema: smBoardInputSchema,
|
|
3036
|
+
async execute(input) {
|
|
3037
|
+
const r = await withSession(auth, READ, (token) => dataBridge.getSmartMoneyBoard(token, input));
|
|
3038
|
+
return r.ok ? text(renderBoard(r.value), r.value) : pendingResult(r.pending);
|
|
3039
|
+
}
|
|
3040
|
+
};
|
|
3041
|
+
const smMoves = {
|
|
3042
|
+
name: "sm_moves",
|
|
3043
|
+
title: "Smart Money Moves",
|
|
3044
|
+
description: "Top traders\u2019 latest trades. Poll with since=next for new ones only.",
|
|
3045
|
+
inputSchema: smMovesInputSchema,
|
|
3046
|
+
async execute(input) {
|
|
3047
|
+
const r = await withSession(
|
|
3048
|
+
auth,
|
|
3049
|
+
READ,
|
|
3050
|
+
(token) => dataBridge.getSmartMoneyMoves(token, { since: input.since, coin: input.coin, kind: input.kind, minUsd: input.min_usd, limit: input.limit })
|
|
3051
|
+
);
|
|
3052
|
+
return r.ok ? text(renderMoves(r.value), r.value) : pendingResult(r.pending);
|
|
3053
|
+
}
|
|
3054
|
+
};
|
|
3055
|
+
const smCoin = {
|
|
3056
|
+
name: "sm_coin",
|
|
3057
|
+
title: "Smart Money Coin Brief",
|
|
3058
|
+
description: "One coin in one call: top traders\u2019 book, entries and trades, price, setup, funding, OI, your position.",
|
|
3059
|
+
inputSchema: smCoinInputSchema,
|
|
3060
|
+
async execute(input) {
|
|
3061
|
+
const r = await withSession(auth, READ, (token) => dataBridge.getSmartMoneyCoin(token, input.coin));
|
|
3062
|
+
if (!r.ok) return pendingResult(r.pending);
|
|
3063
|
+
let you = null;
|
|
3064
|
+
if ((await sessionScopes(auth)).has(AGENT_SCOPES.ACCOUNT_READ)) {
|
|
3065
|
+
const p = await withSession(auth, [AGENT_SCOPES.ACCOUNT_READ], (token) => dataBridge.getPortfolio(token, { provider: "hyperliquid" })).catch(
|
|
3066
|
+
() => null
|
|
3067
|
+
);
|
|
3068
|
+
if (p?.ok) {
|
|
3069
|
+
you = {
|
|
3070
|
+
position: p.value.positions.find((pos) => sameCoin(pos, r.value.coin)) ?? null,
|
|
3071
|
+
availableBalance: p.value.availableBalance
|
|
3072
|
+
};
|
|
3073
|
+
}
|
|
3074
|
+
}
|
|
3075
|
+
return text(renderCoin(r.value, you), { ...r.value, you });
|
|
3076
|
+
}
|
|
3077
|
+
};
|
|
3078
|
+
const market = {
|
|
3079
|
+
name: "market",
|
|
3080
|
+
title: "Market Scan",
|
|
3081
|
+
description: "All coins: price, moves, volume, funding, OI, setup label, top traders\u2019 lean. Filter, sort, page.",
|
|
3082
|
+
inputSchema: marketInputSchema,
|
|
3083
|
+
async execute(input) {
|
|
3084
|
+
const coins = input.coins?.split(",").map((c) => c.trim()).filter(Boolean).slice(0, 50);
|
|
3085
|
+
const r = await withSession(
|
|
3086
|
+
auth,
|
|
3087
|
+
READ,
|
|
3088
|
+
(token) => dataBridge.getAgentMarket(token, {
|
|
3089
|
+
...coins?.length ? { coins } : {},
|
|
3090
|
+
limit: input.limit,
|
|
3091
|
+
offset: input.offset,
|
|
3092
|
+
sort: input.sort,
|
|
3093
|
+
filter: input.filter
|
|
3094
|
+
})
|
|
3095
|
+
);
|
|
3096
|
+
return r.ok ? text(renderMarket(r.value), r.value) : pendingResult(r.pending);
|
|
3097
|
+
}
|
|
3098
|
+
};
|
|
3099
|
+
const account = {
|
|
3100
|
+
name: "account",
|
|
3101
|
+
title: "Account",
|
|
3102
|
+
description: "Balance, positions with TP/SL ids, open orders, 30-day result. paper=true for the practice account.",
|
|
3103
|
+
inputSchema: accountInputSchema,
|
|
3104
|
+
async execute(input) {
|
|
3105
|
+
if (input.paper) {
|
|
3106
|
+
const p = await withSession(auth, [AGENT_SCOPES.ACCOUNT_READ], (token) => dataBridge.getPaperPortfolio(token));
|
|
3107
|
+
if (!p.ok) return pendingResult(p.pending);
|
|
3108
|
+
return text(`PRACTICE ${renderAccount(p.value, null, null)}${paperTotals(p.value)}`, { paper: true, portfolio: p.value });
|
|
3109
|
+
}
|
|
3110
|
+
const r = await withSession(
|
|
3111
|
+
auth,
|
|
3112
|
+
[AGENT_SCOPES.ACCOUNT_READ],
|
|
3113
|
+
(token) => Promise.all([
|
|
3114
|
+
dataBridge.getPortfolio(token, { provider: "hyperliquid" }),
|
|
3115
|
+
dataBridge.getOpenOrders(token).catch(() => null),
|
|
3116
|
+
dataBridge.getLedgerSummary(token, { window: "30d" }).catch(() => null)
|
|
3117
|
+
])
|
|
3118
|
+
);
|
|
3119
|
+
if (!r.ok) return pendingResult(r.pending);
|
|
3120
|
+
const [portfolio, orders, summary] = r.value;
|
|
3121
|
+
return text(renderAccount(portfolio, orders, summary), { portfolio, orders, summary });
|
|
3122
|
+
}
|
|
3123
|
+
};
|
|
3124
|
+
const trade = {
|
|
3125
|
+
name: "trade",
|
|
3126
|
+
title: "Trade",
|
|
3127
|
+
description: "Open, add to, close (all or part) or reverse a position, sized by margin in USD. Answers with the position after the order. paper=true to practice.",
|
|
3128
|
+
inputSchema: tradeInputSchema,
|
|
3129
|
+
acts: true,
|
|
3130
|
+
async execute(input) {
|
|
3131
|
+
if (input.fraction !== void 0 && input.action !== "close") throw new Error("fraction is for close only");
|
|
3132
|
+
if (input.action === "reset") {
|
|
3133
|
+
if (!input.paper) throw new Error("reset is for the practice account only: pass paper=true");
|
|
3134
|
+
const r2 = await withSession(auth, [AGENT_SCOPES.ORDERS_WRITE], (token) => dataBridge.resetPaperAccount(token, input.margin_usd));
|
|
3135
|
+
if (!r2.ok) return pendingResult(r2.pending);
|
|
3136
|
+
return text(`Practice account started over with ${input.margin_usd ? `$${input.margin_usd}` : "the default balance"}.`, { paper: true, reset: true });
|
|
3137
|
+
}
|
|
3138
|
+
if (input.paper) return paperTrade(input);
|
|
3139
|
+
if (input.action === "close") {
|
|
3140
|
+
const r2 = await withSession(
|
|
3141
|
+
auth,
|
|
3142
|
+
[AGENT_SCOPES.ORDERS_WRITE],
|
|
3143
|
+
(token) => dataBridge.closePosition(token, {
|
|
3144
|
+
provider: "hyperliquid",
|
|
3145
|
+
symbol: input.coin,
|
|
3146
|
+
...input.fraction !== void 0 && input.fraction < 1 ? { fraction: input.fraction } : {},
|
|
3147
|
+
// A close is a market order: the same cap applies (unset, data-bridge uses its default).
|
|
3148
|
+
...input.slippage_bps ? { maxSlippageBps: input.slippage_bps } : {}
|
|
3149
|
+
})
|
|
3150
|
+
);
|
|
3151
|
+
if (!r2.ok) return pendingResult(r2.pending);
|
|
3152
|
+
if (r2.value.outcome === "no_position") return text(`No open ${input.coin} position: nothing was sent.`, r2.value);
|
|
3153
|
+
const after2 = await positionAfter(input.coin, false);
|
|
3154
|
+
const what = r2.value.mode === "PARTIAL" ? `${Math.round((input.fraction ?? 1) * 100)}% of the ${input.coin} position` : `the whole ${input.coin} position`;
|
|
3155
|
+
return text(`Sent a market order closing ${what}. ${after2.text}`, { ...r2.value, after: after2.position });
|
|
3156
|
+
}
|
|
3157
|
+
if (!input.side || !input.margin_usd) throw new Error(`${input.action} needs side and margin_usd`);
|
|
3158
|
+
if (input.limit_px && input.slippage_bps) throw new Error("slippage_bps is for market orders: a limit order fills at limit_px or better");
|
|
3159
|
+
const prot = await protectionFor(input);
|
|
3160
|
+
if ("pending" in prot) return pendingResult(prot.pending);
|
|
3161
|
+
const protection = prot.value;
|
|
3162
|
+
const intentType = input.action === "open" ? "OPEN" : input.action === "add" ? "DCA" : "REVERSE";
|
|
3163
|
+
let idempotencyKey = input.key;
|
|
3164
|
+
const r = await withSession(auth, [AGENT_SCOPES.ORDERS_WRITE], async (token) => {
|
|
3165
|
+
if (!idempotencyKey) {
|
|
3166
|
+
const session = await auth.requireSession();
|
|
3167
|
+
idempotencyKey = tradeKeyFor(input, `${session.ownerUserId}|${session.agentGrantId}`);
|
|
3168
|
+
}
|
|
3169
|
+
return dataBridge.submitTradeIntent(token, {
|
|
3170
|
+
provider: "hyperliquid",
|
|
3171
|
+
symbol: input.coin,
|
|
3172
|
+
intentType,
|
|
3173
|
+
direction: input.side === "long" ? "LONG" : "SHORT",
|
|
3174
|
+
size: { mode: "MARGIN_USD", value: input.margin_usd },
|
|
3175
|
+
execution: input.limit_px ? { style: "LIMIT_GTC", limitPrice: input.limit_px } : { style: "MARKET" },
|
|
3176
|
+
// Unset, data-bridge caps a market order at its own default (300 bps).
|
|
3177
|
+
...input.slippage_bps ? { maxSlippageBps: input.slippage_bps } : {},
|
|
3178
|
+
...input.leverage ? { leverageOverride: input.leverage } : {},
|
|
3179
|
+
...protection ? { protection } : {},
|
|
3180
|
+
idempotencyKey,
|
|
3181
|
+
...input.key ? {} : { idempotencyWindowSeconds: TRADE_KEY_WINDOW_SECONDS }
|
|
3182
|
+
});
|
|
3183
|
+
}).catch((error) => {
|
|
3184
|
+
if (!isHeliosAgentApiError(error) || error.code !== "CONFLICT" || !/being placed/.test(error.message)) throw error;
|
|
3185
|
+
const why = input.key ? `key ${idempotencyKey} was already used` : `an identical trade call within ${TRADE_KEY_WINDOW_SECONDS}s of the first is the same order`;
|
|
3186
|
+
throw new Error(
|
|
3187
|
+
`${error.message} (${why}, so this may be that earlier order.) Call account to see whether it went through. To place another one on purpose, call trade again with a new key.`
|
|
3188
|
+
);
|
|
3189
|
+
});
|
|
3190
|
+
if (!r.ok) return pendingResult(r.pending);
|
|
3191
|
+
const s = r.value;
|
|
3192
|
+
const parts = [
|
|
3193
|
+
s.outcome === "replayed" ? `Already sent earlier with key ${idempotencyKey}${input.key ? "" : ` (an identical call within ${TRADE_KEY_WINDOW_SECONDS}s of the first is the same order)`}: nothing new was placed. To place another on purpose, pass a new key.` : `Sent: ${input.action} ${input.side} ${input.coin}, $${input.margin_usd} margin${input.leverage ? ` at ${input.leverage}x` : ""}, ${input.limit_px ? `limit ${price(input.limit_px)}` : "market"}.`
|
|
3194
|
+
];
|
|
3195
|
+
if (protection?.stopLoss) parts.push(`Stop ${price(protection.stopLoss.triggerPrice)}.`);
|
|
3196
|
+
if (protection?.takeProfit) parts.push(`Take profit ${price(protection.takeProfit.triggerPrice)}.`);
|
|
3197
|
+
if (s.orderStatus) parts.push(`Order status: ${s.orderStatus}.`);
|
|
3198
|
+
if (s.rejectedProtection?.length) parts.push(`Hyperliquid refused part of the TP/SL; check account and set it with protect.`);
|
|
3199
|
+
parts.push(input.key ? `key=${idempotencyKey}` : `key=${idempotencyKey} (an identical call within ${TRADE_KEY_WINDOW_SECONDS}s of the first is the same order)`);
|
|
3200
|
+
let after;
|
|
3201
|
+
if (!input.limit_px && s.outcome !== "replayed") {
|
|
3202
|
+
const a = await positionAfter(input.coin, false);
|
|
3203
|
+
parts.push(a.text);
|
|
3204
|
+
after = a.position;
|
|
3205
|
+
} else if (input.limit_px) parts.push("A limit order rests until filled: see it in account; move it with protect op=move.");
|
|
3206
|
+
return text(parts.join(" "), { ...s, idempotencyKey, ...after !== void 0 ? { after } : {} });
|
|
3207
|
+
}
|
|
3208
|
+
};
|
|
3209
|
+
const protect = {
|
|
3210
|
+
name: "protect",
|
|
3211
|
+
title: "Protect",
|
|
3212
|
+
description: "Set or remove TP/SL on a position, or cancel an order.",
|
|
3213
|
+
inputSchema: protectInputSchema,
|
|
3214
|
+
acts: true,
|
|
3215
|
+
async execute(input) {
|
|
3216
|
+
if (input.op === "set") {
|
|
3217
|
+
if (!input.tp_px && !input.sl_px) throw new Error("set needs tp_px, sl_px or both");
|
|
3218
|
+
const p = await withSession(
|
|
3219
|
+
auth,
|
|
3220
|
+
[AGENT_SCOPES.ACCOUNT_READ],
|
|
3221
|
+
(token) => input.paper ? dataBridge.getPaperPortfolio(token) : dataBridge.getPortfolio(token, { provider: "hyperliquid" })
|
|
3222
|
+
).catch(
|
|
3223
|
+
(error) => {
|
|
3224
|
+
throw new Error(
|
|
3225
|
+
`Could not read your ${input.coin} position to check the TP/SL side (${error instanceof Error ? error.message : String(error)}); nothing was set.`
|
|
3226
|
+
);
|
|
3227
|
+
}
|
|
3228
|
+
);
|
|
3229
|
+
if (!p.ok) return pendingResult(p.pending);
|
|
3230
|
+
const position = p.value.positions.find((pos) => sameCoin(pos, input.coin));
|
|
3231
|
+
if (!position) throw new Error(`No open ${input.coin} position: nothing was set.`);
|
|
3232
|
+
if (!(position.markPrice > 0)) throw new Error(`No price for ${input.coin} to check the TP/SL against: nothing was set. Try again shortly.`);
|
|
3233
|
+
const wrong = wrongSideTpSl(position.side, position.markPrice, input.tp_px, input.sl_px);
|
|
3234
|
+
if (wrong) throw new Error(`${wrong}: it would trigger at once and close the position. Nothing was set.`);
|
|
3235
|
+
const tpSl = {
|
|
3236
|
+
provider: "hyperliquid",
|
|
3237
|
+
symbol: input.coin,
|
|
3238
|
+
applyTo: "FULL_POSITION",
|
|
3239
|
+
// TP/SL attach to the position on Hyperliquid; data-bridge links them to the order that opened it.
|
|
3240
|
+
parentClientOrderId: `hl-position:${input.coin}`,
|
|
3241
|
+
...input.tp_px ? { takeProfit: { kind: "MARKET", triggerPrice: input.tp_px } } : {},
|
|
3242
|
+
...input.sl_px ? { stopLoss: { kind: "MARKET", triggerPrice: input.sl_px } } : {}
|
|
3243
|
+
};
|
|
3244
|
+
const r2 = await withSession(
|
|
3245
|
+
auth,
|
|
3246
|
+
[AGENT_SCOPES.ORDERS_WRITE],
|
|
3247
|
+
(token) => input.paper ? dataBridge.setPaperTpSl(token, tpSl) : dataBridge.setTpSl(token, tpSl)
|
|
3248
|
+
);
|
|
3249
|
+
if (!r2.ok) return pendingResult(r2.pending);
|
|
3250
|
+
const legs = [input.tp_px ? `take profit ${price(input.tp_px)}` : null, input.sl_px ? `stop ${price(input.sl_px)}` : null].filter(Boolean);
|
|
3251
|
+
return text(`${input.paper ? "Practice: s" : "S"}et on the whole ${input.coin} position: ${legs.join(", ")}.`, r2.value);
|
|
3252
|
+
}
|
|
3253
|
+
if (!input.order_id) throw new Error(`${input.op} needs order_id (from account)`);
|
|
3254
|
+
if (input.op === "remove") {
|
|
3255
|
+
const del = { provider: "hyperliquid", symbol: input.coin, orderId: input.order_id };
|
|
3256
|
+
const r2 = await withSession(
|
|
3257
|
+
auth,
|
|
3258
|
+
[AGENT_SCOPES.ORDERS_CANCEL],
|
|
3259
|
+
(token) => input.paper ? dataBridge.deletePaperTpSl(token, del) : dataBridge.deleteTpSl(token, del)
|
|
3260
|
+
);
|
|
3261
|
+
return r2.ok ? text(`Removed TP/SL ${input.order_id} on ${input.coin}.`, r2.value) : pendingResult(r2.pending);
|
|
3262
|
+
}
|
|
3263
|
+
if (input.paper) throw new Error("The practice account has no resting orders (every order fills at market): nothing to cancel or move");
|
|
3264
|
+
const oid = Number(input.order_id);
|
|
3265
|
+
if (!Number.isInteger(oid) || oid < 0) throw new Error(`${input.op} needs the numeric order oid from account`);
|
|
3266
|
+
if (input.op === "move") {
|
|
3267
|
+
if (!input.px) throw new Error("move needs px, the new price");
|
|
3268
|
+
const o = await withSession(auth, [AGENT_SCOPES.ACCOUNT_READ], (token) => dataBridge.getOpenOrders(token));
|
|
3269
|
+
if (!o.ok) return pendingResult(o.pending);
|
|
3270
|
+
const order = o.value.find((x) => x.oid === oid);
|
|
3271
|
+
if (!order) throw new Error(`No open order ${oid}: it may have filled or been cancelled. Call account.`);
|
|
3272
|
+
const size = Number(order.sz);
|
|
3273
|
+
const r2 = await withSession(auth, [AGENT_SCOPES.ORDERS_WRITE], (token) => dataBridge.modifyOrder(token, { symbol: order.coin, oid, price: input.px, size }));
|
|
3274
|
+
return r2.ok ? text(`Moved order ${oid} on ${order.coin} to ${price(input.px)} (size ${order.sz}).`, r2.value) : pendingResult(r2.pending);
|
|
3275
|
+
}
|
|
3276
|
+
const r = await withSession(auth, [AGENT_SCOPES.ORDERS_CANCEL], (token) => dataBridge.cancelOrder(token, { symbol: input.coin, oid }));
|
|
3277
|
+
return r.ok ? text(`Cancelled order ${oid} on ${input.coin}.`, r.value) : pendingResult(r.pending);
|
|
3278
|
+
}
|
|
3279
|
+
};
|
|
3280
|
+
const trades = {
|
|
3281
|
+
name: "trades",
|
|
3282
|
+
title: "Your Trades",
|
|
3283
|
+
description: "Your recent fills, newest first: price, size, realized PnL and fees, with totals. Learn from results. paper=true for practice.",
|
|
3284
|
+
inputSchema: tradesInputSchema,
|
|
3285
|
+
async execute(input) {
|
|
3286
|
+
const window = input.window ?? "7d";
|
|
3287
|
+
const want = input.limit ?? 20;
|
|
3288
|
+
const ask = { window, limit: input.coin ? 200 : want };
|
|
3289
|
+
const r = await withSession(
|
|
3290
|
+
auth,
|
|
3291
|
+
[AGENT_SCOPES.ACCOUNT_READ],
|
|
3292
|
+
(token) => input.paper ? dataBridge.getPaperLedgerFills(token, ask) : dataBridge.getLedgerFills(token, ask)
|
|
3293
|
+
);
|
|
3294
|
+
if (!r.ok) return pendingResult(r.pending);
|
|
3295
|
+
const fills = r.value.fills.filter((f) => !input.coin || sameCoinName(f.coin, input.coin)).slice(0, want);
|
|
3296
|
+
const page = { ...r.value, fills };
|
|
3297
|
+
return text(renderTrades(page, Boolean(input.paper), input.coin ?? null), { paper: Boolean(input.paper), ...page });
|
|
3298
|
+
}
|
|
3299
|
+
};
|
|
3300
|
+
const candles = {
|
|
3301
|
+
name: "candles",
|
|
3302
|
+
title: "Candles",
|
|
3303
|
+
description: "OHLCV candles for one coin (up to 7 days), oldest first, as CSV.",
|
|
3304
|
+
inputSchema: candlesInputSchema,
|
|
3305
|
+
async execute(input) {
|
|
3306
|
+
const r = await withSession(
|
|
3307
|
+
auth,
|
|
3308
|
+
READ,
|
|
3309
|
+
(token) => dataBridge.getMarketCandles(token, { symbol: input.coin, interval: input.interval, limit: input.limit ?? 100 })
|
|
3310
|
+
);
|
|
3311
|
+
return r.ok ? text(renderCandles(input.coin, input.interval, r.value), r.value) : pendingResult(r.pending);
|
|
3312
|
+
}
|
|
3313
|
+
};
|
|
3314
|
+
async function positionAfter(coinName, isPaper) {
|
|
3315
|
+
const p = await withSession(
|
|
3316
|
+
auth,
|
|
3317
|
+
[AGENT_SCOPES.ACCOUNT_READ],
|
|
3318
|
+
(token) => isPaper ? dataBridge.getPaperPortfolio(token) : dataBridge.getPortfolio(token, { provider: "hyperliquid" })
|
|
3319
|
+
).catch(() => null);
|
|
3320
|
+
if (!p?.ok || !Array.isArray(p.value?.positions)) return { text: "Call account to see the position.", position: null };
|
|
3321
|
+
const position = p.value.positions.find((pos) => sameCoin(pos, coinName)) ?? null;
|
|
3322
|
+
return { text: position ? `Now: ${positionLine(position)}.` : `Now: no ${coinName} position.`, position };
|
|
3323
|
+
}
|
|
3324
|
+
async function paperTrade(input) {
|
|
3325
|
+
if (input.limit_px) throw new Error("The practice account fills market orders only: leave out limit_px");
|
|
3326
|
+
if (input.action === "close") {
|
|
3327
|
+
const r2 = await withSession(
|
|
3328
|
+
auth,
|
|
3329
|
+
[AGENT_SCOPES.ORDERS_WRITE],
|
|
3330
|
+
(token) => dataBridge.closePaperPosition(token, { symbol: input.coin, ...input.fraction !== void 0 && input.fraction < 1 ? { fraction: input.fraction } : {} })
|
|
3331
|
+
);
|
|
3332
|
+
if (!r2.ok) return pendingResult(r2.pending);
|
|
3333
|
+
if (!r2.value.length) return text(`Practice: no open ${input.coin} position, nothing closed.`, { paper: true, fills: [] });
|
|
3334
|
+
const after2 = await positionAfter(input.coin, true);
|
|
3335
|
+
const pnl = r2.value.reduce((sum, f) => sum + f.pnl - f.fee, 0);
|
|
3336
|
+
return text(`Practice: closed ${input.fraction && input.fraction < 1 ? `${Math.round(input.fraction * 100)}% of ` : ""}${input.coin} @ ${price(r2.value[r2.value.length - 1].price)}, ${pnl >= 0 ? "+" : "-"}$${Math.abs(pnl).toFixed(2)} after fees. ${after2.text}`, {
|
|
3337
|
+
paper: true,
|
|
3338
|
+
fills: r2.value,
|
|
3339
|
+
after: after2.position
|
|
3340
|
+
});
|
|
3341
|
+
}
|
|
3342
|
+
if (!input.side || !input.margin_usd) throw new Error(`${input.action} needs side and margin_usd`);
|
|
3343
|
+
const protection = await protectionFor(input);
|
|
3344
|
+
if ("pending" in protection) return pendingResult(protection.pending);
|
|
3345
|
+
const r = await withSession(
|
|
3346
|
+
auth,
|
|
3347
|
+
[AGENT_SCOPES.ORDERS_WRITE],
|
|
3348
|
+
(token) => dataBridge.submitPaperIntent(token, {
|
|
3349
|
+
provider: "hyperliquid",
|
|
3350
|
+
symbol: input.coin,
|
|
3351
|
+
intentType: input.action === "open" ? "OPEN" : input.action === "add" ? "DCA" : "REVERSE",
|
|
3352
|
+
direction: input.side === "long" ? "LONG" : "SHORT",
|
|
3353
|
+
size: { mode: "MARGIN_USD", value: input.margin_usd },
|
|
3354
|
+
execution: { style: "MARKET" },
|
|
3355
|
+
...input.leverage ? { leverageOverride: input.leverage } : {},
|
|
3356
|
+
...protection.value ? { protection: protection.value } : {}
|
|
3357
|
+
})
|
|
3358
|
+
);
|
|
3359
|
+
if (!r.ok) return pendingResult(r.pending);
|
|
3360
|
+
const after = await positionAfter(input.coin, true);
|
|
3361
|
+
const filled = r.value.filter((f) => f.side !== null);
|
|
3362
|
+
const parts = [
|
|
3363
|
+
filled.length ? `Practice: ${input.action} ${input.side} ${input.coin} filled @ ${price(filled[filled.length - 1].price)}, $${input.margin_usd} margin${input.leverage ? ` at ${input.leverage}x` : ""}.` : `Practice: ${input.action} ${input.side} ${input.coin} sent, no fill reported.`
|
|
3364
|
+
];
|
|
3365
|
+
if (protection.value?.stopLoss) parts.push(`Stop ${price(protection.value.stopLoss.triggerPrice)}.`);
|
|
3366
|
+
if (protection.value?.takeProfit) parts.push(`Take profit ${price(protection.value.takeProfit.triggerPrice)}.`);
|
|
3367
|
+
parts.push(after.text);
|
|
3368
|
+
return text(parts.join(" "), { paper: true, fills: r.value, after: after.position });
|
|
3369
|
+
}
|
|
3370
|
+
async function protectionFor(input) {
|
|
3371
|
+
if (input.side === "short" && (input.tp_pct ?? 0) >= 100) throw new Error("A short can gain at most 100%: tp_pct must be under 100");
|
|
3372
|
+
const m = await withSession(auth, READ, (token) => dataBridge.getAgentMarket(token, { coins: [input.coin], limit: 1 })).catch(() => null);
|
|
3373
|
+
if (m && !m.ok) return { pending: m.pending };
|
|
3374
|
+
const row = m?.value?.rows?.find((r) => sameCoinName(r.coin, input.coin)) ?? null;
|
|
3375
|
+
if (row?.maxLev && input.leverage && input.leverage > row.maxLev)
|
|
3376
|
+
throw new Error(`${input.coin} allows at most ${row.maxLev}x on Hyperliquid: lower leverage. Nothing was sent.`);
|
|
3377
|
+
if (input.leverage && input.margin_usd && input.margin_usd * input.leverage < MIN_ORDER_USD)
|
|
3378
|
+
throw new Error(`Order value $${(input.margin_usd * input.leverage).toFixed(2)} is under Hyperliquid's $${MIN_ORDER_USD} minimum (margin x leverage). Nothing was sent.`);
|
|
3379
|
+
if (!input.sl_pct && !input.tp_pct) return { value: void 0 };
|
|
3380
|
+
const ref = input.limit_px ?? row?.px ?? null;
|
|
3381
|
+
if (ref === null) throw new Error(`No price for ${input.coin} to place the stop from; trade without sl_pct/tp_pct, then use protect`);
|
|
3382
|
+
const long = input.side === "long";
|
|
3383
|
+
const at = (p, away) => Number((p * (1 + away / 100)).toPrecision(6));
|
|
3384
|
+
return {
|
|
3385
|
+
value: {
|
|
3386
|
+
...input.sl_pct ? { stopLoss: { kind: "MARKET", triggerPrice: at(ref, long ? -input.sl_pct : input.sl_pct) } } : {},
|
|
3387
|
+
...input.tp_pct ? { takeProfit: { kind: "MARKET", triggerPrice: at(ref, long ? input.tp_pct : -input.tp_pct) } } : {}
|
|
3388
|
+
}
|
|
3389
|
+
};
|
|
3390
|
+
}
|
|
3391
|
+
return { smBoard, smMoves, smCoin, market, account, trade, protect, trades, candles };
|
|
3392
|
+
}
|
|
3393
|
+
function practiceOnly(tool) {
|
|
3394
|
+
return {
|
|
3395
|
+
...tool,
|
|
3396
|
+
description: `Practice account only on this link (no real money). ${tool.description}`,
|
|
3397
|
+
execute: (input) => tool.execute({ ...input, paper: true })
|
|
3398
|
+
};
|
|
3399
|
+
}
|
|
3400
|
+
function leanToolsFor(tools, profile) {
|
|
3401
|
+
const reads = [tools.smBoard, tools.smMoves, tools.smCoin, tools.market];
|
|
3402
|
+
const list = profile === "signals" ? [...reads, tools.account, tools.trades, practiceOnly(tools.trade), practiceOnly(tools.protect), tools.candles] : [...reads, tools.account, tools.trades, tools.trade, tools.protect, tools.candles];
|
|
3403
|
+
return list;
|
|
3404
|
+
}
|
|
3405
|
+
|
|
2188
3406
|
// src/tools.ts
|
|
2189
|
-
var
|
|
2190
|
-
var pollLoginInputSchema =
|
|
2191
|
-
/**
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
3407
|
+
var import_zod7 = require("zod");
|
|
3408
|
+
var pollLoginInputSchema = import_zod7.z.object({
|
|
3409
|
+
/**
|
|
3410
|
+
* Optional, kept for older agents: the login in progress is always the one polled, and any other code (one that isn't
|
|
3411
|
+
* its user code) is refused.
|
|
3412
|
+
*/
|
|
3413
|
+
deviceCode: import_zod7.z.string().min(1).optional()
|
|
3414
|
+
});
|
|
3415
|
+
var startLoginOutputSchema = import_zod7.z.object({
|
|
3416
|
+
status: import_zod7.z.literal("pending"),
|
|
3417
|
+
userCode: import_zod7.z.string(),
|
|
3418
|
+
verificationUri: import_zod7.z.string().url(),
|
|
3419
|
+
verificationUriComplete: import_zod7.z.string().url(),
|
|
3420
|
+
expiresInSeconds: import_zod7.z.number().int().positive(),
|
|
3421
|
+
pollIntervalSeconds: import_zod7.z.number().int().positive(),
|
|
3422
|
+
requestedScope: import_zod7.z.array(agentScopeSchema).min(1),
|
|
3423
|
+
startedAt: import_zod7.z.string().datetime()
|
|
3424
|
+
});
|
|
3425
|
+
var ensureLoginInputSchema = import_zod7.z.object({
|
|
3426
|
+
clientName: import_zod7.z.string().min(1).max(64).optional(),
|
|
3427
|
+
scope: import_zod7.z.array(agentScopeSchema).min(1).optional(),
|
|
2208
3428
|
constraints: agentConstraintsSchema.optional()
|
|
2209
3429
|
});
|
|
2210
|
-
var providerSelectionInputSchema =
|
|
3430
|
+
var providerSelectionInputSchema = import_zod7.z.object({
|
|
2211
3431
|
provider: providerSchema.optional()
|
|
2212
3432
|
});
|
|
2213
|
-
var providerSetInputSchema =
|
|
3433
|
+
var providerSetInputSchema = import_zod7.z.object({
|
|
2214
3434
|
provider: providerSchema
|
|
2215
3435
|
});
|
|
2216
|
-
var activeProviderSchema =
|
|
2217
|
-
configured:
|
|
3436
|
+
var activeProviderSchema = import_zod7.z.object({
|
|
3437
|
+
configured: import_zod7.z.boolean(),
|
|
2218
3438
|
provider: providerSchema.nullable()
|
|
2219
3439
|
});
|
|
2220
|
-
var ensureLoginOutputSchema =
|
|
3440
|
+
var ensureLoginOutputSchema = import_zod7.z.discriminatedUnion("status", [
|
|
2221
3441
|
agentSessionViewSchema.extend({
|
|
2222
|
-
status:
|
|
2223
|
-
connected:
|
|
3442
|
+
status: import_zod7.z.literal("connected"),
|
|
3443
|
+
connected: import_zod7.z.literal(true)
|
|
2224
3444
|
}),
|
|
2225
3445
|
startLoginOutputSchema.extend({
|
|
2226
|
-
status:
|
|
2227
|
-
connected:
|
|
2228
|
-
message:
|
|
3446
|
+
status: import_zod7.z.literal("pending"),
|
|
3447
|
+
connected: import_zod7.z.literal(false),
|
|
3448
|
+
message: import_zod7.z.string()
|
|
2229
3449
|
})
|
|
2230
3450
|
]);
|
|
2231
|
-
var pollLoginOutputSchema =
|
|
3451
|
+
var pollLoginOutputSchema = import_zod7.z.discriminatedUnion("status", [
|
|
2232
3452
|
agentSessionViewSchema.extend({
|
|
2233
|
-
status:
|
|
2234
|
-
connected:
|
|
3453
|
+
status: import_zod7.z.literal("connected"),
|
|
3454
|
+
connected: import_zod7.z.literal(true)
|
|
2235
3455
|
}),
|
|
2236
|
-
|
|
2237
|
-
status:
|
|
2238
|
-
connected:
|
|
2239
|
-
message:
|
|
3456
|
+
import_zod7.z.object({
|
|
3457
|
+
status: import_zod7.z.enum(["pending", "rejected", "expired", "failed"]),
|
|
3458
|
+
connected: import_zod7.z.literal(false),
|
|
3459
|
+
message: import_zod7.z.string()
|
|
2240
3460
|
})
|
|
2241
3461
|
]);
|
|
2242
|
-
var walletStateToolOutputSchema =
|
|
2243
|
-
|
|
2244
|
-
status:
|
|
2245
|
-
connected:
|
|
3462
|
+
var walletStateToolOutputSchema = import_zod7.z.discriminatedUnion("status", [
|
|
3463
|
+
import_zod7.z.object({
|
|
3464
|
+
status: import_zod7.z.literal("connected"),
|
|
3465
|
+
connected: import_zod7.z.literal(true),
|
|
2246
3466
|
walletState: walletStateSchema
|
|
2247
3467
|
}),
|
|
2248
3468
|
startLoginOutputSchema.extend({
|
|
2249
|
-
status:
|
|
2250
|
-
connected:
|
|
2251
|
-
message:
|
|
3469
|
+
status: import_zod7.z.literal("pending"),
|
|
3470
|
+
connected: import_zod7.z.literal(false),
|
|
3471
|
+
message: import_zod7.z.string()
|
|
2252
3472
|
})
|
|
2253
3473
|
]);
|
|
2254
|
-
var portfolioToolOutputSchema =
|
|
2255
|
-
|
|
2256
|
-
status:
|
|
2257
|
-
connected:
|
|
3474
|
+
var portfolioToolOutputSchema = import_zod7.z.discriminatedUnion("status", [
|
|
3475
|
+
import_zod7.z.object({
|
|
3476
|
+
status: import_zod7.z.literal("connected"),
|
|
3477
|
+
connected: import_zod7.z.literal(true),
|
|
2258
3478
|
portfolio: portfolioSnapshotSchema
|
|
2259
3479
|
}),
|
|
2260
3480
|
startLoginOutputSchema.extend({
|
|
2261
|
-
status:
|
|
2262
|
-
connected:
|
|
2263
|
-
message:
|
|
3481
|
+
status: import_zod7.z.literal("pending"),
|
|
3482
|
+
connected: import_zod7.z.literal(false),
|
|
3483
|
+
message: import_zod7.z.string()
|
|
2264
3484
|
})
|
|
2265
3485
|
]);
|
|
2266
|
-
var openPositionsToolOutputSchema =
|
|
2267
|
-
|
|
2268
|
-
status:
|
|
2269
|
-
connected:
|
|
3486
|
+
var openPositionsToolOutputSchema = import_zod7.z.discriminatedUnion("status", [
|
|
3487
|
+
import_zod7.z.object({
|
|
3488
|
+
status: import_zod7.z.literal("connected"),
|
|
3489
|
+
connected: import_zod7.z.literal(true),
|
|
2270
3490
|
openPositions: openPositionsSnapshotSchema
|
|
2271
3491
|
}),
|
|
2272
3492
|
startLoginOutputSchema.extend({
|
|
2273
|
-
status:
|
|
2274
|
-
connected:
|
|
2275
|
-
message:
|
|
3493
|
+
status: import_zod7.z.literal("pending"),
|
|
3494
|
+
connected: import_zod7.z.literal(false),
|
|
3495
|
+
message: import_zod7.z.string()
|
|
2276
3496
|
})
|
|
2277
3497
|
]);
|
|
2278
|
-
var ledgerFillsToolOutputSchema =
|
|
2279
|
-
|
|
2280
|
-
status:
|
|
2281
|
-
connected:
|
|
3498
|
+
var ledgerFillsToolOutputSchema = import_zod7.z.discriminatedUnion("status", [
|
|
3499
|
+
import_zod7.z.object({
|
|
3500
|
+
status: import_zod7.z.literal("connected"),
|
|
3501
|
+
connected: import_zod7.z.literal(true),
|
|
2282
3502
|
fills: ledgerFillsPageSchema
|
|
2283
3503
|
}),
|
|
2284
3504
|
startLoginOutputSchema.extend({
|
|
2285
|
-
status:
|
|
2286
|
-
connected:
|
|
2287
|
-
message:
|
|
3505
|
+
status: import_zod7.z.literal("pending"),
|
|
3506
|
+
connected: import_zod7.z.literal(false),
|
|
3507
|
+
message: import_zod7.z.string()
|
|
2288
3508
|
})
|
|
2289
3509
|
]);
|
|
2290
|
-
var ledgerSummaryToolOutputSchema =
|
|
2291
|
-
|
|
2292
|
-
status:
|
|
2293
|
-
connected:
|
|
3510
|
+
var ledgerSummaryToolOutputSchema = import_zod7.z.discriminatedUnion("status", [
|
|
3511
|
+
import_zod7.z.object({
|
|
3512
|
+
status: import_zod7.z.literal("connected"),
|
|
3513
|
+
connected: import_zod7.z.literal(true),
|
|
2294
3514
|
summary: ledgerSummarySchema
|
|
2295
3515
|
}),
|
|
2296
3516
|
startLoginOutputSchema.extend({
|
|
2297
|
-
status:
|
|
2298
|
-
connected:
|
|
2299
|
-
message:
|
|
3517
|
+
status: import_zod7.z.literal("pending"),
|
|
3518
|
+
connected: import_zod7.z.literal(false),
|
|
3519
|
+
message: import_zod7.z.string()
|
|
2300
3520
|
})
|
|
2301
3521
|
]);
|
|
2302
|
-
var marketSnapshotToolOutputSchema =
|
|
2303
|
-
|
|
2304
|
-
status:
|
|
2305
|
-
connected:
|
|
3522
|
+
var marketSnapshotToolOutputSchema = import_zod7.z.discriminatedUnion("status", [
|
|
3523
|
+
import_zod7.z.object({
|
|
3524
|
+
status: import_zod7.z.literal("connected"),
|
|
3525
|
+
connected: import_zod7.z.literal(true),
|
|
2306
3526
|
snapshot: marketSnapshotSchema
|
|
2307
3527
|
}),
|
|
2308
3528
|
startLoginOutputSchema.extend({
|
|
2309
|
-
status:
|
|
2310
|
-
connected:
|
|
2311
|
-
message:
|
|
3529
|
+
status: import_zod7.z.literal("pending"),
|
|
3530
|
+
connected: import_zod7.z.literal(false),
|
|
3531
|
+
message: import_zod7.z.string()
|
|
2312
3532
|
})
|
|
2313
3533
|
]);
|
|
2314
|
-
var marketSnapshotChunkToolOutputSchema =
|
|
2315
|
-
|
|
2316
|
-
status:
|
|
2317
|
-
connected:
|
|
3534
|
+
var marketSnapshotChunkToolOutputSchema = import_zod7.z.discriminatedUnion("status", [
|
|
3535
|
+
import_zod7.z.object({
|
|
3536
|
+
status: import_zod7.z.literal("connected"),
|
|
3537
|
+
connected: import_zod7.z.literal(true),
|
|
2318
3538
|
snapshotChunk: marketSnapshotChunkSchema
|
|
2319
3539
|
}),
|
|
2320
3540
|
startLoginOutputSchema.extend({
|
|
2321
|
-
status:
|
|
2322
|
-
connected:
|
|
2323
|
-
message:
|
|
3541
|
+
status: import_zod7.z.literal("pending"),
|
|
3542
|
+
connected: import_zod7.z.literal(false),
|
|
3543
|
+
message: import_zod7.z.string()
|
|
2324
3544
|
})
|
|
2325
3545
|
]);
|
|
2326
|
-
var marketSymbolDetailToolOutputSchema =
|
|
2327
|
-
|
|
2328
|
-
status:
|
|
2329
|
-
connected:
|
|
3546
|
+
var marketSymbolDetailToolOutputSchema = import_zod7.z.discriminatedUnion("status", [
|
|
3547
|
+
import_zod7.z.object({
|
|
3548
|
+
status: import_zod7.z.literal("connected"),
|
|
3549
|
+
connected: import_zod7.z.literal(true),
|
|
2330
3550
|
detail: marketSymbolDetailSchema
|
|
2331
3551
|
}),
|
|
2332
3552
|
startLoginOutputSchema.extend({
|
|
2333
|
-
status:
|
|
2334
|
-
connected:
|
|
2335
|
-
message:
|
|
3553
|
+
status: import_zod7.z.literal("pending"),
|
|
3554
|
+
connected: import_zod7.z.literal(false),
|
|
3555
|
+
message: import_zod7.z.string()
|
|
2336
3556
|
})
|
|
2337
3557
|
]);
|
|
2338
3558
|
var tradeActionInputSchema = submitTradeIntentInputSchema.omit({
|
|
2339
3559
|
intentType: true
|
|
2340
3560
|
});
|
|
2341
|
-
var tradeActionToolOutputSchema =
|
|
2342
|
-
|
|
2343
|
-
status:
|
|
2344
|
-
connected:
|
|
3561
|
+
var tradeActionToolOutputSchema = import_zod7.z.discriminatedUnion("status", [
|
|
3562
|
+
import_zod7.z.object({
|
|
3563
|
+
status: import_zod7.z.literal("connected"),
|
|
3564
|
+
connected: import_zod7.z.literal(true),
|
|
2345
3565
|
submission: submitTradeIntentResultSchema
|
|
2346
3566
|
}),
|
|
2347
3567
|
startLoginOutputSchema.extend({
|
|
2348
|
-
status:
|
|
2349
|
-
connected:
|
|
2350
|
-
message:
|
|
3568
|
+
status: import_zod7.z.literal("pending"),
|
|
3569
|
+
connected: import_zod7.z.literal(false),
|
|
3570
|
+
message: import_zod7.z.string()
|
|
2351
3571
|
})
|
|
2352
3572
|
]);
|
|
2353
|
-
var closeTradeToolOutputSchema =
|
|
2354
|
-
|
|
2355
|
-
status:
|
|
2356
|
-
connected:
|
|
3573
|
+
var closeTradeToolOutputSchema = import_zod7.z.discriminatedUnion("status", [
|
|
3574
|
+
import_zod7.z.object({
|
|
3575
|
+
status: import_zod7.z.literal("connected"),
|
|
3576
|
+
connected: import_zod7.z.literal(true),
|
|
2357
3577
|
closure: closePositionResultSchema
|
|
2358
3578
|
}),
|
|
2359
3579
|
startLoginOutputSchema.extend({
|
|
2360
|
-
status:
|
|
2361
|
-
connected:
|
|
2362
|
-
message:
|
|
3580
|
+
status: import_zod7.z.literal("pending"),
|
|
3581
|
+
connected: import_zod7.z.literal(false),
|
|
3582
|
+
message: import_zod7.z.string()
|
|
2363
3583
|
})
|
|
2364
3584
|
]);
|
|
2365
|
-
var setTpSlToolOutputSchema =
|
|
2366
|
-
|
|
2367
|
-
status:
|
|
2368
|
-
connected:
|
|
3585
|
+
var setTpSlToolOutputSchema = import_zod7.z.discriminatedUnion("status", [
|
|
3586
|
+
import_zod7.z.object({
|
|
3587
|
+
status: import_zod7.z.literal("connected"),
|
|
3588
|
+
connected: import_zod7.z.literal(true),
|
|
2369
3589
|
protection: setTpSlResultSchema
|
|
2370
3590
|
}),
|
|
2371
3591
|
startLoginOutputSchema.extend({
|
|
2372
|
-
status:
|
|
2373
|
-
connected:
|
|
2374
|
-
message:
|
|
3592
|
+
status: import_zod7.z.literal("pending"),
|
|
3593
|
+
connected: import_zod7.z.literal(false),
|
|
3594
|
+
message: import_zod7.z.string()
|
|
2375
3595
|
})
|
|
2376
3596
|
]);
|
|
2377
|
-
var deleteTpSlToolOutputSchema =
|
|
2378
|
-
|
|
2379
|
-
status:
|
|
2380
|
-
connected:
|
|
3597
|
+
var deleteTpSlToolOutputSchema = import_zod7.z.discriminatedUnion("status", [
|
|
3598
|
+
import_zod7.z.object({
|
|
3599
|
+
status: import_zod7.z.literal("connected"),
|
|
3600
|
+
connected: import_zod7.z.literal(true),
|
|
2381
3601
|
deletion: deleteTpSlResultSchema
|
|
2382
3602
|
}),
|
|
2383
3603
|
startLoginOutputSchema.extend({
|
|
2384
|
-
status:
|
|
2385
|
-
connected:
|
|
2386
|
-
message:
|
|
3604
|
+
status: import_zod7.z.literal("pending"),
|
|
3605
|
+
connected: import_zod7.z.literal(false),
|
|
3606
|
+
message: import_zod7.z.string()
|
|
2387
3607
|
})
|
|
2388
3608
|
]);
|
|
2389
|
-
var openOrdersToolOutputSchema =
|
|
2390
|
-
|
|
2391
|
-
status:
|
|
2392
|
-
connected:
|
|
2393
|
-
orders:
|
|
3609
|
+
var openOrdersToolOutputSchema = import_zod7.z.discriminatedUnion("status", [
|
|
3610
|
+
import_zod7.z.object({
|
|
3611
|
+
status: import_zod7.z.literal("connected"),
|
|
3612
|
+
connected: import_zod7.z.literal(true),
|
|
3613
|
+
orders: import_zod7.z.array(openOrderSchema)
|
|
2394
3614
|
}),
|
|
2395
3615
|
startLoginOutputSchema.extend({
|
|
2396
|
-
status:
|
|
2397
|
-
connected:
|
|
2398
|
-
message:
|
|
3616
|
+
status: import_zod7.z.literal("pending"),
|
|
3617
|
+
connected: import_zod7.z.literal(false),
|
|
3618
|
+
message: import_zod7.z.string()
|
|
2399
3619
|
})
|
|
2400
3620
|
]);
|
|
2401
|
-
var cancelOrderToolOutputSchema =
|
|
2402
|
-
|
|
2403
|
-
status:
|
|
2404
|
-
connected:
|
|
3621
|
+
var cancelOrderToolOutputSchema = import_zod7.z.discriminatedUnion("status", [
|
|
3622
|
+
import_zod7.z.object({
|
|
3623
|
+
status: import_zod7.z.literal("connected"),
|
|
3624
|
+
connected: import_zod7.z.literal(true),
|
|
2405
3625
|
cancellation: cancelOrderResultSchema
|
|
2406
3626
|
}),
|
|
2407
3627
|
startLoginOutputSchema.extend({
|
|
2408
|
-
status:
|
|
2409
|
-
connected:
|
|
2410
|
-
message:
|
|
3628
|
+
status: import_zod7.z.literal("pending"),
|
|
3629
|
+
connected: import_zod7.z.literal(false),
|
|
3630
|
+
message: import_zod7.z.string()
|
|
2411
3631
|
})
|
|
2412
3632
|
]);
|
|
2413
|
-
var documentPathInputSchema =
|
|
2414
|
-
path:
|
|
2415
|
-
});
|
|
2416
|
-
var docsIndexSchema =
|
|
2417
|
-
root:
|
|
2418
|
-
documents:
|
|
2419
|
-
|
|
2420
|
-
path:
|
|
2421
|
-
section:
|
|
2422
|
-
title:
|
|
2423
|
-
bytes:
|
|
3633
|
+
var documentPathInputSchema = import_zod7.z.object({
|
|
3634
|
+
path: import_zod7.z.string().min(1)
|
|
3635
|
+
});
|
|
3636
|
+
var docsIndexSchema = import_zod7.z.object({
|
|
3637
|
+
root: import_zod7.z.literal("docs"),
|
|
3638
|
+
documents: import_zod7.z.array(
|
|
3639
|
+
import_zod7.z.object({
|
|
3640
|
+
path: import_zod7.z.string(),
|
|
3641
|
+
section: import_zod7.z.string(),
|
|
3642
|
+
title: import_zod7.z.string(),
|
|
3643
|
+
bytes: import_zod7.z.number().int().nonnegative()
|
|
2424
3644
|
})
|
|
2425
3645
|
)
|
|
2426
3646
|
});
|
|
2427
|
-
var documentSchema =
|
|
2428
|
-
path:
|
|
2429
|
-
section:
|
|
2430
|
-
title:
|
|
2431
|
-
body:
|
|
2432
|
-
bytes:
|
|
3647
|
+
var documentSchema = import_zod7.z.object({
|
|
3648
|
+
path: import_zod7.z.string(),
|
|
3649
|
+
section: import_zod7.z.string(),
|
|
3650
|
+
title: import_zod7.z.string(),
|
|
3651
|
+
body: import_zod7.z.string(),
|
|
3652
|
+
bytes: import_zod7.z.number().int().nonnegative()
|
|
2433
3653
|
});
|
|
2434
3654
|
function createHeliosDocsToolDefinitions(service) {
|
|
2435
3655
|
return [
|
|
@@ -2437,7 +3657,7 @@ function createHeliosDocsToolDefinitions(service) {
|
|
|
2437
3657
|
name: "helios_docs_get_index",
|
|
2438
3658
|
title: "Get Helios Docs Index",
|
|
2439
3659
|
description: "Lists the package-local Helios MCP and indicator .txt documentation available to the agent.",
|
|
2440
|
-
inputSchema:
|
|
3660
|
+
inputSchema: import_zod7.z.object({}),
|
|
2441
3661
|
async execute() {
|
|
2442
3662
|
const result = docsIndexSchema.parse(await service.getIndex());
|
|
2443
3663
|
return {
|
|
@@ -2520,7 +3740,7 @@ function createHeliosAuthToolDefinitions(service) {
|
|
|
2520
3740
|
{
|
|
2521
3741
|
name: "helios_auth_poll_login",
|
|
2522
3742
|
title: "Poll Helios Login",
|
|
2523
|
-
description: "Checks whether the Helios login was approved and finishes connecting.
|
|
3743
|
+
description: "Checks whether the Helios login in progress was approved and finishes connecting. No code is needed.",
|
|
2524
3744
|
inputSchema: pollLoginInputSchema,
|
|
2525
3745
|
async execute(input) {
|
|
2526
3746
|
const result = pollLoginOutputSchema.parse(await service.pollLogin(input.deviceCode));
|
|
@@ -2539,7 +3759,7 @@ function createHeliosAuthToolDefinitions(service) {
|
|
|
2539
3759
|
name: "helios_auth_whoami",
|
|
2540
3760
|
title: "Helios Login Status",
|
|
2541
3761
|
description: "Returns the delegated Helios session state currently stored by the MCP server.",
|
|
2542
|
-
inputSchema:
|
|
3762
|
+
inputSchema: import_zod7.z.object({}),
|
|
2543
3763
|
async execute() {
|
|
2544
3764
|
const result = agentSessionViewSchema.parse(await service.whoami());
|
|
2545
3765
|
return {
|
|
@@ -2561,7 +3781,7 @@ function createHeliosProviderToolDefinitions(service) {
|
|
|
2561
3781
|
name: "helios_provider_get_active",
|
|
2562
3782
|
title: "Get Helios Active Provider",
|
|
2563
3783
|
description: "Returns the provider currently pinned for this MCP session, if any.",
|
|
2564
|
-
inputSchema:
|
|
3784
|
+
inputSchema: import_zod7.z.object({}),
|
|
2565
3785
|
async execute() {
|
|
2566
3786
|
const result = activeProviderSchema.parse(await service.getActiveProvider());
|
|
2567
3787
|
return {
|
|
@@ -2601,7 +3821,7 @@ function createHeliosAccountToolDefinitions(service) {
|
|
|
2601
3821
|
name: "helios_account_get_wallet_state",
|
|
2602
3822
|
title: "Get Helios Wallet State",
|
|
2603
3823
|
description: "Returns the current Hyperliquid wallet snapshot for the connected Helios account.",
|
|
2604
|
-
inputSchema:
|
|
3824
|
+
inputSchema: import_zod7.z.object({}),
|
|
2605
3825
|
async execute() {
|
|
2606
3826
|
const result = walletStateToolOutputSchema.parse(await service.getWalletState());
|
|
2607
3827
|
const structuredContent = result.status === "connected" ? result : {
|
|
@@ -2649,7 +3869,7 @@ function createHeliosLedgerToolDefinitions(service) {
|
|
|
2649
3869
|
name: "helios_positions_get_open",
|
|
2650
3870
|
title: "Get Helios Open Positions",
|
|
2651
3871
|
description: "Returns the current open Hyperliquid positions for the connected Helios account.",
|
|
2652
|
-
inputSchema:
|
|
3872
|
+
inputSchema: import_zod7.z.object({}),
|
|
2653
3873
|
async execute() {
|
|
2654
3874
|
const result = openPositionsToolOutputSchema.parse(await service.getOpenPositions());
|
|
2655
3875
|
const structuredContent = result.status === "connected" ? result : {
|
|
@@ -2915,7 +4135,7 @@ function createHeliosTradingToolDefinitions(service) {
|
|
|
2915
4135
|
name: "helios_orders_get_open",
|
|
2916
4136
|
title: "Get Helios Open Orders",
|
|
2917
4137
|
description: "Lists the currently open Hyperliquid orders for the connected account.",
|
|
2918
|
-
inputSchema:
|
|
4138
|
+
inputSchema: import_zod7.z.object({}),
|
|
2919
4139
|
async execute() {
|
|
2920
4140
|
const result = openOrdersToolOutputSchema.parse(await service.getOpenOrders());
|
|
2921
4141
|
return {
|
|
@@ -2976,6 +4196,8 @@ After approving access, call helios_auth_ensure_login (or helios_auth_poll_login
|
|
|
2976
4196
|
}
|
|
2977
4197
|
|
|
2978
4198
|
// src/server.ts
|
|
4199
|
+
var MCP_PROFILES = ["lean", "signals", "full"];
|
|
4200
|
+
var MAX_TOOL_INPUT_ELEMENTS = 1e4;
|
|
2979
4201
|
async function createHeliosAgentMcpServer(options) {
|
|
2980
4202
|
if (!options.identityServiceUrl) {
|
|
2981
4203
|
throw new Error("identityServiceUrl is required");
|
|
@@ -2984,11 +4206,23 @@ async function createHeliosAgentMcpServer(options) {
|
|
|
2984
4206
|
throw new Error("dataBridgeUrl is required");
|
|
2985
4207
|
}
|
|
2986
4208
|
const { McpServer } = await import("@modelcontextprotocol/sdk/server/mcp.js");
|
|
2987
|
-
const server = new McpServer(
|
|
2988
|
-
|
|
2989
|
-
|
|
4209
|
+
const server = new McpServer(
|
|
4210
|
+
{
|
|
4211
|
+
name: "helios",
|
|
4212
|
+
version: "0.5.0"
|
|
4213
|
+
},
|
|
4214
|
+
// Caps the array elements plus object keys one tools/call may carry (GHSA-46jr-4wx8-v453: without it, one request
|
|
4215
|
+
// under the body size limit could run the hosted server out of memory while validating). The largest real input is
|
|
4216
|
+
// a snapshot over every symbol and field, a few hundred entries, so this is far above normal use.
|
|
4217
|
+
{ maxToolInputElements: MAX_TOOL_INPUT_ELEMENTS }
|
|
4218
|
+
);
|
|
4219
|
+
if (!options.hosted) {
|
|
4220
|
+
assertHeliosBaseUrl("identityServiceUrl (HELIOS_IDENTITY_SERVICE_URL)", options.identityServiceUrl);
|
|
4221
|
+
assertHeliosBaseUrl("dataBridgeUrl (HELIOS_DATA_BRIDGE_URL)", options.dataBridgeUrl);
|
|
4222
|
+
}
|
|
4223
|
+
const sessionStore = options.sessionStore ?? new FileAgentSessionStore(options.sessionPath ?? getDefaultHeliosAgentMcpSessionPath(), {
|
|
4224
|
+
issuer: { identityServiceUrl: options.identityServiceUrl, dataBridgeUrl: options.dataBridgeUrl }
|
|
2990
4225
|
});
|
|
2991
|
-
const sessionStore = options.sessionStore ?? new FileAgentSessionStore(options.sessionPath ?? getDefaultHeliosAgentMcpSessionPath());
|
|
2992
4226
|
const client = new HeliosAgentAuthClient({
|
|
2993
4227
|
baseUrl: options.identityServiceUrl,
|
|
2994
4228
|
fetch: options.fetch
|
|
@@ -3027,39 +4261,29 @@ async function createHeliosAgentMcpServer(options) {
|
|
|
3027
4261
|
const docsService = new HeliosAgentMcpDocsService({
|
|
3028
4262
|
docsRoot: options.docsRoot ?? getDefaultHeliosAgentMcpDocsRoot()
|
|
3029
4263
|
});
|
|
3030
|
-
const
|
|
3031
|
-
const
|
|
3032
|
-
const
|
|
3033
|
-
|
|
3034
|
-
|
|
3035
|
-
|
|
3036
|
-
|
|
3037
|
-
|
|
3038
|
-
|
|
3039
|
-
|
|
3040
|
-
|
|
3041
|
-
|
|
3042
|
-
|
|
3043
|
-
|
|
3044
|
-
|
|
3045
|
-
|
|
3046
|
-
|
|
3047
|
-
|
|
3048
|
-
|
|
3049
|
-
|
|
3050
|
-
|
|
3051
|
-
|
|
3052
|
-
|
|
3053
|
-
registerTool(server, marketTools[1]);
|
|
3054
|
-
registerTool(server, marketTools[2]);
|
|
3055
|
-
registerTool(server, tradingTools[0]);
|
|
3056
|
-
registerTool(server, tradingTools[1]);
|
|
3057
|
-
registerTool(server, tradingTools[2]);
|
|
3058
|
-
registerTool(server, tradingTools[3]);
|
|
3059
|
-
registerTool(server, tradingTools[4]);
|
|
3060
|
-
registerTool(server, tradingTools[5]);
|
|
3061
|
-
registerTool(server, tradingTools[6]);
|
|
3062
|
-
registerTool(server, tradingTools[7]);
|
|
4264
|
+
const profile = options.profile ?? "full";
|
|
4265
|
+
const lean = createLeanToolDefinitions({ auth: authService, dataBridge: dataBridgeClient });
|
|
4266
|
+
for (const tool of leanToolsFor(lean, profile)) {
|
|
4267
|
+
registerTool(server, { ...tool, textIsResult: true });
|
|
4268
|
+
}
|
|
4269
|
+
if (profile === "full") {
|
|
4270
|
+
const docsTools = createHeliosDocsToolDefinitions(docsService);
|
|
4271
|
+
const authTools = createHeliosAuthToolDefinitions(authService);
|
|
4272
|
+
const ledgerTools = createHeliosLedgerToolDefinitions(ledgerService);
|
|
4273
|
+
const marketTools = createHeliosMarketToolDefinitions(marketService);
|
|
4274
|
+
if (!options.hosted) {
|
|
4275
|
+
registerTool(server, authTools[0]);
|
|
4276
|
+
registerTool(server, authTools[1]);
|
|
4277
|
+
registerTool(server, authTools[2]);
|
|
4278
|
+
}
|
|
4279
|
+
registerTool(server, authTools[3]);
|
|
4280
|
+
registerTool(server, marketTools[0]);
|
|
4281
|
+
registerTool(server, marketTools[1]);
|
|
4282
|
+
registerTool(server, marketTools[2]);
|
|
4283
|
+
registerTool(server, ledgerTools[1]);
|
|
4284
|
+
registerTool(server, docsTools[0]);
|
|
4285
|
+
registerTool(server, docsTools[1]);
|
|
4286
|
+
}
|
|
3063
4287
|
return {
|
|
3064
4288
|
server,
|
|
3065
4289
|
authService,
|
|
@@ -3073,9 +4297,10 @@ async function createHeliosAgentMcpServer(options) {
|
|
|
3073
4297
|
};
|
|
3074
4298
|
}
|
|
3075
4299
|
var ACTS_ON_ACCOUNT = /^helios_(trading_|positions_set_tp_sl|positions_delete_tp_sl|orders_cancel)/;
|
|
4300
|
+
var LEAN_ACTS = /* @__PURE__ */ new Set(["trade", "protect"]);
|
|
3076
4301
|
var CHANGES_SESSION = /^helios_(auth_start_login|auth_ensure_login|auth_poll_login|provider_set_active)$/;
|
|
3077
4302
|
function toolAnnotations(name) {
|
|
3078
|
-
if (ACTS_ON_ACCOUNT.test(name)) return { readOnlyHint: false, destructiveHint: true, openWorldHint: true };
|
|
4303
|
+
if (ACTS_ON_ACCOUNT.test(name) || LEAN_ACTS.has(name)) return { readOnlyHint: false, destructiveHint: true, openWorldHint: true };
|
|
3079
4304
|
if (CHANGES_SESSION.test(name)) return { readOnlyHint: false, destructiveHint: false, openWorldHint: false };
|
|
3080
4305
|
return { readOnlyHint: true, destructiveHint: false, openWorldHint: true };
|
|
3081
4306
|
}
|
|
@@ -3099,7 +4324,7 @@ function registerTool(server, tool) {
|
|
|
3099
4324
|
async (input) => {
|
|
3100
4325
|
const result = await tool.execute(tool.inputSchema.parse(input));
|
|
3101
4326
|
return {
|
|
3102
|
-
content: withDataText(tool.name, result.content, result.structuredContent),
|
|
4327
|
+
content: tool.textIsResult ? result.content : withDataText(tool.name, result.content, result.structuredContent),
|
|
3103
4328
|
structuredContent: result.structuredContent
|
|
3104
4329
|
};
|
|
3105
4330
|
}
|
|
@@ -3107,6 +4332,8 @@ function registerTool(server, tool) {
|
|
|
3107
4332
|
}
|
|
3108
4333
|
// Annotate the CommonJS export names for ESM import in node:
|
|
3109
4334
|
0 && (module.exports = {
|
|
4335
|
+
DEFAULT_HELIOS_BASE_URL,
|
|
4336
|
+
DEFAULT_HELIOS_SESSION_ISSUER,
|
|
3110
4337
|
DEFAULT_SNAPSHOT_FIELDS,
|
|
3111
4338
|
DEFAULT_SNAPSHOT_TOP,
|
|
3112
4339
|
FileAgentSessionStore,
|
|
@@ -3118,7 +4345,15 @@ function registerTool(server, tool) {
|
|
|
3118
4345
|
HeliosAgentMcpMarketService,
|
|
3119
4346
|
HeliosAgentMcpProviderService,
|
|
3120
4347
|
HeliosAgentMcpTradingService,
|
|
4348
|
+
MAX_TOOL_INPUT_ELEMENTS,
|
|
4349
|
+
MCP_PROFILES,
|
|
4350
|
+
MIN_ORDER_USD,
|
|
3121
4351
|
MemoryAgentMcpSessionStore,
|
|
4352
|
+
TRADE_KEY_WINDOW_SECONDS,
|
|
4353
|
+
accountInputSchema,
|
|
4354
|
+
ago,
|
|
4355
|
+
assertHeliosBaseUrl,
|
|
4356
|
+
candlesInputSchema,
|
|
3122
4357
|
compactSymbolDetail,
|
|
3123
4358
|
createHeliosAccountToolDefinitions,
|
|
3124
4359
|
createHeliosAgentMcpServer,
|
|
@@ -3128,13 +4363,41 @@ function registerTool(server, tool) {
|
|
|
3128
4363
|
createHeliosMarketToolDefinitions,
|
|
3129
4364
|
createHeliosProviderToolDefinitions,
|
|
3130
4365
|
createHeliosTradingToolDefinitions,
|
|
4366
|
+
createLeanToolDefinitions,
|
|
4367
|
+
entryText,
|
|
3131
4368
|
formatHeliosLoginPrompt,
|
|
3132
4369
|
getDefaultHeliosAgentMcpDocsRoot,
|
|
3133
4370
|
getDefaultHeliosAgentMcpSessionPath,
|
|
4371
|
+
isSameHeliosIssuer,
|
|
4372
|
+
leanToolsFor,
|
|
4373
|
+
marketInputSchema,
|
|
4374
|
+
moveText,
|
|
4375
|
+
pct,
|
|
3134
4376
|
pendingAgentLoginSchema,
|
|
4377
|
+
positionLine,
|
|
4378
|
+
practiceOnly,
|
|
4379
|
+
price,
|
|
4380
|
+
protectInputSchema,
|
|
4381
|
+
renderAccount,
|
|
4382
|
+
renderBoard,
|
|
4383
|
+
renderCandles,
|
|
4384
|
+
renderCoin,
|
|
4385
|
+
renderMarket,
|
|
4386
|
+
renderMoves,
|
|
4387
|
+
renderTrades,
|
|
3135
4388
|
retryAfterUnauthorizedRefresh,
|
|
4389
|
+
smBoardInputSchema,
|
|
4390
|
+
smCoinInputSchema,
|
|
4391
|
+
smMovesInputSchema,
|
|
3136
4392
|
toolAnnotations,
|
|
3137
4393
|
topOfSnapshot,
|
|
4394
|
+
tradeInputSchema,
|
|
4395
|
+
tradeKeyFor,
|
|
4396
|
+
trader,
|
|
4397
|
+
tradesInputSchema,
|
|
4398
|
+
tradingLine,
|
|
4399
|
+
usd,
|
|
3138
4400
|
withDataText,
|
|
3139
|
-
withSnapshotDefaults
|
|
4401
|
+
withSnapshotDefaults,
|
|
4402
|
+
wrongSideTpSl
|
|
3140
4403
|
});
|