@okx_ai/okx-trade-mcp 1.3.0-beta.4 → 1.3.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/dist/index.js +132 -358
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/scripts/postinstall.js +2 -112
package/dist/index.js
CHANGED
|
@@ -5,32 +5,20 @@ import { parseArgs } from "util";
|
|
|
5
5
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
6
6
|
|
|
7
7
|
// ../core/dist/index.js
|
|
8
|
-
import {
|
|
9
|
-
import { execFile } from "child_process";
|
|
10
|
-
import { homedir } from "os";
|
|
11
|
-
import { join } from "path";
|
|
12
|
-
import {
|
|
13
|
-
readFileSync,
|
|
14
|
-
writeFileSync,
|
|
15
|
-
mkdirSync,
|
|
16
|
-
unlinkSync,
|
|
17
|
-
renameSync
|
|
18
|
-
} from "fs";
|
|
19
|
-
import { homedir as homedir2 } from "os";
|
|
20
|
-
import { join as join2, dirname } from "path";
|
|
8
|
+
import { ProxyAgent } from "undici";
|
|
21
9
|
import { createHmac } from "crypto";
|
|
22
10
|
import fs from "fs";
|
|
23
11
|
import path from "path";
|
|
24
12
|
import os from "os";
|
|
25
|
-
import { writeFileSync
|
|
26
|
-
import { join
|
|
13
|
+
import { writeFileSync, renameSync, unlinkSync, mkdirSync } from "fs";
|
|
14
|
+
import { join, resolve, basename, sep } from "path";
|
|
27
15
|
import { randomUUID } from "crypto";
|
|
28
16
|
import yauzl from "yauzl";
|
|
29
|
-
import { join as
|
|
30
|
-
import { homedir
|
|
31
|
-
import { readFileSync as
|
|
32
|
-
import { join as
|
|
33
|
-
import { homedir as
|
|
17
|
+
import { join as join3, dirname as dirname2 } from "path";
|
|
18
|
+
import { homedir } from "os";
|
|
19
|
+
import { readFileSync as readFileSync3, writeFileSync as writeFileSync3, mkdirSync as mkdirSync4, existsSync as existsSync3 } from "fs";
|
|
20
|
+
import { join as join4, dirname as dirname3 } from "path";
|
|
21
|
+
import { homedir as homedir2 } from "os";
|
|
34
22
|
|
|
35
23
|
// ../../node_modules/.pnpm/smol-toml@1.6.0/node_modules/smol-toml/dist/error.js
|
|
36
24
|
function getLineColFromPtr(string, ptr) {
|
|
@@ -718,9 +706,9 @@ function parse(toml, { maxDepth = 1e3, integersAsBigInt } = {}) {
|
|
|
718
706
|
}
|
|
719
707
|
|
|
720
708
|
// ../core/dist/index.js
|
|
721
|
-
import { readFileSync as
|
|
722
|
-
import { join as
|
|
723
|
-
import { homedir as
|
|
709
|
+
import { readFileSync as readFileSync4, writeFileSync as writeFileSync4, mkdirSync as mkdirSync5, existsSync as existsSync4 } from "fs";
|
|
710
|
+
import { join as join5 } from "path";
|
|
711
|
+
import { homedir as homedir3 } from "os";
|
|
724
712
|
import fs2 from "fs";
|
|
725
713
|
import path2 from "path";
|
|
726
714
|
import os2 from "os";
|
|
@@ -728,123 +716,6 @@ import * as fs3 from "fs";
|
|
|
728
716
|
import * as path3 from "path";
|
|
729
717
|
import * as os3 from "os";
|
|
730
718
|
import { execFileSync } from "child_process";
|
|
731
|
-
var EXEC_TIMEOUT_MS = 3e4;
|
|
732
|
-
var DOH_BIN_DIR = join(homedir(), ".okx", "bin");
|
|
733
|
-
function getDohBinaryPath() {
|
|
734
|
-
if (process.env.OKX_DOH_BINARY_PATH) {
|
|
735
|
-
return process.env.OKX_DOH_BINARY_PATH;
|
|
736
|
-
}
|
|
737
|
-
const ext = process.platform === "win32" ? ".exe" : "";
|
|
738
|
-
return join(DOH_BIN_DIR, `okx-doh-resolver${ext}`);
|
|
739
|
-
}
|
|
740
|
-
function execDohBinary(domain, exclude = [], userAgent) {
|
|
741
|
-
const binPath = getDohBinaryPath();
|
|
742
|
-
const args = ["--domain", domain];
|
|
743
|
-
if (exclude.length > 0) {
|
|
744
|
-
args.push("--exclude", exclude.join(","));
|
|
745
|
-
}
|
|
746
|
-
if (userAgent) {
|
|
747
|
-
args.push("--user-agent", userAgent);
|
|
748
|
-
}
|
|
749
|
-
return new Promise((resolve3) => {
|
|
750
|
-
execFile(
|
|
751
|
-
binPath,
|
|
752
|
-
args,
|
|
753
|
-
{ timeout: EXEC_TIMEOUT_MS, encoding: "utf-8" },
|
|
754
|
-
(error, stdout) => {
|
|
755
|
-
if (error) {
|
|
756
|
-
resolve3(null);
|
|
757
|
-
return;
|
|
758
|
-
}
|
|
759
|
-
try {
|
|
760
|
-
const result = JSON.parse(stdout);
|
|
761
|
-
if (result.code === 0 && result.data) {
|
|
762
|
-
resolve3(result.data);
|
|
763
|
-
} else {
|
|
764
|
-
resolve3(null);
|
|
765
|
-
}
|
|
766
|
-
} catch {
|
|
767
|
-
resolve3(null);
|
|
768
|
-
}
|
|
769
|
-
}
|
|
770
|
-
);
|
|
771
|
-
});
|
|
772
|
-
}
|
|
773
|
-
var DOH_CACHE_PATH = join2(homedir2(), ".okx", "doh-node-cache.json");
|
|
774
|
-
function readCache(hostname, cachePath = DOH_CACHE_PATH) {
|
|
775
|
-
try {
|
|
776
|
-
const raw = readFileSync(cachePath, "utf-8");
|
|
777
|
-
const file = JSON.parse(raw);
|
|
778
|
-
return file[hostname] ?? null;
|
|
779
|
-
} catch {
|
|
780
|
-
return null;
|
|
781
|
-
}
|
|
782
|
-
}
|
|
783
|
-
function writeCache(hostname, entry, cachePath = DOH_CACHE_PATH) {
|
|
784
|
-
try {
|
|
785
|
-
const dir = dirname(cachePath);
|
|
786
|
-
mkdirSync(dir, { recursive: true });
|
|
787
|
-
let file = {};
|
|
788
|
-
try {
|
|
789
|
-
file = JSON.parse(readFileSync(cachePath, "utf-8"));
|
|
790
|
-
} catch {
|
|
791
|
-
}
|
|
792
|
-
file[hostname] = entry;
|
|
793
|
-
const tmpPath = `${cachePath}.tmp`;
|
|
794
|
-
writeFileSync(tmpPath, JSON.stringify(file));
|
|
795
|
-
renameSync(tmpPath, cachePath);
|
|
796
|
-
} catch {
|
|
797
|
-
}
|
|
798
|
-
}
|
|
799
|
-
var FAILED_NODE_TTL_MS = 60 * 60 * 1e3;
|
|
800
|
-
function classifyAndCache(node, hostname, failedNodes, cachePath) {
|
|
801
|
-
if (!node) {
|
|
802
|
-
return { mode: null, node: null };
|
|
803
|
-
}
|
|
804
|
-
if (node.ip === hostname || node.host === hostname) {
|
|
805
|
-
writeCache(hostname, {
|
|
806
|
-
mode: "direct",
|
|
807
|
-
node: null,
|
|
808
|
-
failedNodes,
|
|
809
|
-
updatedAt: Date.now()
|
|
810
|
-
}, cachePath);
|
|
811
|
-
return { mode: "direct", node: null };
|
|
812
|
-
}
|
|
813
|
-
writeCache(hostname, {
|
|
814
|
-
mode: "proxy",
|
|
815
|
-
node,
|
|
816
|
-
failedNodes,
|
|
817
|
-
updatedAt: Date.now()
|
|
818
|
-
}, cachePath);
|
|
819
|
-
return { mode: "proxy", node };
|
|
820
|
-
}
|
|
821
|
-
function getActiveFailedNodes(nodes) {
|
|
822
|
-
if (!nodes || nodes.length === 0) return [];
|
|
823
|
-
const now = Date.now();
|
|
824
|
-
return nodes.filter((n) => now - n.failedAt < FAILED_NODE_TTL_MS);
|
|
825
|
-
}
|
|
826
|
-
function resolveDoh(hostname, cachePath) {
|
|
827
|
-
const entry = readCache(hostname, cachePath);
|
|
828
|
-
if (entry) {
|
|
829
|
-
if (entry.mode === "direct") {
|
|
830
|
-
return { mode: "direct", node: null };
|
|
831
|
-
}
|
|
832
|
-
if (entry.mode === "proxy" && entry.node) {
|
|
833
|
-
return { mode: "proxy", node: entry.node };
|
|
834
|
-
}
|
|
835
|
-
}
|
|
836
|
-
return { mode: null, node: null };
|
|
837
|
-
}
|
|
838
|
-
async function reResolveDoh(hostname, failedIp, userAgent, cachePath) {
|
|
839
|
-
const entry = readCache(hostname, cachePath);
|
|
840
|
-
const active = getActiveFailedNodes(entry?.failedNodes);
|
|
841
|
-
const now = Date.now();
|
|
842
|
-
const alreadyFailed = failedIp && active.some((n) => n.ip === failedIp);
|
|
843
|
-
const failedNodes = failedIp && !alreadyFailed ? [...active, { ip: failedIp, failedAt: now }] : active;
|
|
844
|
-
const excludeIps = failedNodes.map((n) => n.ip);
|
|
845
|
-
const node = await execDohBinary(hostname, excludeIps, userAgent);
|
|
846
|
-
return classifyAndCache(node, hostname, failedNodes, cachePath);
|
|
847
|
-
}
|
|
848
719
|
function getNow() {
|
|
849
720
|
return (/* @__PURE__ */ new Date()).toISOString();
|
|
850
721
|
}
|
|
@@ -1063,14 +934,6 @@ var OkxRestClient = class _OkxRestClient {
|
|
|
1063
934
|
config;
|
|
1064
935
|
rateLimiter;
|
|
1065
936
|
dispatcher;
|
|
1066
|
-
// DoH proxy state (lazy-resolved on first request)
|
|
1067
|
-
dohResolved = false;
|
|
1068
|
-
dohRetried = false;
|
|
1069
|
-
directUnverified = false;
|
|
1070
|
-
// The first direct connection has not yet been verified
|
|
1071
|
-
dohNode = null;
|
|
1072
|
-
dohAgent = null;
|
|
1073
|
-
dohBaseUrl = null;
|
|
1074
937
|
constructor(config) {
|
|
1075
938
|
this.config = config;
|
|
1076
939
|
this.rateLimiter = new RateLimiter(3e4, config.verbose);
|
|
@@ -1078,98 +941,6 @@ var OkxRestClient = class _OkxRestClient {
|
|
|
1078
941
|
this.dispatcher = new ProxyAgent(config.proxyUrl);
|
|
1079
942
|
}
|
|
1080
943
|
}
|
|
1081
|
-
/**
|
|
1082
|
-
* Lazily resolve the DoH proxy node on the first request.
|
|
1083
|
-
* Uses cache-first strategy via the resolver.
|
|
1084
|
-
*/
|
|
1085
|
-
ensureDoh() {
|
|
1086
|
-
if (this.dohResolved || this.dispatcher) return;
|
|
1087
|
-
this.dohResolved = true;
|
|
1088
|
-
try {
|
|
1089
|
-
const { hostname, protocol } = new URL(this.config.baseUrl);
|
|
1090
|
-
const result = resolveDoh(hostname);
|
|
1091
|
-
if (!result.mode) {
|
|
1092
|
-
this.directUnverified = true;
|
|
1093
|
-
if (this.config.verbose) {
|
|
1094
|
-
vlog("DoH: no cache, trying direct connection first");
|
|
1095
|
-
}
|
|
1096
|
-
return;
|
|
1097
|
-
}
|
|
1098
|
-
if (result.mode === "direct") {
|
|
1099
|
-
if (this.config.verbose) {
|
|
1100
|
-
vlog("DoH: mode=direct (overseas or cached), using direct connection");
|
|
1101
|
-
}
|
|
1102
|
-
return;
|
|
1103
|
-
}
|
|
1104
|
-
if (result.node) {
|
|
1105
|
-
this.applyDohNode(result.node, protocol);
|
|
1106
|
-
}
|
|
1107
|
-
} catch (err) {
|
|
1108
|
-
if (this.config.verbose) {
|
|
1109
|
-
const cause = err instanceof Error ? err.message : String(err);
|
|
1110
|
-
vlog(`DoH resolution failed, falling back to direct: ${cause}`);
|
|
1111
|
-
}
|
|
1112
|
-
}
|
|
1113
|
-
}
|
|
1114
|
-
/** Apply a DoH node: set up the custom Agent + base URL. */
|
|
1115
|
-
applyDohNode(node, protocol) {
|
|
1116
|
-
this.dohNode = node;
|
|
1117
|
-
this.dohBaseUrl = `${protocol}//${node.host}`;
|
|
1118
|
-
this.dohAgent = new Agent({
|
|
1119
|
-
connect: {
|
|
1120
|
-
lookup: (_hostname, options, callback) => {
|
|
1121
|
-
if (options?.all) {
|
|
1122
|
-
callback(null, [{ address: node.ip, family: 4 }]);
|
|
1123
|
-
} else {
|
|
1124
|
-
callback(null, node.ip, 4);
|
|
1125
|
-
}
|
|
1126
|
-
}
|
|
1127
|
-
}
|
|
1128
|
-
});
|
|
1129
|
-
if (this.config.verbose) {
|
|
1130
|
-
vlog(`DoH proxy active: \u2192 ${node.host} (${node.ip}), ttl=${node.ttl}s`);
|
|
1131
|
-
}
|
|
1132
|
-
}
|
|
1133
|
-
/**
|
|
1134
|
-
* Handle network failure: re-resolve with --exclude and retry once.
|
|
1135
|
-
* Returns true if retry should proceed, false if already retried.
|
|
1136
|
-
*/
|
|
1137
|
-
async handleDohNetworkFailure() {
|
|
1138
|
-
if (this.dohRetried) return false;
|
|
1139
|
-
this.dohRetried = true;
|
|
1140
|
-
const failedIp = this.dohNode?.ip ?? "";
|
|
1141
|
-
const { hostname, protocol } = new URL(this.config.baseUrl);
|
|
1142
|
-
this.dohNode = null;
|
|
1143
|
-
this.dohAgent = null;
|
|
1144
|
-
this.dohBaseUrl = null;
|
|
1145
|
-
if (!failedIp) this.directUnverified = false;
|
|
1146
|
-
if (this.config.verbose) {
|
|
1147
|
-
vlog(failedIp ? `DoH: proxy node ${failedIp} failed, re-resolving with --exclude` : "DoH: direct connection failed, calling binary for DoH resolution");
|
|
1148
|
-
}
|
|
1149
|
-
try {
|
|
1150
|
-
const result = await reResolveDoh(hostname, failedIp, this.dohUserAgent);
|
|
1151
|
-
if (result.mode === "proxy" && result.node) {
|
|
1152
|
-
this.applyDohNode(result.node, protocol);
|
|
1153
|
-
this.dohRetried = false;
|
|
1154
|
-
return true;
|
|
1155
|
-
}
|
|
1156
|
-
} catch {
|
|
1157
|
-
}
|
|
1158
|
-
if (this.config.verbose) {
|
|
1159
|
-
vlog("DoH: re-resolution failed or switched to direct, retrying with direct connection");
|
|
1160
|
-
}
|
|
1161
|
-
return true;
|
|
1162
|
-
}
|
|
1163
|
-
get activeBaseUrl() {
|
|
1164
|
-
return this.dohNode ? this.dohBaseUrl : this.config.baseUrl;
|
|
1165
|
-
}
|
|
1166
|
-
get activeDispatcher() {
|
|
1167
|
-
return this.dispatcher ?? this.dohAgent ?? void 0;
|
|
1168
|
-
}
|
|
1169
|
-
/** User-Agent for DoH proxy requests: OKX/@okx_ai/{packageName}/{version} */
|
|
1170
|
-
get dohUserAgent() {
|
|
1171
|
-
return `OKX/@okx_ai/${this.config.userAgent ?? "unknown"}`;
|
|
1172
|
-
}
|
|
1173
944
|
logRequest(method, url, auth) {
|
|
1174
945
|
if (!this.config.verbose) return;
|
|
1175
946
|
vlog(`\u2192 ${method} ${url}`);
|
|
@@ -1184,13 +955,14 @@ var OkxRestClient = class _OkxRestClient {
|
|
|
1184
955
|
vlog(`\u2190 ${status} | code=${code ?? "0"} | ${rawLen}B | ${elapsed}ms | trace=${traceId ?? "-"}`);
|
|
1185
956
|
}
|
|
1186
957
|
}
|
|
1187
|
-
async publicGet(path4, query, rateLimit) {
|
|
958
|
+
async publicGet(path4, query, rateLimit, simulatedTrading) {
|
|
1188
959
|
return this.request({
|
|
1189
960
|
method: "GET",
|
|
1190
961
|
path: path4,
|
|
1191
962
|
auth: "public",
|
|
1192
963
|
query,
|
|
1193
|
-
rateLimit
|
|
964
|
+
rateLimit,
|
|
965
|
+
simulatedTrading
|
|
1194
966
|
});
|
|
1195
967
|
}
|
|
1196
968
|
async privateGet(path4, query, rateLimit) {
|
|
@@ -1339,17 +1111,13 @@ var OkxRestClient = class _OkxRestClient {
|
|
|
1339
1111
|
* Security: validates Content-Type and enforces maxBytes limit.
|
|
1340
1112
|
*/
|
|
1341
1113
|
async privatePostBinary(path4, body, opts) {
|
|
1342
|
-
this.ensureDoh();
|
|
1343
1114
|
const maxBytes = opts?.maxBytes ?? _OkxRestClient.DEFAULT_MAX_BYTES;
|
|
1344
1115
|
const expectedCT = opts?.expectedContentType ?? "application/octet-stream";
|
|
1345
1116
|
const bodyJson = body ? JSON.stringify(body) : "";
|
|
1346
1117
|
const endpoint = `POST ${path4}`;
|
|
1347
|
-
this.logRequest("POST", `${this.
|
|
1118
|
+
this.logRequest("POST", `${this.config.baseUrl}${path4}`, "private");
|
|
1348
1119
|
const reqConfig = { method: "POST", path: path4, auth: "private" };
|
|
1349
1120
|
const headers = this.buildHeaders(reqConfig, path4, bodyJson, getNow());
|
|
1350
|
-
if (this.dohNode) {
|
|
1351
|
-
headers.set("User-Agent", this.dohUserAgent);
|
|
1352
|
-
}
|
|
1353
1121
|
const t0 = Date.now();
|
|
1354
1122
|
const response = await this.fetchBinary(path4, endpoint, headers, bodyJson, t0);
|
|
1355
1123
|
const elapsed = Date.now() - t0;
|
|
@@ -1383,10 +1151,10 @@ var OkxRestClient = class _OkxRestClient {
|
|
|
1383
1151
|
method: "POST",
|
|
1384
1152
|
headers,
|
|
1385
1153
|
body: bodyJson || void 0,
|
|
1386
|
-
signal: AbortSignal.timeout(this.config.timeoutMs)
|
|
1387
|
-
dispatcher: this.activeDispatcher
|
|
1154
|
+
signal: AbortSignal.timeout(this.config.timeoutMs)
|
|
1388
1155
|
};
|
|
1389
|
-
|
|
1156
|
+
if (this.dispatcher) fetchOptions.dispatcher = this.dispatcher;
|
|
1157
|
+
return await fetch(`${this.config.baseUrl}${path4}`, fetchOptions);
|
|
1390
1158
|
} catch (error) {
|
|
1391
1159
|
if (this.config.verbose) {
|
|
1392
1160
|
vlog(`\u2717 NetworkError after ${Date.now() - t0}ms: ${error instanceof Error ? error.message : String(error)}`);
|
|
@@ -1408,7 +1176,8 @@ var OkxRestClient = class _OkxRestClient {
|
|
|
1408
1176
|
if (reqConfig.auth === "private") {
|
|
1409
1177
|
this.setAuthHeaders(headers, reqConfig.method, requestPath, bodyJson, timestamp);
|
|
1410
1178
|
}
|
|
1411
|
-
|
|
1179
|
+
const useSimulated = reqConfig.simulatedTrading !== void 0 ? reqConfig.simulatedTrading : this.config.demo;
|
|
1180
|
+
if (useSimulated) {
|
|
1412
1181
|
headers.set("x-simulated-trading", "1");
|
|
1413
1182
|
}
|
|
1414
1183
|
return headers;
|
|
@@ -1416,55 +1185,10 @@ var OkxRestClient = class _OkxRestClient {
|
|
|
1416
1185
|
// ---------------------------------------------------------------------------
|
|
1417
1186
|
// JSON request
|
|
1418
1187
|
// ---------------------------------------------------------------------------
|
|
1419
|
-
/**
|
|
1420
|
-
* Handle network error during a JSON request: refresh DoH and maybe retry.
|
|
1421
|
-
* Always either returns a retry result or throws NetworkError.
|
|
1422
|
-
*/
|
|
1423
|
-
async handleRequestNetworkError(error, reqConfig, requestPath, t0) {
|
|
1424
|
-
if (!this.dohRetried) {
|
|
1425
|
-
if (this.config.verbose) {
|
|
1426
|
-
const cause = error instanceof Error ? error.message : String(error);
|
|
1427
|
-
vlog(`Network failure, refreshing DoH: ${cause}`);
|
|
1428
|
-
}
|
|
1429
|
-
const shouldRetry = await this.handleDohNetworkFailure();
|
|
1430
|
-
if (shouldRetry && reqConfig.method === "GET") {
|
|
1431
|
-
return this.request(reqConfig);
|
|
1432
|
-
}
|
|
1433
|
-
}
|
|
1434
|
-
if (this.config.verbose) {
|
|
1435
|
-
const elapsed = Date.now() - t0;
|
|
1436
|
-
const cause = error instanceof Error ? error.message : String(error);
|
|
1437
|
-
vlog(`\u2717 NetworkError after ${elapsed}ms: ${cause}`);
|
|
1438
|
-
}
|
|
1439
|
-
throw new NetworkError(
|
|
1440
|
-
`Failed to call OKX endpoint ${reqConfig.method} ${requestPath}.`,
|
|
1441
|
-
`${reqConfig.method} ${requestPath}`,
|
|
1442
|
-
error
|
|
1443
|
-
);
|
|
1444
|
-
}
|
|
1445
|
-
/**
|
|
1446
|
-
* After a successful HTTP response on direct connection, cache mode=direct.
|
|
1447
|
-
* (Even if the business response is an error, the network path is valid.)
|
|
1448
|
-
*/
|
|
1449
|
-
cacheDirectConnectionIfNeeded() {
|
|
1450
|
-
if (!this.directUnverified || this.dohNode) return;
|
|
1451
|
-
this.directUnverified = false;
|
|
1452
|
-
const { hostname } = new URL(this.config.baseUrl);
|
|
1453
|
-
writeCache(hostname, {
|
|
1454
|
-
mode: "direct",
|
|
1455
|
-
node: null,
|
|
1456
|
-
failedNodes: [],
|
|
1457
|
-
updatedAt: Date.now()
|
|
1458
|
-
});
|
|
1459
|
-
if (this.config.verbose) {
|
|
1460
|
-
vlog("DoH: direct connection succeeded, cached mode=direct");
|
|
1461
|
-
}
|
|
1462
|
-
}
|
|
1463
1188
|
async request(reqConfig) {
|
|
1464
|
-
this.ensureDoh();
|
|
1465
1189
|
const queryString = buildQueryString(reqConfig.query);
|
|
1466
1190
|
const requestPath = queryString.length > 0 ? `${reqConfig.path}?${queryString}` : reqConfig.path;
|
|
1467
|
-
const url = `${this.
|
|
1191
|
+
const url = `${this.config.baseUrl}${requestPath}`;
|
|
1468
1192
|
const bodyJson = reqConfig.body ? JSON.stringify(reqConfig.body) : "";
|
|
1469
1193
|
const timestamp = getNow();
|
|
1470
1194
|
this.logRequest(reqConfig.method, url, reqConfig.auth);
|
|
@@ -1472,9 +1196,6 @@ var OkxRestClient = class _OkxRestClient {
|
|
|
1472
1196
|
await this.rateLimiter.consume(reqConfig.rateLimit);
|
|
1473
1197
|
}
|
|
1474
1198
|
const headers = this.buildHeaders(reqConfig, requestPath, bodyJson, timestamp);
|
|
1475
|
-
if (this.dohNode) {
|
|
1476
|
-
headers.set("User-Agent", this.dohUserAgent);
|
|
1477
|
-
}
|
|
1478
1199
|
const t0 = Date.now();
|
|
1479
1200
|
let response;
|
|
1480
1201
|
try {
|
|
@@ -1482,17 +1203,27 @@ var OkxRestClient = class _OkxRestClient {
|
|
|
1482
1203
|
method: reqConfig.method,
|
|
1483
1204
|
headers,
|
|
1484
1205
|
body: reqConfig.method === "POST" ? bodyJson : void 0,
|
|
1485
|
-
signal: AbortSignal.timeout(this.config.timeoutMs)
|
|
1486
|
-
dispatcher: this.activeDispatcher
|
|
1206
|
+
signal: AbortSignal.timeout(this.config.timeoutMs)
|
|
1487
1207
|
};
|
|
1208
|
+
if (this.dispatcher) {
|
|
1209
|
+
fetchOptions.dispatcher = this.dispatcher;
|
|
1210
|
+
}
|
|
1488
1211
|
response = await fetch(url, fetchOptions);
|
|
1489
1212
|
} catch (error) {
|
|
1490
|
-
|
|
1213
|
+
if (this.config.verbose) {
|
|
1214
|
+
const elapsed2 = Date.now() - t0;
|
|
1215
|
+
const cause = error instanceof Error ? error.message : String(error);
|
|
1216
|
+
vlog(`\u2717 NetworkError after ${elapsed2}ms: ${cause}`);
|
|
1217
|
+
}
|
|
1218
|
+
throw new NetworkError(
|
|
1219
|
+
`Failed to call OKX endpoint ${reqConfig.method} ${requestPath}.`,
|
|
1220
|
+
`${reqConfig.method} ${requestPath}`,
|
|
1221
|
+
error
|
|
1222
|
+
);
|
|
1491
1223
|
}
|
|
1492
1224
|
const rawText = await response.text();
|
|
1493
1225
|
const elapsed = Date.now() - t0;
|
|
1494
1226
|
const traceId = extractTraceId(response.headers);
|
|
1495
|
-
this.cacheDirectConnectionIfNeeded();
|
|
1496
1227
|
return this.processResponse(rawText, response, elapsed, traceId, reqConfig, requestPath);
|
|
1497
1228
|
}
|
|
1498
1229
|
};
|
|
@@ -3371,19 +3102,19 @@ function safeWriteFile(targetDir, fileName, data) {
|
|
|
3371
3102
|
throw new Error(`Invalid file name: "${fileName}"`);
|
|
3372
3103
|
}
|
|
3373
3104
|
const resolvedDir = resolve(targetDir);
|
|
3374
|
-
const filePath =
|
|
3105
|
+
const filePath = join(resolvedDir, safeName);
|
|
3375
3106
|
const resolvedPath = resolve(filePath);
|
|
3376
3107
|
if (!resolvedPath.startsWith(resolvedDir + sep)) {
|
|
3377
3108
|
throw new Error(`Path traversal detected: "${fileName}" resolves outside target directory`);
|
|
3378
3109
|
}
|
|
3379
|
-
|
|
3110
|
+
mkdirSync(resolvedDir, { recursive: true });
|
|
3380
3111
|
const tmpPath = `${resolvedPath}.${randomUUID()}.tmp`;
|
|
3381
3112
|
try {
|
|
3382
|
-
|
|
3383
|
-
|
|
3113
|
+
writeFileSync(tmpPath, data);
|
|
3114
|
+
renameSync(tmpPath, resolvedPath);
|
|
3384
3115
|
} catch (err) {
|
|
3385
3116
|
try {
|
|
3386
|
-
|
|
3117
|
+
unlinkSync(tmpPath);
|
|
3387
3118
|
} catch {
|
|
3388
3119
|
}
|
|
3389
3120
|
throw err;
|
|
@@ -3391,18 +3122,19 @@ function safeWriteFile(targetDir, fileName, data) {
|
|
|
3391
3122
|
return resolvedPath;
|
|
3392
3123
|
}
|
|
3393
3124
|
var MAX_DOWNLOAD_BYTES = 50 * 1024 * 1024;
|
|
3394
|
-
async function downloadSkillZip(client, name, targetDir) {
|
|
3125
|
+
async function downloadSkillZip(client, name, targetDir, format = "zip") {
|
|
3395
3126
|
const result = await client.privatePostBinary(
|
|
3396
3127
|
"/api/v5/skill/download",
|
|
3397
3128
|
{ name },
|
|
3398
3129
|
{ maxBytes: MAX_DOWNLOAD_BYTES }
|
|
3399
3130
|
);
|
|
3400
|
-
const
|
|
3131
|
+
const ext = format === "skill" ? "skill" : "zip";
|
|
3132
|
+
const fileName = `${name}.${ext}`;
|
|
3401
3133
|
const filePath = safeWriteFile(targetDir, fileName, result.data);
|
|
3402
3134
|
return filePath;
|
|
3403
3135
|
}
|
|
3404
3136
|
var DEFAULT_MAX_TOTAL_BYTES = 100 * 1024 * 1024;
|
|
3405
|
-
var DEFAULT_REGISTRY_PATH =
|
|
3137
|
+
var DEFAULT_REGISTRY_PATH = join3(homedir(), ".okx", "skills", "registry.json");
|
|
3406
3138
|
function registerSkillsTools() {
|
|
3407
3139
|
return [
|
|
3408
3140
|
{
|
|
@@ -3449,7 +3181,7 @@ function registerSkillsTools() {
|
|
|
3449
3181
|
{
|
|
3450
3182
|
name: "skills_download",
|
|
3451
3183
|
module: "skills",
|
|
3452
|
-
description: "Download a skill
|
|
3184
|
+
description: "Download a skill package from OKX Skills Marketplace to a local directory. Always call skills_search first to confirm the skill name exists. Downloads the latest approved version. NOTE: Downloads third-party developer content \u2014 does NOT install to agents. For full installation use CLI: okx skill add <name>. Use when the user wants to inspect or manually install a skill package.",
|
|
3453
3185
|
inputSchema: {
|
|
3454
3186
|
type: "object",
|
|
3455
3187
|
properties: {
|
|
@@ -3459,7 +3191,12 @@ function registerSkillsTools() {
|
|
|
3459
3191
|
},
|
|
3460
3192
|
targetDir: {
|
|
3461
3193
|
type: "string",
|
|
3462
|
-
description: "Directory path where the
|
|
3194
|
+
description: "Directory path where the file will be saved"
|
|
3195
|
+
},
|
|
3196
|
+
format: {
|
|
3197
|
+
type: "string",
|
|
3198
|
+
description: "Output file format: 'zip' or 'skill' (default: 'skill')",
|
|
3199
|
+
enum: ["zip", "skill"]
|
|
3463
3200
|
}
|
|
3464
3201
|
},
|
|
3465
3202
|
required: ["name", "targetDir"],
|
|
@@ -3492,7 +3229,8 @@ async function handleSearch(args, ctx) {
|
|
|
3492
3229
|
async function handleDownload(args, ctx) {
|
|
3493
3230
|
const name = String(args.name);
|
|
3494
3231
|
const targetDir = String(args.targetDir);
|
|
3495
|
-
const
|
|
3232
|
+
const format = args.format === "zip" ? "zip" : "skill";
|
|
3233
|
+
const filePath = await downloadSkillZip(ctx.client, name, targetDir, format);
|
|
3496
3234
|
return {
|
|
3497
3235
|
endpoint: "POST /api/v5/skill/download",
|
|
3498
3236
|
requestTime: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -5639,6 +5377,12 @@ function registerFuturesTools() {
|
|
|
5639
5377
|
];
|
|
5640
5378
|
}
|
|
5641
5379
|
var TWO_DAYS_MS = 2 * 24 * 60 * 60 * 1e3;
|
|
5380
|
+
var DEMO_PROPERTY = {
|
|
5381
|
+
demo: {
|
|
5382
|
+
type: "boolean",
|
|
5383
|
+
description: "Query simulated trading (demo) market data. Default: false (live market data)."
|
|
5384
|
+
}
|
|
5385
|
+
};
|
|
5642
5386
|
function registerMarketTools() {
|
|
5643
5387
|
return [
|
|
5644
5388
|
{
|
|
@@ -5652,7 +5396,8 @@ function registerMarketTools() {
|
|
|
5652
5396
|
instId: {
|
|
5653
5397
|
type: "string",
|
|
5654
5398
|
description: "e.g. BTC-USDT, BTC-USDT-SWAP"
|
|
5655
|
-
}
|
|
5399
|
+
},
|
|
5400
|
+
...DEMO_PROPERTY
|
|
5656
5401
|
},
|
|
5657
5402
|
required: ["instId"]
|
|
5658
5403
|
},
|
|
@@ -5661,7 +5406,8 @@ function registerMarketTools() {
|
|
|
5661
5406
|
const response = await context.client.publicGet(
|
|
5662
5407
|
"/api/v5/market/ticker",
|
|
5663
5408
|
{ instId: requireString(args, "instId") },
|
|
5664
|
-
publicRateLimit("market_get_ticker", 20)
|
|
5409
|
+
publicRateLimit("market_get_ticker", 20),
|
|
5410
|
+
readBoolean(args, "demo") ?? false
|
|
5665
5411
|
);
|
|
5666
5412
|
return normalizeResponse(response);
|
|
5667
5413
|
}
|
|
@@ -5685,7 +5431,8 @@ function registerMarketTools() {
|
|
|
5685
5431
|
instFamily: {
|
|
5686
5432
|
type: "string",
|
|
5687
5433
|
description: "e.g. BTC-USD"
|
|
5688
|
-
}
|
|
5434
|
+
},
|
|
5435
|
+
...DEMO_PROPERTY
|
|
5689
5436
|
},
|
|
5690
5437
|
required: ["instType"]
|
|
5691
5438
|
},
|
|
@@ -5698,7 +5445,8 @@ function registerMarketTools() {
|
|
|
5698
5445
|
uly: readString(args, "uly"),
|
|
5699
5446
|
instFamily: readString(args, "instFamily")
|
|
5700
5447
|
}),
|
|
5701
|
-
publicRateLimit("market_get_tickers", 20)
|
|
5448
|
+
publicRateLimit("market_get_tickers", 20),
|
|
5449
|
+
readBoolean(args, "demo") ?? false
|
|
5702
5450
|
);
|
|
5703
5451
|
return normalizeResponse(response);
|
|
5704
5452
|
}
|
|
@@ -5718,7 +5466,8 @@ function registerMarketTools() {
|
|
|
5718
5466
|
sz: {
|
|
5719
5467
|
type: "number",
|
|
5720
5468
|
description: "Depth per side, default 1, max 400"
|
|
5721
|
-
}
|
|
5469
|
+
},
|
|
5470
|
+
...DEMO_PROPERTY
|
|
5722
5471
|
},
|
|
5723
5472
|
required: ["instId"]
|
|
5724
5473
|
},
|
|
@@ -5730,7 +5479,8 @@ function registerMarketTools() {
|
|
|
5730
5479
|
instId: requireString(args, "instId"),
|
|
5731
5480
|
sz: readNumber(args, "sz")
|
|
5732
5481
|
}),
|
|
5733
|
-
publicRateLimit("market_get_orderbook", 20)
|
|
5482
|
+
publicRateLimit("market_get_orderbook", 20),
|
|
5483
|
+
readBoolean(args, "demo") ?? false
|
|
5734
5484
|
);
|
|
5735
5485
|
return normalizeResponse(response);
|
|
5736
5486
|
}
|
|
@@ -5763,7 +5513,8 @@ function registerMarketTools() {
|
|
|
5763
5513
|
limit: {
|
|
5764
5514
|
type: "number",
|
|
5765
5515
|
description: "Max results (default 100)"
|
|
5766
|
-
}
|
|
5516
|
+
},
|
|
5517
|
+
...DEMO_PROPERTY
|
|
5767
5518
|
},
|
|
5768
5519
|
required: ["instId"]
|
|
5769
5520
|
},
|
|
@@ -5771,6 +5522,7 @@ function registerMarketTools() {
|
|
|
5771
5522
|
const args = asRecord(rawArgs);
|
|
5772
5523
|
const afterTs = readString(args, "after");
|
|
5773
5524
|
const beforeTs = readString(args, "before");
|
|
5525
|
+
const demo = readBoolean(args, "demo") ?? false;
|
|
5774
5526
|
const query = compactObject({
|
|
5775
5527
|
instId: requireString(args, "instId"),
|
|
5776
5528
|
bar: readString(args, "bar"),
|
|
@@ -5782,9 +5534,9 @@ function registerMarketTools() {
|
|
|
5782
5534
|
const hasTimestamp = afterTs !== void 0 || beforeTs !== void 0;
|
|
5783
5535
|
const useHistory = afterTs !== void 0 && Number(afterTs) < Date.now() - TWO_DAYS_MS;
|
|
5784
5536
|
const path4 = useHistory ? "/api/v5/market/history-candles" : "/api/v5/market/candles";
|
|
5785
|
-
const response = await context.client.publicGet(path4, query, rateLimit);
|
|
5537
|
+
const response = await context.client.publicGet(path4, query, rateLimit, demo);
|
|
5786
5538
|
if (!useHistory && hasTimestamp && Array.isArray(response.data) && response.data.length === 0) {
|
|
5787
|
-
return normalizeResponse(await context.client.publicGet("/api/v5/market/history-candles", query, rateLimit));
|
|
5539
|
+
return normalizeResponse(await context.client.publicGet("/api/v5/market/history-candles", query, rateLimit, demo));
|
|
5788
5540
|
}
|
|
5789
5541
|
return normalizeResponse(response);
|
|
5790
5542
|
}
|
|
@@ -5812,7 +5564,8 @@ function registerMarketTools() {
|
|
|
5812
5564
|
instFamily: {
|
|
5813
5565
|
type: "string",
|
|
5814
5566
|
description: "e.g. BTC-USD"
|
|
5815
|
-
}
|
|
5567
|
+
},
|
|
5568
|
+
...DEMO_PROPERTY
|
|
5816
5569
|
},
|
|
5817
5570
|
required: ["instType"]
|
|
5818
5571
|
},
|
|
@@ -5826,7 +5579,8 @@ function registerMarketTools() {
|
|
|
5826
5579
|
uly: readString(args, "uly"),
|
|
5827
5580
|
instFamily: readString(args, "instFamily")
|
|
5828
5581
|
}),
|
|
5829
|
-
publicRateLimit("market_get_instruments", 20)
|
|
5582
|
+
publicRateLimit("market_get_instruments", 20),
|
|
5583
|
+
readBoolean(args, "demo") ?? false
|
|
5830
5584
|
);
|
|
5831
5585
|
return normalizeResponse(response);
|
|
5832
5586
|
}
|
|
@@ -5858,13 +5612,15 @@ function registerMarketTools() {
|
|
|
5858
5612
|
limit: {
|
|
5859
5613
|
type: "number",
|
|
5860
5614
|
description: "History records (default 20, max 100)"
|
|
5861
|
-
}
|
|
5615
|
+
},
|
|
5616
|
+
...DEMO_PROPERTY
|
|
5862
5617
|
},
|
|
5863
5618
|
required: ["instId"]
|
|
5864
5619
|
},
|
|
5865
5620
|
handler: async (rawArgs, context) => {
|
|
5866
5621
|
const args = asRecord(rawArgs);
|
|
5867
5622
|
const isHistory = readBoolean(args, "history") ?? false;
|
|
5623
|
+
const demo = readBoolean(args, "demo") ?? false;
|
|
5868
5624
|
if (isHistory) {
|
|
5869
5625
|
const response2 = await context.client.publicGet(
|
|
5870
5626
|
"/api/v5/public/funding-rate-history",
|
|
@@ -5874,14 +5630,16 @@ function registerMarketTools() {
|
|
|
5874
5630
|
before: readString(args, "before"),
|
|
5875
5631
|
limit: readNumber(args, "limit") ?? 20
|
|
5876
5632
|
}),
|
|
5877
|
-
publicRateLimit("market_get_funding_rate", 20)
|
|
5633
|
+
publicRateLimit("market_get_funding_rate", 20),
|
|
5634
|
+
demo
|
|
5878
5635
|
);
|
|
5879
5636
|
return normalizeResponse(response2);
|
|
5880
5637
|
}
|
|
5881
5638
|
const response = await context.client.publicGet(
|
|
5882
5639
|
"/api/v5/public/funding-rate",
|
|
5883
5640
|
{ instId: requireString(args, "instId") },
|
|
5884
|
-
publicRateLimit("market_get_funding_rate", 20)
|
|
5641
|
+
publicRateLimit("market_get_funding_rate", 20),
|
|
5642
|
+
demo
|
|
5885
5643
|
);
|
|
5886
5644
|
return normalizeResponse(response);
|
|
5887
5645
|
}
|
|
@@ -5908,7 +5666,8 @@ function registerMarketTools() {
|
|
|
5908
5666
|
},
|
|
5909
5667
|
instFamily: {
|
|
5910
5668
|
type: "string"
|
|
5911
|
-
}
|
|
5669
|
+
},
|
|
5670
|
+
...DEMO_PROPERTY
|
|
5912
5671
|
},
|
|
5913
5672
|
required: ["instType"]
|
|
5914
5673
|
},
|
|
@@ -5922,7 +5681,8 @@ function registerMarketTools() {
|
|
|
5922
5681
|
uly: readString(args, "uly"),
|
|
5923
5682
|
instFamily: readString(args, "instFamily")
|
|
5924
5683
|
}),
|
|
5925
|
-
publicRateLimit("market_get_mark_price", 10)
|
|
5684
|
+
publicRateLimit("market_get_mark_price", 10),
|
|
5685
|
+
readBoolean(args, "demo") ?? false
|
|
5926
5686
|
);
|
|
5927
5687
|
return normalizeResponse(response);
|
|
5928
5688
|
}
|
|
@@ -5942,7 +5702,8 @@ function registerMarketTools() {
|
|
|
5942
5702
|
limit: {
|
|
5943
5703
|
type: "number",
|
|
5944
5704
|
description: "Default 20, max 500"
|
|
5945
|
-
}
|
|
5705
|
+
},
|
|
5706
|
+
...DEMO_PROPERTY
|
|
5946
5707
|
},
|
|
5947
5708
|
required: ["instId"]
|
|
5948
5709
|
},
|
|
@@ -5954,7 +5715,8 @@ function registerMarketTools() {
|
|
|
5954
5715
|
instId: requireString(args, "instId"),
|
|
5955
5716
|
limit: readNumber(args, "limit") ?? 20
|
|
5956
5717
|
}),
|
|
5957
|
-
publicRateLimit("market_get_trades", 20)
|
|
5718
|
+
publicRateLimit("market_get_trades", 20),
|
|
5719
|
+
readBoolean(args, "demo") ?? false
|
|
5958
5720
|
);
|
|
5959
5721
|
return normalizeResponse(response);
|
|
5960
5722
|
}
|
|
@@ -5974,7 +5736,8 @@ function registerMarketTools() {
|
|
|
5974
5736
|
quoteCcy: {
|
|
5975
5737
|
type: "string",
|
|
5976
5738
|
description: "e.g. USD or USDT"
|
|
5977
|
-
}
|
|
5739
|
+
},
|
|
5740
|
+
...DEMO_PROPERTY
|
|
5978
5741
|
}
|
|
5979
5742
|
},
|
|
5980
5743
|
handler: async (rawArgs, context) => {
|
|
@@ -5985,7 +5748,8 @@ function registerMarketTools() {
|
|
|
5985
5748
|
instId: readString(args, "instId"),
|
|
5986
5749
|
quoteCcy: readString(args, "quoteCcy")
|
|
5987
5750
|
}),
|
|
5988
|
-
publicRateLimit("market_get_index_ticker", 20)
|
|
5751
|
+
publicRateLimit("market_get_index_ticker", 20),
|
|
5752
|
+
readBoolean(args, "demo") ?? false
|
|
5989
5753
|
);
|
|
5990
5754
|
return normalizeResponse(response);
|
|
5991
5755
|
}
|
|
@@ -6022,7 +5786,8 @@ function registerMarketTools() {
|
|
|
6022
5786
|
history: {
|
|
6023
5787
|
type: "boolean",
|
|
6024
5788
|
description: "true=older historical data"
|
|
6025
|
-
}
|
|
5789
|
+
},
|
|
5790
|
+
...DEMO_PROPERTY
|
|
6026
5791
|
},
|
|
6027
5792
|
required: ["instId"]
|
|
6028
5793
|
},
|
|
@@ -6039,7 +5804,8 @@ function registerMarketTools() {
|
|
|
6039
5804
|
before: readString(args, "before"),
|
|
6040
5805
|
limit: readNumber(args, "limit")
|
|
6041
5806
|
}),
|
|
6042
|
-
publicRateLimit("market_get_index_candles", 20)
|
|
5807
|
+
publicRateLimit("market_get_index_candles", 20),
|
|
5808
|
+
readBoolean(args, "demo") ?? false
|
|
6043
5809
|
);
|
|
6044
5810
|
return normalizeResponse(response);
|
|
6045
5811
|
}
|
|
@@ -6055,7 +5821,8 @@ function registerMarketTools() {
|
|
|
6055
5821
|
instId: {
|
|
6056
5822
|
type: "string",
|
|
6057
5823
|
description: "SWAP or FUTURES ID, e.g. BTC-USDT-SWAP"
|
|
6058
|
-
}
|
|
5824
|
+
},
|
|
5825
|
+
...DEMO_PROPERTY
|
|
6059
5826
|
},
|
|
6060
5827
|
required: ["instId"]
|
|
6061
5828
|
},
|
|
@@ -6064,7 +5831,8 @@ function registerMarketTools() {
|
|
|
6064
5831
|
const response = await context.client.publicGet(
|
|
6065
5832
|
"/api/v5/public/price-limit",
|
|
6066
5833
|
{ instId: requireString(args, "instId") },
|
|
6067
|
-
publicRateLimit("market_get_price_limit", 20)
|
|
5834
|
+
publicRateLimit("market_get_price_limit", 20),
|
|
5835
|
+
readBoolean(args, "demo") ?? false
|
|
6068
5836
|
);
|
|
6069
5837
|
return normalizeResponse(response);
|
|
6070
5838
|
}
|
|
@@ -6091,7 +5859,8 @@ function registerMarketTools() {
|
|
|
6091
5859
|
},
|
|
6092
5860
|
instFamily: {
|
|
6093
5861
|
type: "string"
|
|
6094
|
-
}
|
|
5862
|
+
},
|
|
5863
|
+
...DEMO_PROPERTY
|
|
6095
5864
|
},
|
|
6096
5865
|
required: ["instType"]
|
|
6097
5866
|
},
|
|
@@ -6105,7 +5874,8 @@ function registerMarketTools() {
|
|
|
6105
5874
|
uly: readString(args, "uly"),
|
|
6106
5875
|
instFamily: readString(args, "instFamily")
|
|
6107
5876
|
}),
|
|
6108
|
-
publicRateLimit("market_get_open_interest", 20)
|
|
5877
|
+
publicRateLimit("market_get_open_interest", 20),
|
|
5878
|
+
readBoolean(args, "demo") ?? false
|
|
6109
5879
|
);
|
|
6110
5880
|
return normalizeResponse(response);
|
|
6111
5881
|
}
|
|
@@ -6126,7 +5896,8 @@ function registerMarketTools() {
|
|
|
6126
5896
|
instId: {
|
|
6127
5897
|
type: "string",
|
|
6128
5898
|
description: "Optional: filter by specific instrument ID, e.g. AAPL-USDT-SWAP"
|
|
6129
|
-
}
|
|
5899
|
+
},
|
|
5900
|
+
...DEMO_PROPERTY
|
|
6130
5901
|
},
|
|
6131
5902
|
required: []
|
|
6132
5903
|
},
|
|
@@ -6137,7 +5908,8 @@ function registerMarketTools() {
|
|
|
6137
5908
|
const response = await context.client.publicGet(
|
|
6138
5909
|
"/api/v5/public/instruments",
|
|
6139
5910
|
compactObject({ instType, instId }),
|
|
6140
|
-
publicRateLimit("market_get_stock_tokens", 20)
|
|
5911
|
+
publicRateLimit("market_get_stock_tokens", 20),
|
|
5912
|
+
readBoolean(args, "demo") ?? false
|
|
6141
5913
|
);
|
|
6142
5914
|
const data = response.data;
|
|
6143
5915
|
const filtered = Array.isArray(data) ? data.filter((item) => item.instCategory === "3") : data;
|
|
@@ -6147,7 +5919,7 @@ function registerMarketTools() {
|
|
|
6147
5919
|
{
|
|
6148
5920
|
name: "market_get_instruments_by_category",
|
|
6149
5921
|
module: "market",
|
|
6150
|
-
description: "Discover tradeable instruments by asset category. Stock tokens (instCategory=3, e.g. AAPL-USDT-SWAP, TSLA-USDT-SWAP), Metals (4, e.g. XAUUSDT-USDT-SWAP for gold), Commodities (5, e.g. OIL-USDT-SWAP for crude oil), Forex (6, e.g. EURUSDT-USDT-SWAP for EUR/USD), Bonds (7, e.g. US30Y-USDT-SWAP). Use this to find instIds before querying prices or placing orders. Filters client-side by instCategory.",
|
|
5922
|
+
description: "Discover tradeable instruments by asset category. Stock tokens (instCategory=3, e.g. AAPL-USDT-SWAP, TSLA-USDT-SWAP), Metals (4, e.g. XAUUSDT-USDT-SWAP for gold), Commodities (5, e.g. OIL-USDT-SWAP for crude oil), Forex (6, e.g. EURUSDT-USDT-SWAP for EUR/USD), Bonds (7, e.g. US30Y-USDT-SWAP for crude oil). Use this to find instIds before querying prices or placing orders. Filters client-side by instCategory.",
|
|
6151
5923
|
isWrite: false,
|
|
6152
5924
|
inputSchema: {
|
|
6153
5925
|
type: "object",
|
|
@@ -6165,7 +5937,8 @@ function registerMarketTools() {
|
|
|
6165
5937
|
instId: {
|
|
6166
5938
|
type: "string",
|
|
6167
5939
|
description: "Optional: filter by specific instrument ID"
|
|
6168
|
-
}
|
|
5940
|
+
},
|
|
5941
|
+
...DEMO_PROPERTY
|
|
6169
5942
|
},
|
|
6170
5943
|
required: ["instCategory"]
|
|
6171
5944
|
},
|
|
@@ -6177,7 +5950,8 @@ function registerMarketTools() {
|
|
|
6177
5950
|
const response = await context.client.publicGet(
|
|
6178
5951
|
"/api/v5/public/instruments",
|
|
6179
5952
|
compactObject({ instType, instId }),
|
|
6180
|
-
publicRateLimit("market_get_instruments_by_category", 20)
|
|
5953
|
+
publicRateLimit("market_get_instruments_by_category", 20),
|
|
5954
|
+
readBoolean(args, "demo") ?? false
|
|
6181
5955
|
);
|
|
6182
5956
|
const data = response.data;
|
|
6183
5957
|
const filtered = Array.isArray(data) ? data.filter((item) => item.instCategory === instCategory) : data;
|
|
@@ -7709,12 +7483,12 @@ function toMcpTool(tool) {
|
|
|
7709
7483
|
};
|
|
7710
7484
|
}
|
|
7711
7485
|
function configFilePath() {
|
|
7712
|
-
return
|
|
7486
|
+
return join4(homedir2(), ".okx", "config.toml");
|
|
7713
7487
|
}
|
|
7714
7488
|
function readFullConfig() {
|
|
7715
7489
|
const path4 = configFilePath();
|
|
7716
7490
|
if (!existsSync3(path4)) return { profiles: {} };
|
|
7717
|
-
const raw =
|
|
7491
|
+
const raw = readFileSync3(path4, "utf-8");
|
|
7718
7492
|
try {
|
|
7719
7493
|
return parse(raw);
|
|
7720
7494
|
} catch (err) {
|
|
@@ -7845,21 +7619,21 @@ function loadConfig(cli) {
|
|
|
7845
7619
|
verbose: cli.verbose ?? false
|
|
7846
7620
|
};
|
|
7847
7621
|
}
|
|
7848
|
-
var CACHE_FILE =
|
|
7622
|
+
var CACHE_FILE = join5(homedir3(), ".okx", "update-check.json");
|
|
7849
7623
|
var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
7850
|
-
function
|
|
7624
|
+
function readCache() {
|
|
7851
7625
|
try {
|
|
7852
7626
|
if (existsSync4(CACHE_FILE)) {
|
|
7853
|
-
return JSON.parse(
|
|
7627
|
+
return JSON.parse(readFileSync4(CACHE_FILE, "utf-8"));
|
|
7854
7628
|
}
|
|
7855
7629
|
} catch {
|
|
7856
7630
|
}
|
|
7857
7631
|
return {};
|
|
7858
7632
|
}
|
|
7859
|
-
function
|
|
7633
|
+
function writeCache(cache) {
|
|
7860
7634
|
try {
|
|
7861
|
-
|
|
7862
|
-
|
|
7635
|
+
mkdirSync5(join5(homedir3(), ".okx"), { recursive: true });
|
|
7636
|
+
writeFileSync4(CACHE_FILE, JSON.stringify(cache, null, 2), "utf-8");
|
|
7863
7637
|
} catch {
|
|
7864
7638
|
}
|
|
7865
7639
|
}
|
|
@@ -7890,14 +7664,14 @@ async function fetchLatestVersion(packageName) {
|
|
|
7890
7664
|
function refreshCacheInBackground(packageName) {
|
|
7891
7665
|
fetchLatestVersion(packageName).then((latest) => {
|
|
7892
7666
|
if (!latest) return;
|
|
7893
|
-
const cache =
|
|
7667
|
+
const cache = readCache();
|
|
7894
7668
|
cache[packageName] = { latestVersion: latest, checkedAt: Date.now() };
|
|
7895
|
-
|
|
7669
|
+
writeCache(cache);
|
|
7896
7670
|
}).catch(() => {
|
|
7897
7671
|
});
|
|
7898
7672
|
}
|
|
7899
7673
|
function checkForUpdates(packageName, currentVersion) {
|
|
7900
|
-
const cache =
|
|
7674
|
+
const cache = readCache();
|
|
7901
7675
|
const entry = cache[packageName];
|
|
7902
7676
|
if (entry && isNewerVersion(currentVersion, entry.latestVersion)) {
|
|
7903
7677
|
process.stderr.write(
|
|
@@ -8127,7 +7901,7 @@ var _require = createRequire(import.meta.url);
|
|
|
8127
7901
|
var pkg = _require("../package.json");
|
|
8128
7902
|
var SERVER_NAME = "okx-trade-mcp";
|
|
8129
7903
|
var SERVER_VERSION = pkg.version;
|
|
8130
|
-
var GIT_HASH = true ? "
|
|
7904
|
+
var GIT_HASH = true ? "6d4d559" : "dev";
|
|
8131
7905
|
|
|
8132
7906
|
// src/server.ts
|
|
8133
7907
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|