@okx_ai/okx-trade-cli 1.3.0-beta.2 → 1.3.0-beta.3
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 +483 -925
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/scripts/postinstall.js +111 -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,16 +863,137 @@ 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
|
|
857
|
-
import fs2 from "fs";
|
|
858
|
-
import path2 from "path";
|
|
859
|
-
import os2 from "os";
|
|
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";
|
|
860
869
|
import * as fs3 from "fs";
|
|
861
870
|
import * as path3 from "path";
|
|
862
871
|
import * as os3 from "os";
|
|
863
872
|
import { execFileSync } from "child_process";
|
|
873
|
+
var EXEC_TIMEOUT_MS = 3e4;
|
|
874
|
+
var DOH_BIN_DIR = join(homedir(), ".okx", "bin");
|
|
875
|
+
function getDohBinaryPath() {
|
|
876
|
+
if (process.env.OKX_DOH_BINARY_PATH) {
|
|
877
|
+
return process.env.OKX_DOH_BINARY_PATH;
|
|
878
|
+
}
|
|
879
|
+
const ext = process.platform === "win32" ? ".exe" : "";
|
|
880
|
+
return join(DOH_BIN_DIR, `okx-doh-resolver${ext}`);
|
|
881
|
+
}
|
|
882
|
+
function execDohBinary(domain, exclude = [], userAgent) {
|
|
883
|
+
const binPath = getDohBinaryPath();
|
|
884
|
+
const args = ["--domain", domain];
|
|
885
|
+
if (exclude.length > 0) {
|
|
886
|
+
args.push("--exclude", exclude.join(","));
|
|
887
|
+
}
|
|
888
|
+
if (userAgent) {
|
|
889
|
+
args.push("--user-agent", userAgent);
|
|
890
|
+
}
|
|
891
|
+
return new Promise((resolve3) => {
|
|
892
|
+
execFile(
|
|
893
|
+
binPath,
|
|
894
|
+
args,
|
|
895
|
+
{ timeout: EXEC_TIMEOUT_MS, encoding: "utf-8" },
|
|
896
|
+
(error, stdout) => {
|
|
897
|
+
if (error) {
|
|
898
|
+
resolve3(null);
|
|
899
|
+
return;
|
|
900
|
+
}
|
|
901
|
+
try {
|
|
902
|
+
const result = JSON.parse(stdout);
|
|
903
|
+
if (result.code === 0 && result.data) {
|
|
904
|
+
resolve3(result.data);
|
|
905
|
+
} else {
|
|
906
|
+
resolve3(null);
|
|
907
|
+
}
|
|
908
|
+
} catch {
|
|
909
|
+
resolve3(null);
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
);
|
|
913
|
+
});
|
|
914
|
+
}
|
|
915
|
+
var DOH_CACHE_PATH = join2(homedir2(), ".okx", "doh-node-cache.json");
|
|
916
|
+
function readCache(hostname, cachePath = DOH_CACHE_PATH) {
|
|
917
|
+
try {
|
|
918
|
+
const raw = readFileSync(cachePath, "utf-8");
|
|
919
|
+
const file = JSON.parse(raw);
|
|
920
|
+
return file[hostname] ?? null;
|
|
921
|
+
} catch {
|
|
922
|
+
return null;
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
function writeCache(hostname, entry, cachePath = DOH_CACHE_PATH) {
|
|
926
|
+
try {
|
|
927
|
+
const dir = dirname(cachePath);
|
|
928
|
+
mkdirSync(dir, { recursive: true });
|
|
929
|
+
let file = {};
|
|
930
|
+
try {
|
|
931
|
+
file = JSON.parse(readFileSync(cachePath, "utf-8"));
|
|
932
|
+
} catch {
|
|
933
|
+
}
|
|
934
|
+
file[hostname] = entry;
|
|
935
|
+
const tmpPath = `${cachePath}.tmp`;
|
|
936
|
+
writeFileSync(tmpPath, JSON.stringify(file));
|
|
937
|
+
renameSync(tmpPath, cachePath);
|
|
938
|
+
} catch {
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
var FAILED_NODE_TTL_MS = 60 * 60 * 1e3;
|
|
942
|
+
function classifyAndCache(node, hostname, failedNodes) {
|
|
943
|
+
if (!node) {
|
|
944
|
+
if (failedNodes.length > 0) {
|
|
945
|
+
writeCache(hostname, {
|
|
946
|
+
mode: "direct",
|
|
947
|
+
node: null,
|
|
948
|
+
failedNodes,
|
|
949
|
+
updatedAt: Date.now()
|
|
950
|
+
});
|
|
951
|
+
}
|
|
952
|
+
return { mode: null, node: null };
|
|
953
|
+
}
|
|
954
|
+
if (node.ip === hostname || node.host === hostname) {
|
|
955
|
+
writeCache(hostname, {
|
|
956
|
+
mode: "direct",
|
|
957
|
+
node: null,
|
|
958
|
+
failedNodes,
|
|
959
|
+
updatedAt: Date.now()
|
|
960
|
+
});
|
|
961
|
+
return { mode: "direct", node: null };
|
|
962
|
+
}
|
|
963
|
+
writeCache(hostname, {
|
|
964
|
+
mode: "proxy",
|
|
965
|
+
node,
|
|
966
|
+
failedNodes,
|
|
967
|
+
updatedAt: Date.now()
|
|
968
|
+
});
|
|
969
|
+
return { mode: "proxy", node };
|
|
970
|
+
}
|
|
971
|
+
function getActiveFailedNodes(nodes) {
|
|
972
|
+
if (!nodes || nodes.length === 0) return [];
|
|
973
|
+
const now = Date.now();
|
|
974
|
+
return nodes.filter((n) => now - n.failedAt < FAILED_NODE_TTL_MS);
|
|
975
|
+
}
|
|
976
|
+
async function resolveDoh(hostname) {
|
|
977
|
+
const entry = readCache(hostname);
|
|
978
|
+
if (entry) {
|
|
979
|
+
if (entry.mode === "direct") {
|
|
980
|
+
return { mode: "direct", node: null };
|
|
981
|
+
}
|
|
982
|
+
if (entry.mode === "proxy" && entry.node) {
|
|
983
|
+
return { mode: "proxy", node: entry.node };
|
|
984
|
+
}
|
|
985
|
+
}
|
|
986
|
+
return { mode: null, node: null };
|
|
987
|
+
}
|
|
988
|
+
async function reResolveDoh(hostname, failedIp, userAgent) {
|
|
989
|
+
const entry = readCache(hostname);
|
|
990
|
+
const active = getActiveFailedNodes(entry?.failedNodes);
|
|
991
|
+
const now = Date.now();
|
|
992
|
+
const failedNodes = failedIp ? active.some((n) => n.ip === failedIp) ? active : [...active, { ip: failedIp, failedAt: now }] : active;
|
|
993
|
+
const excludeIps = failedNodes.map((n) => n.ip);
|
|
994
|
+
const node = await execDohBinary(hostname, excludeIps, userAgent);
|
|
995
|
+
return classifyAndCache(node, hostname, failedNodes);
|
|
996
|
+
}
|
|
864
997
|
function getNow() {
|
|
865
998
|
return (/* @__PURE__ */ new Date()).toISOString();
|
|
866
999
|
}
|
|
@@ -1079,6 +1212,14 @@ var OkxRestClient = class _OkxRestClient {
|
|
|
1079
1212
|
config;
|
|
1080
1213
|
rateLimiter;
|
|
1081
1214
|
dispatcher;
|
|
1215
|
+
// DoH proxy state (lazy-resolved on first request)
|
|
1216
|
+
dohResolved = false;
|
|
1217
|
+
dohRetried = false;
|
|
1218
|
+
directUnverified = false;
|
|
1219
|
+
// The first direct connection has not yet been verified
|
|
1220
|
+
dohNode = null;
|
|
1221
|
+
dohAgent = null;
|
|
1222
|
+
dohBaseUrl = null;
|
|
1082
1223
|
constructor(config) {
|
|
1083
1224
|
this.config = config;
|
|
1084
1225
|
this.rateLimiter = new RateLimiter(3e4, config.verbose);
|
|
@@ -1086,6 +1227,97 @@ var OkxRestClient = class _OkxRestClient {
|
|
|
1086
1227
|
this.dispatcher = new ProxyAgent(config.proxyUrl);
|
|
1087
1228
|
}
|
|
1088
1229
|
}
|
|
1230
|
+
/**
|
|
1231
|
+
* Lazily resolve the DoH proxy node on the first request.
|
|
1232
|
+
* Uses cache-first strategy via the resolver.
|
|
1233
|
+
*/
|
|
1234
|
+
async ensureDoh() {
|
|
1235
|
+
if (this.dohResolved || this.dispatcher) return;
|
|
1236
|
+
this.dohResolved = true;
|
|
1237
|
+
try {
|
|
1238
|
+
const { hostname, protocol } = new URL(this.config.baseUrl);
|
|
1239
|
+
const result = await resolveDoh(hostname);
|
|
1240
|
+
if (!result.mode) {
|
|
1241
|
+
this.directUnverified = true;
|
|
1242
|
+
if (this.config.verbose) {
|
|
1243
|
+
vlog("DoH: no cache, trying direct connection first");
|
|
1244
|
+
}
|
|
1245
|
+
return;
|
|
1246
|
+
}
|
|
1247
|
+
if (result.mode === "direct") {
|
|
1248
|
+
if (this.config.verbose) {
|
|
1249
|
+
vlog("DoH: mode=direct (overseas or cached), using direct connection");
|
|
1250
|
+
}
|
|
1251
|
+
return;
|
|
1252
|
+
}
|
|
1253
|
+
if (result.node) {
|
|
1254
|
+
this.applyDohNode(result.node, protocol);
|
|
1255
|
+
}
|
|
1256
|
+
} catch (err) {
|
|
1257
|
+
if (this.config.verbose) {
|
|
1258
|
+
const cause = err instanceof Error ? err.message : String(err);
|
|
1259
|
+
vlog(`DoH resolution failed, falling back to direct: ${cause}`);
|
|
1260
|
+
}
|
|
1261
|
+
}
|
|
1262
|
+
}
|
|
1263
|
+
/** Apply a DoH node: set up the custom Agent + base URL. */
|
|
1264
|
+
applyDohNode(node, protocol) {
|
|
1265
|
+
this.dohNode = node;
|
|
1266
|
+
this.dohBaseUrl = `${protocol}//${node.host}`;
|
|
1267
|
+
this.dohAgent = new Agent({
|
|
1268
|
+
connect: {
|
|
1269
|
+
lookup: (_hostname, options, callback) => {
|
|
1270
|
+
if (options?.all) {
|
|
1271
|
+
callback(null, [{ address: node.ip, family: 4 }]);
|
|
1272
|
+
} else {
|
|
1273
|
+
callback(null, node.ip, 4);
|
|
1274
|
+
}
|
|
1275
|
+
}
|
|
1276
|
+
}
|
|
1277
|
+
});
|
|
1278
|
+
if (this.config.verbose) {
|
|
1279
|
+
vlog(`DoH proxy active: \u2192 ${node.host} (${node.ip}), ttl=${node.ttl}s`);
|
|
1280
|
+
}
|
|
1281
|
+
}
|
|
1282
|
+
/**
|
|
1283
|
+
* Handle network failure: re-resolve with --exclude and retry once.
|
|
1284
|
+
* Returns true if retry should proceed, false if already retried.
|
|
1285
|
+
*/
|
|
1286
|
+
async handleDohNetworkFailure() {
|
|
1287
|
+
if (this.dohRetried) return false;
|
|
1288
|
+
this.dohRetried = true;
|
|
1289
|
+
const failedIp = this.dohNode?.ip ?? "";
|
|
1290
|
+
const { hostname, protocol } = new URL(this.config.baseUrl);
|
|
1291
|
+
this.dohNode = null;
|
|
1292
|
+
this.dohAgent = null;
|
|
1293
|
+
this.dohBaseUrl = null;
|
|
1294
|
+
if (!failedIp) this.directUnverified = false;
|
|
1295
|
+
if (this.config.verbose) {
|
|
1296
|
+
vlog(failedIp ? `DoH: proxy node ${failedIp} failed, re-resolving with --exclude` : "DoH: direct connection failed, calling binary for DoH resolution");
|
|
1297
|
+
}
|
|
1298
|
+
try {
|
|
1299
|
+
const result = await reResolveDoh(hostname, failedIp, this.dohUserAgent);
|
|
1300
|
+
if (result.mode === "proxy" && result.node) {
|
|
1301
|
+
this.applyDohNode(result.node, protocol);
|
|
1302
|
+
return true;
|
|
1303
|
+
}
|
|
1304
|
+
} catch {
|
|
1305
|
+
}
|
|
1306
|
+
if (this.config.verbose) {
|
|
1307
|
+
vlog("DoH: re-resolution failed or switched to direct, retrying with direct connection");
|
|
1308
|
+
}
|
|
1309
|
+
return true;
|
|
1310
|
+
}
|
|
1311
|
+
get activeBaseUrl() {
|
|
1312
|
+
return this.dohNode ? this.dohBaseUrl : this.config.baseUrl;
|
|
1313
|
+
}
|
|
1314
|
+
get activeDispatcher() {
|
|
1315
|
+
return this.dispatcher ?? this.dohAgent ?? void 0;
|
|
1316
|
+
}
|
|
1317
|
+
/** User-Agent for DoH proxy requests: OKX/@okx_ai/{packageName}/{version} */
|
|
1318
|
+
get dohUserAgent() {
|
|
1319
|
+
return `OKX/@okx_ai/${this.config.userAgent ?? "unknown"}`;
|
|
1320
|
+
}
|
|
1089
1321
|
logRequest(method, url, auth) {
|
|
1090
1322
|
if (!this.config.verbose) return;
|
|
1091
1323
|
vlog(`\u2192 ${method} ${url}`);
|
|
@@ -1255,13 +1487,17 @@ var OkxRestClient = class _OkxRestClient {
|
|
|
1255
1487
|
* Security: validates Content-Type and enforces maxBytes limit.
|
|
1256
1488
|
*/
|
|
1257
1489
|
async privatePostBinary(path42, body, opts) {
|
|
1490
|
+
await this.ensureDoh();
|
|
1258
1491
|
const maxBytes = opts?.maxBytes ?? _OkxRestClient.DEFAULT_MAX_BYTES;
|
|
1259
1492
|
const expectedCT = opts?.expectedContentType ?? "application/octet-stream";
|
|
1260
1493
|
const bodyJson = body ? JSON.stringify(body) : "";
|
|
1261
1494
|
const endpoint = `POST ${path42}`;
|
|
1262
|
-
this.logRequest("POST", `${this.
|
|
1495
|
+
this.logRequest("POST", `${this.activeBaseUrl}${path42}`, "private");
|
|
1263
1496
|
const reqConfig = { method: "POST", path: path42, auth: "private" };
|
|
1264
1497
|
const headers = this.buildHeaders(reqConfig, path42, bodyJson, getNow());
|
|
1498
|
+
if (this.dohNode) {
|
|
1499
|
+
headers.set("User-Agent", this.dohUserAgent);
|
|
1500
|
+
}
|
|
1265
1501
|
const t0 = Date.now();
|
|
1266
1502
|
const response = await this.fetchBinary(path42, endpoint, headers, bodyJson, t0);
|
|
1267
1503
|
const elapsed = Date.now() - t0;
|
|
@@ -1295,10 +1531,10 @@ var OkxRestClient = class _OkxRestClient {
|
|
|
1295
1531
|
method: "POST",
|
|
1296
1532
|
headers,
|
|
1297
1533
|
body: bodyJson || void 0,
|
|
1298
|
-
signal: AbortSignal.timeout(this.config.timeoutMs)
|
|
1534
|
+
signal: AbortSignal.timeout(this.config.timeoutMs),
|
|
1535
|
+
dispatcher: this.activeDispatcher
|
|
1299
1536
|
};
|
|
1300
|
-
|
|
1301
|
-
return await fetch(`${this.config.baseUrl}${path42}`, fetchOptions);
|
|
1537
|
+
return await fetch(`${this.activeBaseUrl}${path42}`, fetchOptions);
|
|
1302
1538
|
} catch (error) {
|
|
1303
1539
|
if (this.config.verbose) {
|
|
1304
1540
|
vlog(`\u2717 NetworkError after ${Date.now() - t0}ms: ${error instanceof Error ? error.message : String(error)}`);
|
|
@@ -1329,9 +1565,10 @@ var OkxRestClient = class _OkxRestClient {
|
|
|
1329
1565
|
// JSON request
|
|
1330
1566
|
// ---------------------------------------------------------------------------
|
|
1331
1567
|
async request(reqConfig) {
|
|
1568
|
+
await this.ensureDoh();
|
|
1332
1569
|
const queryString = buildQueryString(reqConfig.query);
|
|
1333
1570
|
const requestPath = queryString.length > 0 ? `${reqConfig.path}?${queryString}` : reqConfig.path;
|
|
1334
|
-
const url = `${this.
|
|
1571
|
+
const url = `${this.activeBaseUrl}${requestPath}`;
|
|
1335
1572
|
const bodyJson = reqConfig.body ? JSON.stringify(reqConfig.body) : "";
|
|
1336
1573
|
const timestamp = getNow();
|
|
1337
1574
|
this.logRequest(reqConfig.method, url, reqConfig.auth);
|
|
@@ -1339,6 +1576,9 @@ var OkxRestClient = class _OkxRestClient {
|
|
|
1339
1576
|
await this.rateLimiter.consume(reqConfig.rateLimit);
|
|
1340
1577
|
}
|
|
1341
1578
|
const headers = this.buildHeaders(reqConfig, requestPath, bodyJson, timestamp);
|
|
1579
|
+
if (this.dohNode) {
|
|
1580
|
+
headers.set("User-Agent", this.dohUserAgent);
|
|
1581
|
+
}
|
|
1342
1582
|
const t0 = Date.now();
|
|
1343
1583
|
let response;
|
|
1344
1584
|
try {
|
|
@@ -1346,13 +1586,20 @@ var OkxRestClient = class _OkxRestClient {
|
|
|
1346
1586
|
method: reqConfig.method,
|
|
1347
1587
|
headers,
|
|
1348
1588
|
body: reqConfig.method === "POST" ? bodyJson : void 0,
|
|
1349
|
-
signal: AbortSignal.timeout(this.config.timeoutMs)
|
|
1589
|
+
signal: AbortSignal.timeout(this.config.timeoutMs),
|
|
1590
|
+
dispatcher: this.activeDispatcher
|
|
1350
1591
|
};
|
|
1351
|
-
if (this.dispatcher) {
|
|
1352
|
-
fetchOptions.dispatcher = this.dispatcher;
|
|
1353
|
-
}
|
|
1354
1592
|
response = await fetch(url, fetchOptions);
|
|
1355
1593
|
} catch (error) {
|
|
1594
|
+
if (!this.dohRetried) {
|
|
1595
|
+
if (this.config.verbose) {
|
|
1596
|
+
vlog(`Network failure, refreshing DoH: ${error instanceof Error ? error.message : String(error)}`);
|
|
1597
|
+
}
|
|
1598
|
+
const shouldRetry = await this.handleDohNetworkFailure();
|
|
1599
|
+
if (shouldRetry && reqConfig.method === "GET") {
|
|
1600
|
+
return this.request(reqConfig);
|
|
1601
|
+
}
|
|
1602
|
+
}
|
|
1356
1603
|
if (this.config.verbose) {
|
|
1357
1604
|
const elapsed2 = Date.now() - t0;
|
|
1358
1605
|
const cause = error instanceof Error ? error.message : String(error);
|
|
@@ -1367,6 +1614,19 @@ var OkxRestClient = class _OkxRestClient {
|
|
|
1367
1614
|
const rawText = await response.text();
|
|
1368
1615
|
const elapsed = Date.now() - t0;
|
|
1369
1616
|
const traceId = extractTraceId(response.headers);
|
|
1617
|
+
if (this.directUnverified && !this.dohNode) {
|
|
1618
|
+
this.directUnverified = false;
|
|
1619
|
+
const { hostname: h } = new URL(this.config.baseUrl);
|
|
1620
|
+
writeCache(h, {
|
|
1621
|
+
mode: "direct",
|
|
1622
|
+
node: null,
|
|
1623
|
+
failedNodes: [],
|
|
1624
|
+
updatedAt: Date.now()
|
|
1625
|
+
});
|
|
1626
|
+
if (this.config.verbose) {
|
|
1627
|
+
vlog("DoH: direct connection succeeded, cached mode=direct");
|
|
1628
|
+
}
|
|
1629
|
+
}
|
|
1370
1630
|
return this.processResponse(rawText, response, elapsed, traceId, reqConfig, requestPath);
|
|
1371
1631
|
}
|
|
1372
1632
|
};
|
|
@@ -1506,137 +1766,14 @@ var INDICATOR_BARS = [
|
|
|
1506
1766
|
"1Wutc"
|
|
1507
1767
|
];
|
|
1508
1768
|
var INDICATOR_CODE_OVERRIDES = {
|
|
1509
|
-
// Aliases
|
|
1510
|
-
"boll": "BB",
|
|
1511
|
-
// server supports BB not BOLL
|
|
1512
|
-
// Names where default rule produces underscores but backend uses no separator
|
|
1513
1769
|
"rainbow": "BTCRAINBOW",
|
|
1514
|
-
|
|
1770
|
+
"range-filter": "RANGEFILTER",
|
|
1515
1771
|
"stoch-rsi": "STOCHRSI",
|
|
1516
|
-
|
|
1517
|
-
"
|
|
1518
|
-
//
|
|
1519
|
-
"
|
|
1520
|
-
// default: BEAR_ENGULF
|
|
1521
|
-
"bull-harami": "BULLHARAMI",
|
|
1522
|
-
// default: BULL_HARAMI
|
|
1523
|
-
"bear-harami": "BEARHARAMI",
|
|
1524
|
-
// default: BEAR_HARAMI
|
|
1525
|
-
"bull-harami-cross": "BULLHARAMICROSS",
|
|
1526
|
-
// default: BULL_HARAMI_CROSS
|
|
1527
|
-
"bear-harami-cross": "BEARHARAMICROSS",
|
|
1528
|
-
// default: BEAR_HARAMI_CROSS
|
|
1529
|
-
"three-soldiers": "THREESOLDIERS",
|
|
1530
|
-
// default: THREE_SOLDIERS
|
|
1531
|
-
"three-crows": "THREECROWS",
|
|
1532
|
-
// default: THREE_CROWS
|
|
1533
|
-
"hanging-man": "HANGINGMAN",
|
|
1534
|
-
// default: HANGING_MAN
|
|
1535
|
-
"inverted-hammer": "INVERTEDH",
|
|
1536
|
-
// default: INVERTED_HAMMER (backend uses INVERTEDH)
|
|
1537
|
-
"shooting-star": "SHOOTINGSTAR",
|
|
1538
|
-
// default: SHOOTING_STAR
|
|
1539
|
-
"nvi-pvi": "NVIPVI",
|
|
1540
|
-
// default: NVI_PVI
|
|
1541
|
-
"top-long-short": "TOPLONGSHORT"
|
|
1542
|
-
// default: TOP_LONG_SHORT
|
|
1543
|
-
// Note: range-filter → RANGE_FILTER is correct via the default rule; no override needed.
|
|
1772
|
+
"pi-cycle-top": "PI_CYCLE_TOP",
|
|
1773
|
+
"pi-cycle-bottom": "PI_CYCLE_BOTTOM",
|
|
1774
|
+
// boll is an alias for bb; server supports BB not BOLL
|
|
1775
|
+
"boll": "BB"
|
|
1544
1776
|
};
|
|
1545
|
-
var KNOWN_INDICATORS = [
|
|
1546
|
-
// Moving Averages
|
|
1547
|
-
{ name: "ma", description: "Simple Moving Average" },
|
|
1548
|
-
{ name: "ema", description: "Exponential Moving Average" },
|
|
1549
|
-
{ name: "wma", description: "Weighted Moving Average" },
|
|
1550
|
-
{ name: "dema", description: "Double Exponential Moving Average" },
|
|
1551
|
-
{ name: "tema", description: "Triple Exponential Moving Average" },
|
|
1552
|
-
{ name: "zlema", description: "Zero-Lag Exponential Moving Average" },
|
|
1553
|
-
{ name: "hma", description: "Hull Moving Average" },
|
|
1554
|
-
{ name: "kama", description: "Kaufman Adaptive Moving Average" },
|
|
1555
|
-
// Trend
|
|
1556
|
-
{ name: "macd", description: "MACD" },
|
|
1557
|
-
{ name: "sar", description: "Parabolic SAR" },
|
|
1558
|
-
{ name: "adx", description: "Average Directional Index" },
|
|
1559
|
-
{ name: "aroon", description: "Aroon Indicator" },
|
|
1560
|
-
{ name: "cci", description: "Commodity Channel Index" },
|
|
1561
|
-
{ name: "dpo", description: "Detrended Price Oscillator" },
|
|
1562
|
-
{ name: "envelope", description: "Envelope" },
|
|
1563
|
-
{ name: "halftrend", description: "HalfTrend" },
|
|
1564
|
-
{ name: "alphatrend", description: "AlphaTrend" },
|
|
1565
|
-
// Momentum
|
|
1566
|
-
{ name: "rsi", description: "Relative Strength Index" },
|
|
1567
|
-
{ name: "stoch-rsi", description: "Stochastic RSI" },
|
|
1568
|
-
{ name: "stoch", description: "Stochastic Oscillator" },
|
|
1569
|
-
{ name: "roc", description: "Rate of Change" },
|
|
1570
|
-
{ name: "mom", description: "Momentum" },
|
|
1571
|
-
{ name: "ppo", description: "Price Percentage Oscillator" },
|
|
1572
|
-
{ name: "trix", description: "TRIX" },
|
|
1573
|
-
{ name: "ao", description: "Awesome Oscillator" },
|
|
1574
|
-
{ name: "uo", description: "Ultimate Oscillator" },
|
|
1575
|
-
{ name: "wr", description: "Williams %R" },
|
|
1576
|
-
// Volatility
|
|
1577
|
-
{ name: "bb", description: "Bollinger Bands" },
|
|
1578
|
-
{ name: "boll", description: "Bollinger Bands (alias for bb)" },
|
|
1579
|
-
{ name: "bbwidth", description: "Bollinger Band Width" },
|
|
1580
|
-
{ name: "bbpct", description: "Bollinger Band %B" },
|
|
1581
|
-
{ name: "atr", description: "Average True Range" },
|
|
1582
|
-
{ name: "keltner", description: "Keltner Channel" },
|
|
1583
|
-
{ name: "donchian", description: "Donchian Channel" },
|
|
1584
|
-
{ name: "hv", description: "Historical Volatility" },
|
|
1585
|
-
{ name: "stddev", description: "Standard Deviation" },
|
|
1586
|
-
// Volume
|
|
1587
|
-
{ name: "obv", description: "On-Balance Volume" },
|
|
1588
|
-
{ name: "vwap", description: "Volume Weighted Average Price" },
|
|
1589
|
-
{ name: "mvwap", description: "Moving VWAP" },
|
|
1590
|
-
{ name: "cmf", description: "Chaikin Money Flow" },
|
|
1591
|
-
{ name: "mfi", description: "Money Flow Index" },
|
|
1592
|
-
{ name: "ad", description: "Accumulation/Distribution" },
|
|
1593
|
-
// Statistical
|
|
1594
|
-
{ name: "lr", description: "Linear Regression" },
|
|
1595
|
-
{ name: "slope", description: "Linear Regression Slope" },
|
|
1596
|
-
{ name: "angle", description: "Linear Regression Angle" },
|
|
1597
|
-
{ name: "variance", description: "Variance" },
|
|
1598
|
-
{ name: "meandev", description: "Mean Deviation" },
|
|
1599
|
-
{ name: "sigma", description: "Sigma" },
|
|
1600
|
-
{ name: "stderr", description: "Standard Error" },
|
|
1601
|
-
// Custom
|
|
1602
|
-
{ name: "kdj", description: "KDJ Stochastic Oscillator" },
|
|
1603
|
-
{ name: "supertrend", description: "Supertrend" },
|
|
1604
|
-
// Ichimoku
|
|
1605
|
-
{ name: "tenkan", description: "Ichimoku Tenkan-sen (Conversion Line)" },
|
|
1606
|
-
{ name: "kijun", description: "Ichimoku Kijun-sen (Base Line)" },
|
|
1607
|
-
{ name: "senkoa", description: "Ichimoku Senkou Span A (Leading Span A)" },
|
|
1608
|
-
{ name: "senkob", description: "Ichimoku Senkou Span B (Leading Span B)" },
|
|
1609
|
-
{ name: "chikou", description: "Ichimoku Chikou Span (Lagging Span)" },
|
|
1610
|
-
// Candlestick Patterns
|
|
1611
|
-
{ name: "doji", description: "Doji candlestick pattern" },
|
|
1612
|
-
{ name: "bull-engulf", description: "Bullish Engulfing pattern" },
|
|
1613
|
-
{ name: "bear-engulf", description: "Bearish Engulfing pattern" },
|
|
1614
|
-
{ name: "bull-harami", description: "Bullish Harami pattern" },
|
|
1615
|
-
{ name: "bear-harami", description: "Bearish Harami pattern" },
|
|
1616
|
-
{ name: "bull-harami-cross", description: "Bullish Harami Cross pattern" },
|
|
1617
|
-
{ name: "bear-harami-cross", description: "Bearish Harami Cross pattern" },
|
|
1618
|
-
{ name: "three-soldiers", description: "Three White Soldiers pattern" },
|
|
1619
|
-
{ name: "three-crows", description: "Three Black Crows pattern" },
|
|
1620
|
-
{ name: "hanging-man", description: "Hanging Man pattern" },
|
|
1621
|
-
{ name: "inverted-hammer", description: "Inverted Hammer pattern" },
|
|
1622
|
-
{ name: "shooting-star", description: "Shooting Star pattern" },
|
|
1623
|
-
// Bitcoin On-Chain
|
|
1624
|
-
{ name: "ahr999", description: "AHR999 Bitcoin accumulation index" },
|
|
1625
|
-
{ name: "rainbow", description: "Bitcoin Rainbow Chart" },
|
|
1626
|
-
// Other
|
|
1627
|
-
{ name: "fisher", description: "Fisher Transform" },
|
|
1628
|
-
{ name: "nvi-pvi", description: "Negative/Positive Volume Index (returns both)" },
|
|
1629
|
-
{ name: "pmax", description: "PMAX" },
|
|
1630
|
-
{ name: "qqe", description: "QQE Mod" },
|
|
1631
|
-
{ name: "tdi", description: "Traders Dynamic Index" },
|
|
1632
|
-
{ name: "waddah", description: "Waddah Attar Explosion" },
|
|
1633
|
-
{ name: "range-filter", description: "Range Filter" },
|
|
1634
|
-
{ name: "cho", description: "Chande Momentum Oscillator" },
|
|
1635
|
-
{ name: "tr", description: "True Range" },
|
|
1636
|
-
{ name: "tp", description: "Typical Price" },
|
|
1637
|
-
{ name: "mp", description: "Median Price" },
|
|
1638
|
-
{ name: "top-long-short", description: "Top Trader Long/Short Ratio (timeframe-independent)" }
|
|
1639
|
-
];
|
|
1640
1777
|
function resolveIndicatorCode(name) {
|
|
1641
1778
|
const lower = name.toLowerCase();
|
|
1642
1779
|
return INDICATOR_CODE_OVERRIDES[lower] ?? name.toUpperCase().replace(/-/g, "_");
|
|
@@ -1663,7 +1800,7 @@ function registerIndicatorTools() {
|
|
|
1663
1800
|
},
|
|
1664
1801
|
indicator: {
|
|
1665
1802
|
type: "string",
|
|
1666
|
-
description: "Indicator name (case-insensitive).
|
|
1803
|
+
description: "Indicator name (case-insensitive). Examples: ma, ema, rsi, macd, bb, kdj, supertrend, ahr999, rainbow, pi-cycle-top, pi-cycle-bottom, mayer, envelope, halftrend, alphatrend, pmax, waddah, tdi, qqe, range-filter"
|
|
1667
1804
|
},
|
|
1668
1805
|
bar: {
|
|
1669
1806
|
type: "string",
|
|
@@ -1720,14 +1857,6 @@ function registerIndicatorTools() {
|
|
|
1720
1857
|
);
|
|
1721
1858
|
return normalizeResponse(response);
|
|
1722
1859
|
}
|
|
1723
|
-
},
|
|
1724
|
-
{
|
|
1725
|
-
name: "market_list_indicators",
|
|
1726
|
-
module: "market",
|
|
1727
|
-
description: "List all supported technical indicator names and descriptions. Call this before market_get_indicator to discover valid indicator names. No credentials required.",
|
|
1728
|
-
isWrite: false,
|
|
1729
|
-
inputSchema: { type: "object", properties: {} },
|
|
1730
|
-
handler: async () => ({ data: KNOWN_INDICATORS })
|
|
1731
1860
|
}
|
|
1732
1861
|
];
|
|
1733
1862
|
}
|
|
@@ -2325,112 +2454,6 @@ function registerAccountTools() {
|
|
|
2325
2454
|
}
|
|
2326
2455
|
];
|
|
2327
2456
|
}
|
|
2328
|
-
function extractInstrumentParams(instId, data) {
|
|
2329
|
-
const instruments = Array.isArray(data) ? data : [];
|
|
2330
|
-
if (instruments.length === 0) {
|
|
2331
|
-
throw new Error(`Failed to fetch instrument info for ${instId}: empty instrument list. Cannot determine ctVal for conversion.`);
|
|
2332
|
-
}
|
|
2333
|
-
const inst = instruments[0];
|
|
2334
|
-
const ctValStr = String(inst.ctVal ?? "");
|
|
2335
|
-
const ctVal = parseFloat(ctValStr);
|
|
2336
|
-
if (!isFinite(ctVal) || ctVal <= 0) {
|
|
2337
|
-
throw new Error(`Invalid ctVal "${ctValStr}" for ${instId}. ctVal must be a positive number for conversion.`);
|
|
2338
|
-
}
|
|
2339
|
-
const minSzStr = String(inst.minSz ?? "1");
|
|
2340
|
-
const minSz = parseFloat(minSzStr);
|
|
2341
|
-
if (!isFinite(minSz) || minSz <= 0) {
|
|
2342
|
-
throw new Error(`Invalid minSz "${minSzStr}" for ${instId}. minSz must be a positive number for conversion.`);
|
|
2343
|
-
}
|
|
2344
|
-
const lotSzStr = String(inst.lotSz ?? "1");
|
|
2345
|
-
const lotSz = parseFloat(lotSzStr);
|
|
2346
|
-
if (!isFinite(lotSz) || lotSz <= 0) {
|
|
2347
|
-
throw new Error(`Invalid lotSz "${lotSzStr}" for ${instId}. lotSz must be a positive number for conversion.`);
|
|
2348
|
-
}
|
|
2349
|
-
return { ctVal, ctValStr, minSz, minSzStr, lotSz, lotSzStr };
|
|
2350
|
-
}
|
|
2351
|
-
function extractLastPx(instId, data) {
|
|
2352
|
-
const tickers = Array.isArray(data) ? data : [];
|
|
2353
|
-
if (tickers.length === 0) {
|
|
2354
|
-
throw new Error(`Failed to fetch ticker price for ${instId}: empty ticker response. Cannot determine last price for conversion.`);
|
|
2355
|
-
}
|
|
2356
|
-
const lastStr = String(tickers[0].last ?? "");
|
|
2357
|
-
const lastPx = parseFloat(lastStr);
|
|
2358
|
-
if (!isFinite(lastPx) || lastPx <= 0) {
|
|
2359
|
-
throw new Error(`Invalid last price "${lastStr}" for ${instId}. Last price must be a positive number for conversion.`);
|
|
2360
|
-
}
|
|
2361
|
-
return { lastPx, lastStr };
|
|
2362
|
-
}
|
|
2363
|
-
function extractLeverage(instId, mgnMode, data) {
|
|
2364
|
-
const leverageData = Array.isArray(data) ? data : [];
|
|
2365
|
-
if (leverageData.length === 0) {
|
|
2366
|
-
throw new Error(
|
|
2367
|
-
`Failed to fetch leverage info for ${instId} (mgnMode=${mgnMode}): empty response. Cannot determine leverage for margin conversion. Please set leverage first using set_leverage.`
|
|
2368
|
-
);
|
|
2369
|
-
}
|
|
2370
|
-
const leverStr = String(leverageData[0].lever ?? "1");
|
|
2371
|
-
const lever = parseFloat(leverStr);
|
|
2372
|
-
if (!isFinite(lever) || lever <= 0) {
|
|
2373
|
-
throw new Error(`Invalid leverage "${leverStr}" for ${instId}. Leverage must be a positive number for margin conversion.`);
|
|
2374
|
-
}
|
|
2375
|
-
return { lever, leverStr };
|
|
2376
|
-
}
|
|
2377
|
-
function computeContracts(p) {
|
|
2378
|
-
const { instId, sz, isMarginMode, inst, lastPx, lastStr, lever, leverStr } = p;
|
|
2379
|
-
const { ctVal, ctValStr, minSz, minSzStr, lotSz, lotSzStr } = inst;
|
|
2380
|
-
const userAmount = parseFloat(sz);
|
|
2381
|
-
const contractValue = ctVal * lastPx;
|
|
2382
|
-
const effectiveNotional = isMarginMode ? userAmount * lever : userAmount;
|
|
2383
|
-
const lotSzDecimals = lotSzStr.includes(".") ? lotSzStr.split(".")[1].length : 0;
|
|
2384
|
-
const precision = 10 ** (lotSzDecimals + 4);
|
|
2385
|
-
const rawContracts = Math.round(effectiveNotional / contractValue * precision) / precision;
|
|
2386
|
-
const rawLots = Math.round(rawContracts / lotSz * precision) / precision;
|
|
2387
|
-
const contractsRounded = parseFloat((Math.floor(rawLots) * lotSz).toFixed(lotSzDecimals));
|
|
2388
|
-
if (contractsRounded < minSz) {
|
|
2389
|
-
const minAmount = isMarginMode ? (minSz * contractValue / lever).toFixed(2) : (minSz * contractValue).toFixed(2);
|
|
2390
|
-
const unit = isMarginMode ? "USDT margin" : "USDT";
|
|
2391
|
-
throw new Error(
|
|
2392
|
-
`sz=${sz} ${unit} is too small for ${instId}. Minimum order size is ${minSzStr} contracts (\u2248 ${minAmount} ${unit}). (ctVal=${ctValStr}, lastPx=${lastStr}, minSz=${minSzStr}, lotSz=${lotSzStr}` + (isMarginMode ? `, lever=${leverStr}` : "") + `).
|
|
2393
|
-
`
|
|
2394
|
-
);
|
|
2395
|
-
}
|
|
2396
|
-
const contractsStr = contractsRounded.toFixed(lotSzDecimals);
|
|
2397
|
-
const conversionNote = isMarginMode ? `Converting ${sz} USDT margin (${leverStr}x leverage) \u2192 ${contractsStr} contracts (notional value \u2248 ${(contractsRounded * contractValue).toFixed(2)} USDT, ctVal=${ctValStr}, lastPx=${lastStr}, lever=${leverStr}, minSz=${minSzStr}, lotSz=${lotSzStr})` : `Converting ${sz} USDT \u2192 ${contractsStr} contracts (ctVal=${ctValStr}, lastPx=${lastStr}, minSz=${minSzStr}, lotSz=${lotSzStr})`;
|
|
2398
|
-
return { contractsStr, conversionNote };
|
|
2399
|
-
}
|
|
2400
|
-
async function resolveQuoteCcySz(instId, sz, tgtCcy, instType, client, tdMode) {
|
|
2401
|
-
if (tgtCcy !== "quote_ccy" && tgtCcy !== "margin") {
|
|
2402
|
-
return { sz, tgtCcy, conversionNote: void 0 };
|
|
2403
|
-
}
|
|
2404
|
-
const isMarginMode = tgtCcy === "margin";
|
|
2405
|
-
if (isMarginMode && !tdMode) {
|
|
2406
|
-
throw new Error(
|
|
2407
|
-
"tdMode (cross or isolated) is required when tgtCcy=margin. Cannot determine leverage without knowing the margin mode."
|
|
2408
|
-
);
|
|
2409
|
-
}
|
|
2410
|
-
const mgnMode = tdMode === "cross" ? "cross" : "isolated";
|
|
2411
|
-
const fetchPromises = [
|
|
2412
|
-
client.publicGet("/api/v5/public/instruments", { instType, instId }),
|
|
2413
|
-
client.publicGet("/api/v5/market/ticker", { instId })
|
|
2414
|
-
];
|
|
2415
|
-
if (isMarginMode) {
|
|
2416
|
-
fetchPromises.push(client.privateGet("/api/v5/account/leverage-info", { instId, mgnMode }));
|
|
2417
|
-
}
|
|
2418
|
-
const results = await Promise.all(fetchPromises);
|
|
2419
|
-
const inst = extractInstrumentParams(instId, results[0].data);
|
|
2420
|
-
const { lastPx, lastStr } = extractLastPx(instId, results[1].data);
|
|
2421
|
-
const { lever, leverStr } = isMarginMode ? extractLeverage(instId, mgnMode, results[2].data) : { lever: 1, leverStr: "1" };
|
|
2422
|
-
const { contractsStr, conversionNote } = computeContracts({
|
|
2423
|
-
instId,
|
|
2424
|
-
sz,
|
|
2425
|
-
isMarginMode,
|
|
2426
|
-
inst,
|
|
2427
|
-
lastPx,
|
|
2428
|
-
lastStr,
|
|
2429
|
-
lever,
|
|
2430
|
-
leverStr
|
|
2431
|
-
});
|
|
2432
|
-
return { sz: contractsStr, tgtCcy: void 0, conversionNote };
|
|
2433
|
-
}
|
|
2434
2457
|
function registerAlgoTradeTools() {
|
|
2435
2458
|
return [
|
|
2436
2459
|
{
|
|
@@ -2509,8 +2532,8 @@ function registerAlgoTradeTools() {
|
|
|
2509
2532
|
},
|
|
2510
2533
|
tgtCcy: {
|
|
2511
2534
|
type: "string",
|
|
2512
|
-
enum: ["base_ccy", "quote_ccy"
|
|
2513
|
-
description: "Size unit. base_ccy(default): sz in contracts
|
|
2535
|
+
enum: ["base_ccy", "quote_ccy"],
|
|
2536
|
+
description: "Size unit. base_ccy(default): sz in contracts, quote_ccy: sz in USDT"
|
|
2514
2537
|
},
|
|
2515
2538
|
reduceOnly: {
|
|
2516
2539
|
type: "boolean",
|
|
@@ -2526,14 +2549,6 @@ function registerAlgoTradeTools() {
|
|
|
2526
2549
|
handler: async (rawArgs, context) => {
|
|
2527
2550
|
const args = asRecord(rawArgs);
|
|
2528
2551
|
const reduceOnly = args.reduceOnly;
|
|
2529
|
-
const resolved = await resolveQuoteCcySz(
|
|
2530
|
-
requireString(args, "instId"),
|
|
2531
|
-
requireString(args, "sz"),
|
|
2532
|
-
readString(args, "tgtCcy"),
|
|
2533
|
-
"SWAP",
|
|
2534
|
-
context.client,
|
|
2535
|
-
readString(args, "tdMode")
|
|
2536
|
-
);
|
|
2537
2552
|
const response = await context.client.privatePost(
|
|
2538
2553
|
"/api/v5/trade/order-algo",
|
|
2539
2554
|
compactObject({
|
|
@@ -2542,8 +2557,8 @@ function registerAlgoTradeTools() {
|
|
|
2542
2557
|
side: requireString(args, "side"),
|
|
2543
2558
|
posSide: readString(args, "posSide"),
|
|
2544
2559
|
ordType: requireString(args, "ordType"),
|
|
2545
|
-
sz:
|
|
2546
|
-
tgtCcy:
|
|
2560
|
+
sz: requireString(args, "sz"),
|
|
2561
|
+
tgtCcy: readString(args, "tgtCcy"),
|
|
2547
2562
|
tpTriggerPx: readString(args, "tpTriggerPx"),
|
|
2548
2563
|
tpOrdPx: readString(args, "tpOrdPx"),
|
|
2549
2564
|
tpTriggerPxType: readString(args, "tpTriggerPxType"),
|
|
@@ -2559,11 +2574,7 @@ function registerAlgoTradeTools() {
|
|
|
2559
2574
|
}),
|
|
2560
2575
|
privateRateLimit("swap_place_algo_order", 20)
|
|
2561
2576
|
);
|
|
2562
|
-
|
|
2563
|
-
if (resolved.conversionNote) {
|
|
2564
|
-
result._conversion = resolved.conversionNote;
|
|
2565
|
-
}
|
|
2566
|
-
return result;
|
|
2577
|
+
return normalizeResponse(response);
|
|
2567
2578
|
}
|
|
2568
2579
|
},
|
|
2569
2580
|
{
|
|
@@ -2854,8 +2865,8 @@ function registerFuturesAlgoTools() {
|
|
|
2854
2865
|
},
|
|
2855
2866
|
tgtCcy: {
|
|
2856
2867
|
type: "string",
|
|
2857
|
-
enum: ["base_ccy", "quote_ccy"
|
|
2858
|
-
description: "Size unit. base_ccy(default): sz in contracts
|
|
2868
|
+
enum: ["base_ccy", "quote_ccy"],
|
|
2869
|
+
description: "Size unit. base_ccy(default): sz in contracts, quote_ccy: sz in USDT"
|
|
2859
2870
|
},
|
|
2860
2871
|
reduceOnly: {
|
|
2861
2872
|
type: "boolean",
|
|
@@ -2871,14 +2882,6 @@ function registerFuturesAlgoTools() {
|
|
|
2871
2882
|
handler: async (rawArgs, context) => {
|
|
2872
2883
|
const args = asRecord(rawArgs);
|
|
2873
2884
|
const reduceOnly = args.reduceOnly;
|
|
2874
|
-
const resolved = await resolveQuoteCcySz(
|
|
2875
|
-
requireString(args, "instId"),
|
|
2876
|
-
requireString(args, "sz"),
|
|
2877
|
-
readString(args, "tgtCcy"),
|
|
2878
|
-
"FUTURES",
|
|
2879
|
-
context.client,
|
|
2880
|
-
readString(args, "tdMode")
|
|
2881
|
-
);
|
|
2882
2885
|
const response = await context.client.privatePost(
|
|
2883
2886
|
"/api/v5/trade/order-algo",
|
|
2884
2887
|
compactObject({
|
|
@@ -2887,8 +2890,8 @@ function registerFuturesAlgoTools() {
|
|
|
2887
2890
|
side: requireString(args, "side"),
|
|
2888
2891
|
posSide: readString(args, "posSide"),
|
|
2889
2892
|
ordType: requireString(args, "ordType"),
|
|
2890
|
-
sz:
|
|
2891
|
-
tgtCcy:
|
|
2893
|
+
sz: requireString(args, "sz"),
|
|
2894
|
+
tgtCcy: readString(args, "tgtCcy"),
|
|
2892
2895
|
tpTriggerPx: readString(args, "tpTriggerPx"),
|
|
2893
2896
|
tpOrdPx: readString(args, "tpOrdPx"),
|
|
2894
2897
|
tpTriggerPxType: readString(args, "tpTriggerPxType"),
|
|
@@ -2904,11 +2907,7 @@ function registerFuturesAlgoTools() {
|
|
|
2904
2907
|
}),
|
|
2905
2908
|
privateRateLimit("futures_place_algo_order", 20)
|
|
2906
2909
|
);
|
|
2907
|
-
|
|
2908
|
-
if (resolved.conversionNote) {
|
|
2909
|
-
result._conversion = resolved.conversionNote;
|
|
2910
|
-
}
|
|
2911
|
-
return result;
|
|
2910
|
+
return normalizeResponse(response);
|
|
2912
2911
|
}
|
|
2913
2912
|
},
|
|
2914
2913
|
{
|
|
@@ -3239,19 +3238,19 @@ function safeWriteFile(targetDir, fileName, data) {
|
|
|
3239
3238
|
throw new Error(`Invalid file name: "${fileName}"`);
|
|
3240
3239
|
}
|
|
3241
3240
|
const resolvedDir = resolve(targetDir);
|
|
3242
|
-
const filePath =
|
|
3241
|
+
const filePath = join3(resolvedDir, safeName);
|
|
3243
3242
|
const resolvedPath = resolve(filePath);
|
|
3244
3243
|
if (!resolvedPath.startsWith(resolvedDir + sep)) {
|
|
3245
3244
|
throw new Error(`Path traversal detected: "${fileName}" resolves outside target directory`);
|
|
3246
3245
|
}
|
|
3247
|
-
|
|
3246
|
+
mkdirSync2(resolvedDir, { recursive: true });
|
|
3248
3247
|
const tmpPath = `${resolvedPath}.${randomUUID()}.tmp`;
|
|
3249
3248
|
try {
|
|
3250
|
-
|
|
3251
|
-
|
|
3249
|
+
writeFileSync2(tmpPath, data);
|
|
3250
|
+
renameSync2(tmpPath, resolvedPath);
|
|
3252
3251
|
} catch (err) {
|
|
3253
3252
|
try {
|
|
3254
|
-
|
|
3253
|
+
unlinkSync2(tmpPath);
|
|
3255
3254
|
} catch {
|
|
3256
3255
|
}
|
|
3257
3256
|
throw err;
|
|
@@ -3316,7 +3315,7 @@ async function extractSkillZip(zipPath, targetDir, limits) {
|
|
|
3316
3315
|
const maxFiles = limits?.maxFiles ?? DEFAULT_MAX_FILES;
|
|
3317
3316
|
const maxCompressionRatio = limits?.maxCompressionRatio ?? DEFAULT_MAX_COMPRESSION_RATIO;
|
|
3318
3317
|
const resolvedTarget = resolve2(targetDir);
|
|
3319
|
-
|
|
3318
|
+
mkdirSync3(resolvedTarget, { recursive: true });
|
|
3320
3319
|
return new Promise((resolvePromise, reject) => {
|
|
3321
3320
|
yauzl.open(zipPath, { lazyEntries: true }, (err, zipfile) => {
|
|
3322
3321
|
if (err) return reject(err);
|
|
@@ -3339,7 +3338,7 @@ async function extractSkillZip(zipPath, targetDir, limits) {
|
|
|
3339
3338
|
zipfile.close();
|
|
3340
3339
|
return reject(streamErr);
|
|
3341
3340
|
}
|
|
3342
|
-
|
|
3341
|
+
mkdirSync3(dirname2(resolvedPath), { recursive: true });
|
|
3343
3342
|
const writeStream = createWriteStream(resolvedPath);
|
|
3344
3343
|
readStream.pipe(writeStream);
|
|
3345
3344
|
writeStream.on("close", () => zipfile.readEntry());
|
|
@@ -3355,11 +3354,11 @@ async function extractSkillZip(zipPath, targetDir, limits) {
|
|
|
3355
3354
|
});
|
|
3356
3355
|
}
|
|
3357
3356
|
function readMetaJson(contentDir) {
|
|
3358
|
-
const metaPath =
|
|
3357
|
+
const metaPath = join4(contentDir, "_meta.json");
|
|
3359
3358
|
if (!existsSync(metaPath)) {
|
|
3360
3359
|
throw new Error(`_meta.json not found in ${contentDir}. Invalid skill package.`);
|
|
3361
3360
|
}
|
|
3362
|
-
const raw =
|
|
3361
|
+
const raw = readFileSync2(metaPath, "utf-8");
|
|
3363
3362
|
let parsed;
|
|
3364
3363
|
try {
|
|
3365
3364
|
parsed = JSON.parse(raw);
|
|
@@ -3381,26 +3380,26 @@ function readMetaJson(contentDir) {
|
|
|
3381
3380
|
};
|
|
3382
3381
|
}
|
|
3383
3382
|
function validateSkillMdExists(contentDir) {
|
|
3384
|
-
const skillMdPath =
|
|
3383
|
+
const skillMdPath = join4(contentDir, "SKILL.md");
|
|
3385
3384
|
if (!existsSync(skillMdPath)) {
|
|
3386
3385
|
throw new Error(`SKILL.md not found in ${contentDir}. Invalid skill package.`);
|
|
3387
3386
|
}
|
|
3388
3387
|
}
|
|
3389
|
-
var DEFAULT_REGISTRY_PATH =
|
|
3388
|
+
var DEFAULT_REGISTRY_PATH = join5(homedir3(), ".okx", "skills", "registry.json");
|
|
3390
3389
|
function readRegistry(registryPath = DEFAULT_REGISTRY_PATH) {
|
|
3391
3390
|
if (!existsSync2(registryPath)) {
|
|
3392
3391
|
return { version: 1, skills: {} };
|
|
3393
3392
|
}
|
|
3394
3393
|
try {
|
|
3395
|
-
const raw =
|
|
3394
|
+
const raw = readFileSync3(registryPath, "utf-8");
|
|
3396
3395
|
return JSON.parse(raw);
|
|
3397
3396
|
} catch {
|
|
3398
3397
|
return { version: 1, skills: {} };
|
|
3399
3398
|
}
|
|
3400
3399
|
}
|
|
3401
3400
|
function writeRegistry(registry, registryPath = DEFAULT_REGISTRY_PATH) {
|
|
3402
|
-
|
|
3403
|
-
|
|
3401
|
+
mkdirSync4(dirname3(registryPath), { recursive: true });
|
|
3402
|
+
writeFileSync3(registryPath, JSON.stringify(registry, null, 2) + "\n", "utf-8");
|
|
3404
3403
|
}
|
|
3405
3404
|
function upsertSkillRecord(meta, registryPath = DEFAULT_REGISTRY_PATH) {
|
|
3406
3405
|
const registry = readRegistry(registryPath);
|
|
@@ -3473,7 +3472,7 @@ function registerSkillsTools() {
|
|
|
3473
3472
|
{
|
|
3474
3473
|
name: "skills_download",
|
|
3475
3474
|
module: "skills",
|
|
3476
|
-
description: "Download a skill zip file 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:
|
|
3475
|
+
description: "Download a skill zip file 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: This only downloads the zip \u2014 it 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.",
|
|
3477
3476
|
inputSchema: {
|
|
3478
3477
|
type: "object",
|
|
3479
3478
|
properties: {
|
|
@@ -4079,7 +4078,7 @@ function registerEarnTools() {
|
|
|
4079
4078
|
{
|
|
4080
4079
|
name: "earn_get_savings_balance",
|
|
4081
4080
|
module: "earn.savings",
|
|
4082
|
-
description: "Get Simple Earn (savings/flexible earn) balance. Returns current holdings
|
|
4081
|
+
description: "Get Simple Earn (savings/flexible earn) balance. Returns current holdings, lent amount, pending interest, and the user's set rate. Response fields: amt (total held), loanAmt (actively lent), pendingAmt (awaiting match), earnings (cumulative interest), rate (user's own minimum lending rate setting \u2014 NOT market yield, NOT APY). To get the actual market lending rate, call earn_get_lending_rate_history instead.",
|
|
4083
4082
|
isWrite: false,
|
|
4084
4083
|
inputSchema: {
|
|
4085
4084
|
type: "object",
|
|
@@ -4100,43 +4099,6 @@ function registerEarnTools() {
|
|
|
4100
4099
|
return normalizeResponse(response);
|
|
4101
4100
|
}
|
|
4102
4101
|
},
|
|
4103
|
-
{
|
|
4104
|
-
name: "earn_get_fixed_order_list",
|
|
4105
|
-
module: "earn.savings",
|
|
4106
|
-
description: "Get Simple Earn Fixed (\u5B9A\u671F\u8D5A\u5E01) lending order list. Returns orders sorted by creation time descending. Use this to check status of fixed-term lending orders (pending/earning/expired/settled/cancelled). Do NOT use for flexible earn balance \u2014 use earn_get_savings_balance instead. If the result is empty, do NOT display any fixed-term section in the output.",
|
|
4107
|
-
isWrite: false,
|
|
4108
|
-
inputSchema: {
|
|
4109
|
-
type: "object",
|
|
4110
|
-
properties: {
|
|
4111
|
-
ccy: {
|
|
4112
|
-
type: "string",
|
|
4113
|
-
description: "Currency, e.g. USDT. Omit for all."
|
|
4114
|
-
},
|
|
4115
|
-
state: {
|
|
4116
|
-
type: "string",
|
|
4117
|
-
description: "Order state: pending (\u5339\u914D\u4E2D), earning (\u8D5A\u5E01\u4E2D), expired (\u903E\u671F), settled (\u5DF2\u7ED3\u7B97), cancelled (\u5DF2\u64A4\u9500). Omit for all."
|
|
4118
|
-
}
|
|
4119
|
-
}
|
|
4120
|
-
},
|
|
4121
|
-
handler: async (rawArgs, context) => {
|
|
4122
|
-
const args = asRecord(rawArgs);
|
|
4123
|
-
const response = await context.client.privateGet(
|
|
4124
|
-
"/api/v5/finance/simple-earn-fixed/order-list",
|
|
4125
|
-
compactObject({
|
|
4126
|
-
ccy: readString(args, "ccy"),
|
|
4127
|
-
state: readString(args, "state")
|
|
4128
|
-
}),
|
|
4129
|
-
privateRateLimit("earn_get_fixed_order_list", 3)
|
|
4130
|
-
);
|
|
4131
|
-
const result = normalizeResponse(response);
|
|
4132
|
-
if (Array.isArray(result["data"])) {
|
|
4133
|
-
result["data"] = result["data"].map(
|
|
4134
|
-
({ finalSettlementDate: _, ...rest }) => rest
|
|
4135
|
-
);
|
|
4136
|
-
}
|
|
4137
|
-
return result;
|
|
4138
|
-
}
|
|
4139
|
-
},
|
|
4140
4102
|
{
|
|
4141
4103
|
name: "earn_savings_purchase",
|
|
4142
4104
|
module: "earn.savings",
|
|
@@ -4281,112 +4243,10 @@ function registerEarnTools() {
|
|
|
4281
4243
|
return normalizeResponse(response);
|
|
4282
4244
|
}
|
|
4283
4245
|
},
|
|
4284
|
-
{
|
|
4285
|
-
name: "earn_fixed_purchase",
|
|
4286
|
-
module: "earn.savings",
|
|
4287
|
-
description: "Purchase Simple Earn Fixed (\u5B9A\u671F) product, two-step flow. First call (confirm omitted or false): returns purchase preview with product details and risk warning. Preview offer fields: lendQuota = remaining quota (\u5269\u4F59\u989D\u5EA6), soldOut = whether product is sold out (lendQuota is 0). YOU MUST display the 'warning' field from the preview response to the user VERBATIM before asking for confirmation \u2014 do NOT omit or summarize it. Second call (confirm=true): executes the purchase. Only proceed after the user explicitly confirms. IMPORTANT: Orders in 'pending' (\u5339\u914D\u4E2D) state can still be cancelled via earn_fixed_redeem; once the status changes to 'earning' (\u8D5A\u5E01\u4E2D), funds are LOCKED until maturity \u2014 no early redemption allowed.",
|
|
4288
|
-
isWrite: true,
|
|
4289
|
-
inputSchema: {
|
|
4290
|
-
type: "object",
|
|
4291
|
-
properties: {
|
|
4292
|
-
ccy: {
|
|
4293
|
-
type: "string",
|
|
4294
|
-
description: "Currency, e.g. USDT"
|
|
4295
|
-
},
|
|
4296
|
-
amt: {
|
|
4297
|
-
type: "string",
|
|
4298
|
-
description: "Purchase amount"
|
|
4299
|
-
},
|
|
4300
|
-
term: {
|
|
4301
|
-
type: "string",
|
|
4302
|
-
description: "Term, e.g. 90D"
|
|
4303
|
-
},
|
|
4304
|
-
confirm: {
|
|
4305
|
-
type: "boolean",
|
|
4306
|
-
description: "Omit or false on the first call to preview the purchase details; set to true on the second call to execute after user confirms."
|
|
4307
|
-
}
|
|
4308
|
-
},
|
|
4309
|
-
required: ["ccy", "amt", "term"]
|
|
4310
|
-
},
|
|
4311
|
-
handler: async (rawArgs, context) => {
|
|
4312
|
-
const args = asRecord(rawArgs);
|
|
4313
|
-
const ccy = requireString(args, "ccy");
|
|
4314
|
-
const amt = requireString(args, "amt");
|
|
4315
|
-
const term = requireString(args, "term");
|
|
4316
|
-
const confirm = readBoolean(args, "confirm") ?? false;
|
|
4317
|
-
if (!confirm) {
|
|
4318
|
-
const [rateResponse, fixedResponse] = await Promise.all([
|
|
4319
|
-
context.client.publicGet(
|
|
4320
|
-
"/api/v5/finance/savings/lending-rate-history",
|
|
4321
|
-
compactObject({ ccy, limit: 1 }),
|
|
4322
|
-
publicRateLimit("earn_get_lending_rate_history", 6)
|
|
4323
|
-
),
|
|
4324
|
-
context.client.privateGet(
|
|
4325
|
-
"/api/v5/finance/simple-earn-fixed/offers",
|
|
4326
|
-
compactObject({ ccy }),
|
|
4327
|
-
privateRateLimit("earn_fixed_purchase_preview_offers", 2)
|
|
4328
|
-
).catch(() => null)
|
|
4329
|
-
]);
|
|
4330
|
-
const rateResult = normalizeResponse(rateResponse);
|
|
4331
|
-
const fixedResult = fixedResponse ? normalizeResponse(fixedResponse) : { data: [] };
|
|
4332
|
-
const rateArr = Array.isArray(rateResult["data"]) ? rateResult["data"] : [];
|
|
4333
|
-
const allOffers = Array.isArray(fixedResult["data"]) ? fixedResult["data"] : [];
|
|
4334
|
-
const matchedOffer = allOffers.find(
|
|
4335
|
-
(o) => o["term"] === term && o["ccy"] === ccy
|
|
4336
|
-
);
|
|
4337
|
-
const { borrowingOrderQuota: _, ...offerWithoutTotal } = matchedOffer ?? {};
|
|
4338
|
-
const offerWithSoldOut = matchedOffer ? { ...offerWithoutTotal, soldOut: offerWithoutTotal["lendQuota"] === "0" } : null;
|
|
4339
|
-
return {
|
|
4340
|
-
preview: true,
|
|
4341
|
-
ccy,
|
|
4342
|
-
amt,
|
|
4343
|
-
term,
|
|
4344
|
-
offer: offerWithSoldOut,
|
|
4345
|
-
currentFlexibleRate: rateArr[0]?.["lendingRate"] ?? null,
|
|
4346
|
-
warning: "\u26A0\uFE0F Orders still in 'pending' state can be cancelled before matching completes. Once the status changes to 'earning', funds are LOCKED until maturity \u2014 early redemption is NOT allowed. Please call again with confirm=true to proceed."
|
|
4347
|
-
};
|
|
4348
|
-
}
|
|
4349
|
-
assertNotDemo(context.config, "earn_fixed_purchase");
|
|
4350
|
-
const response = await context.client.privatePost(
|
|
4351
|
-
"/api/v5/finance/simple-earn-fixed/purchase",
|
|
4352
|
-
{ ccy, amt, term },
|
|
4353
|
-
privateRateLimit("earn_fixed_purchase", 2)
|
|
4354
|
-
);
|
|
4355
|
-
return normalizeResponse(response);
|
|
4356
|
-
}
|
|
4357
|
-
},
|
|
4358
|
-
{
|
|
4359
|
-
name: "earn_fixed_redeem",
|
|
4360
|
-
module: "earn.savings",
|
|
4361
|
-
description: "Redeem Simple Earn Fixed (\u5B9A\u671F\u8D5A\u5E01) order. [CAUTION] Redeems a fixed-term lending order. Always redeems the full order amount. Only orders in 'pending' (\u5339\u914D\u4E2D) state can be redeemed \u2014 orders in 'earning' state are locked until maturity and cannot be redeemed early. Do NOT use for flexible earn redemption \u2014 use earn_savings_redeem instead.",
|
|
4362
|
-
isWrite: true,
|
|
4363
|
-
inputSchema: {
|
|
4364
|
-
type: "object",
|
|
4365
|
-
properties: {
|
|
4366
|
-
reqId: {
|
|
4367
|
-
type: "string",
|
|
4368
|
-
description: "Request ID of the fixed-term order to redeem"
|
|
4369
|
-
}
|
|
4370
|
-
},
|
|
4371
|
-
required: ["reqId"]
|
|
4372
|
-
},
|
|
4373
|
-
handler: async (rawArgs, context) => {
|
|
4374
|
-
assertNotDemo(context.config, "earn_fixed_redeem");
|
|
4375
|
-
const args = asRecord(rawArgs);
|
|
4376
|
-
const response = await context.client.privatePost(
|
|
4377
|
-
"/api/v5/finance/simple-earn-fixed/redeem",
|
|
4378
|
-
{
|
|
4379
|
-
reqId: requireString(args, "reqId")
|
|
4380
|
-
},
|
|
4381
|
-
privateRateLimit("earn_fixed_redeem", 2)
|
|
4382
|
-
);
|
|
4383
|
-
return normalizeResponse(response);
|
|
4384
|
-
}
|
|
4385
|
-
},
|
|
4386
4246
|
{
|
|
4387
4247
|
name: "earn_get_lending_rate_history",
|
|
4388
4248
|
module: "earn.savings",
|
|
4389
|
-
description: "Query Simple Earn lending rates
|
|
4249
|
+
description: "Query Simple Earn lending rates. Public endpoint (no API key required). Use this tool when the user asks about current or historical lending rates for Simple Earn, or when displaying savings balance with market rate context. Response fields per record: rate (market lending rate \u2014 the rate borrowers pay this period; user's minimum setting must be \u2264 this to be eligible), lendingRate (actual yield received by lenders; stablecoins e.g. USDT/USDC only: subject to pro-rata dilution \u2014 when eligible supply exceeds borrowing demand total interest is shared so lendingRate < rate; non-stablecoins: lendingRate = rate, no dilution; always use lendingRate as the true APY to show users), ts (settlement timestamp ms). To get current APY: use limit=1 and read lendingRate.",
|
|
4390
4250
|
isWrite: false,
|
|
4391
4251
|
inputSchema: {
|
|
4392
4252
|
type: "object",
|
|
@@ -4405,45 +4265,23 @@ function registerEarnTools() {
|
|
|
4405
4265
|
},
|
|
4406
4266
|
limit: {
|
|
4407
4267
|
type: "number",
|
|
4408
|
-
description: "Max results (default
|
|
4268
|
+
description: "Max results (default 100)"
|
|
4409
4269
|
}
|
|
4410
4270
|
}
|
|
4411
4271
|
},
|
|
4412
4272
|
handler: async (rawArgs, context) => {
|
|
4413
4273
|
const args = asRecord(rawArgs);
|
|
4414
|
-
const
|
|
4415
|
-
|
|
4416
|
-
|
|
4417
|
-
"
|
|
4418
|
-
|
|
4419
|
-
|
|
4420
|
-
|
|
4421
|
-
|
|
4422
|
-
|
|
4423
|
-
|
|
4424
|
-
|
|
4425
|
-
),
|
|
4426
|
-
context.client.privateGet(
|
|
4427
|
-
"/api/v5/finance/simple-earn-fixed/offers",
|
|
4428
|
-
compactObject({ ccy }),
|
|
4429
|
-
privateRateLimit("earn_get_lending_rate_history_fixed", 2)
|
|
4430
|
-
).catch(() => null)
|
|
4431
|
-
]);
|
|
4432
|
-
const rateResult = normalizeResponse(rateResponse);
|
|
4433
|
-
const rateData = Array.isArray(rateResult["data"]) ? rateResult["data"].map(
|
|
4434
|
-
({ rate: _, ...rest }) => rest
|
|
4435
|
-
) : [];
|
|
4436
|
-
const fixedResult = fixedResponse ? normalizeResponse(fixedResponse) : { data: [] };
|
|
4437
|
-
const allOffers = fixedResult["data"] ?? [];
|
|
4438
|
-
const fixedOffers = allOffers.map(({ borrowingOrderQuota: _, ...rest }) => ({
|
|
4439
|
-
...rest,
|
|
4440
|
-
soldOut: rest["lendQuota"] === "0"
|
|
4441
|
-
}));
|
|
4442
|
-
return {
|
|
4443
|
-
...rateResult,
|
|
4444
|
-
data: rateData,
|
|
4445
|
-
fixedOffers
|
|
4446
|
-
};
|
|
4274
|
+
const response = await context.client.publicGet(
|
|
4275
|
+
"/api/v5/finance/savings/lending-rate-history",
|
|
4276
|
+
compactObject({
|
|
4277
|
+
ccy: readString(args, "ccy"),
|
|
4278
|
+
after: readString(args, "after"),
|
|
4279
|
+
before: readString(args, "before"),
|
|
4280
|
+
limit: readNumber(args, "limit")
|
|
4281
|
+
}),
|
|
4282
|
+
publicRateLimit("earn_get_lending_rate_history", 6)
|
|
4283
|
+
);
|
|
4284
|
+
return normalizeResponse(response);
|
|
4447
4285
|
}
|
|
4448
4286
|
}
|
|
4449
4287
|
];
|
|
@@ -4782,7 +4620,7 @@ function registerDcdTools() {
|
|
|
4782
4620
|
{
|
|
4783
4621
|
name: "dcd_get_products",
|
|
4784
4622
|
module: "earn.dcd",
|
|
4785
|
-
description: "Get DCD products with yield and quota info.
|
|
4623
|
+
description: "Get DCD products with yield and quota info.",
|
|
4786
4624
|
isWrite: false,
|
|
4787
4625
|
inputSchema: {
|
|
4788
4626
|
type: "object",
|
|
@@ -4836,7 +4674,7 @@ function registerDcdTools() {
|
|
|
4836
4674
|
{
|
|
4837
4675
|
name: "dcd_get_orders",
|
|
4838
4676
|
module: "earn.dcd",
|
|
4839
|
-
description: "Get DCD order history.
|
|
4677
|
+
description: "Get DCD order history.",
|
|
4840
4678
|
isWrite: false,
|
|
4841
4679
|
inputSchema: {
|
|
4842
4680
|
type: "object",
|
|
@@ -4880,7 +4718,7 @@ function registerDcdTools() {
|
|
|
4880
4718
|
{
|
|
4881
4719
|
name: "dcd_subscribe",
|
|
4882
4720
|
module: "earn.dcd",
|
|
4883
|
-
description: "Subscribe to a DCD product: get quote and execute atomically. Confirm product, amount, and currency with user before calling. Optional minAnnualizedYield rejects the order if quote yield falls below threshold. Returns order result with quote snapshot (
|
|
4721
|
+
description: "Subscribe to a DCD product: get quote and execute atomically. Confirm product, amount, and currency with user before calling. Optional minAnnualizedYield (percent) rejects the order if quote yield falls below threshold. Returns order result with quote snapshot (annualizedYield, absYield).",
|
|
4884
4722
|
isWrite: true,
|
|
4885
4723
|
inputSchema: {
|
|
4886
4724
|
type: "object",
|
|
@@ -4919,23 +4757,13 @@ function registerDcdTools() {
|
|
|
4919
4757
|
});
|
|
4920
4758
|
}
|
|
4921
4759
|
if (minAnnualizedYield !== void 0) {
|
|
4922
|
-
const
|
|
4923
|
-
if (isNaN(
|
|
4924
|
-
throw new OkxApiError(
|
|
4925
|
-
"Quote returned non-numeric annualizedYield, cannot verify minimum yield threshold.",
|
|
4926
|
-
{
|
|
4927
|
-
code: "INVALID_YIELD_VALUE",
|
|
4928
|
-
suggestion: "Order not placed. The quote did not include a valid annualizedYield. Retry or pick a different product."
|
|
4929
|
-
}
|
|
4930
|
-
);
|
|
4931
|
-
}
|
|
4932
|
-
const actualYieldPct = rawYield * 100;
|
|
4933
|
-
if (actualYieldPct < minAnnualizedYield) {
|
|
4760
|
+
const actualYield = parseFloat(quote["annualizedYield"]);
|
|
4761
|
+
if (!isNaN(actualYield) && actualYield < minAnnualizedYield) {
|
|
4934
4762
|
throw new OkxApiError(
|
|
4935
|
-
`Quote yield ${
|
|
4763
|
+
`Quote yield ${actualYield}% is below the minimum threshold of ${minAnnualizedYield}%.`,
|
|
4936
4764
|
{
|
|
4937
4765
|
code: "YIELD_BELOW_MIN",
|
|
4938
|
-
suggestion: `Order not placed. Actual: ${
|
|
4766
|
+
suggestion: `Order not placed. Actual: ${actualYield}%, required: >= ${minAnnualizedYield}%. Try a different product or lower your minimum yield.`
|
|
4939
4767
|
}
|
|
4940
4768
|
);
|
|
4941
4769
|
}
|
|
@@ -5080,7 +4908,7 @@ function registerAutoEarnTools() {
|
|
|
5080
4908
|
}
|
|
5081
4909
|
var EARN_DEMO_MESSAGE = "Earn features (savings, DCD, on-chain staking, auto-earn) are not available in simulated trading mode.";
|
|
5082
4910
|
var EARN_DEMO_SUGGESTION = "Switch to a live account to use Earn features.";
|
|
5083
|
-
var DEMO_GUARD_SKIP = /* @__PURE__ */ new Set(["dcd_redeem"
|
|
4911
|
+
var DEMO_GUARD_SKIP = /* @__PURE__ */ new Set(["dcd_redeem"]);
|
|
5084
4912
|
function withDemoGuard(tool) {
|
|
5085
4913
|
if (!tool.isWrite || DEMO_GUARD_SKIP.has(tool.name)) return tool;
|
|
5086
4914
|
const originalHandler = tool.handler;
|
|
@@ -5141,12 +4969,12 @@ function buildContractTradeTools(cfg) {
|
|
|
5141
4969
|
},
|
|
5142
4970
|
sz: {
|
|
5143
4971
|
type: "string",
|
|
5144
|
-
description: "Number of contracts. Each contract = ctVal units (e.g. 0.1 ETH for ETH-USDT-SWAP). Query market_get_instruments for exact ctVal. Set tgtCcy=quote_ccy to specify sz in USDT
|
|
4972
|
+
description: "Number of contracts. Each contract = ctVal units (e.g. 0.1 ETH for ETH-USDT-SWAP). Query market_get_instruments for exact ctVal. Set tgtCcy=quote_ccy to specify sz in USDT instead."
|
|
5145
4973
|
},
|
|
5146
4974
|
tgtCcy: {
|
|
5147
4975
|
type: "string",
|
|
5148
|
-
enum: ["base_ccy", "quote_ccy"
|
|
5149
|
-
description: "Size unit. base_ccy(default): sz in contracts
|
|
4976
|
+
enum: ["base_ccy", "quote_ccy"],
|
|
4977
|
+
description: "Size unit. base_ccy(default): sz in contracts, quote_ccy: sz in USDT"
|
|
5150
4978
|
},
|
|
5151
4979
|
px: { type: "string", description: "Required for limit/post_only/fok/ioc" },
|
|
5152
4980
|
reduceOnly: {
|
|
@@ -5165,14 +4993,6 @@ function buildContractTradeTools(cfg) {
|
|
|
5165
4993
|
const args = asRecord(rawArgs);
|
|
5166
4994
|
const reduceOnly = args.reduceOnly;
|
|
5167
4995
|
const attachAlgoOrds = buildAttachAlgoOrds(args);
|
|
5168
|
-
const resolved = await resolveQuoteCcySz(
|
|
5169
|
-
requireString(args, "instId"),
|
|
5170
|
-
requireString(args, "sz"),
|
|
5171
|
-
readString(args, "tgtCcy"),
|
|
5172
|
-
defaultType,
|
|
5173
|
-
context.client,
|
|
5174
|
-
readString(args, "tdMode")
|
|
5175
|
-
);
|
|
5176
4996
|
const response = await context.client.privatePost(
|
|
5177
4997
|
"/api/v5/trade/order",
|
|
5178
4998
|
compactObject({
|
|
@@ -5181,8 +5001,8 @@ function buildContractTradeTools(cfg) {
|
|
|
5181
5001
|
side: requireString(args, "side"),
|
|
5182
5002
|
posSide: readString(args, "posSide"),
|
|
5183
5003
|
ordType: requireString(args, "ordType"),
|
|
5184
|
-
sz:
|
|
5185
|
-
tgtCcy:
|
|
5004
|
+
sz: requireString(args, "sz"),
|
|
5005
|
+
tgtCcy: readString(args, "tgtCcy"),
|
|
5186
5006
|
px: readString(args, "px"),
|
|
5187
5007
|
reduceOnly: typeof reduceOnly === "boolean" ? String(reduceOnly) : void 0,
|
|
5188
5008
|
clOrdId: readString(args, "clOrdId"),
|
|
@@ -5191,11 +5011,7 @@ function buildContractTradeTools(cfg) {
|
|
|
5191
5011
|
}),
|
|
5192
5012
|
privateRateLimit(n("place_order"), 60)
|
|
5193
5013
|
);
|
|
5194
|
-
|
|
5195
|
-
if (resolved.conversionNote) {
|
|
5196
|
-
result._conversion = resolved.conversionNote;
|
|
5197
|
-
}
|
|
5198
|
-
return result;
|
|
5014
|
+
return normalizeResponse(response);
|
|
5199
5015
|
}
|
|
5200
5016
|
},
|
|
5201
5017
|
// ── cancel_order ─────────────────────────────────────────────────────────
|
|
@@ -6269,8 +6085,8 @@ function registerOptionAlgoTools() {
|
|
|
6269
6085
|
},
|
|
6270
6086
|
tgtCcy: {
|
|
6271
6087
|
type: "string",
|
|
6272
|
-
enum: ["base_ccy", "quote_ccy"
|
|
6273
|
-
description: "Size unit. base_ccy(default): sz in contracts
|
|
6088
|
+
enum: ["base_ccy", "quote_ccy"],
|
|
6089
|
+
description: "Size unit. base_ccy(default): sz in contracts, quote_ccy: sz in USDT (may not be supported for options)"
|
|
6274
6090
|
},
|
|
6275
6091
|
reduceOnly: {
|
|
6276
6092
|
type: "boolean",
|
|
@@ -6286,14 +6102,6 @@ function registerOptionAlgoTools() {
|
|
|
6286
6102
|
handler: async (rawArgs, context) => {
|
|
6287
6103
|
const args = asRecord(rawArgs);
|
|
6288
6104
|
const reduceOnly = readBoolean(args, "reduceOnly");
|
|
6289
|
-
const resolved = await resolveQuoteCcySz(
|
|
6290
|
-
requireString(args, "instId"),
|
|
6291
|
-
requireString(args, "sz"),
|
|
6292
|
-
readString(args, "tgtCcy"),
|
|
6293
|
-
"OPTION",
|
|
6294
|
-
context.client,
|
|
6295
|
-
readString(args, "tdMode")
|
|
6296
|
-
);
|
|
6297
6105
|
const response = await context.client.privatePost(
|
|
6298
6106
|
"/api/v5/trade/order-algo",
|
|
6299
6107
|
compactObject({
|
|
@@ -6301,8 +6109,8 @@ function registerOptionAlgoTools() {
|
|
|
6301
6109
|
tdMode: requireString(args, "tdMode"),
|
|
6302
6110
|
side: requireString(args, "side"),
|
|
6303
6111
|
ordType: requireString(args, "ordType"),
|
|
6304
|
-
sz:
|
|
6305
|
-
tgtCcy:
|
|
6112
|
+
sz: requireString(args, "sz"),
|
|
6113
|
+
tgtCcy: readString(args, "tgtCcy"),
|
|
6306
6114
|
tpTriggerPx: readString(args, "tpTriggerPx"),
|
|
6307
6115
|
tpOrdPx: readString(args, "tpOrdPx"),
|
|
6308
6116
|
tpTriggerPxType: readString(args, "tpTriggerPxType"),
|
|
@@ -6509,12 +6317,7 @@ function registerOptionTools() {
|
|
|
6509
6317
|
},
|
|
6510
6318
|
sz: {
|
|
6511
6319
|
type: "string",
|
|
6512
|
-
description: "Contracts count
|
|
6513
|
-
},
|
|
6514
|
-
tgtCcy: {
|
|
6515
|
-
type: "string",
|
|
6516
|
-
enum: ["base_ccy", "quote_ccy", "margin"],
|
|
6517
|
-
description: "Size unit. base_ccy(default): sz in contracts; quote_ccy: sz in USDT notional value; margin: sz in USDT margin cost (actual position = sz * leverage)"
|
|
6320
|
+
description: "Contracts count (NOT USDT). Use market_get_instruments for ctVal."
|
|
6518
6321
|
},
|
|
6519
6322
|
px: {
|
|
6520
6323
|
type: "string",
|
|
@@ -6551,14 +6354,6 @@ function registerOptionTools() {
|
|
|
6551
6354
|
const args = asRecord(rawArgs);
|
|
6552
6355
|
const reduceOnly = args.reduceOnly;
|
|
6553
6356
|
const attachAlgoOrds = buildAttachAlgoOrds(args);
|
|
6554
|
-
const resolved = await resolveQuoteCcySz(
|
|
6555
|
-
requireString(args, "instId"),
|
|
6556
|
-
requireString(args, "sz"),
|
|
6557
|
-
readString(args, "tgtCcy"),
|
|
6558
|
-
"OPTION",
|
|
6559
|
-
context.client,
|
|
6560
|
-
readString(args, "tdMode")
|
|
6561
|
-
);
|
|
6562
6357
|
const response = await context.client.privatePost(
|
|
6563
6358
|
"/api/v5/trade/order",
|
|
6564
6359
|
compactObject({
|
|
@@ -6566,8 +6361,7 @@ function registerOptionTools() {
|
|
|
6566
6361
|
tdMode: requireString(args, "tdMode"),
|
|
6567
6362
|
side: requireString(args, "side"),
|
|
6568
6363
|
ordType: requireString(args, "ordType"),
|
|
6569
|
-
sz:
|
|
6570
|
-
tgtCcy: resolved.tgtCcy,
|
|
6364
|
+
sz: requireString(args, "sz"),
|
|
6571
6365
|
px: readString(args, "px"),
|
|
6572
6366
|
reduceOnly: typeof reduceOnly === "boolean" ? String(reduceOnly) : void 0,
|
|
6573
6367
|
clOrdId: readString(args, "clOrdId"),
|
|
@@ -7723,12 +7517,12 @@ function createToolRunner(client, config) {
|
|
|
7723
7517
|
};
|
|
7724
7518
|
}
|
|
7725
7519
|
function configFilePath() {
|
|
7726
|
-
return
|
|
7520
|
+
return join6(homedir4(), ".okx", "config.toml");
|
|
7727
7521
|
}
|
|
7728
7522
|
function readFullConfig() {
|
|
7729
7523
|
const path42 = configFilePath();
|
|
7730
7524
|
if (!existsSync3(path42)) return { profiles: {} };
|
|
7731
|
-
const raw =
|
|
7525
|
+
const raw = readFileSync4(path42, "utf-8");
|
|
7732
7526
|
try {
|
|
7733
7527
|
return parse(raw);
|
|
7734
7528
|
} catch (err) {
|
|
@@ -7756,11 +7550,11 @@ var CONFIG_HEADER = `# OKX Trade Kit Configuration
|
|
|
7756
7550
|
`;
|
|
7757
7551
|
function writeFullConfig(config) {
|
|
7758
7552
|
const path42 = configFilePath();
|
|
7759
|
-
const dir =
|
|
7553
|
+
const dir = dirname4(path42);
|
|
7760
7554
|
if (!existsSync3(dir)) {
|
|
7761
|
-
|
|
7555
|
+
mkdirSync5(dir, { recursive: true });
|
|
7762
7556
|
}
|
|
7763
|
-
|
|
7557
|
+
writeFileSync4(path42, CONFIG_HEADER + stringify(config), "utf-8");
|
|
7764
7558
|
}
|
|
7765
7559
|
function expandShorthand(moduleId) {
|
|
7766
7560
|
if (moduleId === "all") return [...MODULES];
|
|
@@ -7874,21 +7668,21 @@ function loadConfig(cli) {
|
|
|
7874
7668
|
verbose: cli.verbose ?? false
|
|
7875
7669
|
};
|
|
7876
7670
|
}
|
|
7877
|
-
var CACHE_FILE =
|
|
7671
|
+
var CACHE_FILE = join7(homedir5(), ".okx", "update-check.json");
|
|
7878
7672
|
var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
7879
|
-
function
|
|
7673
|
+
function readCache2() {
|
|
7880
7674
|
try {
|
|
7881
7675
|
if (existsSync4(CACHE_FILE)) {
|
|
7882
|
-
return JSON.parse(
|
|
7676
|
+
return JSON.parse(readFileSync5(CACHE_FILE, "utf-8"));
|
|
7883
7677
|
}
|
|
7884
7678
|
} catch {
|
|
7885
7679
|
}
|
|
7886
7680
|
return {};
|
|
7887
7681
|
}
|
|
7888
|
-
function
|
|
7682
|
+
function writeCache2(cache) {
|
|
7889
7683
|
try {
|
|
7890
|
-
|
|
7891
|
-
|
|
7684
|
+
mkdirSync6(join7(homedir5(), ".okx"), { recursive: true });
|
|
7685
|
+
writeFileSync5(CACHE_FILE, JSON.stringify(cache, null, 2), "utf-8");
|
|
7892
7686
|
} catch {
|
|
7893
7687
|
}
|
|
7894
7688
|
}
|
|
@@ -7935,14 +7729,14 @@ async function fetchLatestVersion(packageName) {
|
|
|
7935
7729
|
function refreshCacheInBackground(packageName) {
|
|
7936
7730
|
fetchLatestVersion(packageName).then((latest) => {
|
|
7937
7731
|
if (!latest) return;
|
|
7938
|
-
const cache =
|
|
7732
|
+
const cache = readCache2();
|
|
7939
7733
|
cache[packageName] = { latestVersion: latest, checkedAt: Date.now() };
|
|
7940
|
-
|
|
7734
|
+
writeCache2(cache);
|
|
7941
7735
|
}).catch(() => {
|
|
7942
7736
|
});
|
|
7943
7737
|
}
|
|
7944
7738
|
function checkForUpdates(packageName, currentVersion) {
|
|
7945
|
-
const cache =
|
|
7739
|
+
const cache = readCache2();
|
|
7946
7740
|
const entry = cache[packageName];
|
|
7947
7741
|
if (entry && isNewerVersion(currentVersion, entry.latestVersion)) {
|
|
7948
7742
|
process.stderr.write(
|
|
@@ -7957,69 +7751,6 @@ Run: npm install -g ${packageName}
|
|
|
7957
7751
|
refreshCacheInBackground(packageName);
|
|
7958
7752
|
}
|
|
7959
7753
|
}
|
|
7960
|
-
var LOG_LEVEL_PRIORITY = {
|
|
7961
|
-
error: 0,
|
|
7962
|
-
warn: 1,
|
|
7963
|
-
info: 2,
|
|
7964
|
-
debug: 3
|
|
7965
|
-
};
|
|
7966
|
-
var SENSITIVE_KEY_PATTERN = /apiKey|secretKey|passphrase|password|secret/i;
|
|
7967
|
-
function sanitize(value) {
|
|
7968
|
-
if (value === null || value === void 0) {
|
|
7969
|
-
return value;
|
|
7970
|
-
}
|
|
7971
|
-
if (Array.isArray(value)) {
|
|
7972
|
-
return value.map(sanitize);
|
|
7973
|
-
}
|
|
7974
|
-
if (typeof value === "object") {
|
|
7975
|
-
const result = {};
|
|
7976
|
-
for (const [k, v] of Object.entries(value)) {
|
|
7977
|
-
if (SENSITIVE_KEY_PATTERN.test(k)) {
|
|
7978
|
-
result[k] = "[REDACTED]";
|
|
7979
|
-
} else {
|
|
7980
|
-
result[k] = sanitize(v);
|
|
7981
|
-
}
|
|
7982
|
-
}
|
|
7983
|
-
return result;
|
|
7984
|
-
}
|
|
7985
|
-
return value;
|
|
7986
|
-
}
|
|
7987
|
-
var TradeLogger = class {
|
|
7988
|
-
logLevel;
|
|
7989
|
-
logDir;
|
|
7990
|
-
constructor(logLevel = "info", logDir) {
|
|
7991
|
-
this.logLevel = logLevel;
|
|
7992
|
-
this.logDir = logDir ?? path2.join(os2.homedir(), ".okx", "logs");
|
|
7993
|
-
}
|
|
7994
|
-
getLogPath(date) {
|
|
7995
|
-
const d = date ?? /* @__PURE__ */ new Date();
|
|
7996
|
-
const yyyy = d.getUTCFullYear();
|
|
7997
|
-
const mm = String(d.getUTCMonth() + 1).padStart(2, "0");
|
|
7998
|
-
const dd = String(d.getUTCDate()).padStart(2, "0");
|
|
7999
|
-
return path2.join(this.logDir, `trade-${yyyy}-${mm}-${dd}.log`);
|
|
8000
|
-
}
|
|
8001
|
-
log(level, tool, params, result, durationMs) {
|
|
8002
|
-
if (LOG_LEVEL_PRIORITY[level] > LOG_LEVEL_PRIORITY[this.logLevel]) {
|
|
8003
|
-
return;
|
|
8004
|
-
}
|
|
8005
|
-
const entry = {
|
|
8006
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
8007
|
-
level: level.toUpperCase(),
|
|
8008
|
-
tool,
|
|
8009
|
-
durationMs,
|
|
8010
|
-
params: sanitize(params),
|
|
8011
|
-
result: sanitize(result)
|
|
8012
|
-
};
|
|
8013
|
-
try {
|
|
8014
|
-
fs2.mkdirSync(this.logDir, { recursive: true });
|
|
8015
|
-
fs2.appendFileSync(this.getLogPath(), JSON.stringify(entry) + "\n", "utf8");
|
|
8016
|
-
} catch {
|
|
8017
|
-
}
|
|
8018
|
-
}
|
|
8019
|
-
static sanitize(params) {
|
|
8020
|
-
return sanitize(params);
|
|
8021
|
-
}
|
|
8022
|
-
};
|
|
8023
7754
|
var CLIENT_NAMES = {
|
|
8024
7755
|
"claude-desktop": "Claude Desktop",
|
|
8025
7756
|
cursor: "Cursor",
|
|
@@ -8169,11 +7900,11 @@ function runSetup(options) {
|
|
|
8169
7900
|
// src/commands/diagnose.ts
|
|
8170
7901
|
import dns from "dns/promises";
|
|
8171
7902
|
import net from "net";
|
|
8172
|
-
import
|
|
7903
|
+
import os4 from "os";
|
|
8173
7904
|
import tls from "tls";
|
|
8174
7905
|
|
|
8175
7906
|
// src/commands/diagnose-utils.ts
|
|
8176
|
-
import
|
|
7907
|
+
import fs2 from "fs";
|
|
8177
7908
|
import { createRequire } from "module";
|
|
8178
7909
|
|
|
8179
7910
|
// src/formatter.ts
|
|
@@ -8186,10 +7917,6 @@ var activeOutput = stdioOutput;
|
|
|
8186
7917
|
function setOutput(impl) {
|
|
8187
7918
|
activeOutput = impl;
|
|
8188
7919
|
}
|
|
8189
|
-
var envContext = null;
|
|
8190
|
-
function setEnvContext(ctx) {
|
|
8191
|
-
envContext = ctx;
|
|
8192
|
-
}
|
|
8193
7920
|
function output(message) {
|
|
8194
7921
|
activeOutput.out(message);
|
|
8195
7922
|
}
|
|
@@ -8202,23 +7929,10 @@ function outputLine(message) {
|
|
|
8202
7929
|
function errorLine(message) {
|
|
8203
7930
|
activeOutput.err(message + EOL);
|
|
8204
7931
|
}
|
|
8205
|
-
var jsonEnvEnabled = false;
|
|
8206
|
-
function setJsonEnvEnabled(enabled) {
|
|
8207
|
-
jsonEnvEnabled = enabled;
|
|
8208
|
-
}
|
|
8209
7932
|
function printJson(data) {
|
|
8210
|
-
|
|
8211
|
-
env: envContext.demo ? "demo" : "live",
|
|
8212
|
-
profile: envContext.profile,
|
|
8213
|
-
data
|
|
8214
|
-
} : data;
|
|
8215
|
-
activeOutput.out(JSON.stringify(payload, null, 2) + EOL);
|
|
7933
|
+
activeOutput.out(JSON.stringify(data, null, 2) + EOL);
|
|
8216
7934
|
}
|
|
8217
7935
|
function printTable(rows) {
|
|
8218
|
-
if (envContext) {
|
|
8219
|
-
const envLabel = envContext.demo ? "demo (simulated trading)" : "live";
|
|
8220
|
-
activeOutput.out(`Environment: ${envLabel}` + EOL + EOL);
|
|
8221
|
-
}
|
|
8222
7936
|
if (rows.length === 0) {
|
|
8223
7937
|
activeOutput.out("(no data)" + EOL);
|
|
8224
7938
|
return;
|
|
@@ -8295,7 +8009,7 @@ var Report = class {
|
|
|
8295
8009
|
lines.push(`${key.padEnd(14)} ${value}`);
|
|
8296
8010
|
}
|
|
8297
8011
|
lines.push(sep2, "");
|
|
8298
|
-
|
|
8012
|
+
fs2.writeFileSync(filePath, lines.join("\n"), "utf8");
|
|
8299
8013
|
return true;
|
|
8300
8014
|
} catch (_e) {
|
|
8301
8015
|
return false;
|
|
@@ -8330,7 +8044,7 @@ function writeReportIfRequested(report, outputPath) {
|
|
|
8330
8044
|
errorLine(` Warning: failed to write report to: ${outputPath}`);
|
|
8331
8045
|
}
|
|
8332
8046
|
}
|
|
8333
|
-
function
|
|
8047
|
+
function sanitize(value) {
|
|
8334
8048
|
value = value.replace(
|
|
8335
8049
|
/\b[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\b/gi,
|
|
8336
8050
|
"****-uuid-****"
|
|
@@ -8341,14 +8055,14 @@ function sanitize2(value) {
|
|
|
8341
8055
|
}
|
|
8342
8056
|
|
|
8343
8057
|
// src/commands/diagnose-mcp.ts
|
|
8344
|
-
import
|
|
8345
|
-
import
|
|
8346
|
-
import
|
|
8058
|
+
import fs4 from "fs";
|
|
8059
|
+
import path2 from "path";
|
|
8060
|
+
import os2 from "os";
|
|
8347
8061
|
import { spawnSync, spawn } from "child_process";
|
|
8348
8062
|
import { createRequire as createRequire2 } from "module";
|
|
8349
8063
|
import { fileURLToPath } from "url";
|
|
8350
8064
|
var _require2 = createRequire2(import.meta.url);
|
|
8351
|
-
var __dirname =
|
|
8065
|
+
var __dirname = path2.dirname(fileURLToPath(import.meta.url));
|
|
8352
8066
|
function readMcpVersion() {
|
|
8353
8067
|
const candidates = [
|
|
8354
8068
|
// Installed as global or local dependency
|
|
@@ -8401,13 +8115,13 @@ function checkMcpEntryPoint(report) {
|
|
|
8401
8115
|
if (!entryPath) {
|
|
8402
8116
|
const candidates = [
|
|
8403
8117
|
// Installed locally
|
|
8404
|
-
|
|
8118
|
+
path2.join(process.cwd(), "node_modules", ".bin", "okx-trade-mcp"),
|
|
8405
8119
|
// Monorepo workspace (e.g. running from source)
|
|
8406
|
-
|
|
8120
|
+
path2.join(__dirname, "..", "..", "..", "..", "mcp", "dist", "index.js")
|
|
8407
8121
|
];
|
|
8408
8122
|
for (const candidate of candidates) {
|
|
8409
8123
|
try {
|
|
8410
|
-
|
|
8124
|
+
fs4.accessSync(candidate, fs4.constants.X_OK | fs4.constants.R_OK);
|
|
8411
8125
|
entryPath = candidate;
|
|
8412
8126
|
break;
|
|
8413
8127
|
} catch (_e) {
|
|
@@ -8432,9 +8146,9 @@ var CLIENT_LIMITS = {
|
|
|
8432
8146
|
cursor: { perServer: 40, total: 80 }
|
|
8433
8147
|
};
|
|
8434
8148
|
function checkJsonMcpConfig(configPath) {
|
|
8435
|
-
if (!
|
|
8149
|
+
if (!fs4.existsSync(configPath)) return "missing";
|
|
8436
8150
|
try {
|
|
8437
|
-
const raw =
|
|
8151
|
+
const raw = fs4.readFileSync(configPath, "utf8");
|
|
8438
8152
|
const parsed = JSON.parse(raw);
|
|
8439
8153
|
const mcpServers = parsed["mcpServers"];
|
|
8440
8154
|
if (!mcpServers) return "not-configured";
|
|
@@ -8452,15 +8166,15 @@ function checkJsonMcpConfig(configPath) {
|
|
|
8452
8166
|
}
|
|
8453
8167
|
}
|
|
8454
8168
|
function checkClaudeCodeConfig() {
|
|
8455
|
-
const home =
|
|
8169
|
+
const home = os2.homedir();
|
|
8456
8170
|
const candidates = [
|
|
8457
|
-
|
|
8458
|
-
|
|
8171
|
+
path2.join(home, ".claude", "settings.json"),
|
|
8172
|
+
path2.join(home, ".claude.json")
|
|
8459
8173
|
];
|
|
8460
8174
|
let anyFound = false;
|
|
8461
8175
|
let anyParseError = false;
|
|
8462
8176
|
for (const cfgPath of candidates) {
|
|
8463
|
-
if (!
|
|
8177
|
+
if (!fs4.existsSync(cfgPath)) continue;
|
|
8464
8178
|
anyFound = true;
|
|
8465
8179
|
const result = checkJsonMcpConfig(cfgPath);
|
|
8466
8180
|
if (result === "found") return "found";
|
|
@@ -8477,8 +8191,8 @@ function handleJsonClient(clientId, report, configuredClients) {
|
|
|
8477
8191
|
const status = checkJsonMcpConfig(configPath);
|
|
8478
8192
|
if (status === "missing") return false;
|
|
8479
8193
|
if (status === "found") {
|
|
8480
|
-
ok(name, `configured (${
|
|
8481
|
-
report.add(`client_${clientId}`, `OK ${
|
|
8194
|
+
ok(name, `configured (${sanitize(configPath)})`);
|
|
8195
|
+
report.add(`client_${clientId}`, `OK ${sanitize(configPath)}`);
|
|
8482
8196
|
configuredClients.push(clientId);
|
|
8483
8197
|
return false;
|
|
8484
8198
|
}
|
|
@@ -8486,8 +8200,8 @@ function handleJsonClient(clientId, report, configuredClients) {
|
|
|
8486
8200
|
fail(name, "okx-trade-mcp not found in mcpServers", [`Run: okx setup --client ${clientId}`]);
|
|
8487
8201
|
report.add(`client_${clientId}`, "NOT_CONFIGURED");
|
|
8488
8202
|
} else {
|
|
8489
|
-
fail(name, `JSON parse error in ${
|
|
8490
|
-
`Check ${
|
|
8203
|
+
fail(name, `JSON parse error in ${sanitize(configPath)}`, [
|
|
8204
|
+
`Check ${sanitize(configPath)} for JSON syntax errors`,
|
|
8491
8205
|
`Then run: okx setup --client ${clientId}`
|
|
8492
8206
|
]);
|
|
8493
8207
|
report.add(`client_${clientId}`, "PARSE_ERROR");
|
|
@@ -8580,37 +8294,37 @@ function checkToolCount(report, configuredClients, getSpecs = allToolSpecs) {
|
|
|
8580
8294
|
}
|
|
8581
8295
|
}
|
|
8582
8296
|
function readLogTail(logPath) {
|
|
8583
|
-
const stat =
|
|
8297
|
+
const stat = fs4.statSync(logPath);
|
|
8584
8298
|
const readSize = Math.min(8192, stat.size);
|
|
8585
8299
|
const buffer = Buffer.alloc(readSize);
|
|
8586
|
-
const fd =
|
|
8300
|
+
const fd = fs4.openSync(logPath, "r");
|
|
8587
8301
|
try {
|
|
8588
|
-
|
|
8302
|
+
fs4.readSync(fd, buffer, 0, readSize, Math.max(0, stat.size - readSize));
|
|
8589
8303
|
} finally {
|
|
8590
|
-
|
|
8304
|
+
fs4.closeSync(fd);
|
|
8591
8305
|
}
|
|
8592
8306
|
return buffer.toString("utf8").split("\n").filter((l) => l.trim()).slice(-5);
|
|
8593
8307
|
}
|
|
8594
8308
|
function getMcpLogCandidates() {
|
|
8595
8309
|
if (process.platform === "darwin") {
|
|
8596
|
-
const logsDir =
|
|
8310
|
+
const logsDir = path2.join(os2.homedir(), "Library", "Logs", "Claude");
|
|
8597
8311
|
const candidates = [
|
|
8598
|
-
|
|
8599
|
-
|
|
8312
|
+
path2.join(logsDir, "mcp.log"),
|
|
8313
|
+
path2.join(logsDir, "mcp-server-okx-trade-mcp.log")
|
|
8600
8314
|
];
|
|
8601
8315
|
try {
|
|
8602
|
-
const extra =
|
|
8316
|
+
const extra = fs4.readdirSync(logsDir).filter((f) => f.startsWith("mcp") && f.endsWith(".log")).map((f) => path2.join(logsDir, f));
|
|
8603
8317
|
candidates.push(...extra);
|
|
8604
8318
|
} catch (_e) {
|
|
8605
8319
|
}
|
|
8606
8320
|
return candidates;
|
|
8607
8321
|
}
|
|
8608
8322
|
if (process.platform === "win32") {
|
|
8609
|
-
const appData2 = process.env.APPDATA ??
|
|
8610
|
-
return [
|
|
8323
|
+
const appData2 = process.env.APPDATA ?? path2.join(os2.homedir(), "AppData", "Roaming");
|
|
8324
|
+
return [path2.join(appData2, "Claude", "logs", "mcp.log")];
|
|
8611
8325
|
}
|
|
8612
|
-
const configHome = process.env.XDG_CONFIG_HOME ??
|
|
8613
|
-
return [
|
|
8326
|
+
const configHome = process.env.XDG_CONFIG_HOME ?? path2.join(os2.homedir(), ".config");
|
|
8327
|
+
return [path2.join(configHome, "Claude", "logs", "mcp.log")];
|
|
8614
8328
|
}
|
|
8615
8329
|
function checkMcpLogs(report) {
|
|
8616
8330
|
section("MCP Server Logs (recent)");
|
|
@@ -8624,7 +8338,7 @@ function checkMcpLogs(report) {
|
|
|
8624
8338
|
report.add("mcp_log", logPath);
|
|
8625
8339
|
if (lines.length > 0) {
|
|
8626
8340
|
ok("last lines", `(${lines.length} shown)`);
|
|
8627
|
-
for (const line of lines) outputLine(` ${
|
|
8341
|
+
for (const line of lines) outputLine(` ${sanitize(line)}`);
|
|
8628
8342
|
} else {
|
|
8629
8343
|
ok("last lines", "(empty log)");
|
|
8630
8344
|
}
|
|
@@ -8750,11 +8464,11 @@ function checkModuleLoading(entryPath, report) {
|
|
|
8750
8464
|
return true;
|
|
8751
8465
|
} else {
|
|
8752
8466
|
const errMsg = result.stderr?.trim() || result.error?.message || "non-zero exit";
|
|
8753
|
-
fail("module load", `failed: ${
|
|
8467
|
+
fail("module load", `failed: ${sanitize(errMsg)}`, [
|
|
8754
8468
|
"MCP server may have import errors or missing dependencies",
|
|
8755
8469
|
`Try: node ${entryPath} --version`
|
|
8756
8470
|
]);
|
|
8757
|
-
report.add("module_load", `FAIL ${
|
|
8471
|
+
report.add("module_load", `FAIL ${sanitize(errMsg)}`);
|
|
8758
8472
|
return false;
|
|
8759
8473
|
}
|
|
8760
8474
|
}
|
|
@@ -8765,7 +8479,7 @@ async function cmdDiagnoseMcp(options = {}) {
|
|
|
8765
8479
|
const report = new Report();
|
|
8766
8480
|
report.add("ts", (/* @__PURE__ */ new Date()).toISOString());
|
|
8767
8481
|
report.add("mode", "mcp");
|
|
8768
|
-
report.add("os", `${process.platform} ${process.arch} ${
|
|
8482
|
+
report.add("os", `${process.platform} ${process.arch} ${os2.release()}`);
|
|
8769
8483
|
checkMcpPackageVersion(report);
|
|
8770
8484
|
const nodePassed = checkNodeCompat(report);
|
|
8771
8485
|
const { entryPath, passed: entryPassed } = checkMcpEntryPoint(report);
|
|
@@ -8797,7 +8511,7 @@ async function cmdDiagnoseMcp(options = {}) {
|
|
|
8797
8511
|
|
|
8798
8512
|
// src/commands/diagnose.ts
|
|
8799
8513
|
var CLI_VERSION = readCliVersion();
|
|
8800
|
-
var GIT_HASH = true ? "
|
|
8514
|
+
var GIT_HASH = true ? "19e8da3" : "dev";
|
|
8801
8515
|
function maskKey2(key) {
|
|
8802
8516
|
if (!key) return "(not set)";
|
|
8803
8517
|
if (key.length <= 8) return "****";
|
|
@@ -8877,14 +8591,14 @@ function checkEnvironment(report) {
|
|
|
8877
8591
|
}
|
|
8878
8592
|
ok("CLI", `v${CLI_VERSION} (${GIT_HASH})`);
|
|
8879
8593
|
ok("OS", `${process.platform} ${process.arch}`);
|
|
8880
|
-
ok("OS release",
|
|
8594
|
+
ok("OS release", os4.release());
|
|
8881
8595
|
ok("Shell", process.env.SHELL ?? "(unknown)");
|
|
8882
8596
|
ok("Locale", `${process.env.LANG ?? process.env.LC_ALL ?? "(unknown)"}`);
|
|
8883
8597
|
ok("Timezone", Intl.DateTimeFormat().resolvedOptions().timeZone);
|
|
8884
8598
|
report.add("cli", `${CLI_VERSION} (${GIT_HASH})`);
|
|
8885
8599
|
report.add("node", `${nodeVersion} ${process.platform} ${process.arch}`);
|
|
8886
|
-
const machine = typeof
|
|
8887
|
-
report.add("os", `${
|
|
8600
|
+
const machine = typeof os4.machine === "function" ? os4.machine() : process.arch;
|
|
8601
|
+
report.add("os", `${os4.type()} ${os4.release()} ${machine}`);
|
|
8888
8602
|
report.add("shell", process.env.SHELL ?? "-");
|
|
8889
8603
|
report.add("locale", process.env.LANG ?? process.env.LC_ALL ?? "-");
|
|
8890
8604
|
report.add("tz", Intl.DateTimeFormat().resolvedOptions().timeZone);
|
|
@@ -9041,10 +8755,10 @@ async function cmdDiagnose(config, profile, options = {}) {
|
|
|
9041
8755
|
}
|
|
9042
8756
|
function checkConfigFile(report) {
|
|
9043
8757
|
section("Config File");
|
|
9044
|
-
const
|
|
8758
|
+
const path5 = configFilePath();
|
|
9045
8759
|
try {
|
|
9046
8760
|
readFullConfig();
|
|
9047
|
-
ok("Config parse", `${
|
|
8761
|
+
ok("Config parse", `${path5} OK`);
|
|
9048
8762
|
report.add("config_parse", "OK");
|
|
9049
8763
|
return true;
|
|
9050
8764
|
} catch (e) {
|
|
@@ -9094,24 +8808,24 @@ async function runCliChecks(config, profile, outputPath) {
|
|
|
9094
8808
|
|
|
9095
8809
|
// src/commands/upgrade.ts
|
|
9096
8810
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
9097
|
-
import { readFileSync as
|
|
9098
|
-
import { dirname as
|
|
9099
|
-
import { homedir as
|
|
8811
|
+
import { readFileSync as readFileSync7, writeFileSync as writeFileSync7, mkdirSync as mkdirSync8 } from "fs";
|
|
8812
|
+
import { dirname as dirname6, join as join9 } from "path";
|
|
8813
|
+
import { homedir as homedir7 } from "os";
|
|
9100
8814
|
var PACKAGES = ["@okx_ai/okx-trade-mcp", "@okx_ai/okx-trade-cli"];
|
|
9101
|
-
var CACHE_FILE2 =
|
|
8815
|
+
var CACHE_FILE2 = join9(homedir7(), ".okx", "last_check");
|
|
9102
8816
|
var THROTTLE_MS = 12 * 60 * 60 * 1e3;
|
|
9103
|
-
var NPM_BIN =
|
|
8817
|
+
var NPM_BIN = join9(dirname6(process.execPath), process.platform === "win32" ? "npm.cmd" : "npm");
|
|
9104
8818
|
function readLastCheck() {
|
|
9105
8819
|
try {
|
|
9106
|
-
return parseInt(
|
|
8820
|
+
return parseInt(readFileSync7(CACHE_FILE2, "utf-8").trim(), 10) || 0;
|
|
9107
8821
|
} catch {
|
|
9108
8822
|
return 0;
|
|
9109
8823
|
}
|
|
9110
8824
|
}
|
|
9111
8825
|
function writeLastCheck() {
|
|
9112
8826
|
try {
|
|
9113
|
-
|
|
9114
|
-
|
|
8827
|
+
mkdirSync8(join9(homedir7(), ".okx"), { recursive: true });
|
|
8828
|
+
writeFileSync7(CACHE_FILE2, String(Math.floor(Date.now() / 1e3)), "utf-8");
|
|
9115
8829
|
} catch {
|
|
9116
8830
|
}
|
|
9117
8831
|
}
|
|
@@ -9214,7 +8928,7 @@ function loadProfileConfig(opts) {
|
|
|
9214
8928
|
import { EOL as EOL2 } from "os";
|
|
9215
8929
|
|
|
9216
8930
|
// src/commands/client-setup.ts
|
|
9217
|
-
import * as
|
|
8931
|
+
import * as fs5 from "fs";
|
|
9218
8932
|
var DETECTABLE_CLIENTS = ["claude-desktop", "cursor", "windsurf"];
|
|
9219
8933
|
function cmdSetupClient(options) {
|
|
9220
8934
|
runSetup(options);
|
|
@@ -9223,14 +8937,14 @@ function cmdSetupClients() {
|
|
|
9223
8937
|
const detected = [];
|
|
9224
8938
|
for (const id of DETECTABLE_CLIENTS) {
|
|
9225
8939
|
const p = getConfigPath(id);
|
|
9226
|
-
if (p &&
|
|
8940
|
+
if (p && fs5.existsSync(p)) {
|
|
9227
8941
|
detected.push({ id, path: p });
|
|
9228
8942
|
}
|
|
9229
8943
|
}
|
|
9230
8944
|
if (detected.length > 0) {
|
|
9231
8945
|
outputLine("Detected clients:");
|
|
9232
|
-
for (const { id, path:
|
|
9233
|
-
outputLine(` ${id.padEnd(16)} ${
|
|
8946
|
+
for (const { id, path: path5 } of detected) {
|
|
8947
|
+
outputLine(` ${id.padEnd(16)} ${path5}`);
|
|
9234
8948
|
}
|
|
9235
8949
|
outputLine("");
|
|
9236
8950
|
}
|
|
@@ -9614,7 +9328,7 @@ var HELP_TREE = {
|
|
|
9614
9328
|
description: "Earn products \u2014 Simple Earn, On-chain Earn, and DCD (Dual Currency Deposit)",
|
|
9615
9329
|
subgroups: {
|
|
9616
9330
|
savings: {
|
|
9617
|
-
description: "Simple Earn \u2014 flexible savings
|
|
9331
|
+
description: "Simple Earn \u2014 flexible savings and lending",
|
|
9618
9332
|
commands: {
|
|
9619
9333
|
balance: {
|
|
9620
9334
|
usage: "okx earn savings balance [<ccy>]",
|
|
@@ -9638,19 +9352,7 @@ var HELP_TREE = {
|
|
|
9638
9352
|
},
|
|
9639
9353
|
"rate-history": {
|
|
9640
9354
|
usage: "okx earn savings rate-history [--ccy <ccy>] [--limit <n>]",
|
|
9641
|
-
description: "Query Simple Earn lending rates
|
|
9642
|
-
},
|
|
9643
|
-
"fixed-orders": {
|
|
9644
|
-
usage: "okx earn savings fixed-orders [--ccy <ccy>] [--state <pending|earning|expired|settled|cancelled>]",
|
|
9645
|
-
description: "List fixed-term earn orders"
|
|
9646
|
-
},
|
|
9647
|
-
"fixed-purchase": {
|
|
9648
|
-
usage: "okx earn savings fixed-purchase --ccy <ccy> --amt <n> --term <term> [--confirm]",
|
|
9649
|
-
description: "Purchase Simple Earn Fixed (\u5B9A\u671F). Preview by default; add --confirm to execute. Funds locked until maturity"
|
|
9650
|
-
},
|
|
9651
|
-
"fixed-redeem": {
|
|
9652
|
-
usage: "okx earn savings fixed-redeem <reqId>",
|
|
9653
|
-
description: "Redeem a fixed-term earn order (full amount)"
|
|
9355
|
+
description: "Query Simple Earn lending rates (public, no auth needed)"
|
|
9654
9356
|
}
|
|
9655
9357
|
}
|
|
9656
9358
|
},
|
|
@@ -9830,7 +9532,6 @@ function printGlobalHelp() {
|
|
|
9830
9532
|
" --demo Use simulated trading (demo) mode",
|
|
9831
9533
|
" --live Force live trading mode (overrides profile demo=true; mutually exclusive with --demo)",
|
|
9832
9534
|
" --json Output raw JSON",
|
|
9833
|
-
" --env With --json, wrap output as {env, profile, data}",
|
|
9834
9535
|
" --verbose Show detailed network request/response info (stderr)",
|
|
9835
9536
|
" --version, -v Show version",
|
|
9836
9537
|
" --help Show this help",
|
|
@@ -9945,8 +9646,8 @@ function printCommandList(lines, commands) {
|
|
|
9945
9646
|
lines.push("");
|
|
9946
9647
|
}
|
|
9947
9648
|
}
|
|
9948
|
-
function printHelp(...
|
|
9949
|
-
const [moduleName, subgroupName] =
|
|
9649
|
+
function printHelp(...path5) {
|
|
9650
|
+
const [moduleName, subgroupName] = path5;
|
|
9950
9651
|
if (!moduleName) {
|
|
9951
9652
|
printGlobalHelp();
|
|
9952
9653
|
} else if (!subgroupName) {
|
|
@@ -9962,7 +9663,6 @@ var CLI_OPTIONS = {
|
|
|
9962
9663
|
profile: { type: "string" },
|
|
9963
9664
|
demo: { type: "boolean", default: false },
|
|
9964
9665
|
json: { type: "boolean", default: false },
|
|
9965
|
-
env: { type: "boolean", default: false },
|
|
9966
9666
|
help: { type: "boolean", default: false },
|
|
9967
9667
|
version: { type: "boolean", short: "v", default: false },
|
|
9968
9668
|
// setup command
|
|
@@ -10068,8 +9768,6 @@ var CLI_OPTIONS = {
|
|
|
10068
9768
|
orders: { type: "string" },
|
|
10069
9769
|
// earn
|
|
10070
9770
|
rate: { type: "string" },
|
|
10071
|
-
reqId: { type: "string" },
|
|
10072
|
-
confirm: { type: "boolean", default: false },
|
|
10073
9771
|
// audit
|
|
10074
9772
|
since: { type: "string" },
|
|
10075
9773
|
tool: { type: "string" },
|
|
@@ -10345,10 +10043,6 @@ async function cmdMarketCandles(run, instId, opts) {
|
|
|
10345
10043
|
}))
|
|
10346
10044
|
);
|
|
10347
10045
|
}
|
|
10348
|
-
function cmdMarketIndicatorList(json) {
|
|
10349
|
-
if (json) return printJson(KNOWN_INDICATORS);
|
|
10350
|
-
printTable(KNOWN_INDICATORS.map(({ name, description }) => ({ name, description })));
|
|
10351
|
-
}
|
|
10352
10046
|
async function cmdMarketIndicator(run, indicator, instId, opts) {
|
|
10353
10047
|
const params = opts.params ? opts.params.split(",").map((p) => Number(p.trim())).filter((n) => !Number.isNaN(n)) : void 0;
|
|
10354
10048
|
const result = await run("market_get_indicator", {
|
|
@@ -10446,9 +10140,9 @@ async function cmdMarketStockTokens(run, opts) {
|
|
|
10446
10140
|
}
|
|
10447
10141
|
|
|
10448
10142
|
// src/commands/account.ts
|
|
10449
|
-
import * as
|
|
10450
|
-
import * as
|
|
10451
|
-
import * as
|
|
10143
|
+
import * as fs6 from "fs";
|
|
10144
|
+
import * as path4 from "path";
|
|
10145
|
+
import * as os5 from "os";
|
|
10452
10146
|
function getData2(result) {
|
|
10453
10147
|
return result.data;
|
|
10454
10148
|
}
|
|
@@ -10659,10 +10353,10 @@ function readAuditLogs(logDir, days = 7) {
|
|
|
10659
10353
|
const yyyy = d.getUTCFullYear();
|
|
10660
10354
|
const mm = String(d.getUTCMonth() + 1).padStart(2, "0");
|
|
10661
10355
|
const dd = String(d.getUTCDate()).padStart(2, "0");
|
|
10662
|
-
const filePath =
|
|
10356
|
+
const filePath = path4.join(logDir, `trade-${yyyy}-${mm}-${dd}.log`);
|
|
10663
10357
|
let content;
|
|
10664
10358
|
try {
|
|
10665
|
-
content =
|
|
10359
|
+
content = fs6.readFileSync(filePath, "utf8");
|
|
10666
10360
|
} catch {
|
|
10667
10361
|
continue;
|
|
10668
10362
|
}
|
|
@@ -10678,7 +10372,7 @@ function readAuditLogs(logDir, days = 7) {
|
|
|
10678
10372
|
return entries;
|
|
10679
10373
|
}
|
|
10680
10374
|
function cmdAccountAudit(opts) {
|
|
10681
|
-
const logDir =
|
|
10375
|
+
const logDir = path4.join(os5.homedir(), ".okx", "logs");
|
|
10682
10376
|
const limit = Math.min(Number(opts.limit) || 20, 100);
|
|
10683
10377
|
let entries = readAuditLogs(logDir);
|
|
10684
10378
|
if (opts.tool) entries = entries.filter((e) => e.tool === opts.tool);
|
|
@@ -11666,7 +11360,6 @@ async function cmdOptionPlace(run, opts) {
|
|
|
11666
11360
|
side: opts.side,
|
|
11667
11361
|
ordType: opts.ordType,
|
|
11668
11362
|
sz: opts.sz,
|
|
11669
|
-
tgtCcy: opts.tgtCcy,
|
|
11670
11363
|
px: opts.px,
|
|
11671
11364
|
reduceOnly: opts.reduceOnly,
|
|
11672
11365
|
clOrdId: opts.clOrdId,
|
|
@@ -12134,22 +11827,6 @@ async function cmdEarnSavingsBalance(run, ccy, json) {
|
|
|
12134
11827
|
pendingAmt: r["pendingAmt"]
|
|
12135
11828
|
}));
|
|
12136
11829
|
}
|
|
12137
|
-
async function cmdEarnFixedOrderList(run, opts) {
|
|
12138
|
-
const data = extractData(await run("earn_get_fixed_order_list", {
|
|
12139
|
-
ccy: opts.ccy,
|
|
12140
|
-
state: opts.state
|
|
12141
|
-
}));
|
|
12142
|
-
printDataList(data, opts.json, "No fixed earn orders", (r) => ({
|
|
12143
|
-
reqId: r["reqId"],
|
|
12144
|
-
ccy: r["ccy"],
|
|
12145
|
-
amt: r["amt"],
|
|
12146
|
-
rate: r["rate"],
|
|
12147
|
-
term: r["term"],
|
|
12148
|
-
state: r["state"],
|
|
12149
|
-
accruedInterest: r["accruedInterest"],
|
|
12150
|
-
cTime: new Date(Number(r["cTime"])).toLocaleString()
|
|
12151
|
-
}));
|
|
12152
|
-
}
|
|
12153
11830
|
async function cmdEarnSavingsPurchase(run, opts) {
|
|
12154
11831
|
const data = extractData(await run("earn_savings_purchase", { ccy: opts.ccy, amt: opts.amt, rate: opts.rate }));
|
|
12155
11832
|
if (opts.json) {
|
|
@@ -12195,107 +11872,14 @@ async function cmdEarnLendingHistory(run, opts) {
|
|
|
12195
11872
|
ts: new Date(Number(r["ts"])).toLocaleString()
|
|
12196
11873
|
}));
|
|
12197
11874
|
}
|
|
12198
|
-
function printFixedPurchasePreview(rec) {
|
|
12199
|
-
const offer = rec["offer"];
|
|
12200
|
-
outputLine("");
|
|
12201
|
-
outputLine("\u{1F4CB} Fixed Earn Purchase Preview");
|
|
12202
|
-
outputLine(` Currency: ${rec["ccy"]}`);
|
|
12203
|
-
outputLine(` Amount: ${rec["amt"]}`);
|
|
12204
|
-
outputLine(` Term: ${rec["term"]}`);
|
|
12205
|
-
if (rec["currentFlexibleRate"]) {
|
|
12206
|
-
outputLine(` Current flexible rate: ${rec["currentFlexibleRate"]}`);
|
|
12207
|
-
}
|
|
12208
|
-
if (offer) {
|
|
12209
|
-
printKv({
|
|
12210
|
-
rate: offer["rate"],
|
|
12211
|
-
minLend: offer["minLend"],
|
|
12212
|
-
remainingQuota: offer["lendQuota"],
|
|
12213
|
-
soldOut: offer["soldOut"] ? "Yes" : "No"
|
|
12214
|
-
});
|
|
12215
|
-
} else {
|
|
12216
|
-
outputLine(" \u26A0\uFE0F No matching offer found for this term.");
|
|
12217
|
-
}
|
|
12218
|
-
outputLine("");
|
|
12219
|
-
outputLine(rec["warning"] ?? "");
|
|
12220
|
-
outputLine("");
|
|
12221
|
-
outputLine("Re-run with --confirm to execute.");
|
|
12222
|
-
}
|
|
12223
|
-
async function cmdEarnFixedPurchase(run, opts) {
|
|
12224
|
-
const result = await run("earn_fixed_purchase", {
|
|
12225
|
-
ccy: opts.ccy,
|
|
12226
|
-
amt: opts.amt,
|
|
12227
|
-
term: opts.term,
|
|
12228
|
-
confirm: opts.confirm
|
|
12229
|
-
});
|
|
12230
|
-
if (!result || typeof result !== "object") {
|
|
12231
|
-
outputLine("No response data");
|
|
12232
|
-
return;
|
|
12233
|
-
}
|
|
12234
|
-
const rec = result;
|
|
12235
|
-
if (rec["preview"]) {
|
|
12236
|
-
if (opts.json) {
|
|
12237
|
-
printJson(rec);
|
|
12238
|
-
return;
|
|
12239
|
-
}
|
|
12240
|
-
printFixedPurchasePreview(rec);
|
|
12241
|
-
return;
|
|
12242
|
-
}
|
|
12243
|
-
const data = extractData(result);
|
|
12244
|
-
if (opts.json) {
|
|
12245
|
-
printJson(data);
|
|
12246
|
-
return;
|
|
12247
|
-
}
|
|
12248
|
-
const r = data[0];
|
|
12249
|
-
if (!r) {
|
|
12250
|
-
outputLine("No response data");
|
|
12251
|
-
return;
|
|
12252
|
-
}
|
|
12253
|
-
printKv({ reqId: r["reqId"], ccy: r["ccy"], amt: r["amt"], term: r["term"] });
|
|
12254
|
-
}
|
|
12255
|
-
async function cmdEarnFixedRedeem(run, opts) {
|
|
12256
|
-
const data = extractData(await run("earn_fixed_redeem", { reqId: opts.reqId }));
|
|
12257
|
-
if (opts.json) {
|
|
12258
|
-
printJson(data);
|
|
12259
|
-
return;
|
|
12260
|
-
}
|
|
12261
|
-
if (!data.length) {
|
|
12262
|
-
outputLine("No response data");
|
|
12263
|
-
return;
|
|
12264
|
-
}
|
|
12265
|
-
printTable(data.map((r) => ({ reqId: r["reqId"] })));
|
|
12266
|
-
}
|
|
12267
11875
|
async function cmdEarnLendingRateHistory(run, opts) {
|
|
12268
|
-
const
|
|
12269
|
-
|
|
12270
|
-
const fixedOffers = extractFixedOffers(result);
|
|
12271
|
-
if (opts.json) {
|
|
12272
|
-
printJson({ data, fixedOffers });
|
|
12273
|
-
return;
|
|
12274
|
-
}
|
|
12275
|
-
printDataList(data, false, "No rate history data", (r) => ({
|
|
11876
|
+
const data = extractData(await run("earn_get_lending_rate_history", { ccy: opts.ccy, limit: opts.limit }));
|
|
11877
|
+
printDataList(data, opts.json, "No rate history data", (r) => ({
|
|
12276
11878
|
ccy: r["ccy"],
|
|
12277
11879
|
lendingRate: r["lendingRate"],
|
|
11880
|
+
rate: r["rate"],
|
|
12278
11881
|
ts: new Date(Number(r["ts"])).toLocaleString()
|
|
12279
11882
|
}));
|
|
12280
|
-
if (fixedOffers.length > 0) {
|
|
12281
|
-
outputLine("");
|
|
12282
|
-
outputLine("Fixed-term offers:");
|
|
12283
|
-
printTable(fixedOffers.map((r) => ({
|
|
12284
|
-
ccy: r["ccy"],
|
|
12285
|
-
term: r["term"],
|
|
12286
|
-
rate: r["rate"],
|
|
12287
|
-
minLend: r["minLend"],
|
|
12288
|
-
remainingQuota: r["lendQuota"],
|
|
12289
|
-
soldOut: r["soldOut"] ? "Yes" : "No"
|
|
12290
|
-
})));
|
|
12291
|
-
}
|
|
12292
|
-
}
|
|
12293
|
-
function extractFixedOffers(result) {
|
|
12294
|
-
if (result && typeof result === "object") {
|
|
12295
|
-
const offers = result["fixedOffers"];
|
|
12296
|
-
if (Array.isArray(offers)) return offers;
|
|
12297
|
-
}
|
|
12298
|
-
return [];
|
|
12299
11883
|
}
|
|
12300
11884
|
|
|
12301
11885
|
// src/commands/auto-earn.ts
|
|
@@ -12822,7 +12406,7 @@ async function cmdDcdRedeemExecute(run, opts) {
|
|
|
12822
12406
|
ordId: r["ordId"],
|
|
12823
12407
|
state: r["state"],
|
|
12824
12408
|
redeemSz: q["redeemSz"] ? `${parseFloat(q["redeemSz"]).toFixed(8)} ${q["redeemCcy"]}` : "\u2014",
|
|
12825
|
-
termRate: q["termRate"] ? `${
|
|
12409
|
+
termRate: q["termRate"] ? `${q["termRate"]}%` : "\u2014"
|
|
12826
12410
|
});
|
|
12827
12411
|
}
|
|
12828
12412
|
async function cmdDcdOrderState(run, opts) {
|
|
@@ -12875,7 +12459,7 @@ async function cmdDcdOrders(run, opts) {
|
|
|
12875
12459
|
quoteCcy: r["quoteCcy"],
|
|
12876
12460
|
strike: r["strike"],
|
|
12877
12461
|
notionalSz: r["notionalSz"],
|
|
12878
|
-
annualizedYield: r["annualizedYield"]
|
|
12462
|
+
annualizedYield: r["annualizedYield"],
|
|
12879
12463
|
yieldSz: r["yieldSz"],
|
|
12880
12464
|
settleTime: r["settleTime"] ? new Date(Number(r["settleTime"])).toLocaleDateString() : "",
|
|
12881
12465
|
// scheduled settlement time
|
|
@@ -12915,7 +12499,7 @@ async function cmdDcdQuoteAndBuy(run, opts) {
|
|
|
12915
12499
|
outputLine("Quote:");
|
|
12916
12500
|
printKv({
|
|
12917
12501
|
quoteId: q["quoteId"],
|
|
12918
|
-
annualizedYield: q["annualizedYield"] ? `${
|
|
12502
|
+
annualizedYield: q["annualizedYield"] ? `${q["annualizedYield"]}%` : "\u2014",
|
|
12919
12503
|
absYield: q["absYield"],
|
|
12920
12504
|
notionalSz: q["notionalSz"],
|
|
12921
12505
|
notionalCcy: q["notionalCcy"]
|
|
@@ -12939,17 +12523,16 @@ async function cmdDcdQuoteAndBuy(run, opts) {
|
|
|
12939
12523
|
}
|
|
12940
12524
|
|
|
12941
12525
|
// src/commands/skill.ts
|
|
12942
|
-
import { tmpdir, homedir as
|
|
12943
|
-
import { join as
|
|
12944
|
-
import { mkdirSync as
|
|
12526
|
+
import { tmpdir, homedir as homedir9 } from "os";
|
|
12527
|
+
import { join as join11, dirname as dirname7 } from "path";
|
|
12528
|
+
import { mkdirSync as mkdirSync9, rmSync, existsSync as existsSync7, copyFileSync as copyFileSync2 } from "fs";
|
|
12945
12529
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
12946
12530
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
12947
12531
|
function resolveNpx() {
|
|
12948
|
-
const sibling =
|
|
12532
|
+
const sibling = join11(dirname7(process.execPath), "npx");
|
|
12949
12533
|
if (existsSync7(sibling)) return sibling;
|
|
12950
12534
|
return "npx";
|
|
12951
12535
|
}
|
|
12952
|
-
var THIRD_PARTY_INSTALL_NOTICE = "Note: This skill was created by a third-party developer, not by OKX. Review SKILL.md before use.";
|
|
12953
12536
|
async function cmdSkillSearch(run, opts) {
|
|
12954
12537
|
const args = {};
|
|
12955
12538
|
if (opts.keyword) args.keyword = opts.keyword;
|
|
@@ -12999,13 +12582,13 @@ async function cmdSkillCategories(run, json) {
|
|
|
12999
12582
|
outputLine("");
|
|
13000
12583
|
}
|
|
13001
12584
|
async function cmdSkillAdd(name, config, json) {
|
|
13002
|
-
const tmpBase =
|
|
13003
|
-
|
|
12585
|
+
const tmpBase = join11(tmpdir(), `okx-skill-${randomUUID2()}`);
|
|
12586
|
+
mkdirSync9(tmpBase, { recursive: true });
|
|
13004
12587
|
try {
|
|
13005
12588
|
outputLine(`Downloading ${name}...`);
|
|
13006
12589
|
const client = new OkxRestClient(config);
|
|
13007
12590
|
const zipPath = await downloadSkillZip(client, name, tmpBase);
|
|
13008
|
-
const contentDir = await extractSkillZip(zipPath,
|
|
12591
|
+
const contentDir = await extractSkillZip(zipPath, join11(tmpBase, "content"));
|
|
13009
12592
|
const meta = readMetaJson(contentDir);
|
|
13010
12593
|
validateSkillMdExists(contentDir);
|
|
13011
12594
|
outputLine("Installing to detected agents...");
|
|
@@ -13015,7 +12598,7 @@ async function cmdSkillAdd(name, config, json) {
|
|
|
13015
12598
|
timeout: 6e4
|
|
13016
12599
|
});
|
|
13017
12600
|
} catch (e) {
|
|
13018
|
-
const savedZip =
|
|
12601
|
+
const savedZip = join11(process.cwd(), `${name}.zip`);
|
|
13019
12602
|
try {
|
|
13020
12603
|
copyFileSync2(zipPath, savedZip);
|
|
13021
12604
|
} catch {
|
|
@@ -13025,7 +12608,11 @@ async function cmdSkillAdd(name, config, json) {
|
|
|
13025
12608
|
throw e;
|
|
13026
12609
|
}
|
|
13027
12610
|
upsertSkillRecord(meta);
|
|
13028
|
-
|
|
12611
|
+
if (json) {
|
|
12612
|
+
outputLine(JSON.stringify({ name: meta.name, version: meta.version, status: "installed" }, null, 2));
|
|
12613
|
+
} else {
|
|
12614
|
+
outputLine(`\u2713 Skill "${meta.name}" v${meta.version} installed`);
|
|
12615
|
+
}
|
|
13029
12616
|
} finally {
|
|
13030
12617
|
rmSync(tmpBase, { recursive: true, force: true });
|
|
13031
12618
|
}
|
|
@@ -13054,7 +12641,7 @@ function cmdSkillRemove(name, json) {
|
|
|
13054
12641
|
timeout: 6e4
|
|
13055
12642
|
});
|
|
13056
12643
|
} catch {
|
|
13057
|
-
const agentsPath =
|
|
12644
|
+
const agentsPath = join11(homedir9(), ".agents", "skills", name);
|
|
13058
12645
|
try {
|
|
13059
12646
|
rmSync(agentsPath, { recursive: true, force: true });
|
|
13060
12647
|
} catch {
|
|
@@ -13118,19 +12705,11 @@ function cmdSkillList(json) {
|
|
|
13118
12705
|
outputLine("");
|
|
13119
12706
|
outputLine(`${skills.length} skills installed.`);
|
|
13120
12707
|
}
|
|
13121
|
-
function printSkillInstallResult(meta, json) {
|
|
13122
|
-
if (json) {
|
|
13123
|
-
outputLine(JSON.stringify({ name: meta.name, version: meta.version, status: "installed" }, null, 2));
|
|
13124
|
-
} else {
|
|
13125
|
-
outputLine(`\u2713 Skill "${meta.name}" v${meta.version} installed`);
|
|
13126
|
-
outputLine(` ${THIRD_PARTY_INSTALL_NOTICE}`);
|
|
13127
|
-
}
|
|
13128
|
-
}
|
|
13129
12708
|
|
|
13130
12709
|
// src/index.ts
|
|
13131
12710
|
var _require3 = createRequire3(import.meta.url);
|
|
13132
12711
|
var CLI_VERSION2 = _require3("../package.json").version;
|
|
13133
|
-
var GIT_HASH2 = true ? "
|
|
12712
|
+
var GIT_HASH2 = true ? "19e8da3" : "dev";
|
|
13134
12713
|
function handleConfigCommand(action, rest, json, lang, force) {
|
|
13135
12714
|
if (action === "init") return cmdConfigInit(lang === "zh" ? "zh" : "en");
|
|
13136
12715
|
if (action === "show") return cmdConfigShow(json);
|
|
@@ -13180,20 +12759,18 @@ function handleMarketPublicCommand(run, action, rest, v, json) {
|
|
|
13180
12759
|
instId: v.instId,
|
|
13181
12760
|
json
|
|
13182
12761
|
});
|
|
13183
|
-
if (action === "indicator")
|
|
13184
|
-
|
|
13185
|
-
|
|
13186
|
-
|
|
13187
|
-
|
|
13188
|
-
|
|
13189
|
-
|
|
13190
|
-
|
|
13191
|
-
|
|
13192
|
-
|
|
13193
|
-
|
|
13194
|
-
|
|
13195
|
-
json
|
|
13196
|
-
});
|
|
12762
|
+
if (action === "indicator") {
|
|
12763
|
+
const limit = v.limit !== void 0 ? Number(v.limit) : void 0;
|
|
12764
|
+
const backtestTime = v["backtest-time"] !== void 0 ? Number(v["backtest-time"]) : void 0;
|
|
12765
|
+
return cmdMarketIndicator(run, rest[0], rest[1], {
|
|
12766
|
+
bar: v.bar,
|
|
12767
|
+
params: v.params,
|
|
12768
|
+
list: v.list,
|
|
12769
|
+
limit,
|
|
12770
|
+
backtestTime,
|
|
12771
|
+
json
|
|
12772
|
+
});
|
|
12773
|
+
}
|
|
13197
12774
|
}
|
|
13198
12775
|
function handleMarketDataCommand(run, action, rest, v, json) {
|
|
13199
12776
|
const limit = v.limit !== void 0 ? Number(v.limit) : void 0;
|
|
@@ -13539,7 +13116,6 @@ function handleOptionCommand(run, action, rest, v, json) {
|
|
|
13539
13116
|
side: v.side,
|
|
13540
13117
|
ordType: v.ordType,
|
|
13541
13118
|
sz: v.sz,
|
|
13542
|
-
tgtCcy: v.tgtCcy,
|
|
13543
13119
|
px: v.px,
|
|
13544
13120
|
reduceOnly: v.reduceOnly,
|
|
13545
13121
|
clOrdId: v.clOrdId,
|
|
@@ -13830,9 +13406,6 @@ function handleEarnSavingsCommand(run, action, rest, v, json) {
|
|
|
13830
13406
|
if (action === "set-rate") return cmdEarnSetLendingRate(run, { ccy: v.ccy, rate: v.rate, json });
|
|
13831
13407
|
if (action === "lending-history") return cmdEarnLendingHistory(run, { ccy: v.ccy, limit, json });
|
|
13832
13408
|
if (action === "rate-history") return cmdEarnLendingRateHistory(run, { ccy: v.ccy, limit, json });
|
|
13833
|
-
if (action === "fixed-orders") return cmdEarnFixedOrderList(run, { ccy: v.ccy, state: v.state, json });
|
|
13834
|
-
if (action === "fixed-purchase") return cmdEarnFixedPurchase(run, { ccy: v.ccy, amt: v.amt, term: v.term, confirm: v.confirm ?? false, json });
|
|
13835
|
-
if (action === "fixed-redeem") return cmdEarnFixedRedeem(run, { reqId: v.reqId, json });
|
|
13836
13409
|
errorLine(`Unknown earn savings command: ${action}`);
|
|
13837
13410
|
process.exitCode = 1;
|
|
13838
13411
|
}
|
|
@@ -13951,31 +13524,6 @@ function printHelpForLevel(positionals) {
|
|
|
13951
13524
|
else if (!subgroup) printHelp(module);
|
|
13952
13525
|
else printHelp(module, subgroup);
|
|
13953
13526
|
}
|
|
13954
|
-
function wrapRunnerWithLogger(baseRunner, logger) {
|
|
13955
|
-
const writeToolNames = new Set(allToolSpecs().filter((t) => t.isWrite).map((t) => t.name));
|
|
13956
|
-
return async (toolName, args) => {
|
|
13957
|
-
const startTime = Date.now();
|
|
13958
|
-
try {
|
|
13959
|
-
const result = await baseRunner(toolName, args);
|
|
13960
|
-
if (writeToolNames.has(toolName)) {
|
|
13961
|
-
markFailedIfSCodeError(result.data);
|
|
13962
|
-
}
|
|
13963
|
-
logger.log("info", toolName, args, result, Date.now() - startTime);
|
|
13964
|
-
return result;
|
|
13965
|
-
} catch (error) {
|
|
13966
|
-
logger.log("error", toolName, args, error, Date.now() - startTime);
|
|
13967
|
-
throw error;
|
|
13968
|
-
}
|
|
13969
|
-
};
|
|
13970
|
-
}
|
|
13971
|
-
async function runDiagnose(v) {
|
|
13972
|
-
let config;
|
|
13973
|
-
try {
|
|
13974
|
-
config = loadProfileConfig({ profile: v.profile, demo: v.demo, live: v.live, verbose: v.verbose, userAgent: `okx-trade-cli/${CLI_VERSION2}`, sourceTag: "CLI" });
|
|
13975
|
-
} catch {
|
|
13976
|
-
}
|
|
13977
|
-
return cmdDiagnose(config, v.profile ?? "default", { mcp: v.mcp, cli: v.cli, all: v.all, output: v.output });
|
|
13978
|
-
}
|
|
13979
13527
|
async function main() {
|
|
13980
13528
|
setOutput({
|
|
13981
13529
|
out: (m) => process.stdout.write(m),
|
|
@@ -13997,14 +13545,25 @@ async function main() {
|
|
|
13997
13545
|
if (module === "config") return handleConfigCommand(action, rest, json, v.lang, v.force);
|
|
13998
13546
|
if (module === "setup") return handleSetupCommand(v);
|
|
13999
13547
|
if (module === "upgrade") return cmdUpgrade(CLI_VERSION2, { beta: v.beta, check: v.check, force: v.force }, json);
|
|
14000
|
-
if (module === "diagnose")
|
|
13548
|
+
if (module === "diagnose") {
|
|
13549
|
+
let config2;
|
|
13550
|
+
try {
|
|
13551
|
+
config2 = loadProfileConfig({ profile: v.profile, demo: v.demo, live: v.live, verbose: v.verbose, userAgent: `okx-trade-cli/${CLI_VERSION2}`, sourceTag: "CLI" });
|
|
13552
|
+
} catch {
|
|
13553
|
+
}
|
|
13554
|
+
return cmdDiagnose(config2, v.profile ?? "default", { mcp: v.mcp, cli: v.cli, all: v.all, output: v.output });
|
|
13555
|
+
}
|
|
14001
13556
|
const config = loadProfileConfig({ profile: v.profile, demo: v.demo, live: v.live, verbose: v.verbose, userAgent: `okx-trade-cli/${CLI_VERSION2}`, sourceTag: "CLI" });
|
|
14002
|
-
setEnvContext({ demo: config.demo, profile: v.profile ?? "default" });
|
|
14003
|
-
setJsonEnvEnabled(v.env ?? false);
|
|
14004
13557
|
const client = new OkxRestClient(config);
|
|
14005
13558
|
const baseRunner = createToolRunner(client, config);
|
|
14006
|
-
const
|
|
14007
|
-
const run =
|
|
13559
|
+
const writeToolNames = new Set(allToolSpecs().filter((t) => t.isWrite).map((t) => t.name));
|
|
13560
|
+
const run = async (toolName, args) => {
|
|
13561
|
+
const result = await baseRunner(toolName, args);
|
|
13562
|
+
if (writeToolNames.has(toolName)) {
|
|
13563
|
+
markFailedIfSCodeError(result.data);
|
|
13564
|
+
}
|
|
13565
|
+
return result;
|
|
13566
|
+
};
|
|
14008
13567
|
const moduleHandlers = {
|
|
14009
13568
|
market: () => handleMarketCommand(run, action, rest, v, json),
|
|
14010
13569
|
account: () => handleAccountCommand(run, action, rest, v, json),
|
|
@@ -14049,8 +13608,7 @@ export {
|
|
|
14049
13608
|
handleSpotCommand,
|
|
14050
13609
|
handleSwapAlgoCommand,
|
|
14051
13610
|
handleSwapCommand,
|
|
14052
|
-
printHelp
|
|
14053
|
-
wrapRunnerWithLogger
|
|
13611
|
+
printHelp
|
|
14054
13612
|
};
|
|
14055
13613
|
/*! Bundled license information:
|
|
14056
13614
|
|