@okx_ai/okx-trade-cli 1.3.0-beta.1 → 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 +392 -404
- 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,13 +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
|
|
866
|
+
import { readFileSync as readFileSync5, writeFileSync as writeFileSync5, mkdirSync as mkdirSync6, existsSync as existsSync4 } from "fs";
|
|
867
|
+
import { join as join7 } from "path";
|
|
868
|
+
import { homedir as homedir5 } from "os";
|
|
857
869
|
import * as fs3 from "fs";
|
|
858
870
|
import * as path3 from "path";
|
|
859
871
|
import * as os3 from "os";
|
|
860
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
|
+
}
|
|
861
997
|
function getNow() {
|
|
862
998
|
return (/* @__PURE__ */ new Date()).toISOString();
|
|
863
999
|
}
|
|
@@ -1076,6 +1212,14 @@ var OkxRestClient = class _OkxRestClient {
|
|
|
1076
1212
|
config;
|
|
1077
1213
|
rateLimiter;
|
|
1078
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;
|
|
1079
1223
|
constructor(config) {
|
|
1080
1224
|
this.config = config;
|
|
1081
1225
|
this.rateLimiter = new RateLimiter(3e4, config.verbose);
|
|
@@ -1083,6 +1227,97 @@ var OkxRestClient = class _OkxRestClient {
|
|
|
1083
1227
|
this.dispatcher = new ProxyAgent(config.proxyUrl);
|
|
1084
1228
|
}
|
|
1085
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
|
+
}
|
|
1086
1321
|
logRequest(method, url, auth) {
|
|
1087
1322
|
if (!this.config.verbose) return;
|
|
1088
1323
|
vlog(`\u2192 ${method} ${url}`);
|
|
@@ -1252,13 +1487,17 @@ var OkxRestClient = class _OkxRestClient {
|
|
|
1252
1487
|
* Security: validates Content-Type and enforces maxBytes limit.
|
|
1253
1488
|
*/
|
|
1254
1489
|
async privatePostBinary(path42, body, opts) {
|
|
1490
|
+
await this.ensureDoh();
|
|
1255
1491
|
const maxBytes = opts?.maxBytes ?? _OkxRestClient.DEFAULT_MAX_BYTES;
|
|
1256
1492
|
const expectedCT = opts?.expectedContentType ?? "application/octet-stream";
|
|
1257
1493
|
const bodyJson = body ? JSON.stringify(body) : "";
|
|
1258
1494
|
const endpoint = `POST ${path42}`;
|
|
1259
|
-
this.logRequest("POST", `${this.
|
|
1495
|
+
this.logRequest("POST", `${this.activeBaseUrl}${path42}`, "private");
|
|
1260
1496
|
const reqConfig = { method: "POST", path: path42, auth: "private" };
|
|
1261
1497
|
const headers = this.buildHeaders(reqConfig, path42, bodyJson, getNow());
|
|
1498
|
+
if (this.dohNode) {
|
|
1499
|
+
headers.set("User-Agent", this.dohUserAgent);
|
|
1500
|
+
}
|
|
1262
1501
|
const t0 = Date.now();
|
|
1263
1502
|
const response = await this.fetchBinary(path42, endpoint, headers, bodyJson, t0);
|
|
1264
1503
|
const elapsed = Date.now() - t0;
|
|
@@ -1292,10 +1531,10 @@ var OkxRestClient = class _OkxRestClient {
|
|
|
1292
1531
|
method: "POST",
|
|
1293
1532
|
headers,
|
|
1294
1533
|
body: bodyJson || void 0,
|
|
1295
|
-
signal: AbortSignal.timeout(this.config.timeoutMs)
|
|
1534
|
+
signal: AbortSignal.timeout(this.config.timeoutMs),
|
|
1535
|
+
dispatcher: this.activeDispatcher
|
|
1296
1536
|
};
|
|
1297
|
-
|
|
1298
|
-
return await fetch(`${this.config.baseUrl}${path42}`, fetchOptions);
|
|
1537
|
+
return await fetch(`${this.activeBaseUrl}${path42}`, fetchOptions);
|
|
1299
1538
|
} catch (error) {
|
|
1300
1539
|
if (this.config.verbose) {
|
|
1301
1540
|
vlog(`\u2717 NetworkError after ${Date.now() - t0}ms: ${error instanceof Error ? error.message : String(error)}`);
|
|
@@ -1326,9 +1565,10 @@ var OkxRestClient = class _OkxRestClient {
|
|
|
1326
1565
|
// JSON request
|
|
1327
1566
|
// ---------------------------------------------------------------------------
|
|
1328
1567
|
async request(reqConfig) {
|
|
1568
|
+
await this.ensureDoh();
|
|
1329
1569
|
const queryString = buildQueryString(reqConfig.query);
|
|
1330
1570
|
const requestPath = queryString.length > 0 ? `${reqConfig.path}?${queryString}` : reqConfig.path;
|
|
1331
|
-
const url = `${this.
|
|
1571
|
+
const url = `${this.activeBaseUrl}${requestPath}`;
|
|
1332
1572
|
const bodyJson = reqConfig.body ? JSON.stringify(reqConfig.body) : "";
|
|
1333
1573
|
const timestamp = getNow();
|
|
1334
1574
|
this.logRequest(reqConfig.method, url, reqConfig.auth);
|
|
@@ -1336,6 +1576,9 @@ var OkxRestClient = class _OkxRestClient {
|
|
|
1336
1576
|
await this.rateLimiter.consume(reqConfig.rateLimit);
|
|
1337
1577
|
}
|
|
1338
1578
|
const headers = this.buildHeaders(reqConfig, requestPath, bodyJson, timestamp);
|
|
1579
|
+
if (this.dohNode) {
|
|
1580
|
+
headers.set("User-Agent", this.dohUserAgent);
|
|
1581
|
+
}
|
|
1339
1582
|
const t0 = Date.now();
|
|
1340
1583
|
let response;
|
|
1341
1584
|
try {
|
|
@@ -1343,13 +1586,20 @@ var OkxRestClient = class _OkxRestClient {
|
|
|
1343
1586
|
method: reqConfig.method,
|
|
1344
1587
|
headers,
|
|
1345
1588
|
body: reqConfig.method === "POST" ? bodyJson : void 0,
|
|
1346
|
-
signal: AbortSignal.timeout(this.config.timeoutMs)
|
|
1589
|
+
signal: AbortSignal.timeout(this.config.timeoutMs),
|
|
1590
|
+
dispatcher: this.activeDispatcher
|
|
1347
1591
|
};
|
|
1348
|
-
if (this.dispatcher) {
|
|
1349
|
-
fetchOptions.dispatcher = this.dispatcher;
|
|
1350
|
-
}
|
|
1351
1592
|
response = await fetch(url, fetchOptions);
|
|
1352
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
|
+
}
|
|
1353
1603
|
if (this.config.verbose) {
|
|
1354
1604
|
const elapsed2 = Date.now() - t0;
|
|
1355
1605
|
const cause = error instanceof Error ? error.message : String(error);
|
|
@@ -1364,6 +1614,19 @@ var OkxRestClient = class _OkxRestClient {
|
|
|
1364
1614
|
const rawText = await response.text();
|
|
1365
1615
|
const elapsed = Date.now() - t0;
|
|
1366
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
|
+
}
|
|
1367
1630
|
return this.processResponse(rawText, response, elapsed, traceId, reqConfig, requestPath);
|
|
1368
1631
|
}
|
|
1369
1632
|
};
|
|
@@ -1503,137 +1766,14 @@ var INDICATOR_BARS = [
|
|
|
1503
1766
|
"1Wutc"
|
|
1504
1767
|
];
|
|
1505
1768
|
var INDICATOR_CODE_OVERRIDES = {
|
|
1506
|
-
// Aliases
|
|
1507
|
-
"boll": "BB",
|
|
1508
|
-
// server supports BB not BOLL
|
|
1509
|
-
// Names where default rule produces underscores but backend uses no separator
|
|
1510
1769
|
"rainbow": "BTCRAINBOW",
|
|
1511
|
-
|
|
1770
|
+
"range-filter": "RANGEFILTER",
|
|
1512
1771
|
"stoch-rsi": "STOCHRSI",
|
|
1513
|
-
|
|
1514
|
-
"
|
|
1515
|
-
//
|
|
1516
|
-
"
|
|
1517
|
-
// default: BEAR_ENGULF
|
|
1518
|
-
"bull-harami": "BULLHARAMI",
|
|
1519
|
-
// default: BULL_HARAMI
|
|
1520
|
-
"bear-harami": "BEARHARAMI",
|
|
1521
|
-
// default: BEAR_HARAMI
|
|
1522
|
-
"bull-harami-cross": "BULLHARAMICROSS",
|
|
1523
|
-
// default: BULL_HARAMI_CROSS
|
|
1524
|
-
"bear-harami-cross": "BEARHARAMICROSS",
|
|
1525
|
-
// default: BEAR_HARAMI_CROSS
|
|
1526
|
-
"three-soldiers": "THREESOLDIERS",
|
|
1527
|
-
// default: THREE_SOLDIERS
|
|
1528
|
-
"three-crows": "THREECROWS",
|
|
1529
|
-
// default: THREE_CROWS
|
|
1530
|
-
"hanging-man": "HANGINGMAN",
|
|
1531
|
-
// default: HANGING_MAN
|
|
1532
|
-
"inverted-hammer": "INVERTEDH",
|
|
1533
|
-
// default: INVERTED_HAMMER (backend uses INVERTEDH)
|
|
1534
|
-
"shooting-star": "SHOOTINGSTAR",
|
|
1535
|
-
// default: SHOOTING_STAR
|
|
1536
|
-
"nvi-pvi": "NVIPVI",
|
|
1537
|
-
// default: NVI_PVI
|
|
1538
|
-
"top-long-short": "TOPLONGSHORT"
|
|
1539
|
-
// default: TOP_LONG_SHORT
|
|
1540
|
-
// 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"
|
|
1541
1776
|
};
|
|
1542
|
-
var KNOWN_INDICATORS = [
|
|
1543
|
-
// Moving Averages
|
|
1544
|
-
{ name: "ma", description: "Simple Moving Average" },
|
|
1545
|
-
{ name: "ema", description: "Exponential Moving Average" },
|
|
1546
|
-
{ name: "wma", description: "Weighted Moving Average" },
|
|
1547
|
-
{ name: "dema", description: "Double Exponential Moving Average" },
|
|
1548
|
-
{ name: "tema", description: "Triple Exponential Moving Average" },
|
|
1549
|
-
{ name: "zlema", description: "Zero-Lag Exponential Moving Average" },
|
|
1550
|
-
{ name: "hma", description: "Hull Moving Average" },
|
|
1551
|
-
{ name: "kama", description: "Kaufman Adaptive Moving Average" },
|
|
1552
|
-
// Trend
|
|
1553
|
-
{ name: "macd", description: "MACD" },
|
|
1554
|
-
{ name: "sar", description: "Parabolic SAR" },
|
|
1555
|
-
{ name: "adx", description: "Average Directional Index" },
|
|
1556
|
-
{ name: "aroon", description: "Aroon Indicator" },
|
|
1557
|
-
{ name: "cci", description: "Commodity Channel Index" },
|
|
1558
|
-
{ name: "dpo", description: "Detrended Price Oscillator" },
|
|
1559
|
-
{ name: "envelope", description: "Envelope" },
|
|
1560
|
-
{ name: "halftrend", description: "HalfTrend" },
|
|
1561
|
-
{ name: "alphatrend", description: "AlphaTrend" },
|
|
1562
|
-
// Momentum
|
|
1563
|
-
{ name: "rsi", description: "Relative Strength Index" },
|
|
1564
|
-
{ name: "stoch-rsi", description: "Stochastic RSI" },
|
|
1565
|
-
{ name: "stoch", description: "Stochastic Oscillator" },
|
|
1566
|
-
{ name: "roc", description: "Rate of Change" },
|
|
1567
|
-
{ name: "mom", description: "Momentum" },
|
|
1568
|
-
{ name: "ppo", description: "Price Percentage Oscillator" },
|
|
1569
|
-
{ name: "trix", description: "TRIX" },
|
|
1570
|
-
{ name: "ao", description: "Awesome Oscillator" },
|
|
1571
|
-
{ name: "uo", description: "Ultimate Oscillator" },
|
|
1572
|
-
{ name: "wr", description: "Williams %R" },
|
|
1573
|
-
// Volatility
|
|
1574
|
-
{ name: "bb", description: "Bollinger Bands" },
|
|
1575
|
-
{ name: "boll", description: "Bollinger Bands (alias for bb)" },
|
|
1576
|
-
{ name: "bbwidth", description: "Bollinger Band Width" },
|
|
1577
|
-
{ name: "bbpct", description: "Bollinger Band %B" },
|
|
1578
|
-
{ name: "atr", description: "Average True Range" },
|
|
1579
|
-
{ name: "keltner", description: "Keltner Channel" },
|
|
1580
|
-
{ name: "donchian", description: "Donchian Channel" },
|
|
1581
|
-
{ name: "hv", description: "Historical Volatility" },
|
|
1582
|
-
{ name: "stddev", description: "Standard Deviation" },
|
|
1583
|
-
// Volume
|
|
1584
|
-
{ name: "obv", description: "On-Balance Volume" },
|
|
1585
|
-
{ name: "vwap", description: "Volume Weighted Average Price" },
|
|
1586
|
-
{ name: "mvwap", description: "Moving VWAP" },
|
|
1587
|
-
{ name: "cmf", description: "Chaikin Money Flow" },
|
|
1588
|
-
{ name: "mfi", description: "Money Flow Index" },
|
|
1589
|
-
{ name: "ad", description: "Accumulation/Distribution" },
|
|
1590
|
-
// Statistical
|
|
1591
|
-
{ name: "lr", description: "Linear Regression" },
|
|
1592
|
-
{ name: "slope", description: "Linear Regression Slope" },
|
|
1593
|
-
{ name: "angle", description: "Linear Regression Angle" },
|
|
1594
|
-
{ name: "variance", description: "Variance" },
|
|
1595
|
-
{ name: "meandev", description: "Mean Deviation" },
|
|
1596
|
-
{ name: "sigma", description: "Sigma" },
|
|
1597
|
-
{ name: "stderr", description: "Standard Error" },
|
|
1598
|
-
// Custom
|
|
1599
|
-
{ name: "kdj", description: "KDJ Stochastic Oscillator" },
|
|
1600
|
-
{ name: "supertrend", description: "Supertrend" },
|
|
1601
|
-
// Ichimoku
|
|
1602
|
-
{ name: "tenkan", description: "Ichimoku Tenkan-sen (Conversion Line)" },
|
|
1603
|
-
{ name: "kijun", description: "Ichimoku Kijun-sen (Base Line)" },
|
|
1604
|
-
{ name: "senkoa", description: "Ichimoku Senkou Span A (Leading Span A)" },
|
|
1605
|
-
{ name: "senkob", description: "Ichimoku Senkou Span B (Leading Span B)" },
|
|
1606
|
-
{ name: "chikou", description: "Ichimoku Chikou Span (Lagging Span)" },
|
|
1607
|
-
// Candlestick Patterns
|
|
1608
|
-
{ name: "doji", description: "Doji candlestick pattern" },
|
|
1609
|
-
{ name: "bull-engulf", description: "Bullish Engulfing pattern" },
|
|
1610
|
-
{ name: "bear-engulf", description: "Bearish Engulfing pattern" },
|
|
1611
|
-
{ name: "bull-harami", description: "Bullish Harami pattern" },
|
|
1612
|
-
{ name: "bear-harami", description: "Bearish Harami pattern" },
|
|
1613
|
-
{ name: "bull-harami-cross", description: "Bullish Harami Cross pattern" },
|
|
1614
|
-
{ name: "bear-harami-cross", description: "Bearish Harami Cross pattern" },
|
|
1615
|
-
{ name: "three-soldiers", description: "Three White Soldiers pattern" },
|
|
1616
|
-
{ name: "three-crows", description: "Three Black Crows pattern" },
|
|
1617
|
-
{ name: "hanging-man", description: "Hanging Man pattern" },
|
|
1618
|
-
{ name: "inverted-hammer", description: "Inverted Hammer pattern" },
|
|
1619
|
-
{ name: "shooting-star", description: "Shooting Star pattern" },
|
|
1620
|
-
// Bitcoin On-Chain
|
|
1621
|
-
{ name: "ahr999", description: "AHR999 Bitcoin accumulation index" },
|
|
1622
|
-
{ name: "rainbow", description: "Bitcoin Rainbow Chart" },
|
|
1623
|
-
// Other
|
|
1624
|
-
{ name: "fisher", description: "Fisher Transform" },
|
|
1625
|
-
{ name: "nvi-pvi", description: "Negative/Positive Volume Index (returns both)" },
|
|
1626
|
-
{ name: "pmax", description: "PMAX" },
|
|
1627
|
-
{ name: "qqe", description: "QQE Mod" },
|
|
1628
|
-
{ name: "tdi", description: "Traders Dynamic Index" },
|
|
1629
|
-
{ name: "waddah", description: "Waddah Attar Explosion" },
|
|
1630
|
-
{ name: "range-filter", description: "Range Filter" },
|
|
1631
|
-
{ name: "cho", description: "Chande Momentum Oscillator" },
|
|
1632
|
-
{ name: "tr", description: "True Range" },
|
|
1633
|
-
{ name: "tp", description: "Typical Price" },
|
|
1634
|
-
{ name: "mp", description: "Median Price" },
|
|
1635
|
-
{ name: "top-long-short", description: "Top Trader Long/Short Ratio (timeframe-independent)" }
|
|
1636
|
-
];
|
|
1637
1777
|
function resolveIndicatorCode(name) {
|
|
1638
1778
|
const lower = name.toLowerCase();
|
|
1639
1779
|
return INDICATOR_CODE_OVERRIDES[lower] ?? name.toUpperCase().replace(/-/g, "_");
|
|
@@ -1660,7 +1800,7 @@ function registerIndicatorTools() {
|
|
|
1660
1800
|
},
|
|
1661
1801
|
indicator: {
|
|
1662
1802
|
type: "string",
|
|
1663
|
-
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"
|
|
1664
1804
|
},
|
|
1665
1805
|
bar: {
|
|
1666
1806
|
type: "string",
|
|
@@ -1717,14 +1857,6 @@ function registerIndicatorTools() {
|
|
|
1717
1857
|
);
|
|
1718
1858
|
return normalizeResponse(response);
|
|
1719
1859
|
}
|
|
1720
|
-
},
|
|
1721
|
-
{
|
|
1722
|
-
name: "market_list_indicators",
|
|
1723
|
-
module: "market",
|
|
1724
|
-
description: "List all supported technical indicator names and descriptions. Call this before market_get_indicator to discover valid indicator names. No credentials required.",
|
|
1725
|
-
isWrite: false,
|
|
1726
|
-
inputSchema: { type: "object", properties: {} },
|
|
1727
|
-
handler: async () => ({ data: KNOWN_INDICATORS })
|
|
1728
1860
|
}
|
|
1729
1861
|
];
|
|
1730
1862
|
}
|
|
@@ -2322,59 +2454,6 @@ function registerAccountTools() {
|
|
|
2322
2454
|
}
|
|
2323
2455
|
];
|
|
2324
2456
|
}
|
|
2325
|
-
async function resolveQuoteCcySz(instId, sz, tgtCcy, instType, client) {
|
|
2326
|
-
if (tgtCcy !== "quote_ccy") {
|
|
2327
|
-
return { sz, tgtCcy, conversionNote: void 0 };
|
|
2328
|
-
}
|
|
2329
|
-
const [instrumentsRes, tickerRes] = await Promise.all([
|
|
2330
|
-
client.publicGet("/api/v5/public/instruments", {
|
|
2331
|
-
instType,
|
|
2332
|
-
instId
|
|
2333
|
-
}),
|
|
2334
|
-
client.publicGet("/api/v5/market/ticker", { instId })
|
|
2335
|
-
]);
|
|
2336
|
-
const instruments = Array.isArray(instrumentsRes.data) ? instrumentsRes.data : [];
|
|
2337
|
-
if (instruments.length === 0) {
|
|
2338
|
-
throw new Error(
|
|
2339
|
-
`Failed to fetch instrument info for ${instId}: empty instrument list. Cannot determine ctVal for quote_ccy conversion.`
|
|
2340
|
-
);
|
|
2341
|
-
}
|
|
2342
|
-
const ctValStr = String(instruments[0].ctVal ?? "");
|
|
2343
|
-
const ctVal = parseFloat(ctValStr);
|
|
2344
|
-
if (!isFinite(ctVal) || ctVal <= 0) {
|
|
2345
|
-
throw new Error(
|
|
2346
|
-
`Invalid ctVal "${ctValStr}" for ${instId}. ctVal must be a positive number for quote_ccy conversion.`
|
|
2347
|
-
);
|
|
2348
|
-
}
|
|
2349
|
-
const tickers = Array.isArray(tickerRes.data) ? tickerRes.data : [];
|
|
2350
|
-
if (tickers.length === 0) {
|
|
2351
|
-
throw new Error(
|
|
2352
|
-
`Failed to fetch ticker price for ${instId}: empty ticker response. Cannot determine last price for quote_ccy conversion.`
|
|
2353
|
-
);
|
|
2354
|
-
}
|
|
2355
|
-
const lastStr = String(tickers[0].last ?? "");
|
|
2356
|
-
const lastPx = parseFloat(lastStr);
|
|
2357
|
-
if (!isFinite(lastPx) || lastPx <= 0) {
|
|
2358
|
-
throw new Error(
|
|
2359
|
-
`Invalid last price "${lastStr}" for ${instId}. Last price must be a positive number for quote_ccy conversion.`
|
|
2360
|
-
);
|
|
2361
|
-
}
|
|
2362
|
-
const usdtAmount = parseFloat(sz);
|
|
2363
|
-
const contractValue = ctVal * lastPx;
|
|
2364
|
-
const contracts = Math.floor(usdtAmount / contractValue);
|
|
2365
|
-
if (contracts <= 0) {
|
|
2366
|
-
const minUsdt = contractValue.toFixed(2);
|
|
2367
|
-
throw new Error(
|
|
2368
|
-
`sz=${sz} USDT is too small to buy even 1 contract of ${instId}. Minimum amount required is at least ${minUsdt} USDT (ctVal=${ctValStr}, lastPx=${lastStr}, 1 contract = ${minUsdt} USDT).`
|
|
2369
|
-
);
|
|
2370
|
-
}
|
|
2371
|
-
const conversionNote = `Converting ${sz} USDT \u2192 ${contracts} contracts (ctVal=${ctValStr}, lastPx=${lastStr}, formula: floor(${sz} / (${ctValStr} \xD7 ${lastStr})) = ${contracts})`;
|
|
2372
|
-
return {
|
|
2373
|
-
sz: String(contracts),
|
|
2374
|
-
tgtCcy: void 0,
|
|
2375
|
-
conversionNote
|
|
2376
|
-
};
|
|
2377
|
-
}
|
|
2378
2457
|
function registerAlgoTradeTools() {
|
|
2379
2458
|
return [
|
|
2380
2459
|
{
|
|
@@ -2470,13 +2549,6 @@ function registerAlgoTradeTools() {
|
|
|
2470
2549
|
handler: async (rawArgs, context) => {
|
|
2471
2550
|
const args = asRecord(rawArgs);
|
|
2472
2551
|
const reduceOnly = args.reduceOnly;
|
|
2473
|
-
const resolved = await resolveQuoteCcySz(
|
|
2474
|
-
requireString(args, "instId"),
|
|
2475
|
-
requireString(args, "sz"),
|
|
2476
|
-
readString(args, "tgtCcy"),
|
|
2477
|
-
"SWAP",
|
|
2478
|
-
context.client
|
|
2479
|
-
);
|
|
2480
2552
|
const response = await context.client.privatePost(
|
|
2481
2553
|
"/api/v5/trade/order-algo",
|
|
2482
2554
|
compactObject({
|
|
@@ -2485,8 +2557,8 @@ function registerAlgoTradeTools() {
|
|
|
2485
2557
|
side: requireString(args, "side"),
|
|
2486
2558
|
posSide: readString(args, "posSide"),
|
|
2487
2559
|
ordType: requireString(args, "ordType"),
|
|
2488
|
-
sz:
|
|
2489
|
-
tgtCcy:
|
|
2560
|
+
sz: requireString(args, "sz"),
|
|
2561
|
+
tgtCcy: readString(args, "tgtCcy"),
|
|
2490
2562
|
tpTriggerPx: readString(args, "tpTriggerPx"),
|
|
2491
2563
|
tpOrdPx: readString(args, "tpOrdPx"),
|
|
2492
2564
|
tpTriggerPxType: readString(args, "tpTriggerPxType"),
|
|
@@ -2502,11 +2574,7 @@ function registerAlgoTradeTools() {
|
|
|
2502
2574
|
}),
|
|
2503
2575
|
privateRateLimit("swap_place_algo_order", 20)
|
|
2504
2576
|
);
|
|
2505
|
-
|
|
2506
|
-
if (resolved.conversionNote) {
|
|
2507
|
-
result._conversion = resolved.conversionNote;
|
|
2508
|
-
}
|
|
2509
|
-
return result;
|
|
2577
|
+
return normalizeResponse(response);
|
|
2510
2578
|
}
|
|
2511
2579
|
},
|
|
2512
2580
|
{
|
|
@@ -2814,13 +2882,6 @@ function registerFuturesAlgoTools() {
|
|
|
2814
2882
|
handler: async (rawArgs, context) => {
|
|
2815
2883
|
const args = asRecord(rawArgs);
|
|
2816
2884
|
const reduceOnly = args.reduceOnly;
|
|
2817
|
-
const resolved = await resolveQuoteCcySz(
|
|
2818
|
-
requireString(args, "instId"),
|
|
2819
|
-
requireString(args, "sz"),
|
|
2820
|
-
readString(args, "tgtCcy"),
|
|
2821
|
-
"FUTURES",
|
|
2822
|
-
context.client
|
|
2823
|
-
);
|
|
2824
2885
|
const response = await context.client.privatePost(
|
|
2825
2886
|
"/api/v5/trade/order-algo",
|
|
2826
2887
|
compactObject({
|
|
@@ -2829,8 +2890,8 @@ function registerFuturesAlgoTools() {
|
|
|
2829
2890
|
side: requireString(args, "side"),
|
|
2830
2891
|
posSide: readString(args, "posSide"),
|
|
2831
2892
|
ordType: requireString(args, "ordType"),
|
|
2832
|
-
sz:
|
|
2833
|
-
tgtCcy:
|
|
2893
|
+
sz: requireString(args, "sz"),
|
|
2894
|
+
tgtCcy: readString(args, "tgtCcy"),
|
|
2834
2895
|
tpTriggerPx: readString(args, "tpTriggerPx"),
|
|
2835
2896
|
tpOrdPx: readString(args, "tpOrdPx"),
|
|
2836
2897
|
tpTriggerPxType: readString(args, "tpTriggerPxType"),
|
|
@@ -2846,11 +2907,7 @@ function registerFuturesAlgoTools() {
|
|
|
2846
2907
|
}),
|
|
2847
2908
|
privateRateLimit("futures_place_algo_order", 20)
|
|
2848
2909
|
);
|
|
2849
|
-
|
|
2850
|
-
if (resolved.conversionNote) {
|
|
2851
|
-
result._conversion = resolved.conversionNote;
|
|
2852
|
-
}
|
|
2853
|
-
return result;
|
|
2910
|
+
return normalizeResponse(response);
|
|
2854
2911
|
}
|
|
2855
2912
|
},
|
|
2856
2913
|
{
|
|
@@ -3181,19 +3238,19 @@ function safeWriteFile(targetDir, fileName, data) {
|
|
|
3181
3238
|
throw new Error(`Invalid file name: "${fileName}"`);
|
|
3182
3239
|
}
|
|
3183
3240
|
const resolvedDir = resolve(targetDir);
|
|
3184
|
-
const filePath =
|
|
3241
|
+
const filePath = join3(resolvedDir, safeName);
|
|
3185
3242
|
const resolvedPath = resolve(filePath);
|
|
3186
3243
|
if (!resolvedPath.startsWith(resolvedDir + sep)) {
|
|
3187
3244
|
throw new Error(`Path traversal detected: "${fileName}" resolves outside target directory`);
|
|
3188
3245
|
}
|
|
3189
|
-
|
|
3246
|
+
mkdirSync2(resolvedDir, { recursive: true });
|
|
3190
3247
|
const tmpPath = `${resolvedPath}.${randomUUID()}.tmp`;
|
|
3191
3248
|
try {
|
|
3192
|
-
|
|
3193
|
-
|
|
3249
|
+
writeFileSync2(tmpPath, data);
|
|
3250
|
+
renameSync2(tmpPath, resolvedPath);
|
|
3194
3251
|
} catch (err) {
|
|
3195
3252
|
try {
|
|
3196
|
-
|
|
3253
|
+
unlinkSync2(tmpPath);
|
|
3197
3254
|
} catch {
|
|
3198
3255
|
}
|
|
3199
3256
|
throw err;
|
|
@@ -3258,7 +3315,7 @@ async function extractSkillZip(zipPath, targetDir, limits) {
|
|
|
3258
3315
|
const maxFiles = limits?.maxFiles ?? DEFAULT_MAX_FILES;
|
|
3259
3316
|
const maxCompressionRatio = limits?.maxCompressionRatio ?? DEFAULT_MAX_COMPRESSION_RATIO;
|
|
3260
3317
|
const resolvedTarget = resolve2(targetDir);
|
|
3261
|
-
|
|
3318
|
+
mkdirSync3(resolvedTarget, { recursive: true });
|
|
3262
3319
|
return new Promise((resolvePromise, reject) => {
|
|
3263
3320
|
yauzl.open(zipPath, { lazyEntries: true }, (err, zipfile) => {
|
|
3264
3321
|
if (err) return reject(err);
|
|
@@ -3281,7 +3338,7 @@ async function extractSkillZip(zipPath, targetDir, limits) {
|
|
|
3281
3338
|
zipfile.close();
|
|
3282
3339
|
return reject(streamErr);
|
|
3283
3340
|
}
|
|
3284
|
-
|
|
3341
|
+
mkdirSync3(dirname2(resolvedPath), { recursive: true });
|
|
3285
3342
|
const writeStream = createWriteStream(resolvedPath);
|
|
3286
3343
|
readStream.pipe(writeStream);
|
|
3287
3344
|
writeStream.on("close", () => zipfile.readEntry());
|
|
@@ -3297,11 +3354,11 @@ async function extractSkillZip(zipPath, targetDir, limits) {
|
|
|
3297
3354
|
});
|
|
3298
3355
|
}
|
|
3299
3356
|
function readMetaJson(contentDir) {
|
|
3300
|
-
const metaPath =
|
|
3357
|
+
const metaPath = join4(contentDir, "_meta.json");
|
|
3301
3358
|
if (!existsSync(metaPath)) {
|
|
3302
3359
|
throw new Error(`_meta.json not found in ${contentDir}. Invalid skill package.`);
|
|
3303
3360
|
}
|
|
3304
|
-
const raw =
|
|
3361
|
+
const raw = readFileSync2(metaPath, "utf-8");
|
|
3305
3362
|
let parsed;
|
|
3306
3363
|
try {
|
|
3307
3364
|
parsed = JSON.parse(raw);
|
|
@@ -3323,26 +3380,26 @@ function readMetaJson(contentDir) {
|
|
|
3323
3380
|
};
|
|
3324
3381
|
}
|
|
3325
3382
|
function validateSkillMdExists(contentDir) {
|
|
3326
|
-
const skillMdPath =
|
|
3383
|
+
const skillMdPath = join4(contentDir, "SKILL.md");
|
|
3327
3384
|
if (!existsSync(skillMdPath)) {
|
|
3328
3385
|
throw new Error(`SKILL.md not found in ${contentDir}. Invalid skill package.`);
|
|
3329
3386
|
}
|
|
3330
3387
|
}
|
|
3331
|
-
var DEFAULT_REGISTRY_PATH =
|
|
3388
|
+
var DEFAULT_REGISTRY_PATH = join5(homedir3(), ".okx", "skills", "registry.json");
|
|
3332
3389
|
function readRegistry(registryPath = DEFAULT_REGISTRY_PATH) {
|
|
3333
3390
|
if (!existsSync2(registryPath)) {
|
|
3334
3391
|
return { version: 1, skills: {} };
|
|
3335
3392
|
}
|
|
3336
3393
|
try {
|
|
3337
|
-
const raw =
|
|
3394
|
+
const raw = readFileSync3(registryPath, "utf-8");
|
|
3338
3395
|
return JSON.parse(raw);
|
|
3339
3396
|
} catch {
|
|
3340
3397
|
return { version: 1, skills: {} };
|
|
3341
3398
|
}
|
|
3342
3399
|
}
|
|
3343
3400
|
function writeRegistry(registry, registryPath = DEFAULT_REGISTRY_PATH) {
|
|
3344
|
-
|
|
3345
|
-
|
|
3401
|
+
mkdirSync4(dirname3(registryPath), { recursive: true });
|
|
3402
|
+
writeFileSync3(registryPath, JSON.stringify(registry, null, 2) + "\n", "utf-8");
|
|
3346
3403
|
}
|
|
3347
3404
|
function upsertSkillRecord(meta, registryPath = DEFAULT_REGISTRY_PATH) {
|
|
3348
3405
|
const registry = readRegistry(registryPath);
|
|
@@ -3415,7 +3472,7 @@ function registerSkillsTools() {
|
|
|
3415
3472
|
{
|
|
3416
3473
|
name: "skills_download",
|
|
3417
3474
|
module: "skills",
|
|
3418
|
-
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.",
|
|
3419
3476
|
inputSchema: {
|
|
3420
3477
|
type: "object",
|
|
3421
3478
|
properties: {
|
|
@@ -4563,7 +4620,7 @@ function registerDcdTools() {
|
|
|
4563
4620
|
{
|
|
4564
4621
|
name: "dcd_get_products",
|
|
4565
4622
|
module: "earn.dcd",
|
|
4566
|
-
description: "Get DCD products with yield and quota info.
|
|
4623
|
+
description: "Get DCD products with yield and quota info.",
|
|
4567
4624
|
isWrite: false,
|
|
4568
4625
|
inputSchema: {
|
|
4569
4626
|
type: "object",
|
|
@@ -4617,7 +4674,7 @@ function registerDcdTools() {
|
|
|
4617
4674
|
{
|
|
4618
4675
|
name: "dcd_get_orders",
|
|
4619
4676
|
module: "earn.dcd",
|
|
4620
|
-
description: "Get DCD order history.
|
|
4677
|
+
description: "Get DCD order history.",
|
|
4621
4678
|
isWrite: false,
|
|
4622
4679
|
inputSchema: {
|
|
4623
4680
|
type: "object",
|
|
@@ -4661,7 +4718,7 @@ function registerDcdTools() {
|
|
|
4661
4718
|
{
|
|
4662
4719
|
name: "dcd_subscribe",
|
|
4663
4720
|
module: "earn.dcd",
|
|
4664
|
-
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).",
|
|
4665
4722
|
isWrite: true,
|
|
4666
4723
|
inputSchema: {
|
|
4667
4724
|
type: "object",
|
|
@@ -4700,23 +4757,13 @@ function registerDcdTools() {
|
|
|
4700
4757
|
});
|
|
4701
4758
|
}
|
|
4702
4759
|
if (minAnnualizedYield !== void 0) {
|
|
4703
|
-
const
|
|
4704
|
-
if (isNaN(
|
|
4760
|
+
const actualYield = parseFloat(quote["annualizedYield"]);
|
|
4761
|
+
if (!isNaN(actualYield) && actualYield < minAnnualizedYield) {
|
|
4705
4762
|
throw new OkxApiError(
|
|
4706
|
-
|
|
4707
|
-
{
|
|
4708
|
-
code: "INVALID_YIELD_VALUE",
|
|
4709
|
-
suggestion: "Order not placed. The quote did not include a valid annualizedYield. Retry or pick a different product."
|
|
4710
|
-
}
|
|
4711
|
-
);
|
|
4712
|
-
}
|
|
4713
|
-
const actualYieldPct = rawYield * 100;
|
|
4714
|
-
if (actualYieldPct < minAnnualizedYield) {
|
|
4715
|
-
throw new OkxApiError(
|
|
4716
|
-
`Quote yield ${actualYieldPct.toFixed(2)}% is below the minimum threshold of ${minAnnualizedYield}%.`,
|
|
4763
|
+
`Quote yield ${actualYield}% is below the minimum threshold of ${minAnnualizedYield}%.`,
|
|
4717
4764
|
{
|
|
4718
4765
|
code: "YIELD_BELOW_MIN",
|
|
4719
|
-
suggestion: `Order not placed. Actual: ${
|
|
4766
|
+
suggestion: `Order not placed. Actual: ${actualYield}%, required: >= ${minAnnualizedYield}%. Try a different product or lower your minimum yield.`
|
|
4720
4767
|
}
|
|
4721
4768
|
);
|
|
4722
4769
|
}
|
|
@@ -4946,13 +4993,6 @@ function buildContractTradeTools(cfg) {
|
|
|
4946
4993
|
const args = asRecord(rawArgs);
|
|
4947
4994
|
const reduceOnly = args.reduceOnly;
|
|
4948
4995
|
const attachAlgoOrds = buildAttachAlgoOrds(args);
|
|
4949
|
-
const resolved = await resolveQuoteCcySz(
|
|
4950
|
-
requireString(args, "instId"),
|
|
4951
|
-
requireString(args, "sz"),
|
|
4952
|
-
readString(args, "tgtCcy"),
|
|
4953
|
-
defaultType,
|
|
4954
|
-
context.client
|
|
4955
|
-
);
|
|
4956
4996
|
const response = await context.client.privatePost(
|
|
4957
4997
|
"/api/v5/trade/order",
|
|
4958
4998
|
compactObject({
|
|
@@ -4961,8 +5001,8 @@ function buildContractTradeTools(cfg) {
|
|
|
4961
5001
|
side: requireString(args, "side"),
|
|
4962
5002
|
posSide: readString(args, "posSide"),
|
|
4963
5003
|
ordType: requireString(args, "ordType"),
|
|
4964
|
-
sz:
|
|
4965
|
-
tgtCcy:
|
|
5004
|
+
sz: requireString(args, "sz"),
|
|
5005
|
+
tgtCcy: readString(args, "tgtCcy"),
|
|
4966
5006
|
px: readString(args, "px"),
|
|
4967
5007
|
reduceOnly: typeof reduceOnly === "boolean" ? String(reduceOnly) : void 0,
|
|
4968
5008
|
clOrdId: readString(args, "clOrdId"),
|
|
@@ -4971,11 +5011,7 @@ function buildContractTradeTools(cfg) {
|
|
|
4971
5011
|
}),
|
|
4972
5012
|
privateRateLimit(n("place_order"), 60)
|
|
4973
5013
|
);
|
|
4974
|
-
|
|
4975
|
-
if (resolved.conversionNote) {
|
|
4976
|
-
result._conversion = resolved.conversionNote;
|
|
4977
|
-
}
|
|
4978
|
-
return result;
|
|
5014
|
+
return normalizeResponse(response);
|
|
4979
5015
|
}
|
|
4980
5016
|
},
|
|
4981
5017
|
// ── cancel_order ─────────────────────────────────────────────────────────
|
|
@@ -6050,7 +6086,7 @@ function registerOptionAlgoTools() {
|
|
|
6050
6086
|
tgtCcy: {
|
|
6051
6087
|
type: "string",
|
|
6052
6088
|
enum: ["base_ccy", "quote_ccy"],
|
|
6053
|
-
description: "Size unit. base_ccy(default): sz in contracts, quote_ccy: sz in USDT (
|
|
6089
|
+
description: "Size unit. base_ccy(default): sz in contracts, quote_ccy: sz in USDT (may not be supported for options)"
|
|
6054
6090
|
},
|
|
6055
6091
|
reduceOnly: {
|
|
6056
6092
|
type: "boolean",
|
|
@@ -6066,13 +6102,6 @@ function registerOptionAlgoTools() {
|
|
|
6066
6102
|
handler: async (rawArgs, context) => {
|
|
6067
6103
|
const args = asRecord(rawArgs);
|
|
6068
6104
|
const reduceOnly = readBoolean(args, "reduceOnly");
|
|
6069
|
-
const resolved = await resolveQuoteCcySz(
|
|
6070
|
-
requireString(args, "instId"),
|
|
6071
|
-
requireString(args, "sz"),
|
|
6072
|
-
readString(args, "tgtCcy"),
|
|
6073
|
-
"OPTION",
|
|
6074
|
-
context.client
|
|
6075
|
-
);
|
|
6076
6105
|
const response = await context.client.privatePost(
|
|
6077
6106
|
"/api/v5/trade/order-algo",
|
|
6078
6107
|
compactObject({
|
|
@@ -6080,8 +6109,8 @@ function registerOptionAlgoTools() {
|
|
|
6080
6109
|
tdMode: requireString(args, "tdMode"),
|
|
6081
6110
|
side: requireString(args, "side"),
|
|
6082
6111
|
ordType: requireString(args, "ordType"),
|
|
6083
|
-
sz:
|
|
6084
|
-
tgtCcy:
|
|
6112
|
+
sz: requireString(args, "sz"),
|
|
6113
|
+
tgtCcy: readString(args, "tgtCcy"),
|
|
6085
6114
|
tpTriggerPx: readString(args, "tpTriggerPx"),
|
|
6086
6115
|
tpOrdPx: readString(args, "tpOrdPx"),
|
|
6087
6116
|
tpTriggerPxType: readString(args, "tpTriggerPxType"),
|
|
@@ -6288,12 +6317,7 @@ function registerOptionTools() {
|
|
|
6288
6317
|
},
|
|
6289
6318
|
sz: {
|
|
6290
6319
|
type: "string",
|
|
6291
|
-
description: "Contracts count
|
|
6292
|
-
},
|
|
6293
|
-
tgtCcy: {
|
|
6294
|
-
type: "string",
|
|
6295
|
-
enum: ["base_ccy", "quote_ccy"],
|
|
6296
|
-
description: "Size unit. base_ccy(default): sz in contracts, quote_ccy: sz in USDT (auto-converted to contracts)"
|
|
6320
|
+
description: "Contracts count (NOT USDT). Use market_get_instruments for ctVal."
|
|
6297
6321
|
},
|
|
6298
6322
|
px: {
|
|
6299
6323
|
type: "string",
|
|
@@ -6330,13 +6354,6 @@ function registerOptionTools() {
|
|
|
6330
6354
|
const args = asRecord(rawArgs);
|
|
6331
6355
|
const reduceOnly = args.reduceOnly;
|
|
6332
6356
|
const attachAlgoOrds = buildAttachAlgoOrds(args);
|
|
6333
|
-
const resolved = await resolveQuoteCcySz(
|
|
6334
|
-
requireString(args, "instId"),
|
|
6335
|
-
requireString(args, "sz"),
|
|
6336
|
-
readString(args, "tgtCcy"),
|
|
6337
|
-
"OPTION",
|
|
6338
|
-
context.client
|
|
6339
|
-
);
|
|
6340
6357
|
const response = await context.client.privatePost(
|
|
6341
6358
|
"/api/v5/trade/order",
|
|
6342
6359
|
compactObject({
|
|
@@ -6344,8 +6361,7 @@ function registerOptionTools() {
|
|
|
6344
6361
|
tdMode: requireString(args, "tdMode"),
|
|
6345
6362
|
side: requireString(args, "side"),
|
|
6346
6363
|
ordType: requireString(args, "ordType"),
|
|
6347
|
-
sz:
|
|
6348
|
-
tgtCcy: resolved.tgtCcy,
|
|
6364
|
+
sz: requireString(args, "sz"),
|
|
6349
6365
|
px: readString(args, "px"),
|
|
6350
6366
|
reduceOnly: typeof reduceOnly === "boolean" ? String(reduceOnly) : void 0,
|
|
6351
6367
|
clOrdId: readString(args, "clOrdId"),
|
|
@@ -7501,12 +7517,12 @@ function createToolRunner(client, config) {
|
|
|
7501
7517
|
};
|
|
7502
7518
|
}
|
|
7503
7519
|
function configFilePath() {
|
|
7504
|
-
return
|
|
7520
|
+
return join6(homedir4(), ".okx", "config.toml");
|
|
7505
7521
|
}
|
|
7506
7522
|
function readFullConfig() {
|
|
7507
7523
|
const path42 = configFilePath();
|
|
7508
7524
|
if (!existsSync3(path42)) return { profiles: {} };
|
|
7509
|
-
const raw =
|
|
7525
|
+
const raw = readFileSync4(path42, "utf-8");
|
|
7510
7526
|
try {
|
|
7511
7527
|
return parse(raw);
|
|
7512
7528
|
} catch (err) {
|
|
@@ -7534,11 +7550,11 @@ var CONFIG_HEADER = `# OKX Trade Kit Configuration
|
|
|
7534
7550
|
`;
|
|
7535
7551
|
function writeFullConfig(config) {
|
|
7536
7552
|
const path42 = configFilePath();
|
|
7537
|
-
const dir =
|
|
7553
|
+
const dir = dirname4(path42);
|
|
7538
7554
|
if (!existsSync3(dir)) {
|
|
7539
|
-
|
|
7555
|
+
mkdirSync5(dir, { recursive: true });
|
|
7540
7556
|
}
|
|
7541
|
-
|
|
7557
|
+
writeFileSync4(path42, CONFIG_HEADER + stringify(config), "utf-8");
|
|
7542
7558
|
}
|
|
7543
7559
|
function expandShorthand(moduleId) {
|
|
7544
7560
|
if (moduleId === "all") return [...MODULES];
|
|
@@ -7652,21 +7668,21 @@ function loadConfig(cli) {
|
|
|
7652
7668
|
verbose: cli.verbose ?? false
|
|
7653
7669
|
};
|
|
7654
7670
|
}
|
|
7655
|
-
var CACHE_FILE =
|
|
7671
|
+
var CACHE_FILE = join7(homedir5(), ".okx", "update-check.json");
|
|
7656
7672
|
var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
7657
|
-
function
|
|
7673
|
+
function readCache2() {
|
|
7658
7674
|
try {
|
|
7659
7675
|
if (existsSync4(CACHE_FILE)) {
|
|
7660
|
-
return JSON.parse(
|
|
7676
|
+
return JSON.parse(readFileSync5(CACHE_FILE, "utf-8"));
|
|
7661
7677
|
}
|
|
7662
7678
|
} catch {
|
|
7663
7679
|
}
|
|
7664
7680
|
return {};
|
|
7665
7681
|
}
|
|
7666
|
-
function
|
|
7682
|
+
function writeCache2(cache) {
|
|
7667
7683
|
try {
|
|
7668
|
-
|
|
7669
|
-
|
|
7684
|
+
mkdirSync6(join7(homedir5(), ".okx"), { recursive: true });
|
|
7685
|
+
writeFileSync5(CACHE_FILE, JSON.stringify(cache, null, 2), "utf-8");
|
|
7670
7686
|
} catch {
|
|
7671
7687
|
}
|
|
7672
7688
|
}
|
|
@@ -7713,14 +7729,14 @@ async function fetchLatestVersion(packageName) {
|
|
|
7713
7729
|
function refreshCacheInBackground(packageName) {
|
|
7714
7730
|
fetchLatestVersion(packageName).then((latest) => {
|
|
7715
7731
|
if (!latest) return;
|
|
7716
|
-
const cache =
|
|
7732
|
+
const cache = readCache2();
|
|
7717
7733
|
cache[packageName] = { latestVersion: latest, checkedAt: Date.now() };
|
|
7718
|
-
|
|
7734
|
+
writeCache2(cache);
|
|
7719
7735
|
}).catch(() => {
|
|
7720
7736
|
});
|
|
7721
7737
|
}
|
|
7722
7738
|
function checkForUpdates(packageName, currentVersion) {
|
|
7723
|
-
const cache =
|
|
7739
|
+
const cache = readCache2();
|
|
7724
7740
|
const entry = cache[packageName];
|
|
7725
7741
|
if (entry && isNewerVersion(currentVersion, entry.latestVersion)) {
|
|
7726
7742
|
process.stderr.write(
|
|
@@ -7901,10 +7917,6 @@ var activeOutput = stdioOutput;
|
|
|
7901
7917
|
function setOutput(impl) {
|
|
7902
7918
|
activeOutput = impl;
|
|
7903
7919
|
}
|
|
7904
|
-
var envContext = null;
|
|
7905
|
-
function setEnvContext(ctx) {
|
|
7906
|
-
envContext = ctx;
|
|
7907
|
-
}
|
|
7908
7920
|
function output(message) {
|
|
7909
7921
|
activeOutput.out(message);
|
|
7910
7922
|
}
|
|
@@ -7918,18 +7930,9 @@ function errorLine(message) {
|
|
|
7918
7930
|
activeOutput.err(message + EOL);
|
|
7919
7931
|
}
|
|
7920
7932
|
function printJson(data) {
|
|
7921
|
-
|
|
7922
|
-
env: envContext.demo ? "demo" : "live",
|
|
7923
|
-
profile: envContext.profile,
|
|
7924
|
-
data
|
|
7925
|
-
} : data;
|
|
7926
|
-
activeOutput.out(JSON.stringify(payload, null, 2) + EOL);
|
|
7933
|
+
activeOutput.out(JSON.stringify(data, null, 2) + EOL);
|
|
7927
7934
|
}
|
|
7928
7935
|
function printTable(rows) {
|
|
7929
|
-
if (envContext) {
|
|
7930
|
-
const envLabel = envContext.demo ? "demo (simulated trading)" : "live";
|
|
7931
|
-
activeOutput.out(`Environment: ${envLabel}` + EOL + EOL);
|
|
7932
|
-
}
|
|
7933
7936
|
if (rows.length === 0) {
|
|
7934
7937
|
activeOutput.out("(no data)" + EOL);
|
|
7935
7938
|
return;
|
|
@@ -8508,7 +8511,7 @@ async function cmdDiagnoseMcp(options = {}) {
|
|
|
8508
8511
|
|
|
8509
8512
|
// src/commands/diagnose.ts
|
|
8510
8513
|
var CLI_VERSION = readCliVersion();
|
|
8511
|
-
var GIT_HASH = true ? "
|
|
8514
|
+
var GIT_HASH = true ? "19e8da3" : "dev";
|
|
8512
8515
|
function maskKey2(key) {
|
|
8513
8516
|
if (!key) return "(not set)";
|
|
8514
8517
|
if (key.length <= 8) return "****";
|
|
@@ -8805,24 +8808,24 @@ async function runCliChecks(config, profile, outputPath) {
|
|
|
8805
8808
|
|
|
8806
8809
|
// src/commands/upgrade.ts
|
|
8807
8810
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
8808
|
-
import { readFileSync as
|
|
8809
|
-
import { dirname as
|
|
8810
|
-
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";
|
|
8811
8814
|
var PACKAGES = ["@okx_ai/okx-trade-mcp", "@okx_ai/okx-trade-cli"];
|
|
8812
|
-
var CACHE_FILE2 =
|
|
8815
|
+
var CACHE_FILE2 = join9(homedir7(), ".okx", "last_check");
|
|
8813
8816
|
var THROTTLE_MS = 12 * 60 * 60 * 1e3;
|
|
8814
|
-
var NPM_BIN =
|
|
8817
|
+
var NPM_BIN = join9(dirname6(process.execPath), process.platform === "win32" ? "npm.cmd" : "npm");
|
|
8815
8818
|
function readLastCheck() {
|
|
8816
8819
|
try {
|
|
8817
|
-
return parseInt(
|
|
8820
|
+
return parseInt(readFileSync7(CACHE_FILE2, "utf-8").trim(), 10) || 0;
|
|
8818
8821
|
} catch {
|
|
8819
8822
|
return 0;
|
|
8820
8823
|
}
|
|
8821
8824
|
}
|
|
8822
8825
|
function writeLastCheck() {
|
|
8823
8826
|
try {
|
|
8824
|
-
|
|
8825
|
-
|
|
8827
|
+
mkdirSync8(join9(homedir7(), ".okx"), { recursive: true });
|
|
8828
|
+
writeFileSync7(CACHE_FILE2, String(Math.floor(Date.now() / 1e3)), "utf-8");
|
|
8826
8829
|
} catch {
|
|
8827
8830
|
}
|
|
8828
8831
|
}
|
|
@@ -10040,10 +10043,6 @@ async function cmdMarketCandles(run, instId, opts) {
|
|
|
10040
10043
|
}))
|
|
10041
10044
|
);
|
|
10042
10045
|
}
|
|
10043
|
-
function cmdMarketIndicatorList(json) {
|
|
10044
|
-
if (json) return printJson(KNOWN_INDICATORS);
|
|
10045
|
-
printTable(KNOWN_INDICATORS.map(({ name, description }) => ({ name, description })));
|
|
10046
|
-
}
|
|
10047
10046
|
async function cmdMarketIndicator(run, indicator, instId, opts) {
|
|
10048
10047
|
const params = opts.params ? opts.params.split(",").map((p) => Number(p.trim())).filter((n) => !Number.isNaN(n)) : void 0;
|
|
10049
10048
|
const result = await run("market_get_indicator", {
|
|
@@ -11361,7 +11360,6 @@ async function cmdOptionPlace(run, opts) {
|
|
|
11361
11360
|
side: opts.side,
|
|
11362
11361
|
ordType: opts.ordType,
|
|
11363
11362
|
sz: opts.sz,
|
|
11364
|
-
tgtCcy: opts.tgtCcy,
|
|
11365
11363
|
px: opts.px,
|
|
11366
11364
|
reduceOnly: opts.reduceOnly,
|
|
11367
11365
|
clOrdId: opts.clOrdId,
|
|
@@ -12408,7 +12406,7 @@ async function cmdDcdRedeemExecute(run, opts) {
|
|
|
12408
12406
|
ordId: r["ordId"],
|
|
12409
12407
|
state: r["state"],
|
|
12410
12408
|
redeemSz: q["redeemSz"] ? `${parseFloat(q["redeemSz"]).toFixed(8)} ${q["redeemCcy"]}` : "\u2014",
|
|
12411
|
-
termRate: q["termRate"] ? `${
|
|
12409
|
+
termRate: q["termRate"] ? `${q["termRate"]}%` : "\u2014"
|
|
12412
12410
|
});
|
|
12413
12411
|
}
|
|
12414
12412
|
async function cmdDcdOrderState(run, opts) {
|
|
@@ -12461,7 +12459,7 @@ async function cmdDcdOrders(run, opts) {
|
|
|
12461
12459
|
quoteCcy: r["quoteCcy"],
|
|
12462
12460
|
strike: r["strike"],
|
|
12463
12461
|
notionalSz: r["notionalSz"],
|
|
12464
|
-
annualizedYield: r["annualizedYield"]
|
|
12462
|
+
annualizedYield: r["annualizedYield"],
|
|
12465
12463
|
yieldSz: r["yieldSz"],
|
|
12466
12464
|
settleTime: r["settleTime"] ? new Date(Number(r["settleTime"])).toLocaleDateString() : "",
|
|
12467
12465
|
// scheduled settlement time
|
|
@@ -12501,7 +12499,7 @@ async function cmdDcdQuoteAndBuy(run, opts) {
|
|
|
12501
12499
|
outputLine("Quote:");
|
|
12502
12500
|
printKv({
|
|
12503
12501
|
quoteId: q["quoteId"],
|
|
12504
|
-
annualizedYield: q["annualizedYield"] ? `${
|
|
12502
|
+
annualizedYield: q["annualizedYield"] ? `${q["annualizedYield"]}%` : "\u2014",
|
|
12505
12503
|
absYield: q["absYield"],
|
|
12506
12504
|
notionalSz: q["notionalSz"],
|
|
12507
12505
|
notionalCcy: q["notionalCcy"]
|
|
@@ -12525,17 +12523,16 @@ async function cmdDcdQuoteAndBuy(run, opts) {
|
|
|
12525
12523
|
}
|
|
12526
12524
|
|
|
12527
12525
|
// src/commands/skill.ts
|
|
12528
|
-
import { tmpdir, homedir as
|
|
12529
|
-
import { join as
|
|
12530
|
-
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";
|
|
12531
12529
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
12532
12530
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
12533
12531
|
function resolveNpx() {
|
|
12534
|
-
const sibling =
|
|
12532
|
+
const sibling = join11(dirname7(process.execPath), "npx");
|
|
12535
12533
|
if (existsSync7(sibling)) return sibling;
|
|
12536
12534
|
return "npx";
|
|
12537
12535
|
}
|
|
12538
|
-
var THIRD_PARTY_INSTALL_NOTICE = "Note: This skill was created by a third-party developer, not by OKX. Review SKILL.md before use.";
|
|
12539
12536
|
async function cmdSkillSearch(run, opts) {
|
|
12540
12537
|
const args = {};
|
|
12541
12538
|
if (opts.keyword) args.keyword = opts.keyword;
|
|
@@ -12585,13 +12582,13 @@ async function cmdSkillCategories(run, json) {
|
|
|
12585
12582
|
outputLine("");
|
|
12586
12583
|
}
|
|
12587
12584
|
async function cmdSkillAdd(name, config, json) {
|
|
12588
|
-
const tmpBase =
|
|
12589
|
-
|
|
12585
|
+
const tmpBase = join11(tmpdir(), `okx-skill-${randomUUID2()}`);
|
|
12586
|
+
mkdirSync9(tmpBase, { recursive: true });
|
|
12590
12587
|
try {
|
|
12591
12588
|
outputLine(`Downloading ${name}...`);
|
|
12592
12589
|
const client = new OkxRestClient(config);
|
|
12593
12590
|
const zipPath = await downloadSkillZip(client, name, tmpBase);
|
|
12594
|
-
const contentDir = await extractSkillZip(zipPath,
|
|
12591
|
+
const contentDir = await extractSkillZip(zipPath, join11(tmpBase, "content"));
|
|
12595
12592
|
const meta = readMetaJson(contentDir);
|
|
12596
12593
|
validateSkillMdExists(contentDir);
|
|
12597
12594
|
outputLine("Installing to detected agents...");
|
|
@@ -12601,7 +12598,7 @@ async function cmdSkillAdd(name, config, json) {
|
|
|
12601
12598
|
timeout: 6e4
|
|
12602
12599
|
});
|
|
12603
12600
|
} catch (e) {
|
|
12604
|
-
const savedZip =
|
|
12601
|
+
const savedZip = join11(process.cwd(), `${name}.zip`);
|
|
12605
12602
|
try {
|
|
12606
12603
|
copyFileSync2(zipPath, savedZip);
|
|
12607
12604
|
} catch {
|
|
@@ -12611,7 +12608,11 @@ async function cmdSkillAdd(name, config, json) {
|
|
|
12611
12608
|
throw e;
|
|
12612
12609
|
}
|
|
12613
12610
|
upsertSkillRecord(meta);
|
|
12614
|
-
|
|
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
|
+
}
|
|
12615
12616
|
} finally {
|
|
12616
12617
|
rmSync(tmpBase, { recursive: true, force: true });
|
|
12617
12618
|
}
|
|
@@ -12640,7 +12641,7 @@ function cmdSkillRemove(name, json) {
|
|
|
12640
12641
|
timeout: 6e4
|
|
12641
12642
|
});
|
|
12642
12643
|
} catch {
|
|
12643
|
-
const agentsPath =
|
|
12644
|
+
const agentsPath = join11(homedir9(), ".agents", "skills", name);
|
|
12644
12645
|
try {
|
|
12645
12646
|
rmSync(agentsPath, { recursive: true, force: true });
|
|
12646
12647
|
} catch {
|
|
@@ -12704,19 +12705,11 @@ function cmdSkillList(json) {
|
|
|
12704
12705
|
outputLine("");
|
|
12705
12706
|
outputLine(`${skills.length} skills installed.`);
|
|
12706
12707
|
}
|
|
12707
|
-
function printSkillInstallResult(meta, json) {
|
|
12708
|
-
if (json) {
|
|
12709
|
-
outputLine(JSON.stringify({ name: meta.name, version: meta.version, status: "installed" }, null, 2));
|
|
12710
|
-
} else {
|
|
12711
|
-
outputLine(`\u2713 Skill "${meta.name}" v${meta.version} installed`);
|
|
12712
|
-
outputLine(` ${THIRD_PARTY_INSTALL_NOTICE}`);
|
|
12713
|
-
}
|
|
12714
|
-
}
|
|
12715
12708
|
|
|
12716
12709
|
// src/index.ts
|
|
12717
12710
|
var _require3 = createRequire3(import.meta.url);
|
|
12718
12711
|
var CLI_VERSION2 = _require3("../package.json").version;
|
|
12719
|
-
var GIT_HASH2 = true ? "
|
|
12712
|
+
var GIT_HASH2 = true ? "19e8da3" : "dev";
|
|
12720
12713
|
function handleConfigCommand(action, rest, json, lang, force) {
|
|
12721
12714
|
if (action === "init") return cmdConfigInit(lang === "zh" ? "zh" : "en");
|
|
12722
12715
|
if (action === "show") return cmdConfigShow(json);
|
|
@@ -12766,20 +12759,18 @@ function handleMarketPublicCommand(run, action, rest, v, json) {
|
|
|
12766
12759
|
instId: v.instId,
|
|
12767
12760
|
json
|
|
12768
12761
|
});
|
|
12769
|
-
if (action === "indicator")
|
|
12770
|
-
|
|
12771
|
-
|
|
12772
|
-
|
|
12773
|
-
|
|
12774
|
-
|
|
12775
|
-
|
|
12776
|
-
|
|
12777
|
-
|
|
12778
|
-
|
|
12779
|
-
|
|
12780
|
-
|
|
12781
|
-
json
|
|
12782
|
-
});
|
|
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
|
+
}
|
|
12783
12774
|
}
|
|
12784
12775
|
function handleMarketDataCommand(run, action, rest, v, json) {
|
|
12785
12776
|
const limit = v.limit !== void 0 ? Number(v.limit) : void 0;
|
|
@@ -13125,7 +13116,6 @@ function handleOptionCommand(run, action, rest, v, json) {
|
|
|
13125
13116
|
side: v.side,
|
|
13126
13117
|
ordType: v.ordType,
|
|
13127
13118
|
sz: v.sz,
|
|
13128
|
-
tgtCcy: v.tgtCcy,
|
|
13129
13119
|
px: v.px,
|
|
13130
13120
|
reduceOnly: v.reduceOnly,
|
|
13131
13121
|
clOrdId: v.clOrdId,
|
|
@@ -13534,14 +13524,6 @@ function printHelpForLevel(positionals) {
|
|
|
13534
13524
|
else if (!subgroup) printHelp(module);
|
|
13535
13525
|
else printHelp(module, subgroup);
|
|
13536
13526
|
}
|
|
13537
|
-
async function runDiagnose(v) {
|
|
13538
|
-
let config;
|
|
13539
|
-
try {
|
|
13540
|
-
config = loadProfileConfig({ profile: v.profile, demo: v.demo, live: v.live, verbose: v.verbose, userAgent: `okx-trade-cli/${CLI_VERSION2}`, sourceTag: "CLI" });
|
|
13541
|
-
} catch {
|
|
13542
|
-
}
|
|
13543
|
-
return cmdDiagnose(config, v.profile ?? "default", { mcp: v.mcp, cli: v.cli, all: v.all, output: v.output });
|
|
13544
|
-
}
|
|
13545
13527
|
async function main() {
|
|
13546
13528
|
setOutput({
|
|
13547
13529
|
out: (m) => process.stdout.write(m),
|
|
@@ -13563,9 +13545,15 @@ async function main() {
|
|
|
13563
13545
|
if (module === "config") return handleConfigCommand(action, rest, json, v.lang, v.force);
|
|
13564
13546
|
if (module === "setup") return handleSetupCommand(v);
|
|
13565
13547
|
if (module === "upgrade") return cmdUpgrade(CLI_VERSION2, { beta: v.beta, check: v.check, force: v.force }, json);
|
|
13566
|
-
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
|
+
}
|
|
13567
13556
|
const config = loadProfileConfig({ profile: v.profile, demo: v.demo, live: v.live, verbose: v.verbose, userAgent: `okx-trade-cli/${CLI_VERSION2}`, sourceTag: "CLI" });
|
|
13568
|
-
setEnvContext({ demo: config.demo, profile: v.profile ?? "default" });
|
|
13569
13557
|
const client = new OkxRestClient(config);
|
|
13570
13558
|
const baseRunner = createToolRunner(client, config);
|
|
13571
13559
|
const writeToolNames = new Set(allToolSpecs().filter((t) => t.isWrite).map((t) => t.name));
|