@okx_ai/okx-trade-mcp 1.3.2-beta.5 → 1.3.2
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/dist/index.js +200 -97
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/scripts/postinstall.js +78 -1
package/dist/index.js
CHANGED
|
@@ -22,18 +22,21 @@ import {
|
|
|
22
22
|
import { homedir as homedir2 } from "os";
|
|
23
23
|
import { join as join2, dirname } from "path";
|
|
24
24
|
import { createHmac } from "crypto";
|
|
25
|
+
import { spawn, execFile as execFile2 } from "child_process";
|
|
26
|
+
import { homedir as homedir3 } from "os";
|
|
27
|
+
import { join as join3 } from "path";
|
|
25
28
|
import fs from "fs";
|
|
26
29
|
import path from "path";
|
|
27
30
|
import os from "os";
|
|
28
31
|
import { writeFileSync as writeFileSync2, renameSync as renameSync2, unlinkSync as unlinkSync2, mkdirSync as mkdirSync2 } from "fs";
|
|
29
|
-
import { join as
|
|
32
|
+
import { join as join4, resolve, basename, sep } from "path";
|
|
30
33
|
import { randomUUID } from "crypto";
|
|
31
34
|
import yauzl from "yauzl";
|
|
32
|
-
import { join as
|
|
33
|
-
import { homedir as homedir3 } from "os";
|
|
34
|
-
import { readFileSync as readFileSync4, writeFileSync as writeFileSync4, mkdirSync as mkdirSync5, existsSync as existsSync3 } from "fs";
|
|
35
|
-
import { join as join6, dirname as dirname4 } from "path";
|
|
35
|
+
import { join as join6, dirname as dirname3 } from "path";
|
|
36
36
|
import { homedir as homedir4 } from "os";
|
|
37
|
+
import { readFileSync as readFileSync4, writeFileSync as writeFileSync4, mkdirSync as mkdirSync5, existsSync as existsSync3 } from "fs";
|
|
38
|
+
import { join as join7, dirname as dirname4 } from "path";
|
|
39
|
+
import { homedir as homedir5 } from "os";
|
|
37
40
|
|
|
38
41
|
// ../../node_modules/.pnpm/smol-toml@1.6.0/node_modules/smol-toml/dist/error.js
|
|
39
42
|
function getLineColFromPtr(string, ptr) {
|
|
@@ -722,8 +725,8 @@ function parse(toml, { maxDepth = 1e3, integersAsBigInt } = {}) {
|
|
|
722
725
|
|
|
723
726
|
// ../core/dist/index.js
|
|
724
727
|
import { readFileSync as readFileSync5, writeFileSync as writeFileSync5, mkdirSync as mkdirSync6, existsSync as existsSync4 } from "fs";
|
|
725
|
-
import { join as
|
|
726
|
-
import { homedir as
|
|
728
|
+
import { join as join8 } from "path";
|
|
729
|
+
import { homedir as homedir6 } from "os";
|
|
727
730
|
import fs2 from "fs";
|
|
728
731
|
import path2 from "path";
|
|
729
732
|
import os2 from "os";
|
|
@@ -731,6 +734,8 @@ import * as fs3 from "fs";
|
|
|
731
734
|
import * as path3 from "path";
|
|
732
735
|
import * as os3 from "os";
|
|
733
736
|
import { execFileSync } from "child_process";
|
|
737
|
+
import { join as join12 } from "path";
|
|
738
|
+
import { homedir as homedir10 } from "os";
|
|
734
739
|
var EXEC_TIMEOUT_MS = 3e4;
|
|
735
740
|
var ALLOWED_DOMAIN_RE = /^[\w.-]+\.okx\.com$/;
|
|
736
741
|
var PILOT_BIN_DIR = join(homedir(), ".okx", "bin");
|
|
@@ -1042,6 +1047,11 @@ var ConfigError = class extends OkxMcpError {
|
|
|
1042
1047
|
super("ConfigError", message, { suggestion });
|
|
1043
1048
|
}
|
|
1044
1049
|
};
|
|
1050
|
+
var NotLoggedInError = class extends ConfigError {
|
|
1051
|
+
constructor(suggestion = "Run `okx auth login` to authenticate.") {
|
|
1052
|
+
super("Not logged in.", suggestion);
|
|
1053
|
+
}
|
|
1054
|
+
};
|
|
1045
1055
|
var ValidationError = class extends OkxMcpError {
|
|
1046
1056
|
constructor(message, suggestion) {
|
|
1047
1057
|
super("ValidationError", message, { suggestion });
|
|
@@ -1094,6 +1104,97 @@ function toToolErrorPayload(error, fallbackEndpoint) {
|
|
|
1094
1104
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1095
1105
|
};
|
|
1096
1106
|
}
|
|
1107
|
+
var EXIT_CODES = {
|
|
1108
|
+
SUCCESS: 0,
|
|
1109
|
+
UNAUTHORIZED_CALLER: 1,
|
|
1110
|
+
NOT_LOGGED_IN: 2,
|
|
1111
|
+
REFRESH_FAILED: 3
|
|
1112
|
+
};
|
|
1113
|
+
var EXEC_TIMEOUT_MS2 = 5e3;
|
|
1114
|
+
var AUTH_BIN_DIR = join3(homedir3(), ".okx", "bin");
|
|
1115
|
+
function getAuthBinaryPath() {
|
|
1116
|
+
if (process.env.OKX_AUTH_BIN) {
|
|
1117
|
+
return process.env.OKX_AUTH_BIN;
|
|
1118
|
+
}
|
|
1119
|
+
const ext = process.platform === "win32" ? ".exe" : "";
|
|
1120
|
+
return join3(AUTH_BIN_DIR, `okx-auth${ext}`);
|
|
1121
|
+
}
|
|
1122
|
+
function execAuthToken() {
|
|
1123
|
+
const binPath = getAuthBinaryPath();
|
|
1124
|
+
return new Promise((resolve3, reject) => {
|
|
1125
|
+
const child = spawn(binPath, ["token"], {
|
|
1126
|
+
stdio: ["ignore", "ignore", "inherit", "pipe"]
|
|
1127
|
+
// stdin stdout stderr fd3 (pipe)
|
|
1128
|
+
});
|
|
1129
|
+
const chunks = [];
|
|
1130
|
+
const fd3 = child.stdio[3];
|
|
1131
|
+
fd3.on("data", (chunk) => chunks.push(chunk));
|
|
1132
|
+
child.on("error", (err) => {
|
|
1133
|
+
reject(new ConfigError(
|
|
1134
|
+
`Failed to spawn okx-auth: ${err.message}`,
|
|
1135
|
+
"Ensure the okx-auth binary exists and is executable."
|
|
1136
|
+
));
|
|
1137
|
+
});
|
|
1138
|
+
child.on("close", (code) => {
|
|
1139
|
+
if (code === EXIT_CODES.SUCCESS) {
|
|
1140
|
+
const token = Buffer.concat(chunks).toString("utf-8").trim();
|
|
1141
|
+
if (!token) {
|
|
1142
|
+
reject(new AuthenticationError(
|
|
1143
|
+
"okx-auth returned empty token.",
|
|
1144
|
+
"Run `okx auth login` to re-authenticate."
|
|
1145
|
+
));
|
|
1146
|
+
return;
|
|
1147
|
+
}
|
|
1148
|
+
resolve3(token);
|
|
1149
|
+
return;
|
|
1150
|
+
}
|
|
1151
|
+
if (code === EXIT_CODES.NOT_LOGGED_IN) {
|
|
1152
|
+
reject(new NotLoggedInError());
|
|
1153
|
+
return;
|
|
1154
|
+
}
|
|
1155
|
+
if (code === EXIT_CODES.UNAUTHORIZED_CALLER) {
|
|
1156
|
+
reject(new AuthenticationError(
|
|
1157
|
+
"okx-auth rejected the caller (unauthorized).",
|
|
1158
|
+
"Ensure you are running from a trusted OKX tool."
|
|
1159
|
+
));
|
|
1160
|
+
return;
|
|
1161
|
+
}
|
|
1162
|
+
if (code === EXIT_CODES.REFRESH_FAILED) {
|
|
1163
|
+
reject(new AuthenticationError(
|
|
1164
|
+
"Token refresh failed.",
|
|
1165
|
+
"Run `okx auth login` to re-authenticate."
|
|
1166
|
+
));
|
|
1167
|
+
return;
|
|
1168
|
+
}
|
|
1169
|
+
reject(new AuthenticationError(
|
|
1170
|
+
`okx-auth token exited with code ${code}.`,
|
|
1171
|
+
"Run `okx auth login` to re-authenticate."
|
|
1172
|
+
));
|
|
1173
|
+
});
|
|
1174
|
+
});
|
|
1175
|
+
}
|
|
1176
|
+
function execAuthStatus() {
|
|
1177
|
+
const binPath = getAuthBinaryPath();
|
|
1178
|
+
return new Promise((resolve3) => {
|
|
1179
|
+
execFile2(
|
|
1180
|
+
binPath,
|
|
1181
|
+
["status", "--json"],
|
|
1182
|
+
{ timeout: EXEC_TIMEOUT_MS2, encoding: "utf-8" },
|
|
1183
|
+
(error, stdout) => {
|
|
1184
|
+
if (error) {
|
|
1185
|
+
resolve3(null);
|
|
1186
|
+
return;
|
|
1187
|
+
}
|
|
1188
|
+
try {
|
|
1189
|
+
const result = JSON.parse(stdout);
|
|
1190
|
+
resolve3(result);
|
|
1191
|
+
} catch {
|
|
1192
|
+
resolve3(null);
|
|
1193
|
+
}
|
|
1194
|
+
}
|
|
1195
|
+
);
|
|
1196
|
+
});
|
|
1197
|
+
}
|
|
1097
1198
|
function sleep(ms) {
|
|
1098
1199
|
return new Promise((resolve3) => {
|
|
1099
1200
|
setTimeout(resolve3, ms);
|
|
@@ -1225,6 +1326,7 @@ function maskKey(key) {
|
|
|
1225
1326
|
if (key.length <= 8) return "***";
|
|
1226
1327
|
return `${key.slice(0, 3)}***${key.slice(-3)}`;
|
|
1227
1328
|
}
|
|
1329
|
+
var TOKEN_CACHE_TTL_MS = 6e4;
|
|
1228
1330
|
function vlog2(message) {
|
|
1229
1331
|
process.stderr.write(`[verbose] ${message}
|
|
1230
1332
|
`);
|
|
@@ -1233,6 +1335,8 @@ var OkxRestClient = class _OkxRestClient {
|
|
|
1233
1335
|
config;
|
|
1234
1336
|
rateLimiter;
|
|
1235
1337
|
dispatcher;
|
|
1338
|
+
cachedAccessToken;
|
|
1339
|
+
cachedAccessTokenAt = 0;
|
|
1236
1340
|
pilot;
|
|
1237
1341
|
constructor(config) {
|
|
1238
1342
|
this.config = config;
|
|
@@ -1247,6 +1351,51 @@ var OkxRestClient = class _OkxRestClient {
|
|
|
1247
1351
|
hasCustomProxy: !!config.proxyUrl
|
|
1248
1352
|
});
|
|
1249
1353
|
}
|
|
1354
|
+
/**
|
|
1355
|
+
* Resolve OAuth access token via the okx-auth binary (fd3 pipe).
|
|
1356
|
+
* Caches the token for 60 s to avoid spawning the binary on every
|
|
1357
|
+
* request. The binary handles refresh internally (300s TTL lead),
|
|
1358
|
+
* so periodic re-calls let it serve a fresh token when needed.
|
|
1359
|
+
* Returns null when not logged in.
|
|
1360
|
+
*/
|
|
1361
|
+
async resolveAccessToken() {
|
|
1362
|
+
if (this.cachedAccessToken && Date.now() - this.cachedAccessTokenAt < TOKEN_CACHE_TTL_MS) {
|
|
1363
|
+
return this.cachedAccessToken;
|
|
1364
|
+
}
|
|
1365
|
+
try {
|
|
1366
|
+
const token = await execAuthToken();
|
|
1367
|
+
this.cachedAccessToken = token;
|
|
1368
|
+
this.cachedAccessTokenAt = Date.now();
|
|
1369
|
+
return token;
|
|
1370
|
+
} catch (e) {
|
|
1371
|
+
if (e instanceof NotLoggedInError) {
|
|
1372
|
+
return null;
|
|
1373
|
+
}
|
|
1374
|
+
throw e;
|
|
1375
|
+
}
|
|
1376
|
+
}
|
|
1377
|
+
/**
|
|
1378
|
+
* Dynamic auth — determines auth method per request.
|
|
1379
|
+
*
|
|
1380
|
+
* 1. API key in config → HMAC signing (no OAuth fallback)
|
|
1381
|
+
* 2. OAuth token via okx-auth binary → Bearer token
|
|
1382
|
+
* 3. Neither → throw ConfigError
|
|
1383
|
+
*/
|
|
1384
|
+
async applyAuth(headers, method, requestPath, bodyJson, timestamp) {
|
|
1385
|
+
if (this.config.apiKey && this.config.secretKey && this.config.passphrase) {
|
|
1386
|
+
this.setAuthHeaders(headers, method, requestPath, bodyJson, timestamp);
|
|
1387
|
+
return;
|
|
1388
|
+
}
|
|
1389
|
+
const accessToken = await this.resolveAccessToken();
|
|
1390
|
+
if (accessToken) {
|
|
1391
|
+
headers.set("Authorization", `Bearer ${accessToken}`);
|
|
1392
|
+
return;
|
|
1393
|
+
}
|
|
1394
|
+
throw new ConfigError(
|
|
1395
|
+
"No credentials found.",
|
|
1396
|
+
"Run `okx auth login` to authenticate, or configure API key credentials."
|
|
1397
|
+
);
|
|
1398
|
+
}
|
|
1250
1399
|
/** The canonical base URL for this client (e.g. https://www.okx.com). */
|
|
1251
1400
|
get baseUrl() {
|
|
1252
1401
|
return this.config.baseUrl;
|
|
@@ -1305,18 +1454,6 @@ var OkxRestClient = class _OkxRestClient {
|
|
|
1305
1454
|
});
|
|
1306
1455
|
}
|
|
1307
1456
|
setAuthHeaders(headers, method, requestPath, bodyJson, timestamp) {
|
|
1308
|
-
if (!this.config.hasAuth) {
|
|
1309
|
-
throw new ConfigError(
|
|
1310
|
-
"Private endpoint requires API credentials.",
|
|
1311
|
-
"Configure OKX_API_KEY, OKX_SECRET_KEY and OKX_PASSPHRASE."
|
|
1312
|
-
);
|
|
1313
|
-
}
|
|
1314
|
-
if (!this.config.apiKey || !this.config.secretKey || !this.config.passphrase) {
|
|
1315
|
-
throw new ConfigError(
|
|
1316
|
-
"Invalid private API credentials state.",
|
|
1317
|
-
"Ensure all OKX credentials are set."
|
|
1318
|
-
);
|
|
1319
|
-
}
|
|
1320
1457
|
const payload = `${timestamp}${method.toUpperCase()}${requestPath}${bodyJson}`;
|
|
1321
1458
|
const signature = signOkxPayload(payload, this.config.secretKey);
|
|
1322
1459
|
headers.set("OK-ACCESS-KEY", this.config.apiKey);
|
|
@@ -1431,7 +1568,7 @@ var OkxRestClient = class _OkxRestClient {
|
|
|
1431
1568
|
const conn = this.pilot.getConnectionParams();
|
|
1432
1569
|
this.logRequest("POST", `${conn.baseUrl}${path4}`, "private");
|
|
1433
1570
|
const reqConfig = { method: "POST", path: path4, auth: "private" };
|
|
1434
|
-
const headers = this.buildHeaders(reqConfig, path4, bodyJson, getNow());
|
|
1571
|
+
const headers = await this.buildHeaders(reqConfig, path4, bodyJson, getNow());
|
|
1435
1572
|
if (conn.userAgent) {
|
|
1436
1573
|
headers.set("User-Agent", conn.userAgent);
|
|
1437
1574
|
}
|
|
@@ -1552,7 +1689,7 @@ var OkxRestClient = class _OkxRestClient {
|
|
|
1552
1689
|
// Header building
|
|
1553
1690
|
// ---------------------------------------------------------------------------
|
|
1554
1691
|
/** Build HTTP headers. reqConfig.extraHeaders must NOT contain auth keys (OK-ACCESS-*). */
|
|
1555
|
-
buildHeaders(reqConfig, requestPath, bodyJson, timestamp) {
|
|
1692
|
+
async buildHeaders(reqConfig, requestPath, bodyJson, timestamp) {
|
|
1556
1693
|
const headers = new Headers({
|
|
1557
1694
|
"Content-Type": "application/json",
|
|
1558
1695
|
Accept: "application/json"
|
|
@@ -1561,7 +1698,7 @@ var OkxRestClient = class _OkxRestClient {
|
|
|
1561
1698
|
headers.set("User-Agent", this.config.userAgent);
|
|
1562
1699
|
}
|
|
1563
1700
|
if (reqConfig.auth === "private") {
|
|
1564
|
-
this.
|
|
1701
|
+
await this.applyAuth(headers, reqConfig.method, requestPath, bodyJson, timestamp);
|
|
1565
1702
|
}
|
|
1566
1703
|
const useSimulated = reqConfig.simulatedTrading !== void 0 ? reqConfig.simulatedTrading : this.config.demo;
|
|
1567
1704
|
if (useSimulated) {
|
|
@@ -1615,7 +1752,7 @@ var OkxRestClient = class _OkxRestClient {
|
|
|
1615
1752
|
if (reqConfig.rateLimit) {
|
|
1616
1753
|
await this.rateLimiter.consume(reqConfig.rateLimit);
|
|
1617
1754
|
}
|
|
1618
|
-
const headers = this.buildHeaders(reqConfig, requestPath, bodyJson, timestamp);
|
|
1755
|
+
const headers = await this.buildHeaders(reqConfig, requestPath, bodyJson, timestamp);
|
|
1619
1756
|
if (conn.userAgent) {
|
|
1620
1757
|
headers.set("User-Agent", conn.userAgent);
|
|
1621
1758
|
}
|
|
@@ -2042,8 +2179,6 @@ function registerIndicatorTools() {
|
|
|
2042
2179
|
];
|
|
2043
2180
|
}
|
|
2044
2181
|
var DEFAULT_SOURCE_TAG = "MCP";
|
|
2045
|
-
var TOKEN_REGEX = /^[A-Z2-7]{12}$/;
|
|
2046
|
-
var TAG_JOIN = "";
|
|
2047
2182
|
var OKX_SITES = {
|
|
2048
2183
|
global: {
|
|
2049
2184
|
label: "Global",
|
|
@@ -2057,7 +2192,7 @@ var OKX_SITES = {
|
|
|
2057
2192
|
},
|
|
2058
2193
|
us: {
|
|
2059
2194
|
label: "US",
|
|
2060
|
-
apiBaseUrl: "https://
|
|
2195
|
+
apiBaseUrl: "https://us.okx.com",
|
|
2061
2196
|
webUrl: "https://app.okx.com"
|
|
2062
2197
|
}
|
|
2063
2198
|
};
|
|
@@ -2973,8 +3108,7 @@ function registerAlgoTradeTools() {
|
|
|
2973
3108
|
callBackSpread: readString(args, "callbackSpread"),
|
|
2974
3109
|
activePx: readString(args, "activePx"),
|
|
2975
3110
|
reduceOnly: typeof reduceOnly === "boolean" ? String(reduceOnly) : void 0,
|
|
2976
|
-
clOrdId: readString(args, "clOrdId")
|
|
2977
|
-
tag: context.config.sourceTag
|
|
3111
|
+
clOrdId: readString(args, "clOrdId")
|
|
2978
3112
|
}),
|
|
2979
3113
|
privateRateLimit("swap_place_move_stop_order", 20)
|
|
2980
3114
|
);
|
|
@@ -3319,8 +3453,7 @@ function registerFuturesAlgoTools() {
|
|
|
3319
3453
|
callBackSpread: readString(args, "callbackSpread"),
|
|
3320
3454
|
activePx: readString(args, "activePx"),
|
|
3321
3455
|
reduceOnly: typeof reduceOnly === "boolean" ? String(reduceOnly) : void 0,
|
|
3322
|
-
clOrdId: readString(args, "clOrdId")
|
|
3323
|
-
tag: context.config.sourceTag
|
|
3456
|
+
clOrdId: readString(args, "clOrdId")
|
|
3324
3457
|
}),
|
|
3325
3458
|
privateRateLimit("futures_place_move_stop_order", 20)
|
|
3326
3459
|
);
|
|
@@ -3578,7 +3711,7 @@ function safeWriteFile(targetDir, fileName, data) {
|
|
|
3578
3711
|
throw new Error(`Invalid file name: "${fileName}"`);
|
|
3579
3712
|
}
|
|
3580
3713
|
const resolvedDir = resolve(targetDir);
|
|
3581
|
-
const filePath =
|
|
3714
|
+
const filePath = join4(resolvedDir, safeName);
|
|
3582
3715
|
const resolvedPath = resolve(filePath);
|
|
3583
3716
|
if (!resolvedPath.startsWith(resolvedDir + sep)) {
|
|
3584
3717
|
throw new Error(`Path traversal detected: "${fileName}" resolves outside target directory`);
|
|
@@ -3621,7 +3754,7 @@ async function downloadSkillZip(client, name, targetDir, format = "zip") {
|
|
|
3621
3754
|
return filePath;
|
|
3622
3755
|
}
|
|
3623
3756
|
var DEFAULT_MAX_TOTAL_BYTES = 100 * 1024 * 1024;
|
|
3624
|
-
var DEFAULT_REGISTRY_PATH =
|
|
3757
|
+
var DEFAULT_REGISTRY_PATH = join6(homedir4(), ".okx", "skills", "registry.json");
|
|
3625
3758
|
function registerSkillsTools() {
|
|
3626
3759
|
return [
|
|
3627
3760
|
{
|
|
@@ -10006,7 +10139,7 @@ function toMcpTool(tool) {
|
|
|
10006
10139
|
};
|
|
10007
10140
|
}
|
|
10008
10141
|
function configFilePath() {
|
|
10009
|
-
return
|
|
10142
|
+
return join7(homedir5(), ".okx", "config.toml");
|
|
10010
10143
|
}
|
|
10011
10144
|
function readFullConfig() {
|
|
10012
10145
|
const path4 = configFilePath();
|
|
@@ -10025,11 +10158,6 @@ Or re-run: okx config init`
|
|
|
10025
10158
|
);
|
|
10026
10159
|
}
|
|
10027
10160
|
}
|
|
10028
|
-
function readTomlProfile(profileName) {
|
|
10029
|
-
const config = readFullConfig();
|
|
10030
|
-
const name = profileName ?? config.default_profile ?? "default";
|
|
10031
|
-
return config.profiles?.[name] ?? {};
|
|
10032
|
-
}
|
|
10033
10161
|
function expandShorthand(moduleId) {
|
|
10034
10162
|
if (moduleId === "all") return [...MODULES];
|
|
10035
10163
|
if (moduleId === "earn" || moduleId === "earn.all") return [...EARN_SUB_MODULE_IDS];
|
|
@@ -10063,18 +10191,24 @@ function parseModuleList(rawModules) {
|
|
|
10063
10191
|
}
|
|
10064
10192
|
return Array.from(deduped);
|
|
10065
10193
|
}
|
|
10066
|
-
function loadCredentials(toml) {
|
|
10194
|
+
async function loadCredentials(toml) {
|
|
10067
10195
|
const apiKey = process.env.OKX_API_KEY?.trim() ?? toml.api_key;
|
|
10068
10196
|
const secretKey = process.env.OKX_SECRET_KEY?.trim() ?? toml.secret_key;
|
|
10069
10197
|
const passphrase = process.env.OKX_PASSPHRASE?.trim() ?? toml.passphrase;
|
|
10070
|
-
const
|
|
10198
|
+
const hasApiKey = Boolean(apiKey && secretKey && passphrase);
|
|
10071
10199
|
const partialAuth = Boolean(apiKey) || Boolean(secretKey) || Boolean(passphrase);
|
|
10072
|
-
if (partialAuth && !
|
|
10200
|
+
if (partialAuth && !hasApiKey) {
|
|
10073
10201
|
throw new ConfigError(
|
|
10074
10202
|
"Partial API credentials detected.",
|
|
10075
10203
|
"Set OKX_API_KEY, OKX_SECRET_KEY and OKX_PASSPHRASE together (env vars or config.toml profile)."
|
|
10076
10204
|
);
|
|
10077
10205
|
}
|
|
10206
|
+
let hasOAuth = false;
|
|
10207
|
+
if (!hasApiKey) {
|
|
10208
|
+
const status = await execAuthStatus();
|
|
10209
|
+
hasOAuth = status?.status === "logged_in";
|
|
10210
|
+
}
|
|
10211
|
+
const hasAuth = hasOAuth || hasApiKey;
|
|
10078
10212
|
return { apiKey, secretKey, passphrase, hasAuth };
|
|
10079
10213
|
}
|
|
10080
10214
|
function resolveSite(cliSite, tomlSite) {
|
|
@@ -10097,30 +10231,6 @@ function resolveBaseUrl(site, tomlBaseUrl) {
|
|
|
10097
10231
|
}
|
|
10098
10232
|
return rawBaseUrl.replace(/\/+$/, "");
|
|
10099
10233
|
}
|
|
10100
|
-
function resolveSourceTag(cli, defaultTag, opts = {}) {
|
|
10101
|
-
const readEnv = opts.readEnv !== false;
|
|
10102
|
-
const cliSkill = cli.skill?.trim();
|
|
10103
|
-
if (cliSkill) {
|
|
10104
|
-
if (isValidToken(cliSkill)) return composeTag(defaultTag, cliSkill);
|
|
10105
|
-
console.warn("--skill value format invalid (expected 12-char Base32), ignoring");
|
|
10106
|
-
}
|
|
10107
|
-
if (readEnv) {
|
|
10108
|
-
const envToken = process.env.OKX_SKILL_TOKEN?.trim();
|
|
10109
|
-
if (envToken) {
|
|
10110
|
-
if (isValidToken(envToken)) return composeTag(defaultTag, envToken);
|
|
10111
|
-
console.warn("OKX_SKILL_TOKEN format invalid, ignoring");
|
|
10112
|
-
}
|
|
10113
|
-
}
|
|
10114
|
-
const sourceTag = cli.sourceTag?.trim();
|
|
10115
|
-
if (sourceTag) return sourceTag;
|
|
10116
|
-
return defaultTag;
|
|
10117
|
-
}
|
|
10118
|
-
function composeTag(defaultTag, token) {
|
|
10119
|
-
return `${defaultTag}${TAG_JOIN}${token}`;
|
|
10120
|
-
}
|
|
10121
|
-
function isValidToken(s) {
|
|
10122
|
-
return TOKEN_REGEX.test(s);
|
|
10123
|
-
}
|
|
10124
10234
|
function resolveDemo(cli, toml) {
|
|
10125
10235
|
if (cli.demo && cli.live) {
|
|
10126
10236
|
throw new ConfigError(
|
|
@@ -10132,9 +10242,11 @@ function resolveDemo(cli, toml) {
|
|
|
10132
10242
|
if (cli.demo === true) return true;
|
|
10133
10243
|
return process.env.OKX_DEMO === "1" || process.env.OKX_DEMO === "true" || (toml.demo ?? false);
|
|
10134
10244
|
}
|
|
10135
|
-
function loadConfig(cli
|
|
10136
|
-
const
|
|
10137
|
-
const
|
|
10245
|
+
async function loadConfig(cli) {
|
|
10246
|
+
const config = readFullConfig();
|
|
10247
|
+
const profileName = cli.profile ?? config.default_profile ?? "default";
|
|
10248
|
+
const toml = config.profiles?.[profileName] ?? {};
|
|
10249
|
+
const creds = await loadCredentials(toml);
|
|
10138
10250
|
const demo = resolveDemo(cli, toml);
|
|
10139
10251
|
const site = resolveSite(cli.site, toml.site);
|
|
10140
10252
|
const baseUrl = resolveBaseUrl(site, toml.base_url);
|
|
@@ -10152,9 +10264,9 @@ function loadConfig(cli, opts = {}) {
|
|
|
10152
10264
|
"proxy_url must start with http:// or https://. SOCKS proxies are not supported."
|
|
10153
10265
|
);
|
|
10154
10266
|
}
|
|
10155
|
-
const defaultTag = opts.entryPrefix ?? DEFAULT_SOURCE_TAG;
|
|
10156
10267
|
return {
|
|
10157
10268
|
...creds,
|
|
10269
|
+
profile: profileName,
|
|
10158
10270
|
baseUrl,
|
|
10159
10271
|
timeoutMs: Math.floor(rawTimeout),
|
|
10160
10272
|
modules: parseModuleList(cli.modules),
|
|
@@ -10162,14 +10274,12 @@ function loadConfig(cli, opts = {}) {
|
|
|
10162
10274
|
demo,
|
|
10163
10275
|
site,
|
|
10164
10276
|
userAgent: cli.userAgent,
|
|
10165
|
-
|
|
10166
|
-
// MCP entry passes { readEnv: false } to honour the "MCP path not attributed" contract.
|
|
10167
|
-
sourceTag: resolveSourceTag(cli, defaultTag, { readEnv: opts.readEnv }),
|
|
10277
|
+
sourceTag: cli.sourceTag ?? DEFAULT_SOURCE_TAG,
|
|
10168
10278
|
proxyUrl: rawProxyUrl || void 0,
|
|
10169
10279
|
verbose: cli.verbose ?? false
|
|
10170
10280
|
};
|
|
10171
10281
|
}
|
|
10172
|
-
var CACHE_FILE =
|
|
10282
|
+
var CACHE_FILE = join8(homedir6(), ".okx", "update-check.json");
|
|
10173
10283
|
var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
10174
10284
|
function readCache2() {
|
|
10175
10285
|
try {
|
|
@@ -10182,7 +10292,7 @@ function readCache2() {
|
|
|
10182
10292
|
}
|
|
10183
10293
|
function writeCache2(cache) {
|
|
10184
10294
|
try {
|
|
10185
|
-
mkdirSync6(
|
|
10295
|
+
mkdirSync6(join8(homedir6(), ".okx"), { recursive: true });
|
|
10186
10296
|
writeFileSync5(CACHE_FILE, JSON.stringify(cache, null, 2), "utf-8");
|
|
10187
10297
|
} catch {
|
|
10188
10298
|
}
|
|
@@ -10336,13 +10446,13 @@ function findMsStoreClaudePath() {
|
|
|
10336
10446
|
}
|
|
10337
10447
|
function getConfigPath(client) {
|
|
10338
10448
|
const home = os3.homedir();
|
|
10339
|
-
const
|
|
10449
|
+
const platform3 = process.platform;
|
|
10340
10450
|
switch (client) {
|
|
10341
10451
|
case "claude-desktop":
|
|
10342
|
-
if (
|
|
10452
|
+
if (platform3 === "win32") {
|
|
10343
10453
|
return findMsStoreClaudePath() ?? path3.join(appData(), "Claude", CLAUDE_CONFIG_FILE);
|
|
10344
10454
|
}
|
|
10345
|
-
if (
|
|
10455
|
+
if (platform3 === "darwin") {
|
|
10346
10456
|
return path3.join(home, "Library", "Application Support", "Claude", CLAUDE_CONFIG_FILE);
|
|
10347
10457
|
}
|
|
10348
10458
|
return path3.join(process.env.XDG_CONFIG_HOME ?? path3.join(home, ".config"), "Claude", CLAUDE_CONFIG_FILE);
|
|
@@ -10444,6 +10554,8 @@ function runSetup(options) {
|
|
|
10444
10554
|
`);
|
|
10445
10555
|
}
|
|
10446
10556
|
}
|
|
10557
|
+
var CACHE_PATH = join12(homedir10(), ".okx", "auth-binary-check.json");
|
|
10558
|
+
var CHECK_INTERVAL_MS2 = 2 * 60 * 60 * 1e3;
|
|
10447
10559
|
|
|
10448
10560
|
// src/constants.ts
|
|
10449
10561
|
import { createRequire } from "module";
|
|
@@ -10451,7 +10563,7 @@ var _require = createRequire(import.meta.url);
|
|
|
10451
10563
|
var pkg = _require("../package.json");
|
|
10452
10564
|
var SERVER_NAME = "okx-trade-mcp";
|
|
10453
10565
|
var SERVER_VERSION = pkg.version;
|
|
10454
|
-
var GIT_HASH = true ? "
|
|
10566
|
+
var GIT_HASH = true ? "1bb94dcd" : "dev";
|
|
10455
10567
|
|
|
10456
10568
|
// src/server.ts
|
|
10457
10569
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
@@ -10728,25 +10840,16 @@ async function main() {
|
|
|
10728
10840
|
`);
|
|
10729
10841
|
return;
|
|
10730
10842
|
}
|
|
10731
|
-
const config = loadConfig(
|
|
10732
|
-
|
|
10733
|
-
|
|
10734
|
-
|
|
10735
|
-
|
|
10736
|
-
|
|
10737
|
-
|
|
10738
|
-
|
|
10739
|
-
|
|
10740
|
-
|
|
10741
|
-
},
|
|
10742
|
-
// MCP path: never read OKX_SKILL_TOKEN from env — the MCP path is not
|
|
10743
|
-
// attributed; skill token should only flow from SKILL.md-injected --skill
|
|
10744
|
-
// in the CLI path (design doc §1.3). `entryPrefix:"MCP"` is set for
|
|
10745
|
-
// semantic completeness: if a future change flips `readEnv:true`, the
|
|
10746
|
-
// composeTag branch would correctly produce `MCP<token>` rather than
|
|
10747
|
-
// relying on DEFAULT_SOURCE_TAG as an implicit default.
|
|
10748
|
-
{ readEnv: false, entryPrefix: "MCP" }
|
|
10749
|
-
);
|
|
10843
|
+
const config = await loadConfig({
|
|
10844
|
+
modules: cli.modules,
|
|
10845
|
+
profile: cli.profile,
|
|
10846
|
+
site: cli.site,
|
|
10847
|
+
readOnly: cli.readOnly,
|
|
10848
|
+
demo: cli.demo,
|
|
10849
|
+
live: cli.live,
|
|
10850
|
+
userAgent: `${SERVER_NAME}/${SERVER_VERSION}`,
|
|
10851
|
+
sourceTag: "MCP"
|
|
10852
|
+
});
|
|
10750
10853
|
const logger = cli.noLog ? void 0 : new TradeLogger(cli.logLevel);
|
|
10751
10854
|
const server = createServer(config, logger);
|
|
10752
10855
|
const transport = new StdioServerTransport();
|