@okx_ai/okx-trade-cli 1.3.0-beta.2 → 1.3.0-beta.4
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 +360 -83
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/scripts/postinstall.js +112 -2
package/dist/index.js
CHANGED
|
@@ -4,25 +4,37 @@
|
|
|
4
4
|
import { createRequire as createRequire3 } from "module";
|
|
5
5
|
|
|
6
6
|
// ../core/dist/index.js
|
|
7
|
-
import { ProxyAgent } from "undici";
|
|
7
|
+
import { Agent, ProxyAgent } from "undici";
|
|
8
|
+
import { execFile } from "child_process";
|
|
9
|
+
import { homedir } from "os";
|
|
10
|
+
import { join } from "path";
|
|
11
|
+
import {
|
|
12
|
+
readFileSync,
|
|
13
|
+
writeFileSync,
|
|
14
|
+
mkdirSync,
|
|
15
|
+
unlinkSync,
|
|
16
|
+
renameSync
|
|
17
|
+
} from "fs";
|
|
18
|
+
import { homedir as homedir2 } from "os";
|
|
19
|
+
import { join as join2, dirname } from "path";
|
|
8
20
|
import { createHmac } from "crypto";
|
|
9
21
|
import fs from "fs";
|
|
10
22
|
import path from "path";
|
|
11
23
|
import os from "os";
|
|
12
|
-
import { writeFileSync, renameSync, unlinkSync, mkdirSync } from "fs";
|
|
13
|
-
import { join, resolve, basename, sep } from "path";
|
|
24
|
+
import { writeFileSync as writeFileSync2, renameSync as renameSync2, unlinkSync as unlinkSync2, mkdirSync as mkdirSync2 } from "fs";
|
|
25
|
+
import { join as join3, resolve, basename, sep } from "path";
|
|
14
26
|
import { randomUUID } from "crypto";
|
|
15
27
|
import yauzl from "yauzl";
|
|
16
|
-
import { createWriteStream, mkdirSync as
|
|
17
|
-
import { resolve as resolve2, dirname } from "path";
|
|
18
|
-
import { readFileSync, existsSync } from "fs";
|
|
19
|
-
import { join as
|
|
20
|
-
import { readFileSync as
|
|
21
|
-
import { join as
|
|
22
|
-
import { homedir } from "os";
|
|
23
|
-
import { readFileSync as
|
|
24
|
-
import { join as
|
|
25
|
-
import { homedir as
|
|
28
|
+
import { createWriteStream, mkdirSync as mkdirSync3 } from "fs";
|
|
29
|
+
import { resolve as resolve2, dirname as dirname2 } from "path";
|
|
30
|
+
import { readFileSync as readFileSync2, existsSync } from "fs";
|
|
31
|
+
import { join as join4 } from "path";
|
|
32
|
+
import { readFileSync as readFileSync3, writeFileSync as writeFileSync3, mkdirSync as mkdirSync4, existsSync as existsSync2 } from "fs";
|
|
33
|
+
import { join as join5, dirname as dirname3 } from "path";
|
|
34
|
+
import { homedir as homedir3 } from "os";
|
|
35
|
+
import { readFileSync as readFileSync4, writeFileSync as writeFileSync4, mkdirSync as mkdirSync5, existsSync as existsSync3 } from "fs";
|
|
36
|
+
import { join as join6, dirname as dirname4 } from "path";
|
|
37
|
+
import { homedir as homedir4 } from "os";
|
|
26
38
|
|
|
27
39
|
// ../../node_modules/.pnpm/smol-toml@1.6.0/node_modules/smol-toml/dist/error.js
|
|
28
40
|
function getLineColFromPtr(string, ptr) {
|
|
@@ -851,9 +863,9 @@ function stringify(obj, { maxDepth = 1e3, numbersAsFloat = false } = {}) {
|
|
|
851
863
|
}
|
|
852
864
|
|
|
853
865
|
// ../core/dist/index.js
|
|
854
|
-
import { readFileSync as
|
|
855
|
-
import { join as
|
|
856
|
-
import { homedir as
|
|
866
|
+
import { readFileSync as readFileSync5, writeFileSync as writeFileSync5, mkdirSync as mkdirSync6, existsSync as existsSync4 } from "fs";
|
|
867
|
+
import { join as join7 } from "path";
|
|
868
|
+
import { homedir as homedir5 } from "os";
|
|
857
869
|
import fs2 from "fs";
|
|
858
870
|
import path2 from "path";
|
|
859
871
|
import os2 from "os";
|
|
@@ -861,6 +873,123 @@ import * as fs3 from "fs";
|
|
|
861
873
|
import * as path3 from "path";
|
|
862
874
|
import * as os3 from "os";
|
|
863
875
|
import { execFileSync } from "child_process";
|
|
876
|
+
var EXEC_TIMEOUT_MS = 3e4;
|
|
877
|
+
var DOH_BIN_DIR = join(homedir(), ".okx", "bin");
|
|
878
|
+
function getDohBinaryPath() {
|
|
879
|
+
if (process.env.OKX_DOH_BINARY_PATH) {
|
|
880
|
+
return process.env.OKX_DOH_BINARY_PATH;
|
|
881
|
+
}
|
|
882
|
+
const ext = process.platform === "win32" ? ".exe" : "";
|
|
883
|
+
return join(DOH_BIN_DIR, `okx-doh-resolver${ext}`);
|
|
884
|
+
}
|
|
885
|
+
function execDohBinary(domain, exclude = [], userAgent) {
|
|
886
|
+
const binPath = getDohBinaryPath();
|
|
887
|
+
const args = ["--domain", domain];
|
|
888
|
+
if (exclude.length > 0) {
|
|
889
|
+
args.push("--exclude", exclude.join(","));
|
|
890
|
+
}
|
|
891
|
+
if (userAgent) {
|
|
892
|
+
args.push("--user-agent", userAgent);
|
|
893
|
+
}
|
|
894
|
+
return new Promise((resolve3) => {
|
|
895
|
+
execFile(
|
|
896
|
+
binPath,
|
|
897
|
+
args,
|
|
898
|
+
{ timeout: EXEC_TIMEOUT_MS, encoding: "utf-8" },
|
|
899
|
+
(error, stdout) => {
|
|
900
|
+
if (error) {
|
|
901
|
+
resolve3(null);
|
|
902
|
+
return;
|
|
903
|
+
}
|
|
904
|
+
try {
|
|
905
|
+
const result = JSON.parse(stdout);
|
|
906
|
+
if (result.code === 0 && result.data) {
|
|
907
|
+
resolve3(result.data);
|
|
908
|
+
} else {
|
|
909
|
+
resolve3(null);
|
|
910
|
+
}
|
|
911
|
+
} catch {
|
|
912
|
+
resolve3(null);
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
);
|
|
916
|
+
});
|
|
917
|
+
}
|
|
918
|
+
var DOH_CACHE_PATH = join2(homedir2(), ".okx", "doh-node-cache.json");
|
|
919
|
+
function readCache(hostname, cachePath = DOH_CACHE_PATH) {
|
|
920
|
+
try {
|
|
921
|
+
const raw = readFileSync(cachePath, "utf-8");
|
|
922
|
+
const file = JSON.parse(raw);
|
|
923
|
+
return file[hostname] ?? null;
|
|
924
|
+
} catch {
|
|
925
|
+
return null;
|
|
926
|
+
}
|
|
927
|
+
}
|
|
928
|
+
function writeCache(hostname, entry, cachePath = DOH_CACHE_PATH) {
|
|
929
|
+
try {
|
|
930
|
+
const dir = dirname(cachePath);
|
|
931
|
+
mkdirSync(dir, { recursive: true });
|
|
932
|
+
let file = {};
|
|
933
|
+
try {
|
|
934
|
+
file = JSON.parse(readFileSync(cachePath, "utf-8"));
|
|
935
|
+
} catch {
|
|
936
|
+
}
|
|
937
|
+
file[hostname] = entry;
|
|
938
|
+
const tmpPath = `${cachePath}.tmp`;
|
|
939
|
+
writeFileSync(tmpPath, JSON.stringify(file));
|
|
940
|
+
renameSync(tmpPath, cachePath);
|
|
941
|
+
} catch {
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
var FAILED_NODE_TTL_MS = 60 * 60 * 1e3;
|
|
945
|
+
function classifyAndCache(node, hostname, failedNodes, cachePath) {
|
|
946
|
+
if (!node) {
|
|
947
|
+
return { mode: null, node: null };
|
|
948
|
+
}
|
|
949
|
+
if (node.ip === hostname || node.host === hostname) {
|
|
950
|
+
writeCache(hostname, {
|
|
951
|
+
mode: "direct",
|
|
952
|
+
node: null,
|
|
953
|
+
failedNodes,
|
|
954
|
+
updatedAt: Date.now()
|
|
955
|
+
}, cachePath);
|
|
956
|
+
return { mode: "direct", node: null };
|
|
957
|
+
}
|
|
958
|
+
writeCache(hostname, {
|
|
959
|
+
mode: "proxy",
|
|
960
|
+
node,
|
|
961
|
+
failedNodes,
|
|
962
|
+
updatedAt: Date.now()
|
|
963
|
+
}, cachePath);
|
|
964
|
+
return { mode: "proxy", node };
|
|
965
|
+
}
|
|
966
|
+
function getActiveFailedNodes(nodes) {
|
|
967
|
+
if (!nodes || nodes.length === 0) return [];
|
|
968
|
+
const now = Date.now();
|
|
969
|
+
return nodes.filter((n) => now - n.failedAt < FAILED_NODE_TTL_MS);
|
|
970
|
+
}
|
|
971
|
+
function resolveDoh(hostname, cachePath) {
|
|
972
|
+
const entry = readCache(hostname, cachePath);
|
|
973
|
+
if (entry) {
|
|
974
|
+
if (entry.mode === "direct") {
|
|
975
|
+
return { mode: "direct", node: null };
|
|
976
|
+
}
|
|
977
|
+
if (entry.mode === "proxy" && entry.node) {
|
|
978
|
+
return { mode: "proxy", node: entry.node };
|
|
979
|
+
}
|
|
980
|
+
}
|
|
981
|
+
return { mode: null, node: null };
|
|
982
|
+
}
|
|
983
|
+
async function reResolveDoh(hostname, failedIp, userAgent, cachePath) {
|
|
984
|
+
const entry = readCache(hostname, cachePath);
|
|
985
|
+
const active = getActiveFailedNodes(entry?.failedNodes);
|
|
986
|
+
const now = Date.now();
|
|
987
|
+
const alreadyFailed = failedIp && active.some((n) => n.ip === failedIp);
|
|
988
|
+
const failedNodes = failedIp && !alreadyFailed ? [...active, { ip: failedIp, failedAt: now }] : active;
|
|
989
|
+
const excludeIps = failedNodes.map((n) => n.ip);
|
|
990
|
+
const node = await execDohBinary(hostname, excludeIps, userAgent);
|
|
991
|
+
return classifyAndCache(node, hostname, failedNodes, cachePath);
|
|
992
|
+
}
|
|
864
993
|
function getNow() {
|
|
865
994
|
return (/* @__PURE__ */ new Date()).toISOString();
|
|
866
995
|
}
|
|
@@ -1079,6 +1208,14 @@ var OkxRestClient = class _OkxRestClient {
|
|
|
1079
1208
|
config;
|
|
1080
1209
|
rateLimiter;
|
|
1081
1210
|
dispatcher;
|
|
1211
|
+
// DoH proxy state (lazy-resolved on first request)
|
|
1212
|
+
dohResolved = false;
|
|
1213
|
+
dohRetried = false;
|
|
1214
|
+
directUnverified = false;
|
|
1215
|
+
// The first direct connection has not yet been verified
|
|
1216
|
+
dohNode = null;
|
|
1217
|
+
dohAgent = null;
|
|
1218
|
+
dohBaseUrl = null;
|
|
1082
1219
|
constructor(config) {
|
|
1083
1220
|
this.config = config;
|
|
1084
1221
|
this.rateLimiter = new RateLimiter(3e4, config.verbose);
|
|
@@ -1086,6 +1223,98 @@ var OkxRestClient = class _OkxRestClient {
|
|
|
1086
1223
|
this.dispatcher = new ProxyAgent(config.proxyUrl);
|
|
1087
1224
|
}
|
|
1088
1225
|
}
|
|
1226
|
+
/**
|
|
1227
|
+
* Lazily resolve the DoH proxy node on the first request.
|
|
1228
|
+
* Uses cache-first strategy via the resolver.
|
|
1229
|
+
*/
|
|
1230
|
+
ensureDoh() {
|
|
1231
|
+
if (this.dohResolved || this.dispatcher) return;
|
|
1232
|
+
this.dohResolved = true;
|
|
1233
|
+
try {
|
|
1234
|
+
const { hostname, protocol } = new URL(this.config.baseUrl);
|
|
1235
|
+
const result = resolveDoh(hostname);
|
|
1236
|
+
if (!result.mode) {
|
|
1237
|
+
this.directUnverified = true;
|
|
1238
|
+
if (this.config.verbose) {
|
|
1239
|
+
vlog("DoH: no cache, trying direct connection first");
|
|
1240
|
+
}
|
|
1241
|
+
return;
|
|
1242
|
+
}
|
|
1243
|
+
if (result.mode === "direct") {
|
|
1244
|
+
if (this.config.verbose) {
|
|
1245
|
+
vlog("DoH: mode=direct (overseas or cached), using direct connection");
|
|
1246
|
+
}
|
|
1247
|
+
return;
|
|
1248
|
+
}
|
|
1249
|
+
if (result.node) {
|
|
1250
|
+
this.applyDohNode(result.node, protocol);
|
|
1251
|
+
}
|
|
1252
|
+
} catch (err) {
|
|
1253
|
+
if (this.config.verbose) {
|
|
1254
|
+
const cause = err instanceof Error ? err.message : String(err);
|
|
1255
|
+
vlog(`DoH resolution failed, falling back to direct: ${cause}`);
|
|
1256
|
+
}
|
|
1257
|
+
}
|
|
1258
|
+
}
|
|
1259
|
+
/** Apply a DoH node: set up the custom Agent + base URL. */
|
|
1260
|
+
applyDohNode(node, protocol) {
|
|
1261
|
+
this.dohNode = node;
|
|
1262
|
+
this.dohBaseUrl = `${protocol}//${node.host}`;
|
|
1263
|
+
this.dohAgent = new Agent({
|
|
1264
|
+
connect: {
|
|
1265
|
+
lookup: (_hostname, options, callback) => {
|
|
1266
|
+
if (options?.all) {
|
|
1267
|
+
callback(null, [{ address: node.ip, family: 4 }]);
|
|
1268
|
+
} else {
|
|
1269
|
+
callback(null, node.ip, 4);
|
|
1270
|
+
}
|
|
1271
|
+
}
|
|
1272
|
+
}
|
|
1273
|
+
});
|
|
1274
|
+
if (this.config.verbose) {
|
|
1275
|
+
vlog(`DoH proxy active: \u2192 ${node.host} (${node.ip}), ttl=${node.ttl}s`);
|
|
1276
|
+
}
|
|
1277
|
+
}
|
|
1278
|
+
/**
|
|
1279
|
+
* Handle network failure: re-resolve with --exclude and retry once.
|
|
1280
|
+
* Returns true if retry should proceed, false if already retried.
|
|
1281
|
+
*/
|
|
1282
|
+
async handleDohNetworkFailure() {
|
|
1283
|
+
if (this.dohRetried) return false;
|
|
1284
|
+
this.dohRetried = true;
|
|
1285
|
+
const failedIp = this.dohNode?.ip ?? "";
|
|
1286
|
+
const { hostname, protocol } = new URL(this.config.baseUrl);
|
|
1287
|
+
this.dohNode = null;
|
|
1288
|
+
this.dohAgent = null;
|
|
1289
|
+
this.dohBaseUrl = null;
|
|
1290
|
+
if (!failedIp) this.directUnverified = false;
|
|
1291
|
+
if (this.config.verbose) {
|
|
1292
|
+
vlog(failedIp ? `DoH: proxy node ${failedIp} failed, re-resolving with --exclude` : "DoH: direct connection failed, calling binary for DoH resolution");
|
|
1293
|
+
}
|
|
1294
|
+
try {
|
|
1295
|
+
const result = await reResolveDoh(hostname, failedIp, this.dohUserAgent);
|
|
1296
|
+
if (result.mode === "proxy" && result.node) {
|
|
1297
|
+
this.applyDohNode(result.node, protocol);
|
|
1298
|
+
this.dohRetried = false;
|
|
1299
|
+
return true;
|
|
1300
|
+
}
|
|
1301
|
+
} catch {
|
|
1302
|
+
}
|
|
1303
|
+
if (this.config.verbose) {
|
|
1304
|
+
vlog("DoH: re-resolution failed or switched to direct, retrying with direct connection");
|
|
1305
|
+
}
|
|
1306
|
+
return true;
|
|
1307
|
+
}
|
|
1308
|
+
get activeBaseUrl() {
|
|
1309
|
+
return this.dohNode ? this.dohBaseUrl : this.config.baseUrl;
|
|
1310
|
+
}
|
|
1311
|
+
get activeDispatcher() {
|
|
1312
|
+
return this.dispatcher ?? this.dohAgent ?? void 0;
|
|
1313
|
+
}
|
|
1314
|
+
/** User-Agent for DoH proxy requests: OKX/@okx_ai/{packageName}/{version} */
|
|
1315
|
+
get dohUserAgent() {
|
|
1316
|
+
return `OKX/@okx_ai/${this.config.userAgent ?? "unknown"}`;
|
|
1317
|
+
}
|
|
1089
1318
|
logRequest(method, url, auth) {
|
|
1090
1319
|
if (!this.config.verbose) return;
|
|
1091
1320
|
vlog(`\u2192 ${method} ${url}`);
|
|
@@ -1255,13 +1484,17 @@ var OkxRestClient = class _OkxRestClient {
|
|
|
1255
1484
|
* Security: validates Content-Type and enforces maxBytes limit.
|
|
1256
1485
|
*/
|
|
1257
1486
|
async privatePostBinary(path42, body, opts) {
|
|
1487
|
+
this.ensureDoh();
|
|
1258
1488
|
const maxBytes = opts?.maxBytes ?? _OkxRestClient.DEFAULT_MAX_BYTES;
|
|
1259
1489
|
const expectedCT = opts?.expectedContentType ?? "application/octet-stream";
|
|
1260
1490
|
const bodyJson = body ? JSON.stringify(body) : "";
|
|
1261
1491
|
const endpoint = `POST ${path42}`;
|
|
1262
|
-
this.logRequest("POST", `${this.
|
|
1492
|
+
this.logRequest("POST", `${this.activeBaseUrl}${path42}`, "private");
|
|
1263
1493
|
const reqConfig = { method: "POST", path: path42, auth: "private" };
|
|
1264
1494
|
const headers = this.buildHeaders(reqConfig, path42, bodyJson, getNow());
|
|
1495
|
+
if (this.dohNode) {
|
|
1496
|
+
headers.set("User-Agent", this.dohUserAgent);
|
|
1497
|
+
}
|
|
1265
1498
|
const t0 = Date.now();
|
|
1266
1499
|
const response = await this.fetchBinary(path42, endpoint, headers, bodyJson, t0);
|
|
1267
1500
|
const elapsed = Date.now() - t0;
|
|
@@ -1295,10 +1528,10 @@ var OkxRestClient = class _OkxRestClient {
|
|
|
1295
1528
|
method: "POST",
|
|
1296
1529
|
headers,
|
|
1297
1530
|
body: bodyJson || void 0,
|
|
1298
|
-
signal: AbortSignal.timeout(this.config.timeoutMs)
|
|
1531
|
+
signal: AbortSignal.timeout(this.config.timeoutMs),
|
|
1532
|
+
dispatcher: this.activeDispatcher
|
|
1299
1533
|
};
|
|
1300
|
-
|
|
1301
|
-
return await fetch(`${this.config.baseUrl}${path42}`, fetchOptions);
|
|
1534
|
+
return await fetch(`${this.activeBaseUrl}${path42}`, fetchOptions);
|
|
1302
1535
|
} catch (error) {
|
|
1303
1536
|
if (this.config.verbose) {
|
|
1304
1537
|
vlog(`\u2717 NetworkError after ${Date.now() - t0}ms: ${error instanceof Error ? error.message : String(error)}`);
|
|
@@ -1328,10 +1561,55 @@ var OkxRestClient = class _OkxRestClient {
|
|
|
1328
1561
|
// ---------------------------------------------------------------------------
|
|
1329
1562
|
// JSON request
|
|
1330
1563
|
// ---------------------------------------------------------------------------
|
|
1564
|
+
/**
|
|
1565
|
+
* Handle network error during a JSON request: refresh DoH and maybe retry.
|
|
1566
|
+
* Always either returns a retry result or throws NetworkError.
|
|
1567
|
+
*/
|
|
1568
|
+
async handleRequestNetworkError(error, reqConfig, requestPath, t0) {
|
|
1569
|
+
if (!this.dohRetried) {
|
|
1570
|
+
if (this.config.verbose) {
|
|
1571
|
+
const cause = error instanceof Error ? error.message : String(error);
|
|
1572
|
+
vlog(`Network failure, refreshing DoH: ${cause}`);
|
|
1573
|
+
}
|
|
1574
|
+
const shouldRetry = await this.handleDohNetworkFailure();
|
|
1575
|
+
if (shouldRetry && reqConfig.method === "GET") {
|
|
1576
|
+
return this.request(reqConfig);
|
|
1577
|
+
}
|
|
1578
|
+
}
|
|
1579
|
+
if (this.config.verbose) {
|
|
1580
|
+
const elapsed = Date.now() - t0;
|
|
1581
|
+
const cause = error instanceof Error ? error.message : String(error);
|
|
1582
|
+
vlog(`\u2717 NetworkError after ${elapsed}ms: ${cause}`);
|
|
1583
|
+
}
|
|
1584
|
+
throw new NetworkError(
|
|
1585
|
+
`Failed to call OKX endpoint ${reqConfig.method} ${requestPath}.`,
|
|
1586
|
+
`${reqConfig.method} ${requestPath}`,
|
|
1587
|
+
error
|
|
1588
|
+
);
|
|
1589
|
+
}
|
|
1590
|
+
/**
|
|
1591
|
+
* After a successful HTTP response on direct connection, cache mode=direct.
|
|
1592
|
+
* (Even if the business response is an error, the network path is valid.)
|
|
1593
|
+
*/
|
|
1594
|
+
cacheDirectConnectionIfNeeded() {
|
|
1595
|
+
if (!this.directUnverified || this.dohNode) return;
|
|
1596
|
+
this.directUnverified = false;
|
|
1597
|
+
const { hostname } = new URL(this.config.baseUrl);
|
|
1598
|
+
writeCache(hostname, {
|
|
1599
|
+
mode: "direct",
|
|
1600
|
+
node: null,
|
|
1601
|
+
failedNodes: [],
|
|
1602
|
+
updatedAt: Date.now()
|
|
1603
|
+
});
|
|
1604
|
+
if (this.config.verbose) {
|
|
1605
|
+
vlog("DoH: direct connection succeeded, cached mode=direct");
|
|
1606
|
+
}
|
|
1607
|
+
}
|
|
1331
1608
|
async request(reqConfig) {
|
|
1609
|
+
this.ensureDoh();
|
|
1332
1610
|
const queryString = buildQueryString(reqConfig.query);
|
|
1333
1611
|
const requestPath = queryString.length > 0 ? `${reqConfig.path}?${queryString}` : reqConfig.path;
|
|
1334
|
-
const url = `${this.
|
|
1612
|
+
const url = `${this.activeBaseUrl}${requestPath}`;
|
|
1335
1613
|
const bodyJson = reqConfig.body ? JSON.stringify(reqConfig.body) : "";
|
|
1336
1614
|
const timestamp = getNow();
|
|
1337
1615
|
this.logRequest(reqConfig.method, url, reqConfig.auth);
|
|
@@ -1339,6 +1617,9 @@ var OkxRestClient = class _OkxRestClient {
|
|
|
1339
1617
|
await this.rateLimiter.consume(reqConfig.rateLimit);
|
|
1340
1618
|
}
|
|
1341
1619
|
const headers = this.buildHeaders(reqConfig, requestPath, bodyJson, timestamp);
|
|
1620
|
+
if (this.dohNode) {
|
|
1621
|
+
headers.set("User-Agent", this.dohUserAgent);
|
|
1622
|
+
}
|
|
1342
1623
|
const t0 = Date.now();
|
|
1343
1624
|
let response;
|
|
1344
1625
|
try {
|
|
@@ -1346,27 +1627,17 @@ var OkxRestClient = class _OkxRestClient {
|
|
|
1346
1627
|
method: reqConfig.method,
|
|
1347
1628
|
headers,
|
|
1348
1629
|
body: reqConfig.method === "POST" ? bodyJson : void 0,
|
|
1349
|
-
signal: AbortSignal.timeout(this.config.timeoutMs)
|
|
1630
|
+
signal: AbortSignal.timeout(this.config.timeoutMs),
|
|
1631
|
+
dispatcher: this.activeDispatcher
|
|
1350
1632
|
};
|
|
1351
|
-
if (this.dispatcher) {
|
|
1352
|
-
fetchOptions.dispatcher = this.dispatcher;
|
|
1353
|
-
}
|
|
1354
1633
|
response = await fetch(url, fetchOptions);
|
|
1355
1634
|
} catch (error) {
|
|
1356
|
-
|
|
1357
|
-
const elapsed2 = Date.now() - t0;
|
|
1358
|
-
const cause = error instanceof Error ? error.message : String(error);
|
|
1359
|
-
vlog(`\u2717 NetworkError after ${elapsed2}ms: ${cause}`);
|
|
1360
|
-
}
|
|
1361
|
-
throw new NetworkError(
|
|
1362
|
-
`Failed to call OKX endpoint ${reqConfig.method} ${requestPath}.`,
|
|
1363
|
-
`${reqConfig.method} ${requestPath}`,
|
|
1364
|
-
error
|
|
1365
|
-
);
|
|
1635
|
+
return await this.handleRequestNetworkError(error, reqConfig, requestPath, t0);
|
|
1366
1636
|
}
|
|
1367
1637
|
const rawText = await response.text();
|
|
1368
1638
|
const elapsed = Date.now() - t0;
|
|
1369
1639
|
const traceId = extractTraceId(response.headers);
|
|
1640
|
+
this.cacheDirectConnectionIfNeeded();
|
|
1370
1641
|
return this.processResponse(rawText, response, elapsed, traceId, reqConfig, requestPath);
|
|
1371
1642
|
}
|
|
1372
1643
|
};
|
|
@@ -2398,9 +2669,15 @@ function computeContracts(p) {
|
|
|
2398
2669
|
return { contractsStr, conversionNote };
|
|
2399
2670
|
}
|
|
2400
2671
|
async function resolveQuoteCcySz(instId, sz, tgtCcy, instType, client, tdMode) {
|
|
2401
|
-
if (tgtCcy
|
|
2672
|
+
if (tgtCcy === void 0 || tgtCcy === "base_ccy") {
|
|
2402
2673
|
return { sz, tgtCcy, conversionNote: void 0 };
|
|
2403
2674
|
}
|
|
2675
|
+
if (tgtCcy !== "quote_ccy" && tgtCcy !== "margin") {
|
|
2676
|
+
throw new ValidationError(
|
|
2677
|
+
`Unknown tgtCcy value "${tgtCcy}". Valid values: base_ccy, quote_ccy, margin.`,
|
|
2678
|
+
`Check the --tgtCcy flag. Use base_ccy (default, sz in contracts), quote_ccy (sz in USDT notional), or margin (sz in USDT margin cost).`
|
|
2679
|
+
);
|
|
2680
|
+
}
|
|
2404
2681
|
const isMarginMode = tgtCcy === "margin";
|
|
2405
2682
|
if (isMarginMode && !tdMode) {
|
|
2406
2683
|
throw new Error(
|
|
@@ -3239,19 +3516,19 @@ function safeWriteFile(targetDir, fileName, data) {
|
|
|
3239
3516
|
throw new Error(`Invalid file name: "${fileName}"`);
|
|
3240
3517
|
}
|
|
3241
3518
|
const resolvedDir = resolve(targetDir);
|
|
3242
|
-
const filePath =
|
|
3519
|
+
const filePath = join3(resolvedDir, safeName);
|
|
3243
3520
|
const resolvedPath = resolve(filePath);
|
|
3244
3521
|
if (!resolvedPath.startsWith(resolvedDir + sep)) {
|
|
3245
3522
|
throw new Error(`Path traversal detected: "${fileName}" resolves outside target directory`);
|
|
3246
3523
|
}
|
|
3247
|
-
|
|
3524
|
+
mkdirSync2(resolvedDir, { recursive: true });
|
|
3248
3525
|
const tmpPath = `${resolvedPath}.${randomUUID()}.tmp`;
|
|
3249
3526
|
try {
|
|
3250
|
-
|
|
3251
|
-
|
|
3527
|
+
writeFileSync2(tmpPath, data);
|
|
3528
|
+
renameSync2(tmpPath, resolvedPath);
|
|
3252
3529
|
} catch (err) {
|
|
3253
3530
|
try {
|
|
3254
|
-
|
|
3531
|
+
unlinkSync2(tmpPath);
|
|
3255
3532
|
} catch {
|
|
3256
3533
|
}
|
|
3257
3534
|
throw err;
|
|
@@ -3316,7 +3593,7 @@ async function extractSkillZip(zipPath, targetDir, limits) {
|
|
|
3316
3593
|
const maxFiles = limits?.maxFiles ?? DEFAULT_MAX_FILES;
|
|
3317
3594
|
const maxCompressionRatio = limits?.maxCompressionRatio ?? DEFAULT_MAX_COMPRESSION_RATIO;
|
|
3318
3595
|
const resolvedTarget = resolve2(targetDir);
|
|
3319
|
-
|
|
3596
|
+
mkdirSync3(resolvedTarget, { recursive: true });
|
|
3320
3597
|
return new Promise((resolvePromise, reject) => {
|
|
3321
3598
|
yauzl.open(zipPath, { lazyEntries: true }, (err, zipfile) => {
|
|
3322
3599
|
if (err) return reject(err);
|
|
@@ -3339,7 +3616,7 @@ async function extractSkillZip(zipPath, targetDir, limits) {
|
|
|
3339
3616
|
zipfile.close();
|
|
3340
3617
|
return reject(streamErr);
|
|
3341
3618
|
}
|
|
3342
|
-
|
|
3619
|
+
mkdirSync3(dirname2(resolvedPath), { recursive: true });
|
|
3343
3620
|
const writeStream = createWriteStream(resolvedPath);
|
|
3344
3621
|
readStream.pipe(writeStream);
|
|
3345
3622
|
writeStream.on("close", () => zipfile.readEntry());
|
|
@@ -3355,11 +3632,11 @@ async function extractSkillZip(zipPath, targetDir, limits) {
|
|
|
3355
3632
|
});
|
|
3356
3633
|
}
|
|
3357
3634
|
function readMetaJson(contentDir) {
|
|
3358
|
-
const metaPath =
|
|
3635
|
+
const metaPath = join4(contentDir, "_meta.json");
|
|
3359
3636
|
if (!existsSync(metaPath)) {
|
|
3360
3637
|
throw new Error(`_meta.json not found in ${contentDir}. Invalid skill package.`);
|
|
3361
3638
|
}
|
|
3362
|
-
const raw =
|
|
3639
|
+
const raw = readFileSync2(metaPath, "utf-8");
|
|
3363
3640
|
let parsed;
|
|
3364
3641
|
try {
|
|
3365
3642
|
parsed = JSON.parse(raw);
|
|
@@ -3381,26 +3658,26 @@ function readMetaJson(contentDir) {
|
|
|
3381
3658
|
};
|
|
3382
3659
|
}
|
|
3383
3660
|
function validateSkillMdExists(contentDir) {
|
|
3384
|
-
const skillMdPath =
|
|
3661
|
+
const skillMdPath = join4(contentDir, "SKILL.md");
|
|
3385
3662
|
if (!existsSync(skillMdPath)) {
|
|
3386
3663
|
throw new Error(`SKILL.md not found in ${contentDir}. Invalid skill package.`);
|
|
3387
3664
|
}
|
|
3388
3665
|
}
|
|
3389
|
-
var DEFAULT_REGISTRY_PATH =
|
|
3666
|
+
var DEFAULT_REGISTRY_PATH = join5(homedir3(), ".okx", "skills", "registry.json");
|
|
3390
3667
|
function readRegistry(registryPath = DEFAULT_REGISTRY_PATH) {
|
|
3391
3668
|
if (!existsSync2(registryPath)) {
|
|
3392
3669
|
return { version: 1, skills: {} };
|
|
3393
3670
|
}
|
|
3394
3671
|
try {
|
|
3395
|
-
const raw =
|
|
3672
|
+
const raw = readFileSync3(registryPath, "utf-8");
|
|
3396
3673
|
return JSON.parse(raw);
|
|
3397
3674
|
} catch {
|
|
3398
3675
|
return { version: 1, skills: {} };
|
|
3399
3676
|
}
|
|
3400
3677
|
}
|
|
3401
3678
|
function writeRegistry(registry, registryPath = DEFAULT_REGISTRY_PATH) {
|
|
3402
|
-
|
|
3403
|
-
|
|
3679
|
+
mkdirSync4(dirname3(registryPath), { recursive: true });
|
|
3680
|
+
writeFileSync3(registryPath, JSON.stringify(registry, null, 2) + "\n", "utf-8");
|
|
3404
3681
|
}
|
|
3405
3682
|
function upsertSkillRecord(meta, registryPath = DEFAULT_REGISTRY_PATH) {
|
|
3406
3683
|
const registry = readRegistry(registryPath);
|
|
@@ -7723,12 +8000,12 @@ function createToolRunner(client, config) {
|
|
|
7723
8000
|
};
|
|
7724
8001
|
}
|
|
7725
8002
|
function configFilePath() {
|
|
7726
|
-
return
|
|
8003
|
+
return join6(homedir4(), ".okx", "config.toml");
|
|
7727
8004
|
}
|
|
7728
8005
|
function readFullConfig() {
|
|
7729
8006
|
const path42 = configFilePath();
|
|
7730
8007
|
if (!existsSync3(path42)) return { profiles: {} };
|
|
7731
|
-
const raw =
|
|
8008
|
+
const raw = readFileSync4(path42, "utf-8");
|
|
7732
8009
|
try {
|
|
7733
8010
|
return parse(raw);
|
|
7734
8011
|
} catch (err) {
|
|
@@ -7756,11 +8033,11 @@ var CONFIG_HEADER = `# OKX Trade Kit Configuration
|
|
|
7756
8033
|
`;
|
|
7757
8034
|
function writeFullConfig(config) {
|
|
7758
8035
|
const path42 = configFilePath();
|
|
7759
|
-
const dir =
|
|
8036
|
+
const dir = dirname4(path42);
|
|
7760
8037
|
if (!existsSync3(dir)) {
|
|
7761
|
-
|
|
8038
|
+
mkdirSync5(dir, { recursive: true });
|
|
7762
8039
|
}
|
|
7763
|
-
|
|
8040
|
+
writeFileSync4(path42, CONFIG_HEADER + stringify(config), "utf-8");
|
|
7764
8041
|
}
|
|
7765
8042
|
function expandShorthand(moduleId) {
|
|
7766
8043
|
if (moduleId === "all") return [...MODULES];
|
|
@@ -7874,21 +8151,21 @@ function loadConfig(cli) {
|
|
|
7874
8151
|
verbose: cli.verbose ?? false
|
|
7875
8152
|
};
|
|
7876
8153
|
}
|
|
7877
|
-
var CACHE_FILE =
|
|
8154
|
+
var CACHE_FILE = join7(homedir5(), ".okx", "update-check.json");
|
|
7878
8155
|
var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
7879
|
-
function
|
|
8156
|
+
function readCache2() {
|
|
7880
8157
|
try {
|
|
7881
8158
|
if (existsSync4(CACHE_FILE)) {
|
|
7882
|
-
return JSON.parse(
|
|
8159
|
+
return JSON.parse(readFileSync5(CACHE_FILE, "utf-8"));
|
|
7883
8160
|
}
|
|
7884
8161
|
} catch {
|
|
7885
8162
|
}
|
|
7886
8163
|
return {};
|
|
7887
8164
|
}
|
|
7888
|
-
function
|
|
8165
|
+
function writeCache2(cache) {
|
|
7889
8166
|
try {
|
|
7890
|
-
|
|
7891
|
-
|
|
8167
|
+
mkdirSync6(join7(homedir5(), ".okx"), { recursive: true });
|
|
8168
|
+
writeFileSync5(CACHE_FILE, JSON.stringify(cache, null, 2), "utf-8");
|
|
7892
8169
|
} catch {
|
|
7893
8170
|
}
|
|
7894
8171
|
}
|
|
@@ -7935,14 +8212,14 @@ async function fetchLatestVersion(packageName) {
|
|
|
7935
8212
|
function refreshCacheInBackground(packageName) {
|
|
7936
8213
|
fetchLatestVersion(packageName).then((latest) => {
|
|
7937
8214
|
if (!latest) return;
|
|
7938
|
-
const cache =
|
|
8215
|
+
const cache = readCache2();
|
|
7939
8216
|
cache[packageName] = { latestVersion: latest, checkedAt: Date.now() };
|
|
7940
|
-
|
|
8217
|
+
writeCache2(cache);
|
|
7941
8218
|
}).catch(() => {
|
|
7942
8219
|
});
|
|
7943
8220
|
}
|
|
7944
8221
|
function checkForUpdates(packageName, currentVersion) {
|
|
7945
|
-
const cache =
|
|
8222
|
+
const cache = readCache2();
|
|
7946
8223
|
const entry = cache[packageName];
|
|
7947
8224
|
if (entry && isNewerVersion(currentVersion, entry.latestVersion)) {
|
|
7948
8225
|
process.stderr.write(
|
|
@@ -8797,7 +9074,7 @@ async function cmdDiagnoseMcp(options = {}) {
|
|
|
8797
9074
|
|
|
8798
9075
|
// src/commands/diagnose.ts
|
|
8799
9076
|
var CLI_VERSION = readCliVersion();
|
|
8800
|
-
var GIT_HASH = true ? "
|
|
9077
|
+
var GIT_HASH = true ? "caa6dae" : "dev";
|
|
8801
9078
|
function maskKey2(key) {
|
|
8802
9079
|
if (!key) return "(not set)";
|
|
8803
9080
|
if (key.length <= 8) return "****";
|
|
@@ -9094,24 +9371,24 @@ async function runCliChecks(config, profile, outputPath) {
|
|
|
9094
9371
|
|
|
9095
9372
|
// src/commands/upgrade.ts
|
|
9096
9373
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
9097
|
-
import { readFileSync as
|
|
9098
|
-
import { dirname as
|
|
9099
|
-
import { homedir as
|
|
9374
|
+
import { readFileSync as readFileSync7, writeFileSync as writeFileSync7, mkdirSync as mkdirSync8 } from "fs";
|
|
9375
|
+
import { dirname as dirname6, join as join9 } from "path";
|
|
9376
|
+
import { homedir as homedir7 } from "os";
|
|
9100
9377
|
var PACKAGES = ["@okx_ai/okx-trade-mcp", "@okx_ai/okx-trade-cli"];
|
|
9101
|
-
var CACHE_FILE2 =
|
|
9378
|
+
var CACHE_FILE2 = join9(homedir7(), ".okx", "last_check");
|
|
9102
9379
|
var THROTTLE_MS = 12 * 60 * 60 * 1e3;
|
|
9103
|
-
var NPM_BIN =
|
|
9380
|
+
var NPM_BIN = join9(dirname6(process.execPath), process.platform === "win32" ? "npm.cmd" : "npm");
|
|
9104
9381
|
function readLastCheck() {
|
|
9105
9382
|
try {
|
|
9106
|
-
return parseInt(
|
|
9383
|
+
return parseInt(readFileSync7(CACHE_FILE2, "utf-8").trim(), 10) || 0;
|
|
9107
9384
|
} catch {
|
|
9108
9385
|
return 0;
|
|
9109
9386
|
}
|
|
9110
9387
|
}
|
|
9111
9388
|
function writeLastCheck() {
|
|
9112
9389
|
try {
|
|
9113
|
-
|
|
9114
|
-
|
|
9390
|
+
mkdirSync8(join9(homedir7(), ".okx"), { recursive: true });
|
|
9391
|
+
writeFileSync7(CACHE_FILE2, String(Math.floor(Date.now() / 1e3)), "utf-8");
|
|
9115
9392
|
} catch {
|
|
9116
9393
|
}
|
|
9117
9394
|
}
|
|
@@ -12939,13 +13216,13 @@ async function cmdDcdQuoteAndBuy(run, opts) {
|
|
|
12939
13216
|
}
|
|
12940
13217
|
|
|
12941
13218
|
// src/commands/skill.ts
|
|
12942
|
-
import { tmpdir, homedir as
|
|
12943
|
-
import { join as
|
|
12944
|
-
import { mkdirSync as
|
|
13219
|
+
import { tmpdir, homedir as homedir9 } from "os";
|
|
13220
|
+
import { join as join11, dirname as dirname7 } from "path";
|
|
13221
|
+
import { mkdirSync as mkdirSync9, rmSync, existsSync as existsSync7, copyFileSync as copyFileSync2 } from "fs";
|
|
12945
13222
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
12946
13223
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
12947
13224
|
function resolveNpx() {
|
|
12948
|
-
const sibling =
|
|
13225
|
+
const sibling = join11(dirname7(process.execPath), "npx");
|
|
12949
13226
|
if (existsSync7(sibling)) return sibling;
|
|
12950
13227
|
return "npx";
|
|
12951
13228
|
}
|
|
@@ -12999,13 +13276,13 @@ async function cmdSkillCategories(run, json) {
|
|
|
12999
13276
|
outputLine("");
|
|
13000
13277
|
}
|
|
13001
13278
|
async function cmdSkillAdd(name, config, json) {
|
|
13002
|
-
const tmpBase =
|
|
13003
|
-
|
|
13279
|
+
const tmpBase = join11(tmpdir(), `okx-skill-${randomUUID2()}`);
|
|
13280
|
+
mkdirSync9(tmpBase, { recursive: true });
|
|
13004
13281
|
try {
|
|
13005
13282
|
outputLine(`Downloading ${name}...`);
|
|
13006
13283
|
const client = new OkxRestClient(config);
|
|
13007
13284
|
const zipPath = await downloadSkillZip(client, name, tmpBase);
|
|
13008
|
-
const contentDir = await extractSkillZip(zipPath,
|
|
13285
|
+
const contentDir = await extractSkillZip(zipPath, join11(tmpBase, "content"));
|
|
13009
13286
|
const meta = readMetaJson(contentDir);
|
|
13010
13287
|
validateSkillMdExists(contentDir);
|
|
13011
13288
|
outputLine("Installing to detected agents...");
|
|
@@ -13015,7 +13292,7 @@ async function cmdSkillAdd(name, config, json) {
|
|
|
13015
13292
|
timeout: 6e4
|
|
13016
13293
|
});
|
|
13017
13294
|
} catch (e) {
|
|
13018
|
-
const savedZip =
|
|
13295
|
+
const savedZip = join11(process.cwd(), `${name}.zip`);
|
|
13019
13296
|
try {
|
|
13020
13297
|
copyFileSync2(zipPath, savedZip);
|
|
13021
13298
|
} catch {
|
|
@@ -13054,7 +13331,7 @@ function cmdSkillRemove(name, json) {
|
|
|
13054
13331
|
timeout: 6e4
|
|
13055
13332
|
});
|
|
13056
13333
|
} catch {
|
|
13057
|
-
const agentsPath =
|
|
13334
|
+
const agentsPath = join11(homedir9(), ".agents", "skills", name);
|
|
13058
13335
|
try {
|
|
13059
13336
|
rmSync(agentsPath, { recursive: true, force: true });
|
|
13060
13337
|
} catch {
|
|
@@ -13130,7 +13407,7 @@ function printSkillInstallResult(meta, json) {
|
|
|
13130
13407
|
// src/index.ts
|
|
13131
13408
|
var _require3 = createRequire3(import.meta.url);
|
|
13132
13409
|
var CLI_VERSION2 = _require3("../package.json").version;
|
|
13133
|
-
var GIT_HASH2 = true ? "
|
|
13410
|
+
var GIT_HASH2 = true ? "caa6dae" : "dev";
|
|
13134
13411
|
function handleConfigCommand(action, rest, json, lang, force) {
|
|
13135
13412
|
if (action === "init") return cmdConfigInit(lang === "zh" ? "zh" : "en");
|
|
13136
13413
|
if (action === "show") return cmdConfigShow(json);
|