@vtxmacro/cli 0.1.0 → 2026.6.22
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 +6 -0
- package/bin/vtx-mcp.js +28 -1
- package/bin/vtx.js +705 -35
- package/package.json +5 -1
package/README.md
CHANGED
|
@@ -22,8 +22,14 @@ vtx auth login --scopes read,bot:control
|
|
|
22
22
|
vtx --json auth whoami
|
|
23
23
|
vtx --json bots status
|
|
24
24
|
vtx --profile <profile-id> runtime run --follow
|
|
25
|
+
vtx auth login --scopes read,insights:connect
|
|
26
|
+
vtx --profile <profile-id> insights connect
|
|
25
27
|
```
|
|
26
28
|
|
|
29
|
+
The Insights connector talks only to a loopback OpenAI-compatible model server.
|
|
30
|
+
It defaults to LM Studio at `http://127.0.0.1:1234/v1` and reads its optional
|
|
31
|
+
API key from `LM_STUDIO_API_KEY`. The key and local URL never leave the device.
|
|
32
|
+
|
|
27
33
|
## MCP
|
|
28
34
|
|
|
29
35
|
Configure your MCP-compatible agent app to start:
|
package/bin/vtx-mcp.js
CHANGED
|
@@ -256,6 +256,33 @@ var VtxAgentClient = class _VtxAgentClient {
|
|
|
256
256
|
getLlmConfig(profileId) {
|
|
257
257
|
return this.request("/llm/config", { profileId });
|
|
258
258
|
}
|
|
259
|
+
registerInsightConnector(profileId, payload) {
|
|
260
|
+
return this.request("/insights/connectors/register", {
|
|
261
|
+
method: "POST",
|
|
262
|
+
profileId,
|
|
263
|
+
body: payload
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
claimInsightConnectorJob(profileId, connectorId, deviceId) {
|
|
267
|
+
return this.request(
|
|
268
|
+
`/insights/connectors/${encodeURIComponent(connectorId)}/jobs:claim`,
|
|
269
|
+
{ method: "POST", profileId, body: { device_id: deviceId } }
|
|
270
|
+
);
|
|
271
|
+
}
|
|
272
|
+
completeInsightConnectorJob(profileId, connectorId, jobId, deviceId, payload) {
|
|
273
|
+
const params = new URLSearchParams({ device_id: deviceId });
|
|
274
|
+
return this.request(
|
|
275
|
+
`/insights/connectors/${encodeURIComponent(connectorId)}/jobs/${encodeURIComponent(jobId)}:complete?${params}`,
|
|
276
|
+
{ method: "POST", profileId, body: payload }
|
|
277
|
+
);
|
|
278
|
+
}
|
|
279
|
+
heartbeatInsightConnectorJob(profileId, connectorId, jobId, deviceId, claimToken) {
|
|
280
|
+
const params = new URLSearchParams({ device_id: deviceId });
|
|
281
|
+
return this.request(
|
|
282
|
+
`/insights/connectors/${encodeURIComponent(connectorId)}/jobs/${encodeURIComponent(jobId)}:heartbeat?${params}`,
|
|
283
|
+
{ method: "POST", profileId, body: { claim_token: claimToken } }
|
|
284
|
+
);
|
|
285
|
+
}
|
|
259
286
|
heartbeatRuntime(profileId, payload, leaseToken) {
|
|
260
287
|
return this.request("/trading/ai/runtime/heartbeat", {
|
|
261
288
|
method: "POST",
|
|
@@ -968,7 +995,7 @@ async function handleMcpJsonRpcRequest(request, clientFactory = createDefaultMcp
|
|
|
968
995
|
},
|
|
969
996
|
serverInfo: {
|
|
970
997
|
name: "vtx-macro",
|
|
971
|
-
version: "
|
|
998
|
+
version: "2026.6.22"
|
|
972
999
|
}
|
|
973
1000
|
});
|
|
974
1001
|
}
|
package/bin/vtx.js
CHANGED
|
@@ -1632,10 +1632,13 @@ var init_hyperliquid_account_state_adapter = __esm({
|
|
|
1632
1632
|
});
|
|
1633
1633
|
|
|
1634
1634
|
// lib/byokProviderRegistry.ts
|
|
1635
|
+
function normalizeProviderAlias(value) {
|
|
1636
|
+
return String(value ?? "").trim().toLowerCase().replace(/[._-]+/g, " ").replace(/\s+/g, " ");
|
|
1637
|
+
}
|
|
1635
1638
|
function byokProvidersForClientRuntime() {
|
|
1636
1639
|
return BYOK_PROVIDERS.filter((provider) => provider.clientRuntimeSupported);
|
|
1637
1640
|
}
|
|
1638
|
-
var BYOK_UI_GROUP, BYOK_PROVIDERS;
|
|
1641
|
+
var BYOK_UI_GROUP, BYOK_PROVIDERS, BYOK_PROVIDER_KEY_BY_ALIAS;
|
|
1639
1642
|
var init_byokProviderRegistry = __esm({
|
|
1640
1643
|
"lib/byokProviderRegistry.ts"() {
|
|
1641
1644
|
"use strict";
|
|
@@ -1658,6 +1661,15 @@ var init_byokProviderRegistry = __esm({
|
|
|
1658
1661
|
{ key: "venice", runtimeProvider: "venice", displayName: "Venice AI", apiKeyField: "venice_api_key", envKey: "VENICE_API_KEY", baseUrl: "https://api.venice.ai/api/v1", modelsUrl: "https://api.venice.ai/api/v1/models", requestContractStrategy: "venice", clientRuntimeSupported: true, uiGroup: BYOK_UI_GROUP, providerKind: "hosted", displayOrder: 150, aliases: ["venice.ai"] },
|
|
1659
1662
|
{ key: "xai", runtimeProvider: "grok", displayName: "xAI", apiKeyField: "grok_api_key", envKey: "XAI_API_KEY", baseUrl: "https://api.x.ai/v1", modelsUrl: "https://api.x.ai/v1/models", requestContractStrategy: "native", clientRuntimeSupported: true, uiGroup: BYOK_UI_GROUP, providerKind: "direct", displayOrder: 160, aliases: ["x", "x.ai", "grok"] }
|
|
1660
1663
|
];
|
|
1664
|
+
BYOK_PROVIDER_KEY_BY_ALIAS = /* @__PURE__ */ new Map();
|
|
1665
|
+
for (const provider of BYOK_PROVIDERS) {
|
|
1666
|
+
for (const alias of [provider.key, provider.displayName, ...provider.aliases]) {
|
|
1667
|
+
const normalizedAlias = normalizeProviderAlias(alias);
|
|
1668
|
+
if (!BYOK_PROVIDER_KEY_BY_ALIAS.has(normalizedAlias)) {
|
|
1669
|
+
BYOK_PROVIDER_KEY_BY_ALIAS.set(normalizedAlias, provider.key);
|
|
1670
|
+
}
|
|
1671
|
+
}
|
|
1672
|
+
}
|
|
1661
1673
|
}
|
|
1662
1674
|
});
|
|
1663
1675
|
|
|
@@ -1978,22 +1990,22 @@ var init_api = __esm({
|
|
|
1978
1990
|
return process.env.API_URL_SERVER || process.env.NEXT_PUBLIC_API_URL || "http://api:8000";
|
|
1979
1991
|
}
|
|
1980
1992
|
if (process.env.NODE_ENV === "development") {
|
|
1981
|
-
const
|
|
1982
|
-
const isLocalNetworkIP =
|
|
1993
|
+
const hostname2 = window.location.hostname;
|
|
1994
|
+
const isLocalNetworkIP = hostname2.startsWith("192.168.") || hostname2.startsWith("10.") || hostname2.startsWith("172.") && parseInt(hostname2.split(".")[1]) >= 16 && parseInt(hostname2.split(".")[1]) <= 31;
|
|
1983
1995
|
if (isLocalNetworkIP) {
|
|
1984
|
-
return `http://${
|
|
1996
|
+
return `http://${hostname2}:8000`;
|
|
1985
1997
|
}
|
|
1986
|
-
if (
|
|
1987
|
-
return `http://${
|
|
1998
|
+
if (hostname2 === "localhost" || hostname2 === "127.0.0.1") {
|
|
1999
|
+
return `http://${hostname2}:8000`;
|
|
1988
2000
|
}
|
|
1989
2001
|
}
|
|
1990
2002
|
if (process.env.NEXT_PUBLIC_API_URL) {
|
|
1991
2003
|
return process.env.NEXT_PUBLIC_API_URL;
|
|
1992
2004
|
}
|
|
1993
2005
|
try {
|
|
1994
|
-
const
|
|
2006
|
+
const hostname2 = window.location.hostname;
|
|
1995
2007
|
const protocol = window.location.protocol;
|
|
1996
|
-
const baseHost =
|
|
2008
|
+
const baseHost = hostname2.startsWith("www.") ? hostname2.slice(4) : hostname2;
|
|
1997
2009
|
const apiHost = baseHost.startsWith("api.") ? baseHost : `api.${baseHost}`;
|
|
1998
2010
|
return `${protocol}//${apiHost}`;
|
|
1999
2011
|
} catch {
|
|
@@ -11557,7 +11569,7 @@ var init_hyperliquid_signer = __esm({
|
|
|
11557
11569
|
});
|
|
11558
11570
|
|
|
11559
11571
|
// lib/runtime/hyperliquid-client.ts
|
|
11560
|
-
var HyperliquidExchangeRejectionError, normalizeSymbol, HYPERLIQUID_NONCE_STORAGE_KEY, HYPERLIQUID_NONCE_LOCK_NAME, lastGeneratedNonce, readBrowserStoredHyperliquidNonce, writeBrowserStoredHyperliquidNonce, browserNonceCoordinator, allocateHyperliquidNonce, generateHyperliquidNonce, roundToSigFigs, normalizeDecimalString, toPlainDecimalString, roundDirectionalDecimalString, formatHyperliquidPerpPriceWire, countSignificantDigits, countDecimalPlaces, validateHyperliquidPerpPriceWire, validateHyperliquidSizeWire, formatSizeWire, inferApiUrl, inferIsTestnet, postHyperliquidInfo, toFiniteNumber, firstFiniteNumber2, parseFundingUsdcFromInfoPosition2, isExchangeIsolatedOnly, computeHip3AssetId, getPerpDexIndexMap, buildAssetInfosFromMetaResponse, requireWalletAddress2, assertPositiveNumber, normalizeOrderSide, resolveAssets, findResolvedAsset, normalizeExecutionAssetMetadata, resolveHyperliquidAsset, fetchBrowserAvailableAssets, fetchBrowserAllMids, fetchBrowserSymbolMidPrice, fetchBrowserAccountState2, fetchBrowserPosition, buildHyperliquidOrderWire, signHyperliquidPayload, buildHyperliquidUpdateLeveragePayload, buildHyperliquidMarketOrderPayload, extractHyperliquidExchangeError, buildHyperliquidOrderDiagnostics, submitHyperliquidExchangePayload;
|
|
11572
|
+
var HyperliquidExchangeRejectionError, normalizeSymbol, HYPERLIQUID_NONCE_STORAGE_KEY, HYPERLIQUID_NONCE_LOCK_NAME, lastGeneratedNonce, readBrowserStoredHyperliquidNonce, writeBrowserStoredHyperliquidNonce, browserNonceCoordinator, allocateHyperliquidNonce, generateHyperliquidNonce, roundToSigFigs, normalizeDecimalString, toPlainDecimalString, roundDirectionalDecimalString, formatHyperliquidPerpPriceWire, formatHyperliquidTriggerPriceWire, countSignificantDigits, countDecimalPlaces, validateHyperliquidPerpPriceWire, validateHyperliquidSizeWire, formatSizeWire, inferApiUrl, inferIsTestnet, postHyperliquidInfo, toFiniteNumber, firstFiniteNumber2, parseFundingUsdcFromInfoPosition2, isExchangeIsolatedOnly, computeHip3AssetId, getPerpDexIndexMap, buildAssetInfosFromMetaResponse, requireWalletAddress2, assertPositiveNumber, normalizeOrderSide, resolveAssets, findResolvedAsset, normalizeExecutionAssetMetadata, resolveHyperliquidAsset, fetchBrowserAvailableAssets, fetchBrowserAllMids, fetchBrowserSymbolMidPrice, fetchBrowserAccountState2, fetchBrowserPosition, buildHyperliquidOrderWire, signHyperliquidPayload, buildHyperliquidUpdateLeveragePayload, buildHyperliquidMarketOrderPayload, buildHyperliquidTriggerOrderPayload, extractHyperliquidExchangeError, buildHyperliquidOrderDiagnostics, submitHyperliquidExchangePayload;
|
|
11561
11573
|
var init_hyperliquid_client = __esm({
|
|
11562
11574
|
"lib/runtime/hyperliquid-client.ts"() {
|
|
11563
11575
|
"use strict";
|
|
@@ -11692,6 +11704,14 @@ var init_hyperliquid_client = __esm({
|
|
|
11692
11704
|
assertPositiveNumber(decimalRounded, "Hyperliquid order price");
|
|
11693
11705
|
return normalizeDecimalString(roundedWire);
|
|
11694
11706
|
};
|
|
11707
|
+
formatHyperliquidTriggerPriceWire = (value, sizeDecimals) => {
|
|
11708
|
+
const sigFigRounded = roundToSigFigs(value, 5);
|
|
11709
|
+
const priceDecimals = Math.max(0, 6 - sizeDecimals);
|
|
11710
|
+
const scale = 10 ** priceDecimals;
|
|
11711
|
+
const decimalRounded = Math.round(sigFigRounded * scale) / scale;
|
|
11712
|
+
assertPositiveNumber(decimalRounded, "Hyperliquid trigger price");
|
|
11713
|
+
return normalizeDecimalString(decimalRounded.toFixed(priceDecimals));
|
|
11714
|
+
};
|
|
11695
11715
|
countSignificantDigits = (value) => {
|
|
11696
11716
|
const normalized = String(value || "").replace("-", "");
|
|
11697
11717
|
const [integerPartRaw, fractionPartRaw = ""] = normalized.split(".");
|
|
@@ -11818,7 +11838,7 @@ var init_hyperliquid_client = __esm({
|
|
|
11818
11838
|
}
|
|
11819
11839
|
return null;
|
|
11820
11840
|
};
|
|
11821
|
-
isExchangeIsolatedOnly = (asset) => asset.only_isolated === true || String(asset.margin_mode || "").trim().toLowerCase()
|
|
11841
|
+
isExchangeIsolatedOnly = (asset) => asset.only_isolated === true || ["nocross", "strictisolated"].includes(String(asset.margin_mode || "").trim().toLowerCase());
|
|
11822
11842
|
computeHip3AssetId = (perpDexIndex, indexInMeta) => 1e5 + Math.trunc(perpDexIndex) * 1e4 + Math.trunc(indexInMeta);
|
|
11823
11843
|
getPerpDexIndexMap = (payload) => {
|
|
11824
11844
|
if (!Array.isArray(payload)) {
|
|
@@ -12183,6 +12203,44 @@ var init_hyperliquid_client = __esm({
|
|
|
12183
12203
|
grouping: "na"
|
|
12184
12204
|
});
|
|
12185
12205
|
};
|
|
12206
|
+
buildHyperliquidTriggerOrderPayload = async (input) => {
|
|
12207
|
+
assertPositiveNumber(input.size, "Hyperliquid trigger order size");
|
|
12208
|
+
assertPositiveNumber(input.triggerPrice, "Hyperliquid trigger price");
|
|
12209
|
+
const { asset_index: assetIndex, sz_decimals: sizeDecimals } = await resolveHyperliquidAsset(input);
|
|
12210
|
+
const side = normalizeOrderSide(input.side);
|
|
12211
|
+
const roundedSize = Number(input.size.toFixed(sizeDecimals));
|
|
12212
|
+
assertPositiveNumber(roundedSize, "Hyperliquid trigger order size");
|
|
12213
|
+
const priceWire = formatHyperliquidTriggerPriceWire(input.triggerPrice, sizeDecimals);
|
|
12214
|
+
const sizeWire = formatSizeWire(roundedSize, sizeDecimals);
|
|
12215
|
+
const priceValidation = validateHyperliquidPerpPriceWire(priceWire, sizeDecimals);
|
|
12216
|
+
if (!priceValidation.valid) {
|
|
12217
|
+
throw new Error(`Invalid Hyperliquid trigger price wire: ${priceValidation.reason}.`);
|
|
12218
|
+
}
|
|
12219
|
+
const sizeValidation = validateHyperliquidSizeWire(sizeWire, sizeDecimals);
|
|
12220
|
+
if (!sizeValidation.valid) {
|
|
12221
|
+
throw new Error(`Invalid Hyperliquid trigger size wire: ${sizeValidation.reason}.`);
|
|
12222
|
+
}
|
|
12223
|
+
return signHyperliquidPayload(input, {
|
|
12224
|
+
type: "order",
|
|
12225
|
+
orders: [
|
|
12226
|
+
{
|
|
12227
|
+
a: assetIndex,
|
|
12228
|
+
b: side === "buy",
|
|
12229
|
+
p: priceWire,
|
|
12230
|
+
s: sizeWire,
|
|
12231
|
+
r: input.reduceOnly !== false,
|
|
12232
|
+
t: {
|
|
12233
|
+
trigger: {
|
|
12234
|
+
isMarket: true,
|
|
12235
|
+
triggerPx: priceWire,
|
|
12236
|
+
tpsl: input.isTakeProfit === true ? "tp" : "sl"
|
|
12237
|
+
}
|
|
12238
|
+
}
|
|
12239
|
+
}
|
|
12240
|
+
],
|
|
12241
|
+
grouping: "na"
|
|
12242
|
+
});
|
|
12243
|
+
};
|
|
12186
12244
|
extractHyperliquidExchangeError = (payload) => {
|
|
12187
12245
|
if (!payload || typeof payload !== "object") {
|
|
12188
12246
|
return "Unexpected response format from Hyperliquid.";
|
|
@@ -12426,6 +12484,16 @@ function validateDeviceSecrets(value, options) {
|
|
|
12426
12484
|
);
|
|
12427
12485
|
const hasHyperliquidSigningKey = Boolean(hyperliquidSigningKey);
|
|
12428
12486
|
const providerKeyCount = Object.keys(providerKeysWithFirstSlots).length;
|
|
12487
|
+
const activeProviderKeyAliases = activeProvider ? (() => {
|
|
12488
|
+
const provider = byokProvidersForClientRuntime().find(
|
|
12489
|
+
(candidate) => candidate.runtimeProvider === activeProvider
|
|
12490
|
+
);
|
|
12491
|
+
return /* @__PURE__ */ new Set([
|
|
12492
|
+
activeProvider,
|
|
12493
|
+
...provider ? [provider.key, provider.runtimeProvider] : []
|
|
12494
|
+
]);
|
|
12495
|
+
})() : null;
|
|
12496
|
+
const hasActiveProviderKey = activeProviderKeyAliases ? Object.keys(providerKeysWithFirstSlots).some((provider) => activeProviderKeyAliases.has(provider)) : providerKeyCount > 0;
|
|
12429
12497
|
const missing = [];
|
|
12430
12498
|
if (!hasHyperliquidSigningKey) {
|
|
12431
12499
|
missing.push("hyperliquid signing key");
|
|
@@ -12434,10 +12502,10 @@ function validateDeviceSecrets(value, options) {
|
|
|
12434
12502
|
if (!hasLocalAiConfig) {
|
|
12435
12503
|
missing.push("local ai config");
|
|
12436
12504
|
}
|
|
12437
|
-
} else if (
|
|
12505
|
+
} else if (!hasActiveProviderKey && !(activeProvider === "openai" && hasOpenAiCompatibleConfig)) {
|
|
12438
12506
|
missing.push("model provider key");
|
|
12439
12507
|
}
|
|
12440
|
-
const hasProviderRequirement = isLocalProvider ? hasLocalAiConfig : activeProvider === "openai" ?
|
|
12508
|
+
const hasProviderRequirement = isLocalProvider ? hasLocalAiConfig : activeProvider === "openai" ? hasActiveProviderKey || hasOpenAiCompatibleConfig : hasActiveProviderKey;
|
|
12441
12509
|
return {
|
|
12442
12510
|
normalized: {
|
|
12443
12511
|
hyperliquidSigningKey,
|
|
@@ -12469,6 +12537,7 @@ var init_vault = __esm({
|
|
|
12469
12537
|
init_key_preview_contract();
|
|
12470
12538
|
init_client_runtime_provider_visibility_contract();
|
|
12471
12539
|
init_client_runtime_secret_rate_limit_contract();
|
|
12540
|
+
init_byokProviderRegistry();
|
|
12472
12541
|
VAULT_PREFIX = "vtx_vault_";
|
|
12473
12542
|
VAULT_DB_NAME = "vtx-client-runtime-vault";
|
|
12474
12543
|
VAULT_DB_VERSION = 1;
|
|
@@ -12977,7 +13046,7 @@ var init_vault = __esm({
|
|
|
12977
13046
|
});
|
|
12978
13047
|
|
|
12979
13048
|
// lib/runtime/browser-trading.ts
|
|
12980
|
-
var resolveProfileId, requirePositiveNumber, throwIfAborted, loadSigningKey, resolveSigningKey, requireWalletAddress3, resolveEstimatedPrice, executeClientLeverageUpdate, executeClientMarketOrder, executeClientClosePosition;
|
|
13049
|
+
var resolveProfileId, requirePositiveNumber, throwIfAborted, loadSigningKey, resolveSigningKey, requireWalletAddress3, resolveEstimatedPrice, executeClientLeverageUpdate, executeClientMarketOrder, executeClientTriggerOrder, executeClientClosePosition;
|
|
12981
13050
|
var init_browser_trading = __esm({
|
|
12982
13051
|
"lib/runtime/browser-trading.ts"() {
|
|
12983
13052
|
"use strict";
|
|
@@ -13083,6 +13152,26 @@ var init_browser_trading = __esm({
|
|
|
13083
13152
|
signal: input.signal
|
|
13084
13153
|
});
|
|
13085
13154
|
};
|
|
13155
|
+
executeClientTriggerOrder = async (input) => {
|
|
13156
|
+
throwIfAborted(input.signal);
|
|
13157
|
+
const signingKey = await resolveSigningKey(input);
|
|
13158
|
+
throwIfAborted(input.signal);
|
|
13159
|
+
const payload = await buildHyperliquidTriggerOrderPayload({
|
|
13160
|
+
config: input.config ?? null,
|
|
13161
|
+
signingKey,
|
|
13162
|
+
symbol: input.symbol,
|
|
13163
|
+
side: input.side,
|
|
13164
|
+
size: input.size,
|
|
13165
|
+
triggerPrice: input.triggerPrice,
|
|
13166
|
+
reduceOnly: input.reduceOnly,
|
|
13167
|
+
isTakeProfit: input.isTakeProfit
|
|
13168
|
+
});
|
|
13169
|
+
return submitHyperliquidExchangePayload({
|
|
13170
|
+
config: input.config ?? null,
|
|
13171
|
+
payload,
|
|
13172
|
+
signal: input.signal
|
|
13173
|
+
});
|
|
13174
|
+
};
|
|
13086
13175
|
executeClientClosePosition = async (input) => {
|
|
13087
13176
|
throwIfAborted(input.signal);
|
|
13088
13177
|
const profileId = resolveProfileId(input.profileId);
|
|
@@ -13118,7 +13207,7 @@ var init_browser_trading = __esm({
|
|
|
13118
13207
|
});
|
|
13119
13208
|
|
|
13120
13209
|
// lib/runtime/execution-guard-contract.ts
|
|
13121
|
-
var unitsFromPosition, signedUnitsFromPosition, classifyExecutionIntent;
|
|
13210
|
+
var unitsFromPosition, signedUnitsFromPosition, classifyExecutionIntent, resolveExecutionTarget;
|
|
13122
13211
|
var init_execution_guard_contract = __esm({
|
|
13123
13212
|
"lib/runtime/execution-guard-contract.ts"() {
|
|
13124
13213
|
"use strict";
|
|
@@ -13299,11 +13388,52 @@ var init_execution_guard_contract = __esm({
|
|
|
13299
13388
|
targetSignedUnits: -(units - Math.abs(snapshotSignedUnits))
|
|
13300
13389
|
};
|
|
13301
13390
|
};
|
|
13391
|
+
resolveExecutionTarget = (classified, liveSignedUnits) => {
|
|
13392
|
+
const liveUnits = Number.isFinite(liveSignedUnits) ? Math.trunc(liveSignedUnits) : 0;
|
|
13393
|
+
const deltaSignedUnits = classified.targetSignedUnits - liveUnits;
|
|
13394
|
+
if (classified.side !== "buy" && classified.side !== "sell") {
|
|
13395
|
+
return {
|
|
13396
|
+
allowed: false,
|
|
13397
|
+
reasonCode: classified.requestedAction === "HOLD" ? "hold_no_action" : "invalid_decision",
|
|
13398
|
+
deltaSignedUnits,
|
|
13399
|
+
closeOpposingPosition: false,
|
|
13400
|
+
orderUnits: 0
|
|
13401
|
+
};
|
|
13402
|
+
}
|
|
13403
|
+
if (deltaSignedUnits === 0) {
|
|
13404
|
+
return {
|
|
13405
|
+
allowed: false,
|
|
13406
|
+
reasonCode: "noop_already_at_target",
|
|
13407
|
+
deltaSignedUnits: 0,
|
|
13408
|
+
closeOpposingPosition: false,
|
|
13409
|
+
orderUnits: 0
|
|
13410
|
+
};
|
|
13411
|
+
}
|
|
13412
|
+
const expectedDeltaSign = classified.side === "buy" ? 1 : -1;
|
|
13413
|
+
const actualDeltaSign = deltaSignedUnits > 0 ? 1 : -1;
|
|
13414
|
+
if (actualDeltaSign !== expectedDeltaSign) {
|
|
13415
|
+
return {
|
|
13416
|
+
allowed: false,
|
|
13417
|
+
reasonCode: "stale_target_conflict",
|
|
13418
|
+
deltaSignedUnits,
|
|
13419
|
+
closeOpposingPosition: false,
|
|
13420
|
+
orderUnits: 0
|
|
13421
|
+
};
|
|
13422
|
+
}
|
|
13423
|
+
const closeOpposingPosition = classified.intent === "flip" && (classified.side === "buy" && liveUnits < 0 || classified.side === "sell" && liveUnits > 0);
|
|
13424
|
+
return {
|
|
13425
|
+
allowed: true,
|
|
13426
|
+
reasonCode: "executed",
|
|
13427
|
+
deltaSignedUnits,
|
|
13428
|
+
closeOpposingPosition,
|
|
13429
|
+
orderUnits: closeOpposingPosition ? Math.abs(classified.targetSignedUnits) : Math.abs(deltaSignedUnits)
|
|
13430
|
+
};
|
|
13431
|
+
};
|
|
13302
13432
|
}
|
|
13303
13433
|
});
|
|
13304
13434
|
|
|
13305
13435
|
// lib/runtime/runtime-execution.ts
|
|
13306
|
-
var createRuntimeId, _extractOrderId, _toExecutionMetadata, throwIfAborted2, sleep2, roundAssetSize, resolvePositionSize, resolveCurrentPositionSize, buildPositionSnapshot, executeClientRuntimeDecision;
|
|
13436
|
+
var createRuntimeId, _extractOrderId, _toExecutionMetadata, toPositiveNumber, resolveRiskPct, buildTriggerPrice, throwIfAborted2, sleep2, roundAssetSize, resolvePositionSize, resolveCurrentPositionSize, buildPositionSnapshot, executeClientRuntimeDecision;
|
|
13307
13437
|
var init_runtime_execution = __esm({
|
|
13308
13438
|
"lib/runtime/runtime-execution.ts"() {
|
|
13309
13439
|
"use strict";
|
|
@@ -13366,6 +13496,25 @@ var init_runtime_execution = __esm({
|
|
|
13366
13496
|
}
|
|
13367
13497
|
return { raw: value ?? null };
|
|
13368
13498
|
};
|
|
13499
|
+
toPositiveNumber = (value) => {
|
|
13500
|
+
const parsed = Number(value);
|
|
13501
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : 0;
|
|
13502
|
+
};
|
|
13503
|
+
resolveRiskPct = (mode, percentValue, usdcValue, positionNotional) => {
|
|
13504
|
+
if (mode === "usdc") {
|
|
13505
|
+
const usdc = toPositiveNumber(usdcValue);
|
|
13506
|
+
return positionNotional > 0 ? usdc / positionNotional * 100 : 0;
|
|
13507
|
+
}
|
|
13508
|
+
return toPositiveNumber(percentValue);
|
|
13509
|
+
};
|
|
13510
|
+
buildTriggerPrice = (entryPrice, riskPct, isLong, isTakeProfit) => {
|
|
13511
|
+
if (!Number.isFinite(entryPrice) || entryPrice <= 0 || !Number.isFinite(riskPct) || riskPct <= 0) {
|
|
13512
|
+
return 0;
|
|
13513
|
+
}
|
|
13514
|
+
const multiplier = isTakeProfit ? isLong ? 1 + riskPct / 100 : 1 - riskPct / 100 : isLong ? 1 - riskPct / 100 : 1 + riskPct / 100;
|
|
13515
|
+
const price = entryPrice * multiplier;
|
|
13516
|
+
return Number.isFinite(price) && price > 0 ? price : 0;
|
|
13517
|
+
};
|
|
13369
13518
|
throwIfAborted2 = (signal) => {
|
|
13370
13519
|
if (signal?.aborted) {
|
|
13371
13520
|
throw new DOMException("The operation was aborted.", "AbortError");
|
|
@@ -13481,8 +13630,8 @@ var init_runtime_execution = __esm({
|
|
|
13481
13630
|
label: "execution_start",
|
|
13482
13631
|
note: `decision=${input.decision} units=${requestedUnits} symbol=${input.executionContext.symbol}`
|
|
13483
13632
|
});
|
|
13633
|
+
const decisionSnapshotPositionSize = Number.isFinite(input.executionContext.currentPositionSize) ? Number(input.executionContext.currentPositionSize) : 0;
|
|
13484
13634
|
if (input.decision === "HOLD" || requestedUnits <= 0) {
|
|
13485
|
-
const snapshotPositionSize = Number.isFinite(input.executionContext.currentPositionSize) ? Number(input.executionContext.currentPositionSize) : 0;
|
|
13486
13635
|
return {
|
|
13487
13636
|
status: "skipped",
|
|
13488
13637
|
intent: "hold",
|
|
@@ -13490,7 +13639,7 @@ var init_runtime_execution = __esm({
|
|
|
13490
13639
|
reduceOnly: false,
|
|
13491
13640
|
requestedUnits,
|
|
13492
13641
|
targetSignedUnits: signedUnitsFromPosition(
|
|
13493
|
-
|
|
13642
|
+
decisionSnapshotPositionSize,
|
|
13494
13643
|
input.executionContext.lastPrice,
|
|
13495
13644
|
input.executionContext.unitSizeUsdc
|
|
13496
13645
|
),
|
|
@@ -13509,14 +13658,25 @@ var init_runtime_execution = __esm({
|
|
|
13509
13658
|
} = await resolveCurrentPositionSize(input);
|
|
13510
13659
|
throwIfAborted2(input.signal);
|
|
13511
13660
|
const currentPosition = buildPositionSnapshot(input.executionContext.symbol, currentPositionSize);
|
|
13661
|
+
const decisionSnapshotSignedUnits = signedUnitsFromPosition(
|
|
13662
|
+
decisionSnapshotPositionSize,
|
|
13663
|
+
input.executionContext.lastPrice,
|
|
13664
|
+
input.executionContext.unitSizeUsdc
|
|
13665
|
+
);
|
|
13666
|
+
const liveSignedUnits = signedUnitsFromPosition(
|
|
13667
|
+
currentPositionSize,
|
|
13668
|
+
executionPrice,
|
|
13669
|
+
input.executionContext.unitSizeUsdc
|
|
13670
|
+
);
|
|
13512
13671
|
const classified = classifyExecutionIntent(
|
|
13513
13672
|
input.decision,
|
|
13514
13673
|
requestedUnits,
|
|
13515
|
-
|
|
13674
|
+
decisionSnapshotSignedUnits
|
|
13516
13675
|
);
|
|
13676
|
+
const targetResolution = resolveExecutionTarget(classified, liveSignedUnits);
|
|
13517
13677
|
appendRuntimeDebugEvent({
|
|
13518
13678
|
label: "execution_classified",
|
|
13519
|
-
note: `symbol=${input.executionContext.symbol} intent=${classified.intent} side=${classified.side ?? "none"}
|
|
13679
|
+
note: `symbol=${input.executionContext.symbol} intent=${classified.intent} side=${classified.side ?? "none"} decision_snapshot_units=${classified.snapshotSignedUnits} live_units=${liveSignedUnits} target_units=${classified.targetSignedUnits} delta_units=${targetResolution.deltaSignedUnits} requested_units=${classified.requestedUnits} reduceOnly=${String(classified.reduceOnly)} resolution=${targetResolution.reasonCode} execution_snapshot_id=${executionSnapshotId}`
|
|
13520
13680
|
});
|
|
13521
13681
|
if (!classified.side || classified.intent === "hold") {
|
|
13522
13682
|
return {
|
|
@@ -13533,6 +13693,28 @@ var init_runtime_execution = __esm({
|
|
|
13533
13693
|
executionSnapshotCapturedAt
|
|
13534
13694
|
};
|
|
13535
13695
|
}
|
|
13696
|
+
if (!targetResolution.allowed) {
|
|
13697
|
+
const staleTargetConflict = targetResolution.reasonCode === "stale_target_conflict";
|
|
13698
|
+
appendRuntimeDebugEvent({
|
|
13699
|
+
label: staleTargetConflict ? "execution_stale_target_skipped" : "execution_target_noop",
|
|
13700
|
+
note: `symbol=${input.executionContext.symbol} intent=${classified.intent} decision_snapshot_units=${classified.snapshotSignedUnits} live_units=${liveSignedUnits} target_units=${classified.targetSignedUnits} delta_units=${targetResolution.deltaSignedUnits}`,
|
|
13701
|
+
outcome: "ok"
|
|
13702
|
+
});
|
|
13703
|
+
return {
|
|
13704
|
+
status: "skipped",
|
|
13705
|
+
intent: classified.intent,
|
|
13706
|
+
side: classified.side,
|
|
13707
|
+
reduceOnly: classified.reduceOnly,
|
|
13708
|
+
requestedUnits: classified.requestedUnits,
|
|
13709
|
+
targetSignedUnits: classified.targetSignedUnits,
|
|
13710
|
+
executedSizeAsset: 0,
|
|
13711
|
+
executionReports: [],
|
|
13712
|
+
message: staleTargetConflict ? "Live state moved beyond the requested target; skipping stale decision" : "No-op: live position already matches target state",
|
|
13713
|
+
reasonCode: staleTargetConflict ? "stale_target_conflict" : "noop_already_at_target",
|
|
13714
|
+
executionSnapshotId,
|
|
13715
|
+
executionSnapshotCapturedAt
|
|
13716
|
+
};
|
|
13717
|
+
}
|
|
13536
13718
|
if (classified.intent === "open" || classified.intent === "add" || classified.intent === "flip") {
|
|
13537
13719
|
throwIfAborted2(input.signal);
|
|
13538
13720
|
await executeClientLeverageUpdate({
|
|
@@ -13582,7 +13764,7 @@ var init_runtime_execution = __esm({
|
|
|
13582
13764
|
};
|
|
13583
13765
|
}
|
|
13584
13766
|
const executionReports = [];
|
|
13585
|
-
if (
|
|
13767
|
+
if (targetResolution.closeOpposingPosition) {
|
|
13586
13768
|
throwIfAborted2(input.signal);
|
|
13587
13769
|
const closeResponse = await executeClientClosePosition({
|
|
13588
13770
|
profileId: input.profileId,
|
|
@@ -13606,10 +13788,7 @@ var init_runtime_execution = __esm({
|
|
|
13606
13788
|
await sleep2(Math.round(input.executionContext.closePositionDelaySeconds * 1e3), input.signal);
|
|
13607
13789
|
}
|
|
13608
13790
|
}
|
|
13609
|
-
|
|
13610
|
-
if (classified.intent === "flip") {
|
|
13611
|
-
actualUnits = Math.abs(classified.targetSignedUnits);
|
|
13612
|
-
}
|
|
13791
|
+
const actualUnits = targetResolution.orderUnits;
|
|
13613
13792
|
const sizeAsset = roundAssetSize(
|
|
13614
13793
|
input.executionContext.unitSizeUsdc * actualUnits / executionPrice,
|
|
13615
13794
|
input.executionContext.sizeDecimals
|
|
@@ -13658,6 +13837,84 @@ var init_runtime_execution = __esm({
|
|
|
13658
13837
|
status: "executed",
|
|
13659
13838
|
execution_metadata: _toExecutionMetadata(marketOrderResponse)
|
|
13660
13839
|
});
|
|
13840
|
+
const shouldAttachSlTp = input.executionContext.tpSlEnabled === true && classified.reduceOnly !== true && (classified.intent === "open" || classified.intent === "add" || classified.intent === "flip");
|
|
13841
|
+
if (shouldAttachSlTp) {
|
|
13842
|
+
const isLong = classified.side === "buy";
|
|
13843
|
+
const exitSide = isLong ? "sell" : "buy";
|
|
13844
|
+
const positionNotional = Math.abs(executionPrice * sizeAsset);
|
|
13845
|
+
const slPct = input.executionContext.stopLossEnabled === false ? 0 : resolveRiskPct(
|
|
13846
|
+
input.executionContext.stopLossMode,
|
|
13847
|
+
input.executionContext.stopLossPct,
|
|
13848
|
+
input.executionContext.stopLossUsdc,
|
|
13849
|
+
positionNotional
|
|
13850
|
+
);
|
|
13851
|
+
const tpPct = input.executionContext.takeProfitEnabled === false ? 0 : resolveRiskPct(
|
|
13852
|
+
input.executionContext.takeProfitMode,
|
|
13853
|
+
input.executionContext.takeProfitPct,
|
|
13854
|
+
input.executionContext.takeProfitUsdc,
|
|
13855
|
+
positionNotional
|
|
13856
|
+
);
|
|
13857
|
+
const slPrice = buildTriggerPrice(executionPrice, slPct, isLong, false);
|
|
13858
|
+
const tpPrice = buildTriggerPrice(executionPrice, tpPct, isLong, true);
|
|
13859
|
+
const triggerInputs = [
|
|
13860
|
+
{ label: "AUTO_SL", triggerPrice: slPrice, isTakeProfit: false },
|
|
13861
|
+
{ label: "AUTO_TP", triggerPrice: tpPrice, isTakeProfit: true }
|
|
13862
|
+
];
|
|
13863
|
+
for (const triggerInput of triggerInputs) {
|
|
13864
|
+
if (triggerInput.triggerPrice <= 0) {
|
|
13865
|
+
continue;
|
|
13866
|
+
}
|
|
13867
|
+
try {
|
|
13868
|
+
throwIfAborted2(input.signal);
|
|
13869
|
+
const triggerResponse = await executeClientTriggerOrder({
|
|
13870
|
+
profileId: input.profileId,
|
|
13871
|
+
walletAddress: input.executionContext.walletAddress,
|
|
13872
|
+
config: input.config ?? null,
|
|
13873
|
+
signal: input.signal,
|
|
13874
|
+
signingKey: input.signingKey,
|
|
13875
|
+
symbol: input.executionContext.symbol,
|
|
13876
|
+
side: exitSide,
|
|
13877
|
+
size: sizeAsset,
|
|
13878
|
+
triggerPrice: triggerInput.triggerPrice,
|
|
13879
|
+
reduceOnly: true,
|
|
13880
|
+
isTakeProfit: triggerInput.isTakeProfit
|
|
13881
|
+
});
|
|
13882
|
+
executionReports.push({
|
|
13883
|
+
order_id: _extractOrderId(triggerResponse),
|
|
13884
|
+
symbol: input.executionContext.symbol,
|
|
13885
|
+
action: triggerInput.label,
|
|
13886
|
+
status: "executed",
|
|
13887
|
+
execution_metadata: {
|
|
13888
|
+
trigger_price: triggerInput.triggerPrice,
|
|
13889
|
+
is_take_profit: triggerInput.isTakeProfit,
|
|
13890
|
+
..._toExecutionMetadata(triggerResponse)
|
|
13891
|
+
}
|
|
13892
|
+
});
|
|
13893
|
+
appendRuntimeDebugEvent({
|
|
13894
|
+
label: "execution_auto_sl_tp_submitted",
|
|
13895
|
+
note: `symbol=${input.executionContext.symbol} kind=${triggerInput.isTakeProfit ? "tp" : "sl"} side=${exitSide} sizeAsset=${sizeAsset} triggerPrice=${triggerInput.triggerPrice} order_id=${_extractOrderId(triggerResponse) || "none"}`
|
|
13896
|
+
});
|
|
13897
|
+
} catch (triggerError) {
|
|
13898
|
+
const message = triggerError instanceof Error ? triggerError.message : String(triggerError);
|
|
13899
|
+
executionReports.push({
|
|
13900
|
+
order_id: null,
|
|
13901
|
+
symbol: input.executionContext.symbol,
|
|
13902
|
+
action: triggerInput.label,
|
|
13903
|
+
status: "failed",
|
|
13904
|
+
execution_metadata: {
|
|
13905
|
+
trigger_price: triggerInput.triggerPrice,
|
|
13906
|
+
is_take_profit: triggerInput.isTakeProfit,
|
|
13907
|
+
error: message
|
|
13908
|
+
}
|
|
13909
|
+
});
|
|
13910
|
+
appendRuntimeDebugEvent({
|
|
13911
|
+
label: "execution_auto_sl_tp_failed",
|
|
13912
|
+
note: `symbol=${input.executionContext.symbol} kind=${triggerInput.isTakeProfit ? "tp" : "sl"} error=${message}`,
|
|
13913
|
+
outcome: "error"
|
|
13914
|
+
});
|
|
13915
|
+
}
|
|
13916
|
+
}
|
|
13917
|
+
}
|
|
13661
13918
|
appendRuntimeDebugEvent({
|
|
13662
13919
|
label: "execution_order_submitted",
|
|
13663
13920
|
note: `symbol=${input.executionContext.symbol} side=${classified.side} sizeAsset=${sizeAsset} reduceOnly=${String(classified.reduceOnly)} order_id=${_extractOrderId(marketOrderResponse) || "none"}`
|
|
@@ -13763,9 +14020,11 @@ function extractDecisionPayload(content, symbol) {
|
|
|
13763
14020
|
const decisionSource = scoped ?? parsed;
|
|
13764
14021
|
const decision = normalizeDecision(decisionSource.decision);
|
|
13765
14022
|
const units = decision === "HOLD" ? 0 : Math.max(0, Math.trunc(Number(decisionSource.units) || 0));
|
|
14023
|
+
const parsedTradability = Number(decisionSource.tradability);
|
|
14024
|
+
const tradability = Number.isInteger(parsedTradability) && parsedTradability >= 1 && parsedTradability <= 10 ? parsedTradability : null;
|
|
13766
14025
|
const fallbackReasoning = content.trim() || "Headless runtime produced no reasoning.";
|
|
13767
14026
|
const reasoning = optionalText(decisionSource.reasoning) ?? optionalText(decisionSource.analysis) ?? fallbackReasoning;
|
|
13768
|
-
return { decision, units, reasoning };
|
|
14027
|
+
return { decision, units, tradability, reasoning };
|
|
13769
14028
|
}
|
|
13770
14029
|
function buildClientExchangeSnapshot(input) {
|
|
13771
14030
|
const accountSummary = input.accountState.accountSummary;
|
|
@@ -13774,6 +14033,7 @@ function buildClientExchangeSnapshot(input) {
|
|
|
13774
14033
|
captured_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
13775
14034
|
wallet_address: input.walletAddress,
|
|
13776
14035
|
symbol: input.symbol,
|
|
14036
|
+
mark_price: input.accountState.availableToTrade.mark_price ?? null,
|
|
13777
14037
|
account_summary: {
|
|
13778
14038
|
account_value: accountSummary.account_value ?? null,
|
|
13779
14039
|
total_margin_used: accountSummary.total_margin_used ?? null,
|
|
@@ -13848,7 +14108,7 @@ async function fetchLocalHyperliquidSnapshot(input) {
|
|
|
13848
14108
|
const executionConfig = buildHeadlessExchangeConfig(input.llmConfig);
|
|
13849
14109
|
const promptKnobs = objectOrNull2(input.metadata.prompt_knobs) ?? {};
|
|
13850
14110
|
const leverage = requirePositiveNumber2(promptKnobs.leverage ?? input.llmConfig.default_leverage, "leverage");
|
|
13851
|
-
const accountState = await
|
|
14111
|
+
const accountState = await input.fetchAccountState({
|
|
13852
14112
|
config: executionConfig,
|
|
13853
14113
|
walletAddress,
|
|
13854
14114
|
symbol: input.symbol,
|
|
@@ -13974,6 +14234,7 @@ function resolveProviderInvocation(input) {
|
|
|
13974
14234
|
}
|
|
13975
14235
|
function createHeadlessLocalWorker(options) {
|
|
13976
14236
|
const fetchImpl = options.fetchImpl ?? fetch;
|
|
14237
|
+
const fetchAccountState = options.fetchAccountState ?? fetchBrowserAccountState;
|
|
13977
14238
|
return async function runHeadlessLocalWorker(input) {
|
|
13978
14239
|
if (!input.runtimeState.leaseToken) {
|
|
13979
14240
|
throw new Error("Headless runtime worker requires an active runtime lease.");
|
|
@@ -14005,7 +14266,31 @@ function createHeadlessLocalWorker(options) {
|
|
|
14005
14266
|
const secrets = objectOrNull2(input.secrets) ?? {};
|
|
14006
14267
|
const decisionSnapshotId = `headless-decision-${Date.now()}`;
|
|
14007
14268
|
const decisionSnapshotCapturedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
14008
|
-
const
|
|
14269
|
+
const promptKnobs = objectOrNull2(metadata.prompt_knobs) ?? {};
|
|
14270
|
+
const tradabilityEnabled = promptKnobs.tradability_enabled === true;
|
|
14271
|
+
const tradabilityOpenEnabled = promptKnobs.tradability_open_enabled === true;
|
|
14272
|
+
const tradabilityIncreaseEnabled = promptKnobs.tradability_increase_enabled === true;
|
|
14273
|
+
const tradabilityMinimumBound = tradabilityEnabled ? requireFiniteNumber(llmConfig.tradability_threshold_min, "Tradability minimum bound") : 0;
|
|
14274
|
+
const tradabilityMaximumBound = tradabilityEnabled ? requireFiniteNumber(llmConfig.tradability_threshold_max, "Tradability maximum bound") : 0;
|
|
14275
|
+
const resolveTradabilityMinimum = (value, label) => {
|
|
14276
|
+
if (!tradabilityEnabled) return 0;
|
|
14277
|
+
const parsed = requireFiniteNumber(value, label);
|
|
14278
|
+
if (!Number.isInteger(parsed) || parsed < tradabilityMinimumBound || parsed > tradabilityMaximumBound) {
|
|
14279
|
+
throw new Error(
|
|
14280
|
+
`Invalid headless runtime ${label}: expected an integer from ${tradabilityMinimumBound} to ${tradabilityMaximumBound}.`
|
|
14281
|
+
);
|
|
14282
|
+
}
|
|
14283
|
+
return parsed;
|
|
14284
|
+
};
|
|
14285
|
+
const tradabilityOpenMinimum = resolveTradabilityMinimum(
|
|
14286
|
+
promptKnobs.tradability_open_minimum,
|
|
14287
|
+
"Open Position Tradability minimum"
|
|
14288
|
+
);
|
|
14289
|
+
const tradabilityIncreaseMinimum = resolveTradabilityMinimum(
|
|
14290
|
+
promptKnobs.tradability_increase_minimum,
|
|
14291
|
+
"Increase Position Tradability minimum"
|
|
14292
|
+
);
|
|
14293
|
+
const candidateHyperliquidContext = providerDecision.decision === "HOLD" || providerDecision.units <= 0 ? null : await fetchLocalHyperliquidSnapshot({
|
|
14009
14294
|
client: options.client,
|
|
14010
14295
|
profileId: input.profileId,
|
|
14011
14296
|
llmConfig,
|
|
@@ -14014,8 +14299,36 @@ function createHeadlessLocalWorker(options) {
|
|
|
14014
14299
|
symbol,
|
|
14015
14300
|
timeframe,
|
|
14016
14301
|
decisionSnapshotId,
|
|
14017
|
-
decisionSnapshotCapturedAt
|
|
14302
|
+
decisionSnapshotCapturedAt,
|
|
14303
|
+
fetchAccountState
|
|
14018
14304
|
});
|
|
14305
|
+
const classifiedIntent = candidateHyperliquidContext ? classifyExecutionIntent(
|
|
14306
|
+
providerDecision.decision,
|
|
14307
|
+
providerDecision.units,
|
|
14308
|
+
signedUnitsFromPosition(
|
|
14309
|
+
candidateHyperliquidContext.executionContext.currentPositionSize,
|
|
14310
|
+
candidateHyperliquidContext.executionContext.lastPrice,
|
|
14311
|
+
candidateHyperliquidContext.executionContext.unitSizeUsdc
|
|
14312
|
+
)
|
|
14313
|
+
).intent : "hold";
|
|
14314
|
+
const applicableMinimum = classifiedIntent === "open" && tradabilityOpenEnabled ? tradabilityOpenMinimum : classifiedIntent === "add" && tradabilityIncreaseEnabled ? tradabilityIncreaseMinimum : null;
|
|
14315
|
+
const tradabilityRejected = tradabilityEnabled && applicableMinimum !== null && (providerDecision.tradability == null || providerDecision.tradability < applicableMinimum);
|
|
14316
|
+
const localHyperliquidContext = tradabilityRejected ? null : candidateHyperliquidContext;
|
|
14317
|
+
const tradabilityBlockExecution = tradabilityRejected && applicableMinimum !== null ? {
|
|
14318
|
+
order_id: null,
|
|
14319
|
+
symbol,
|
|
14320
|
+
action: providerDecision.decision,
|
|
14321
|
+
status: "blocked",
|
|
14322
|
+
execution_metadata: {
|
|
14323
|
+
reason_code: "tradability_below_minimum",
|
|
14324
|
+
control: "tradability",
|
|
14325
|
+
intent: classifiedIntent,
|
|
14326
|
+
tradability: providerDecision.tradability,
|
|
14327
|
+
minimum: applicableMinimum,
|
|
14328
|
+
decision_snapshot_id: decisionSnapshotId,
|
|
14329
|
+
message: `Tradability ${providerDecision.tradability ?? "missing"} is below the ${classifiedIntent === "open" ? "Open" : "Add"} minimum of ${applicableMinimum}. No order was placed.`
|
|
14330
|
+
}
|
|
14331
|
+
} : null;
|
|
14019
14332
|
const envelope = metadata.envelope ?? {};
|
|
14020
14333
|
const analysisRunId = `headless-${Date.now()}`;
|
|
14021
14334
|
const baseTradeSync = {
|
|
@@ -14038,6 +14351,7 @@ function createHeadlessLocalWorker(options) {
|
|
|
14038
14351
|
policy_generation_id: requireText(envelope.policy_generation_id, "policy generation id"),
|
|
14039
14352
|
decision: providerDecision.decision,
|
|
14040
14353
|
units: providerDecision.units,
|
|
14354
|
+
final_tradability: providerDecision.tradability,
|
|
14041
14355
|
reasoning: providerDecision.reasoning,
|
|
14042
14356
|
model: requireText(prompt.model ?? metadata.model, "model"),
|
|
14043
14357
|
model_source: normalizeModelSource(metadata.model_source),
|
|
@@ -14056,7 +14370,11 @@ function createHeadlessLocalWorker(options) {
|
|
|
14056
14370
|
is_analyzing: false,
|
|
14057
14371
|
is_executing: Boolean(localHyperliquidContext)
|
|
14058
14372
|
},
|
|
14059
|
-
tradeSync: localHyperliquidContext ? null : {
|
|
14373
|
+
tradeSync: localHyperliquidContext ? null : {
|
|
14374
|
+
...baseTradeSync,
|
|
14375
|
+
execution_snapshot_id: tradabilityBlockExecution ? decisionSnapshotId : void 0,
|
|
14376
|
+
executions: tradabilityBlockExecution ? [tradabilityBlockExecution] : []
|
|
14377
|
+
},
|
|
14060
14378
|
afterDecision: localHyperliquidContext ? async () => {
|
|
14061
14379
|
const executionResult = await executeClientRuntimeDecision({
|
|
14062
14380
|
profileId: String(input.profileId),
|
|
@@ -14085,6 +14403,7 @@ var init_headless_local_worker = __esm({
|
|
|
14085
14403
|
init_hyperliquid_account_state_adapter();
|
|
14086
14404
|
init_hyperliquid_market_symbol();
|
|
14087
14405
|
init_runtime_execution();
|
|
14406
|
+
init_execution_guard_contract();
|
|
14088
14407
|
OPENAI_COMPATIBLE_SOURCE_KEY = {
|
|
14089
14408
|
openai: "openai",
|
|
14090
14409
|
groq: "groq",
|
|
@@ -14110,6 +14429,7 @@ var init_headless_local_worker = __esm({
|
|
|
14110
14429
|
// lib/agent-cli/vtx.ts
|
|
14111
14430
|
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
14112
14431
|
import { spawn } from "node:child_process";
|
|
14432
|
+
import { hostname } from "node:os";
|
|
14113
14433
|
|
|
14114
14434
|
// lib/agent-core/config.ts
|
|
14115
14435
|
import { mkdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
@@ -14448,6 +14768,33 @@ var VtxAgentClient = class _VtxAgentClient {
|
|
|
14448
14768
|
getLlmConfig(profileId) {
|
|
14449
14769
|
return this.request("/llm/config", { profileId });
|
|
14450
14770
|
}
|
|
14771
|
+
registerInsightConnector(profileId, payload) {
|
|
14772
|
+
return this.request("/insights/connectors/register", {
|
|
14773
|
+
method: "POST",
|
|
14774
|
+
profileId,
|
|
14775
|
+
body: payload
|
|
14776
|
+
});
|
|
14777
|
+
}
|
|
14778
|
+
claimInsightConnectorJob(profileId, connectorId, deviceId) {
|
|
14779
|
+
return this.request(
|
|
14780
|
+
`/insights/connectors/${encodeURIComponent(connectorId)}/jobs:claim`,
|
|
14781
|
+
{ method: "POST", profileId, body: { device_id: deviceId } }
|
|
14782
|
+
);
|
|
14783
|
+
}
|
|
14784
|
+
completeInsightConnectorJob(profileId, connectorId, jobId, deviceId, payload) {
|
|
14785
|
+
const params = new URLSearchParams({ device_id: deviceId });
|
|
14786
|
+
return this.request(
|
|
14787
|
+
`/insights/connectors/${encodeURIComponent(connectorId)}/jobs/${encodeURIComponent(jobId)}:complete?${params}`,
|
|
14788
|
+
{ method: "POST", profileId, body: payload }
|
|
14789
|
+
);
|
|
14790
|
+
}
|
|
14791
|
+
heartbeatInsightConnectorJob(profileId, connectorId, jobId, deviceId, claimToken) {
|
|
14792
|
+
const params = new URLSearchParams({ device_id: deviceId });
|
|
14793
|
+
return this.request(
|
|
14794
|
+
`/insights/connectors/${encodeURIComponent(connectorId)}/jobs/${encodeURIComponent(jobId)}:heartbeat?${params}`,
|
|
14795
|
+
{ method: "POST", profileId, body: { claim_token: claimToken } }
|
|
14796
|
+
);
|
|
14797
|
+
}
|
|
14451
14798
|
heartbeatRuntime(profileId, payload, leaseToken) {
|
|
14452
14799
|
return this.request("/trading/ai/runtime/heartbeat", {
|
|
14453
14800
|
method: "POST",
|
|
@@ -14710,7 +15057,8 @@ async function startHeadlessRuntime(options) {
|
|
|
14710
15057
|
session_id: runtimeSessionId,
|
|
14711
15058
|
device_id: deviceId,
|
|
14712
15059
|
mode: "trader",
|
|
14713
|
-
force_takeover: Boolean(options.forceTakeover)
|
|
15060
|
+
force_takeover: Boolean(options.forceTakeover),
|
|
15061
|
+
takeover_initiator: options.forceTakeover ? "agent_forced" : void 0
|
|
14714
15062
|
});
|
|
14715
15063
|
if (startResponse.status === "takeover_required") {
|
|
14716
15064
|
throw new HeadlessRuntimeTakeoverRequiredError(startResponse);
|
|
@@ -14805,6 +15153,277 @@ async function startHeadlessRuntime(options) {
|
|
|
14805
15153
|
}
|
|
14806
15154
|
}
|
|
14807
15155
|
|
|
15156
|
+
// lib/agent-core/insights-local-connector.ts
|
|
15157
|
+
var delay = (milliseconds, signal) => new Promise((resolve, reject) => {
|
|
15158
|
+
if (signal?.aborted) {
|
|
15159
|
+
reject(signal?.reason ?? new Error("Local Insights connector stopped"));
|
|
15160
|
+
return;
|
|
15161
|
+
}
|
|
15162
|
+
const onAbort = () => {
|
|
15163
|
+
clearTimeout(timer);
|
|
15164
|
+
reject(signal?.reason ?? new Error("Local Insights connector stopped"));
|
|
15165
|
+
};
|
|
15166
|
+
const timer = setTimeout(() => {
|
|
15167
|
+
signal?.removeEventListener("abort", onAbort);
|
|
15168
|
+
resolve();
|
|
15169
|
+
}, milliseconds);
|
|
15170
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
15171
|
+
});
|
|
15172
|
+
function normalizeLocalModelBaseUrl(value) {
|
|
15173
|
+
const raw = String(value || "http://127.0.0.1:1234/v1").trim().replace(/\/+$/, "");
|
|
15174
|
+
const parsed = new URL(raw);
|
|
15175
|
+
const hostname2 = parsed.hostname.toLowerCase();
|
|
15176
|
+
if (!["localhost", "127.0.0.1", "::1", "[::1]"].includes(hostname2)) {
|
|
15177
|
+
throw new Error("The Local Insights connector only accepts a loopback model URL");
|
|
15178
|
+
}
|
|
15179
|
+
if (!["http:", "https:"].includes(parsed.protocol)) {
|
|
15180
|
+
throw new Error("The local model URL must use HTTP or HTTPS");
|
|
15181
|
+
}
|
|
15182
|
+
if (parsed.username || parsed.password || parsed.search || parsed.hash) {
|
|
15183
|
+
throw new Error("The local model URL cannot contain credentials, a query, or a fragment");
|
|
15184
|
+
}
|
|
15185
|
+
return raw;
|
|
15186
|
+
}
|
|
15187
|
+
function authHeaders(apiKey) {
|
|
15188
|
+
const value = String(apiKey || "").trim();
|
|
15189
|
+
return value ? { Authorization: `Bearer ${value}` } : {};
|
|
15190
|
+
}
|
|
15191
|
+
function isRetryableVtxTransportError(error) {
|
|
15192
|
+
return !(error instanceof AgentClientError) || error.status === 429 || error.status >= 500;
|
|
15193
|
+
}
|
|
15194
|
+
async function withVtxTransportRetry(operation, retryMilliseconds, signal) {
|
|
15195
|
+
while (true) {
|
|
15196
|
+
try {
|
|
15197
|
+
return await operation();
|
|
15198
|
+
} catch (error) {
|
|
15199
|
+
if (!isRetryableVtxTransportError(error)) throw error;
|
|
15200
|
+
await delay(retryMilliseconds, signal);
|
|
15201
|
+
}
|
|
15202
|
+
}
|
|
15203
|
+
}
|
|
15204
|
+
function modelArray(payload) {
|
|
15205
|
+
if (Array.isArray(payload)) return payload.filter((item) => Boolean(item && typeof item === "object"));
|
|
15206
|
+
if (!payload || typeof payload !== "object") return [];
|
|
15207
|
+
const record = payload;
|
|
15208
|
+
for (const key of ["data", "models"]) {
|
|
15209
|
+
if (Array.isArray(record[key])) {
|
|
15210
|
+
return record[key].filter((item) => Boolean(item && typeof item === "object"));
|
|
15211
|
+
}
|
|
15212
|
+
}
|
|
15213
|
+
return [];
|
|
15214
|
+
}
|
|
15215
|
+
function positiveInteger(value) {
|
|
15216
|
+
const parsed = Number(value);
|
|
15217
|
+
return Number.isInteger(parsed) && parsed > 0 ? parsed : null;
|
|
15218
|
+
}
|
|
15219
|
+
function connectorModels(payload) {
|
|
15220
|
+
const models = modelArray(payload).flatMap((item) => {
|
|
15221
|
+
const modelType = String(item.type || "").trim().toLowerCase();
|
|
15222
|
+
if (modelType && !["llm", "vlm"].includes(modelType)) return [];
|
|
15223
|
+
const modelState = String(item.state || "").trim().toLowerCase();
|
|
15224
|
+
if (modelState && modelState !== "loaded") return [];
|
|
15225
|
+
const id2 = String(item.id || item.model || item.key || "").trim();
|
|
15226
|
+
if (!id2) return [];
|
|
15227
|
+
const loadedInstances = Array.isArray(item.loaded_instances) ? item.loaded_instances.filter((instance) => Boolean(instance && typeof instance === "object")) : [];
|
|
15228
|
+
const loadedConfig = loadedInstances[0]?.config;
|
|
15229
|
+
const loadedContextLength = loadedConfig && typeof loadedConfig === "object" ? loadedConfig.context_length : null;
|
|
15230
|
+
return [{
|
|
15231
|
+
id: id2,
|
|
15232
|
+
name: String(item.display_name || item.name || item.id || id2).trim() || id2,
|
|
15233
|
+
context_length: positiveInteger(
|
|
15234
|
+
item.loaded_context_length ?? loadedContextLength ?? item.max_context_length ?? item.context_length ?? item.context_window
|
|
15235
|
+
)
|
|
15236
|
+
}];
|
|
15237
|
+
});
|
|
15238
|
+
return [...new Map(models.map((model) => [model.id, model])).values()].sort((left, right) => left.id.localeCompare(right.id));
|
|
15239
|
+
}
|
|
15240
|
+
async function fetchJson(fetchImpl, url, init, failureMessage) {
|
|
15241
|
+
const response = await fetchImpl(url, { ...init, redirect: "error" });
|
|
15242
|
+
if (!response.ok) throw new Error(`${failureMessage} (${response.status})`);
|
|
15243
|
+
const payload = await response.json();
|
|
15244
|
+
if (!payload || typeof payload !== "object" || Array.isArray(payload)) {
|
|
15245
|
+
throw new Error(`${failureMessage} (invalid JSON response)`);
|
|
15246
|
+
}
|
|
15247
|
+
return payload;
|
|
15248
|
+
}
|
|
15249
|
+
async function discoverLocalConnectorModels(options = {}) {
|
|
15250
|
+
const baseUrl = normalizeLocalModelBaseUrl(options.baseUrl);
|
|
15251
|
+
const fetchImpl = options.fetchImpl ?? fetch;
|
|
15252
|
+
const headers = { Accept: "application/json", ...authHeaders(options.apiKey) };
|
|
15253
|
+
const parsed = new URL(baseUrl);
|
|
15254
|
+
const candidates = [
|
|
15255
|
+
`${parsed.origin}/api/v0/models`,
|
|
15256
|
+
`${baseUrl}/models`
|
|
15257
|
+
];
|
|
15258
|
+
let lastError = null;
|
|
15259
|
+
for (const url of candidates) {
|
|
15260
|
+
try {
|
|
15261
|
+
const payload = await fetchJson(fetchImpl, url, { headers }, "Unable to list local models");
|
|
15262
|
+
const models = connectorModels(payload);
|
|
15263
|
+
if (models.length > 0) return models;
|
|
15264
|
+
} catch (error) {
|
|
15265
|
+
lastError = error;
|
|
15266
|
+
}
|
|
15267
|
+
}
|
|
15268
|
+
throw lastError instanceof Error ? lastError : new Error("The local model server returned no models");
|
|
15269
|
+
}
|
|
15270
|
+
async function completeFailedJob(options, connector, job, errorClass) {
|
|
15271
|
+
try {
|
|
15272
|
+
await withVtxTransportRetry(
|
|
15273
|
+
() => options.client.completeInsightConnectorJob(
|
|
15274
|
+
options.profileId,
|
|
15275
|
+
connector.id,
|
|
15276
|
+
job.id,
|
|
15277
|
+
options.deviceId,
|
|
15278
|
+
{
|
|
15279
|
+
claim_token: job.claim_token,
|
|
15280
|
+
status: "failed",
|
|
15281
|
+
error_class: errorClass.slice(0, 64)
|
|
15282
|
+
}
|
|
15283
|
+
),
|
|
15284
|
+
connector.poll_interval_milliseconds,
|
|
15285
|
+
options.signal
|
|
15286
|
+
);
|
|
15287
|
+
return true;
|
|
15288
|
+
} catch (error) {
|
|
15289
|
+
if (error instanceof AgentClientError && error.status === 409) return false;
|
|
15290
|
+
throw error;
|
|
15291
|
+
}
|
|
15292
|
+
}
|
|
15293
|
+
async function runJob(options, connector, job, baseUrl, fetchImpl) {
|
|
15294
|
+
const requestController = new AbortController();
|
|
15295
|
+
const abortFromParent = () => requestController.abort(
|
|
15296
|
+
options.signal?.reason ?? new Error("Local Insights connector stopped")
|
|
15297
|
+
);
|
|
15298
|
+
options.signal?.addEventListener("abort", abortFromParent, { once: true });
|
|
15299
|
+
let heartbeatError = null;
|
|
15300
|
+
let finished = false;
|
|
15301
|
+
const heartbeatInterval = options.heartbeatIntervalMilliseconds ?? Math.max(1e3, connector.poll_interval_milliseconds * 10);
|
|
15302
|
+
const heartbeatTask = (async () => {
|
|
15303
|
+
try {
|
|
15304
|
+
while (!finished && !requestController.signal.aborted) {
|
|
15305
|
+
await delay(heartbeatInterval, requestController.signal);
|
|
15306
|
+
if (finished) return;
|
|
15307
|
+
try {
|
|
15308
|
+
await options.client.heartbeatInsightConnectorJob(
|
|
15309
|
+
options.profileId,
|
|
15310
|
+
connector.id,
|
|
15311
|
+
job.id,
|
|
15312
|
+
options.deviceId,
|
|
15313
|
+
job.claim_token
|
|
15314
|
+
);
|
|
15315
|
+
} catch (error) {
|
|
15316
|
+
if (isRetryableVtxTransportError(error)) continue;
|
|
15317
|
+
throw error;
|
|
15318
|
+
}
|
|
15319
|
+
}
|
|
15320
|
+
} catch (error) {
|
|
15321
|
+
if (!finished && !options.signal?.aborted) {
|
|
15322
|
+
heartbeatError = error;
|
|
15323
|
+
requestController.abort(error);
|
|
15324
|
+
}
|
|
15325
|
+
}
|
|
15326
|
+
})();
|
|
15327
|
+
let response;
|
|
15328
|
+
try {
|
|
15329
|
+
try {
|
|
15330
|
+
response = await fetchJson(fetchImpl, `${baseUrl}/chat/completions`, {
|
|
15331
|
+
method: "POST",
|
|
15332
|
+
headers: {
|
|
15333
|
+
Accept: "application/json",
|
|
15334
|
+
"Content-Type": "application/json",
|
|
15335
|
+
...authHeaders(options.apiKey)
|
|
15336
|
+
},
|
|
15337
|
+
body: JSON.stringify({ ...job.request, model: job.model_id, stream: false }),
|
|
15338
|
+
signal: requestController.signal
|
|
15339
|
+
}, "The local model request failed");
|
|
15340
|
+
} catch (error) {
|
|
15341
|
+
if (options.signal?.aborted) throw error;
|
|
15342
|
+
if (heartbeatError instanceof AgentClientError && heartbeatError.status === 409) {
|
|
15343
|
+
return false;
|
|
15344
|
+
}
|
|
15345
|
+
if (heartbeatError) throw heartbeatError;
|
|
15346
|
+
const completed = await completeFailedJob(
|
|
15347
|
+
options,
|
|
15348
|
+
connector,
|
|
15349
|
+
job,
|
|
15350
|
+
"local_model_request_failed"
|
|
15351
|
+
);
|
|
15352
|
+
return completed;
|
|
15353
|
+
}
|
|
15354
|
+
if (heartbeatError instanceof AgentClientError && heartbeatError.status === 409) {
|
|
15355
|
+
return false;
|
|
15356
|
+
}
|
|
15357
|
+
if (heartbeatError) throw heartbeatError;
|
|
15358
|
+
try {
|
|
15359
|
+
await withVtxTransportRetry(
|
|
15360
|
+
() => options.client.completeInsightConnectorJob(
|
|
15361
|
+
options.profileId,
|
|
15362
|
+
connector.id,
|
|
15363
|
+
job.id,
|
|
15364
|
+
options.deviceId,
|
|
15365
|
+
{
|
|
15366
|
+
claim_token: job.claim_token,
|
|
15367
|
+
status: "succeeded",
|
|
15368
|
+
response
|
|
15369
|
+
}
|
|
15370
|
+
),
|
|
15371
|
+
connector.poll_interval_milliseconds,
|
|
15372
|
+
options.signal
|
|
15373
|
+
);
|
|
15374
|
+
return true;
|
|
15375
|
+
} catch (error) {
|
|
15376
|
+
if (error instanceof AgentClientError && error.status === 409) return false;
|
|
15377
|
+
throw error;
|
|
15378
|
+
}
|
|
15379
|
+
} finally {
|
|
15380
|
+
finished = true;
|
|
15381
|
+
requestController.abort(new Error("Local model job finished"));
|
|
15382
|
+
options.signal?.removeEventListener("abort", abortFromParent);
|
|
15383
|
+
await heartbeatTask;
|
|
15384
|
+
}
|
|
15385
|
+
}
|
|
15386
|
+
async function runLocalInsightsConnector(options) {
|
|
15387
|
+
const baseUrl = normalizeLocalModelBaseUrl(options.baseUrl);
|
|
15388
|
+
const fetchImpl = options.fetchImpl ?? fetch;
|
|
15389
|
+
const models = await discoverLocalConnectorModels({
|
|
15390
|
+
baseUrl,
|
|
15391
|
+
apiKey: options.apiKey,
|
|
15392
|
+
fetchImpl
|
|
15393
|
+
});
|
|
15394
|
+
const connector = await options.client.registerInsightConnector(options.profileId, {
|
|
15395
|
+
device_id: options.deviceId,
|
|
15396
|
+
display_name: options.displayName,
|
|
15397
|
+
models
|
|
15398
|
+
});
|
|
15399
|
+
options.onRegistered?.(connector);
|
|
15400
|
+
let jobsCompleted = 0;
|
|
15401
|
+
do {
|
|
15402
|
+
let job;
|
|
15403
|
+
try {
|
|
15404
|
+
job = await options.client.claimInsightConnectorJob(
|
|
15405
|
+
options.profileId,
|
|
15406
|
+
connector.id,
|
|
15407
|
+
options.deviceId
|
|
15408
|
+
);
|
|
15409
|
+
} catch (error) {
|
|
15410
|
+
if (!isRetryableVtxTransportError(error)) throw error;
|
|
15411
|
+
await delay(connector.poll_interval_milliseconds, options.signal);
|
|
15412
|
+
continue;
|
|
15413
|
+
}
|
|
15414
|
+
if (job) {
|
|
15415
|
+
if (await runJob(options, connector, job, baseUrl, fetchImpl)) {
|
|
15416
|
+
jobsCompleted += 1;
|
|
15417
|
+
}
|
|
15418
|
+
if (options.once) break;
|
|
15419
|
+
continue;
|
|
15420
|
+
}
|
|
15421
|
+
if (options.once) break;
|
|
15422
|
+
await delay(connector.poll_interval_milliseconds, options.signal);
|
|
15423
|
+
} while (!options.signal?.aborted);
|
|
15424
|
+
return { connector, jobsCompleted };
|
|
15425
|
+
}
|
|
15426
|
+
|
|
14808
15427
|
// lib/agent-cli/vtx.ts
|
|
14809
15428
|
init_hyperliquid_market_symbol();
|
|
14810
15429
|
var WRITABLE_SECRET_FIELDS = /* @__PURE__ */ new Set([
|
|
@@ -14852,7 +15471,7 @@ function redactCliOutput(value) {
|
|
|
14852
15471
|
}
|
|
14853
15472
|
return result2;
|
|
14854
15473
|
}
|
|
14855
|
-
var
|
|
15474
|
+
var delay2 = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
14856
15475
|
function openBrowser(url) {
|
|
14857
15476
|
const platform = process.platform;
|
|
14858
15477
|
const command = platform === "darwin" ? "open" : platform === "win32" ? "cmd" : "xdg-open";
|
|
@@ -15084,11 +15703,11 @@ Waiting for approval...
|
|
|
15084
15703
|
if (polled.status === "denied") {
|
|
15085
15704
|
throw new Error(polled.error || "CLI login was denied");
|
|
15086
15705
|
}
|
|
15087
|
-
await
|
|
15706
|
+
await delay2(Math.max(1, Number(started.interval || 2)) * 1e3);
|
|
15088
15707
|
}
|
|
15089
15708
|
throw new Error("Timed out waiting for CLI login approval");
|
|
15090
15709
|
}
|
|
15091
|
-
async function runVtxCli(argv, env = process.env) {
|
|
15710
|
+
async function runVtxCli(argv, env = process.env, onProgress = () => void 0) {
|
|
15092
15711
|
try {
|
|
15093
15712
|
const parsed = parseFlags(argv);
|
|
15094
15713
|
const [group, command, ...args] = parsed.rest;
|
|
@@ -15243,7 +15862,8 @@ async function runVtxCli(argv, env = process.env) {
|
|
|
15243
15862
|
session_id: runtimeSessionId,
|
|
15244
15863
|
device_id: deviceId,
|
|
15245
15864
|
mode: "trader",
|
|
15246
|
-
force_takeover: args.includes("--force-takeover")
|
|
15865
|
+
force_takeover: args.includes("--force-takeover"),
|
|
15866
|
+
takeover_initiator: args.includes("--force-takeover") ? "agent_forced" : void 0
|
|
15247
15867
|
});
|
|
15248
15868
|
if (objectOrNull3(response)?.status === "takeover_required") {
|
|
15249
15869
|
await clearRuntimeState(config.statePath);
|
|
@@ -15329,6 +15949,52 @@ async function runVtxCli(argv, env = process.env) {
|
|
|
15329
15949
|
if (group === "ai" && command === "config") {
|
|
15330
15950
|
return { exitCode: 0, stdout: render(await client.getLlmConfig(parsed.profileId ?? config.activeProfileId), json), stderr: "" };
|
|
15331
15951
|
}
|
|
15952
|
+
if (group === "insights" && command === "connect") {
|
|
15953
|
+
const profileId = requireProfileId(parsed.profileId, config.activeProfileId);
|
|
15954
|
+
const hostLabel = hostname().replace(/[^A-Za-z0-9._-]+/g, "-").slice(0, 96) || "local-device";
|
|
15955
|
+
const deviceId = optionalValue(args, "--device-id") || config.runtimeDeviceId || `insights-${hostLabel}`;
|
|
15956
|
+
const displayName = optionalValue(args, "--name") || `LM Studio on ${hostLabel}`;
|
|
15957
|
+
const apiKeyEnvironment = optionalValue(args, "--api-key-env") || "LM_STUDIO_API_KEY";
|
|
15958
|
+
const apiKey = String(env[apiKeyEnvironment] || "").trim() || null;
|
|
15959
|
+
const abortController = new AbortController();
|
|
15960
|
+
const stop = () => abortController.abort(new Error("Local Insights connector stopped"));
|
|
15961
|
+
process.once("SIGINT", stop);
|
|
15962
|
+
process.once("SIGTERM", stop);
|
|
15963
|
+
try {
|
|
15964
|
+
const result2 = await runLocalInsightsConnector({
|
|
15965
|
+
client,
|
|
15966
|
+
profileId,
|
|
15967
|
+
deviceId,
|
|
15968
|
+
displayName,
|
|
15969
|
+
baseUrl: optionalValue(args, "--base-url") || void 0,
|
|
15970
|
+
apiKey,
|
|
15971
|
+
once: args.includes("--once"),
|
|
15972
|
+
signal: abortController.signal,
|
|
15973
|
+
onRegistered: (connector) => onProgress(
|
|
15974
|
+
`Local Insights connector online: ${connector.display_name} (${connector.models.length} models)
|
|
15975
|
+
`
|
|
15976
|
+
)
|
|
15977
|
+
});
|
|
15978
|
+
return {
|
|
15979
|
+
exitCode: 0,
|
|
15980
|
+
stdout: render({
|
|
15981
|
+
status: "stopped",
|
|
15982
|
+
connector_id: result2.connector.id,
|
|
15983
|
+
models: result2.connector.models.map((model) => model.id),
|
|
15984
|
+
jobs_completed: result2.jobsCompleted
|
|
15985
|
+
}, json),
|
|
15986
|
+
stderr: ""
|
|
15987
|
+
};
|
|
15988
|
+
} catch (error) {
|
|
15989
|
+
if (abortController.signal.aborted) {
|
|
15990
|
+
return { exitCode: 0, stdout: render({ status: "stopped" }, json), stderr: "" };
|
|
15991
|
+
}
|
|
15992
|
+
throw error;
|
|
15993
|
+
} finally {
|
|
15994
|
+
process.removeListener("SIGINT", stop);
|
|
15995
|
+
process.removeListener("SIGTERM", stop);
|
|
15996
|
+
}
|
|
15997
|
+
}
|
|
15332
15998
|
throw new Error(`Unsupported command: ${[group, command].filter(Boolean).join(" ") || "(none)"}`);
|
|
15333
15999
|
} catch (error) {
|
|
15334
16000
|
const message = error instanceof AgentClientError ? error.message : error instanceof HeadlessRuntimeTakeoverRequiredError ? error.message : error instanceof Error ? error.message : "Unknown CLI error";
|
|
@@ -15338,7 +16004,11 @@ async function runVtxCli(argv, env = process.env) {
|
|
|
15338
16004
|
}
|
|
15339
16005
|
|
|
15340
16006
|
// bin/vtx.ts
|
|
15341
|
-
var result = await runVtxCli(
|
|
16007
|
+
var result = await runVtxCli(
|
|
16008
|
+
process.argv.slice(2),
|
|
16009
|
+
process.env,
|
|
16010
|
+
(message) => process.stderr.write(message)
|
|
16011
|
+
);
|
|
15342
16012
|
if (result.stdout) {
|
|
15343
16013
|
process.stdout.write(result.stdout);
|
|
15344
16014
|
}
|
package/package.json
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vtxmacro/cli",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2026.6.22",
|
|
4
4
|
"description": "VTX Macro CLI and MCP server for agent-token automation.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "UNLICENSED",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/DataDoesYou/VTX-Macro.git"
|
|
10
|
+
},
|
|
7
11
|
"private": false,
|
|
8
12
|
"bin": {
|
|
9
13
|
"vtx": "bin/vtx.js",
|