@okx_ai/okx-trade-cli 1.2.8-beta.3 → 1.2.8-beta.5
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 +1613 -268
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
package/dist/index.js
CHANGED
|
@@ -9,9 +9,20 @@ import { createHmac } from "crypto";
|
|
|
9
9
|
import fs from "fs";
|
|
10
10
|
import path from "path";
|
|
11
11
|
import os from "os";
|
|
12
|
-
import {
|
|
13
|
-
import { join,
|
|
12
|
+
import { writeFileSync, renameSync, unlinkSync, mkdirSync } from "fs";
|
|
13
|
+
import { join, resolve, basename, sep } from "path";
|
|
14
|
+
import { randomUUID } from "crypto";
|
|
15
|
+
import yauzl from "yauzl";
|
|
16
|
+
import { createWriteStream, mkdirSync as mkdirSync2 } from "fs";
|
|
17
|
+
import { resolve as resolve2, dirname } from "path";
|
|
18
|
+
import { readFileSync, existsSync } from "fs";
|
|
19
|
+
import { join as join2 } from "path";
|
|
20
|
+
import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, mkdirSync as mkdirSync3, existsSync as existsSync2 } from "fs";
|
|
21
|
+
import { join as join3, dirname as dirname2 } from "path";
|
|
14
22
|
import { homedir } from "os";
|
|
23
|
+
import { readFileSync as readFileSync3, writeFileSync as writeFileSync3, mkdirSync as mkdirSync4, existsSync as existsSync3 } from "fs";
|
|
24
|
+
import { join as join4, dirname as dirname3 } from "path";
|
|
25
|
+
import { homedir as homedir2 } from "os";
|
|
15
26
|
|
|
16
27
|
// ../../node_modules/.pnpm/smol-toml@1.6.0/node_modules/smol-toml/dist/error.js
|
|
17
28
|
function getLineColFromPtr(string, ptr) {
|
|
@@ -88,7 +99,7 @@ function skipVoid(str, ptr, banNewLines, banComments) {
|
|
|
88
99
|
ptr++;
|
|
89
100
|
return banComments || c !== "#" ? ptr : skipVoid(str, skipComment(str, ptr), banNewLines);
|
|
90
101
|
}
|
|
91
|
-
function skipUntil(str, ptr,
|
|
102
|
+
function skipUntil(str, ptr, sep2, end, banNewLines = false) {
|
|
92
103
|
if (!end) {
|
|
93
104
|
ptr = indexOfNewline(str, ptr);
|
|
94
105
|
return ptr < 0 ? str.length : ptr;
|
|
@@ -97,7 +108,7 @@ function skipUntil(str, ptr, sep, end, banNewLines = false) {
|
|
|
97
108
|
let c = str[i];
|
|
98
109
|
if (c === "#") {
|
|
99
110
|
i = indexOfNewline(str, i);
|
|
100
|
-
} else if (c ===
|
|
111
|
+
} else if (c === sep2) {
|
|
101
112
|
return i + 1;
|
|
102
113
|
} else if (c === end || banNewLines && (c === "\n" || c === "\r" && str[i + 1] === "\n")) {
|
|
103
114
|
return i;
|
|
@@ -840,9 +851,9 @@ function stringify(obj, { maxDepth = 1e3, numbersAsFloat = false } = {}) {
|
|
|
840
851
|
}
|
|
841
852
|
|
|
842
853
|
// ../core/dist/index.js
|
|
843
|
-
import { readFileSync as
|
|
844
|
-
import { join as
|
|
845
|
-
import { homedir as
|
|
854
|
+
import { readFileSync as readFileSync4, writeFileSync as writeFileSync4, mkdirSync as mkdirSync5, existsSync as existsSync4 } from "fs";
|
|
855
|
+
import { join as join5 } from "path";
|
|
856
|
+
import { homedir as homedir3 } from "os";
|
|
846
857
|
import * as fs3 from "fs";
|
|
847
858
|
import * as path3 from "path";
|
|
848
859
|
import * as os3 from "os";
|
|
@@ -927,8 +938,8 @@ function toToolErrorPayload(error, fallbackEndpoint) {
|
|
|
927
938
|
};
|
|
928
939
|
}
|
|
929
940
|
function sleep(ms) {
|
|
930
|
-
return new Promise((
|
|
931
|
-
setTimeout(
|
|
941
|
+
return new Promise((resolve3) => {
|
|
942
|
+
setTimeout(resolve3, ms);
|
|
932
943
|
});
|
|
933
944
|
}
|
|
934
945
|
var RateLimiter = class {
|
|
@@ -1061,7 +1072,7 @@ function vlog(message) {
|
|
|
1061
1072
|
process.stderr.write(`[verbose] ${message}
|
|
1062
1073
|
`);
|
|
1063
1074
|
}
|
|
1064
|
-
var OkxRestClient = class {
|
|
1075
|
+
var OkxRestClient = class _OkxRestClient {
|
|
1065
1076
|
config;
|
|
1066
1077
|
rateLimiter;
|
|
1067
1078
|
dispatcher;
|
|
@@ -1095,13 +1106,14 @@ var OkxRestClient = class {
|
|
|
1095
1106
|
rateLimit
|
|
1096
1107
|
});
|
|
1097
1108
|
}
|
|
1098
|
-
async privateGet(path42, query, rateLimit) {
|
|
1109
|
+
async privateGet(path42, query, rateLimit, extraHeaders) {
|
|
1099
1110
|
return this.request({
|
|
1100
1111
|
method: "GET",
|
|
1101
1112
|
path: path42,
|
|
1102
1113
|
auth: "private",
|
|
1103
1114
|
query,
|
|
1104
|
-
rateLimit
|
|
1115
|
+
rateLimit,
|
|
1116
|
+
extraHeaders
|
|
1105
1117
|
});
|
|
1106
1118
|
}
|
|
1107
1119
|
async publicPost(path42, body, rateLimit) {
|
|
@@ -1213,16 +1225,89 @@ var OkxRestClient = class {
|
|
|
1213
1225
|
raw: parsed
|
|
1214
1226
|
};
|
|
1215
1227
|
}
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1228
|
+
// ---------------------------------------------------------------------------
|
|
1229
|
+
// Binary (non-JSON) download — reuses auth, proxy, rate-limit, verbose
|
|
1230
|
+
// ---------------------------------------------------------------------------
|
|
1231
|
+
static DEFAULT_MAX_BYTES = 50 * 1024 * 1024;
|
|
1232
|
+
// 50 MB
|
|
1233
|
+
/**
|
|
1234
|
+
* Try to parse a text body as OKX JSON error and throw the appropriate error.
|
|
1235
|
+
* Re-throws OkxApiError / AuthenticationError / RateLimitError if matched.
|
|
1236
|
+
* Returns the parsed message string (or fallback) if no specific error was thrown.
|
|
1237
|
+
*/
|
|
1238
|
+
tryThrowJsonError(text, path42, traceId) {
|
|
1239
|
+
try {
|
|
1240
|
+
const parsed = JSON.parse(text);
|
|
1241
|
+
if (parsed.code && parsed.code !== "0") {
|
|
1242
|
+
this.throwOkxError(parsed.code, parsed.msg, { method: "POST", path: path42, auth: "private" }, traceId);
|
|
1243
|
+
}
|
|
1244
|
+
return parsed.msg ?? "";
|
|
1245
|
+
} catch (e) {
|
|
1246
|
+
if (e instanceof OkxApiError || e instanceof AuthenticationError || e instanceof RateLimitError) throw e;
|
|
1247
|
+
return "";
|
|
1248
|
+
}
|
|
1249
|
+
}
|
|
1250
|
+
/**
|
|
1251
|
+
* Send a signed POST request and return the raw binary response.
|
|
1252
|
+
* Inherits all client capabilities: auth, proxy, rate-limit, verbose, user-agent.
|
|
1253
|
+
* Security: validates Content-Type and enforces maxBytes limit.
|
|
1254
|
+
*/
|
|
1255
|
+
async privatePostBinary(path42, body, opts) {
|
|
1256
|
+
const maxBytes = opts?.maxBytes ?? _OkxRestClient.DEFAULT_MAX_BYTES;
|
|
1257
|
+
const expectedCT = opts?.expectedContentType ?? "application/octet-stream";
|
|
1258
|
+
const bodyJson = body ? JSON.stringify(body) : "";
|
|
1259
|
+
const endpoint = `POST ${path42}`;
|
|
1260
|
+
this.logRequest("POST", `${this.config.baseUrl}${path42}`, "private");
|
|
1261
|
+
const reqConfig = { method: "POST", path: path42, auth: "private" };
|
|
1262
|
+
const headers = this.buildHeaders(reqConfig, path42, bodyJson, getNow());
|
|
1263
|
+
const t0 = Date.now();
|
|
1264
|
+
const response = await this.fetchBinary(path42, endpoint, headers, bodyJson, t0);
|
|
1265
|
+
const elapsed = Date.now() - t0;
|
|
1266
|
+
const traceId = extractTraceId(response.headers);
|
|
1267
|
+
if (!response.ok) {
|
|
1268
|
+
const text = await response.text();
|
|
1269
|
+
this.logResponse(response.status, text.length, elapsed, traceId, String(response.status));
|
|
1270
|
+
const msg = this.tryThrowJsonError(text, path42, traceId) || `HTTP ${response.status}`;
|
|
1271
|
+
throw new OkxApiError(msg, { code: String(response.status), endpoint, traceId });
|
|
1272
|
+
}
|
|
1273
|
+
const ct = response.headers.get("content-type") ?? "";
|
|
1274
|
+
if (!ct.includes(expectedCT)) {
|
|
1275
|
+
const text = await response.text();
|
|
1276
|
+
this.logResponse(response.status, text.length, elapsed, traceId, "unexpected-ct");
|
|
1277
|
+
this.tryThrowJsonError(text, path42, traceId);
|
|
1278
|
+
throw new OkxApiError(`Expected binary response (${expectedCT}) but got: ${ct}`, { code: "UNEXPECTED_CONTENT_TYPE", endpoint, traceId });
|
|
1279
|
+
}
|
|
1280
|
+
const buffer = Buffer.from(await response.arrayBuffer());
|
|
1281
|
+
if (buffer.length > maxBytes) {
|
|
1282
|
+
throw new OkxApiError(`Response size ${buffer.length} bytes exceeds limit of ${maxBytes} bytes.`, { code: "RESPONSE_TOO_LARGE", endpoint, traceId });
|
|
1283
|
+
}
|
|
1284
|
+
if (this.config.verbose) {
|
|
1285
|
+
vlog(`\u2190 ${response.status} | binary ${buffer.length}B | ${elapsed}ms | trace=${traceId ?? "-"}`);
|
|
1286
|
+
}
|
|
1287
|
+
return { endpoint, requestTime: (/* @__PURE__ */ new Date()).toISOString(), data: buffer, contentType: ct, contentLength: buffer.length, traceId };
|
|
1288
|
+
}
|
|
1289
|
+
/** Execute fetch for binary endpoint, wrapping network errors. */
|
|
1290
|
+
async fetchBinary(path42, endpoint, headers, bodyJson, t0) {
|
|
1291
|
+
try {
|
|
1292
|
+
const fetchOptions = {
|
|
1293
|
+
method: "POST",
|
|
1294
|
+
headers,
|
|
1295
|
+
body: bodyJson || void 0,
|
|
1296
|
+
signal: AbortSignal.timeout(this.config.timeoutMs)
|
|
1297
|
+
};
|
|
1298
|
+
if (this.dispatcher) fetchOptions.dispatcher = this.dispatcher;
|
|
1299
|
+
return await fetch(`${this.config.baseUrl}${path42}`, fetchOptions);
|
|
1300
|
+
} catch (error) {
|
|
1301
|
+
if (this.config.verbose) {
|
|
1302
|
+
vlog(`\u2717 NetworkError after ${Date.now() - t0}ms: ${error instanceof Error ? error.message : String(error)}`);
|
|
1303
|
+
}
|
|
1304
|
+
throw new NetworkError(`Failed to call OKX endpoint ${endpoint}.`, endpoint, error);
|
|
1225
1305
|
}
|
|
1306
|
+
}
|
|
1307
|
+
// ---------------------------------------------------------------------------
|
|
1308
|
+
// Header building
|
|
1309
|
+
// ---------------------------------------------------------------------------
|
|
1310
|
+
buildHeaders(reqConfig, requestPath, bodyJson, timestamp) {
|
|
1226
1311
|
const headers = new Headers({
|
|
1227
1312
|
"Content-Type": "application/json",
|
|
1228
1313
|
Accept: "application/json"
|
|
@@ -1236,6 +1321,27 @@ var OkxRestClient = class {
|
|
|
1236
1321
|
if (this.config.demo) {
|
|
1237
1322
|
headers.set("x-simulated-trading", "1");
|
|
1238
1323
|
}
|
|
1324
|
+
if (reqConfig.extraHeaders) {
|
|
1325
|
+
for (const [key, value] of Object.entries(reqConfig.extraHeaders)) {
|
|
1326
|
+
headers.set(key, value);
|
|
1327
|
+
}
|
|
1328
|
+
}
|
|
1329
|
+
return headers;
|
|
1330
|
+
}
|
|
1331
|
+
// ---------------------------------------------------------------------------
|
|
1332
|
+
// JSON request
|
|
1333
|
+
// ---------------------------------------------------------------------------
|
|
1334
|
+
async request(reqConfig) {
|
|
1335
|
+
const queryString = buildQueryString(reqConfig.query);
|
|
1336
|
+
const requestPath = queryString.length > 0 ? `${reqConfig.path}?${queryString}` : reqConfig.path;
|
|
1337
|
+
const url = `${this.config.baseUrl}${requestPath}`;
|
|
1338
|
+
const bodyJson = reqConfig.body ? JSON.stringify(reqConfig.body) : "";
|
|
1339
|
+
const timestamp = getNow();
|
|
1340
|
+
this.logRequest(reqConfig.method, url, reqConfig.auth);
|
|
1341
|
+
if (reqConfig.rateLimit) {
|
|
1342
|
+
await this.rateLimiter.consume(reqConfig.rateLimit);
|
|
1343
|
+
}
|
|
1344
|
+
const headers = this.buildHeaders(reqConfig, requestPath, bodyJson, timestamp);
|
|
1239
1345
|
const t0 = Date.now();
|
|
1240
1346
|
let response;
|
|
1241
1347
|
try {
|
|
@@ -1534,10 +1640,12 @@ var MODULES = [
|
|
|
1534
1640
|
"futures",
|
|
1535
1641
|
"option",
|
|
1536
1642
|
"account",
|
|
1643
|
+
"news",
|
|
1537
1644
|
...EARN_SUB_MODULE_IDS,
|
|
1538
|
-
...BOT_SUB_MODULE_IDS
|
|
1645
|
+
...BOT_SUB_MODULE_IDS,
|
|
1646
|
+
"skills"
|
|
1539
1647
|
];
|
|
1540
|
-
var DEFAULT_MODULES = ["spot", "swap", "option", "account", ...BOT_DEFAULT_SUB_MODULES];
|
|
1648
|
+
var DEFAULT_MODULES = ["spot", "swap", "option", "account", ...BOT_DEFAULT_SUB_MODULES, "skills"];
|
|
1541
1649
|
function registerAccountTools() {
|
|
1542
1650
|
return [
|
|
1543
1651
|
{
|
|
@@ -1890,7 +1998,16 @@ function registerAccountTools() {
|
|
|
1890
1998
|
{},
|
|
1891
1999
|
privateRateLimit("account_get_config", 5)
|
|
1892
2000
|
);
|
|
1893
|
-
|
|
2001
|
+
const result = normalizeResponse(response);
|
|
2002
|
+
if (Array.isArray(result.data)) {
|
|
2003
|
+
return {
|
|
2004
|
+
...result,
|
|
2005
|
+
data: result.data.map(
|
|
2006
|
+
({ settleCcy, settleCcyList, ...rest }) => rest
|
|
2007
|
+
)
|
|
2008
|
+
};
|
|
2009
|
+
}
|
|
2010
|
+
return result;
|
|
1894
2011
|
}
|
|
1895
2012
|
},
|
|
1896
2013
|
{
|
|
@@ -2868,6 +2985,299 @@ function registerAuditTools() {
|
|
|
2868
2985
|
}
|
|
2869
2986
|
];
|
|
2870
2987
|
}
|
|
2988
|
+
function safeWriteFile(targetDir, fileName, data) {
|
|
2989
|
+
const safeName = basename(fileName);
|
|
2990
|
+
if (!safeName || safeName === "." || safeName === "..") {
|
|
2991
|
+
throw new Error(`Invalid file name: "${fileName}"`);
|
|
2992
|
+
}
|
|
2993
|
+
const resolvedDir = resolve(targetDir);
|
|
2994
|
+
const filePath = join(resolvedDir, safeName);
|
|
2995
|
+
const resolvedPath = resolve(filePath);
|
|
2996
|
+
if (!resolvedPath.startsWith(resolvedDir + sep)) {
|
|
2997
|
+
throw new Error(`Path traversal detected: "${fileName}" resolves outside target directory`);
|
|
2998
|
+
}
|
|
2999
|
+
mkdirSync(resolvedDir, { recursive: true });
|
|
3000
|
+
const tmpPath = `${resolvedPath}.${randomUUID()}.tmp`;
|
|
3001
|
+
try {
|
|
3002
|
+
writeFileSync(tmpPath, data);
|
|
3003
|
+
renameSync(tmpPath, resolvedPath);
|
|
3004
|
+
} catch (err) {
|
|
3005
|
+
try {
|
|
3006
|
+
unlinkSync(tmpPath);
|
|
3007
|
+
} catch {
|
|
3008
|
+
}
|
|
3009
|
+
throw err;
|
|
3010
|
+
}
|
|
3011
|
+
return resolvedPath;
|
|
3012
|
+
}
|
|
3013
|
+
function validateZipEntryPath(targetDir, entryName) {
|
|
3014
|
+
const resolvedDir = resolve(targetDir);
|
|
3015
|
+
const resolvedEntry = resolve(resolvedDir, entryName);
|
|
3016
|
+
if (!resolvedEntry.startsWith(resolvedDir + sep) && resolvedEntry !== resolvedDir) {
|
|
3017
|
+
throw new Error(`Zip path traversal detected: "${entryName}" resolves outside extraction directory`);
|
|
3018
|
+
}
|
|
3019
|
+
return resolvedEntry;
|
|
3020
|
+
}
|
|
3021
|
+
var MAX_DOWNLOAD_BYTES = 50 * 1024 * 1024;
|
|
3022
|
+
async function downloadSkillZip(client, name, targetDir) {
|
|
3023
|
+
const result = await client.privatePostBinary(
|
|
3024
|
+
"/api/v5/skill/download",
|
|
3025
|
+
{ name },
|
|
3026
|
+
{ maxBytes: MAX_DOWNLOAD_BYTES }
|
|
3027
|
+
);
|
|
3028
|
+
const fileName = `${name}.zip`;
|
|
3029
|
+
const filePath = safeWriteFile(targetDir, fileName, result.data);
|
|
3030
|
+
return filePath;
|
|
3031
|
+
}
|
|
3032
|
+
var DEFAULT_MAX_TOTAL_BYTES = 100 * 1024 * 1024;
|
|
3033
|
+
var DEFAULT_MAX_FILES = 1e3;
|
|
3034
|
+
var DEFAULT_MAX_COMPRESSION_RATIO = 100;
|
|
3035
|
+
function validateEntry(entry, targetDir, state, limits) {
|
|
3036
|
+
state.fileCount++;
|
|
3037
|
+
if (state.fileCount > limits.maxFiles) {
|
|
3038
|
+
throw new Error(
|
|
3039
|
+
`Zip contains more than ${limits.maxFiles} entries, exceeding limit of ${limits.maxFiles}. Possible zip bomb.`
|
|
3040
|
+
);
|
|
3041
|
+
}
|
|
3042
|
+
const resolvedPath = validateZipEntryPath(targetDir, entry.fileName);
|
|
3043
|
+
const externalAttrs = entry.externalFileAttributes;
|
|
3044
|
+
if (externalAttrs) {
|
|
3045
|
+
const unixMode = externalAttrs >>> 16 & 65535;
|
|
3046
|
+
if ((unixMode & 40960) === 40960) {
|
|
3047
|
+
throw new Error(`Zip entry "${entry.fileName}" is a symlink. Symlinks are not allowed for security.`);
|
|
3048
|
+
}
|
|
3049
|
+
}
|
|
3050
|
+
if (entry.compressedSize > 0) {
|
|
3051
|
+
const ratio = entry.uncompressedSize / entry.compressedSize;
|
|
3052
|
+
if (ratio > limits.maxCompressionRatio) {
|
|
3053
|
+
throw new Error(
|
|
3054
|
+
`Zip entry "${entry.fileName}" has compression ratio ${ratio.toFixed(1)}, exceeding limit of ${limits.maxCompressionRatio}. Possible zip bomb.`
|
|
3055
|
+
);
|
|
3056
|
+
}
|
|
3057
|
+
}
|
|
3058
|
+
state.totalBytes += entry.uncompressedSize;
|
|
3059
|
+
if (state.totalBytes > limits.maxTotalBytes) {
|
|
3060
|
+
throw new Error(
|
|
3061
|
+
`Extracted size ${state.totalBytes} bytes exceeds limit of ${limits.maxTotalBytes} bytes. Possible zip bomb.`
|
|
3062
|
+
);
|
|
3063
|
+
}
|
|
3064
|
+
return resolvedPath;
|
|
3065
|
+
}
|
|
3066
|
+
async function extractSkillZip(zipPath, targetDir, limits) {
|
|
3067
|
+
const maxTotalBytes = limits?.maxTotalBytes ?? DEFAULT_MAX_TOTAL_BYTES;
|
|
3068
|
+
const maxFiles = limits?.maxFiles ?? DEFAULT_MAX_FILES;
|
|
3069
|
+
const maxCompressionRatio = limits?.maxCompressionRatio ?? DEFAULT_MAX_COMPRESSION_RATIO;
|
|
3070
|
+
const resolvedTarget = resolve2(targetDir);
|
|
3071
|
+
mkdirSync2(resolvedTarget, { recursive: true });
|
|
3072
|
+
return new Promise((resolvePromise, reject) => {
|
|
3073
|
+
yauzl.open(zipPath, { lazyEntries: true }, (err, zipfile) => {
|
|
3074
|
+
if (err) return reject(err);
|
|
3075
|
+
const state = { fileCount: 0, totalBytes: 0 };
|
|
3076
|
+
zipfile.readEntry();
|
|
3077
|
+
zipfile.on("entry", (entry) => {
|
|
3078
|
+
if (entry.fileName.endsWith("/")) {
|
|
3079
|
+
zipfile.readEntry();
|
|
3080
|
+
return;
|
|
3081
|
+
}
|
|
3082
|
+
let resolvedPath;
|
|
3083
|
+
try {
|
|
3084
|
+
resolvedPath = validateEntry(entry, resolvedTarget, state, { maxFiles, maxTotalBytes, maxCompressionRatio });
|
|
3085
|
+
} catch (e) {
|
|
3086
|
+
zipfile.close();
|
|
3087
|
+
return reject(e);
|
|
3088
|
+
}
|
|
3089
|
+
zipfile.openReadStream(entry, (streamErr, readStream) => {
|
|
3090
|
+
if (streamErr) {
|
|
3091
|
+
zipfile.close();
|
|
3092
|
+
return reject(streamErr);
|
|
3093
|
+
}
|
|
3094
|
+
mkdirSync2(dirname(resolvedPath), { recursive: true });
|
|
3095
|
+
const writeStream = createWriteStream(resolvedPath);
|
|
3096
|
+
readStream.pipe(writeStream);
|
|
3097
|
+
writeStream.on("close", () => zipfile.readEntry());
|
|
3098
|
+
writeStream.on("error", (writeErr) => {
|
|
3099
|
+
zipfile.close();
|
|
3100
|
+
reject(writeErr);
|
|
3101
|
+
});
|
|
3102
|
+
});
|
|
3103
|
+
});
|
|
3104
|
+
zipfile.on("end", () => resolvePromise(resolvedTarget));
|
|
3105
|
+
zipfile.on("error", reject);
|
|
3106
|
+
});
|
|
3107
|
+
});
|
|
3108
|
+
}
|
|
3109
|
+
function readMetaJson(contentDir) {
|
|
3110
|
+
const metaPath = join2(contentDir, "_meta.json");
|
|
3111
|
+
if (!existsSync(metaPath)) {
|
|
3112
|
+
throw new Error(`_meta.json not found in ${contentDir}. Invalid skill package.`);
|
|
3113
|
+
}
|
|
3114
|
+
const raw = readFileSync(metaPath, "utf-8");
|
|
3115
|
+
let parsed;
|
|
3116
|
+
try {
|
|
3117
|
+
parsed = JSON.parse(raw);
|
|
3118
|
+
} catch {
|
|
3119
|
+
throw new Error(`Failed to parse _meta.json: invalid JSON`);
|
|
3120
|
+
}
|
|
3121
|
+
const meta = parsed;
|
|
3122
|
+
if (typeof meta.name !== "string" || !meta.name) {
|
|
3123
|
+
throw new Error(`_meta.json: "name" field is required`);
|
|
3124
|
+
}
|
|
3125
|
+
if (typeof meta.version !== "string" || !meta.version) {
|
|
3126
|
+
throw new Error(`_meta.json: "version" field is required`);
|
|
3127
|
+
}
|
|
3128
|
+
return {
|
|
3129
|
+
name: String(meta.name),
|
|
3130
|
+
version: String(meta.version),
|
|
3131
|
+
title: typeof meta.title === "string" ? meta.title : "",
|
|
3132
|
+
description: typeof meta.description === "string" ? meta.description : ""
|
|
3133
|
+
};
|
|
3134
|
+
}
|
|
3135
|
+
function validateSkillMdExists(contentDir) {
|
|
3136
|
+
const skillMdPath = join2(contentDir, "SKILL.md");
|
|
3137
|
+
if (!existsSync(skillMdPath)) {
|
|
3138
|
+
throw new Error(`SKILL.md not found in ${contentDir}. Invalid skill package.`);
|
|
3139
|
+
}
|
|
3140
|
+
}
|
|
3141
|
+
var DEFAULT_REGISTRY_PATH = join3(homedir(), ".okx", "skills", "registry.json");
|
|
3142
|
+
function readRegistry(registryPath = DEFAULT_REGISTRY_PATH) {
|
|
3143
|
+
if (!existsSync2(registryPath)) {
|
|
3144
|
+
return { version: 1, skills: {} };
|
|
3145
|
+
}
|
|
3146
|
+
try {
|
|
3147
|
+
const raw = readFileSync2(registryPath, "utf-8");
|
|
3148
|
+
return JSON.parse(raw);
|
|
3149
|
+
} catch {
|
|
3150
|
+
return { version: 1, skills: {} };
|
|
3151
|
+
}
|
|
3152
|
+
}
|
|
3153
|
+
function writeRegistry(registry, registryPath = DEFAULT_REGISTRY_PATH) {
|
|
3154
|
+
mkdirSync3(dirname2(registryPath), { recursive: true });
|
|
3155
|
+
writeFileSync2(registryPath, JSON.stringify(registry, null, 2) + "\n", "utf-8");
|
|
3156
|
+
}
|
|
3157
|
+
function upsertSkillRecord(meta, registryPath = DEFAULT_REGISTRY_PATH) {
|
|
3158
|
+
const registry = readRegistry(registryPath);
|
|
3159
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
3160
|
+
const existing = registry.skills[meta.name];
|
|
3161
|
+
registry.skills[meta.name] = {
|
|
3162
|
+
name: meta.name,
|
|
3163
|
+
version: meta.version,
|
|
3164
|
+
description: meta.description,
|
|
3165
|
+
installedAt: existing?.installedAt ?? now,
|
|
3166
|
+
updatedAt: now,
|
|
3167
|
+
source: "marketplace"
|
|
3168
|
+
};
|
|
3169
|
+
writeRegistry(registry, registryPath);
|
|
3170
|
+
}
|
|
3171
|
+
function removeSkillRecord(name, registryPath = DEFAULT_REGISTRY_PATH) {
|
|
3172
|
+
const registry = readRegistry(registryPath);
|
|
3173
|
+
if (!(name in registry.skills)) return false;
|
|
3174
|
+
delete registry.skills[name];
|
|
3175
|
+
writeRegistry(registry, registryPath);
|
|
3176
|
+
return true;
|
|
3177
|
+
}
|
|
3178
|
+
function getSkillRecord(name, registryPath = DEFAULT_REGISTRY_PATH) {
|
|
3179
|
+
const registry = readRegistry(registryPath);
|
|
3180
|
+
return registry.skills[name];
|
|
3181
|
+
}
|
|
3182
|
+
function registerSkillsTools() {
|
|
3183
|
+
return [
|
|
3184
|
+
{
|
|
3185
|
+
name: "skills_get_categories",
|
|
3186
|
+
module: "skills",
|
|
3187
|
+
description: "List all available skill categories in OKX Skills Marketplace. Use the returned categoryId as input to skills_search for category filtering. Do NOT use for searching or downloading skills \u2014 use skills_search or skills_download.",
|
|
3188
|
+
inputSchema: {
|
|
3189
|
+
type: "object",
|
|
3190
|
+
properties: {},
|
|
3191
|
+
additionalProperties: false
|
|
3192
|
+
},
|
|
3193
|
+
isWrite: false,
|
|
3194
|
+
handler: handleGetCategories
|
|
3195
|
+
},
|
|
3196
|
+
{
|
|
3197
|
+
name: "skills_search",
|
|
3198
|
+
module: "skills",
|
|
3199
|
+
description: "Search for skills in OKX Skills Marketplace by keyword or category. To get valid category IDs, call skills_get_categories first. Returns skill names for use with skills_download. Do NOT use for downloading \u2014 use skills_download.",
|
|
3200
|
+
inputSchema: {
|
|
3201
|
+
type: "object",
|
|
3202
|
+
properties: {
|
|
3203
|
+
keyword: {
|
|
3204
|
+
type: "string",
|
|
3205
|
+
description: "Search keyword (matches name, description, tags)"
|
|
3206
|
+
},
|
|
3207
|
+
categories: {
|
|
3208
|
+
type: "string",
|
|
3209
|
+
description: "Filter by category ID"
|
|
3210
|
+
},
|
|
3211
|
+
page: {
|
|
3212
|
+
type: "string",
|
|
3213
|
+
description: "Page number, starting from 1. Default: 1"
|
|
3214
|
+
},
|
|
3215
|
+
limit: {
|
|
3216
|
+
type: "string",
|
|
3217
|
+
description: "Results per page. Default: 20, max: 100"
|
|
3218
|
+
}
|
|
3219
|
+
},
|
|
3220
|
+
additionalProperties: false
|
|
3221
|
+
},
|
|
3222
|
+
isWrite: false,
|
|
3223
|
+
handler: handleSearch
|
|
3224
|
+
},
|
|
3225
|
+
{
|
|
3226
|
+
name: "skills_download",
|
|
3227
|
+
module: "skills",
|
|
3228
|
+
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.",
|
|
3229
|
+
inputSchema: {
|
|
3230
|
+
type: "object",
|
|
3231
|
+
properties: {
|
|
3232
|
+
name: {
|
|
3233
|
+
type: "string",
|
|
3234
|
+
description: "Skill name (unique identifier)"
|
|
3235
|
+
},
|
|
3236
|
+
targetDir: {
|
|
3237
|
+
type: "string",
|
|
3238
|
+
description: "Directory path where the zip file will be saved"
|
|
3239
|
+
}
|
|
3240
|
+
},
|
|
3241
|
+
required: ["name", "targetDir"],
|
|
3242
|
+
additionalProperties: false
|
|
3243
|
+
},
|
|
3244
|
+
isWrite: true,
|
|
3245
|
+
handler: handleDownload
|
|
3246
|
+
}
|
|
3247
|
+
];
|
|
3248
|
+
}
|
|
3249
|
+
async function handleGetCategories(_args, ctx) {
|
|
3250
|
+
const result = await ctx.client.privateGet(
|
|
3251
|
+
"/api/v5/skill/categories"
|
|
3252
|
+
);
|
|
3253
|
+
return result;
|
|
3254
|
+
}
|
|
3255
|
+
async function handleSearch(args, ctx) {
|
|
3256
|
+
const query = {};
|
|
3257
|
+
if (args.keyword) query.keyword = String(args.keyword);
|
|
3258
|
+
if (args.categories) query.categories = String(args.categories);
|
|
3259
|
+
if (args.page) query.page = String(args.page);
|
|
3260
|
+
if (args.limit) query.limit = String(args.limit);
|
|
3261
|
+
const result = await ctx.client.privateGet(
|
|
3262
|
+
"/api/v5/skill/search",
|
|
3263
|
+
query
|
|
3264
|
+
);
|
|
3265
|
+
const totalPage = result.raw?.totalPage;
|
|
3266
|
+
return { ...result, totalPage };
|
|
3267
|
+
}
|
|
3268
|
+
async function handleDownload(args, ctx) {
|
|
3269
|
+
const name = String(args.name);
|
|
3270
|
+
const targetDir = String(args.targetDir);
|
|
3271
|
+
const filePath = await downloadSkillZip(ctx.client, name, targetDir);
|
|
3272
|
+
return {
|
|
3273
|
+
endpoint: "POST /api/v5/skill/download",
|
|
3274
|
+
requestTime: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3275
|
+
data: {
|
|
3276
|
+
name,
|
|
3277
|
+
filePath
|
|
3278
|
+
}
|
|
3279
|
+
};
|
|
3280
|
+
}
|
|
2871
3281
|
function normalizeWrite(response) {
|
|
2872
3282
|
const data = response.data;
|
|
2873
3283
|
if (Array.isArray(data) && data.length > 0) {
|
|
@@ -3466,7 +3876,6 @@ function registerEarnTools() {
|
|
|
3466
3876
|
required: ["ccy", "amt"]
|
|
3467
3877
|
},
|
|
3468
3878
|
handler: async (rawArgs, context) => {
|
|
3469
|
-
assertNotDemo(context.config, "earn_savings_purchase");
|
|
3470
3879
|
const args = asRecord(rawArgs);
|
|
3471
3880
|
const response = await context.client.privatePost(
|
|
3472
3881
|
"/api/v5/finance/savings/purchase-redempt",
|
|
@@ -3501,7 +3910,6 @@ function registerEarnTools() {
|
|
|
3501
3910
|
required: ["ccy", "amt"]
|
|
3502
3911
|
},
|
|
3503
3912
|
handler: async (rawArgs, context) => {
|
|
3504
|
-
assertNotDemo(context.config, "earn_savings_redeem");
|
|
3505
3913
|
const args = asRecord(rawArgs);
|
|
3506
3914
|
const response = await context.client.privatePost(
|
|
3507
3915
|
"/api/v5/finance/savings/purchase-redempt",
|
|
@@ -3535,7 +3943,6 @@ function registerEarnTools() {
|
|
|
3535
3943
|
required: ["ccy", "rate"]
|
|
3536
3944
|
},
|
|
3537
3945
|
handler: async (rawArgs, context) => {
|
|
3538
|
-
assertNotDemo(context.config, "earn_set_lending_rate");
|
|
3539
3946
|
const args = asRecord(rawArgs);
|
|
3540
3947
|
const response = await context.client.privatePost(
|
|
3541
3948
|
"/api/v5/finance/savings/set-lending-rate",
|
|
@@ -3679,7 +4086,7 @@ function registerOnchainEarnTools() {
|
|
|
3679
4086
|
{
|
|
3680
4087
|
name: "onchain_earn_purchase",
|
|
3681
4088
|
module: "earn.onchain",
|
|
3682
|
-
description: "Invest in a staking/DeFi product. [CAUTION] Moves real funds.
|
|
4089
|
+
description: "Invest in a staking/DeFi product. [CAUTION] Moves real funds.",
|
|
3683
4090
|
isWrite: true,
|
|
3684
4091
|
inputSchema: {
|
|
3685
4092
|
type: "object",
|
|
@@ -3712,7 +4119,6 @@ function registerOnchainEarnTools() {
|
|
|
3712
4119
|
required: ["productId", "investData"]
|
|
3713
4120
|
},
|
|
3714
4121
|
handler: async (rawArgs, context) => {
|
|
3715
|
-
assertNotDemo(context.config, "onchain_earn_purchase");
|
|
3716
4122
|
const args = asRecord(rawArgs);
|
|
3717
4123
|
const response = await context.client.privatePost(
|
|
3718
4124
|
"/api/v5/finance/staking-defi/purchase",
|
|
@@ -3733,7 +4139,7 @@ function registerOnchainEarnTools() {
|
|
|
3733
4139
|
{
|
|
3734
4140
|
name: "onchain_earn_redeem",
|
|
3735
4141
|
module: "earn.onchain",
|
|
3736
|
-
description: "Redeem a staking/DeFi investment. [CAUTION] Some products have lock periods, early redemption may incur penalties.
|
|
4142
|
+
description: "Redeem a staking/DeFi investment. [CAUTION] Some products have lock periods, early redemption may incur penalties.",
|
|
3737
4143
|
isWrite: true,
|
|
3738
4144
|
inputSchema: {
|
|
3739
4145
|
type: "object",
|
|
@@ -3754,7 +4160,6 @@ function registerOnchainEarnTools() {
|
|
|
3754
4160
|
required: ["ordId", "protocolType"]
|
|
3755
4161
|
},
|
|
3756
4162
|
handler: async (rawArgs, context) => {
|
|
3757
|
-
assertNotDemo(context.config, "onchain_earn_redeem");
|
|
3758
4163
|
const args = asRecord(rawArgs);
|
|
3759
4164
|
const response = await context.client.privatePost(
|
|
3760
4165
|
"/api/v5/finance/staking-defi/redeem",
|
|
@@ -3774,7 +4179,7 @@ function registerOnchainEarnTools() {
|
|
|
3774
4179
|
{
|
|
3775
4180
|
name: "onchain_earn_cancel",
|
|
3776
4181
|
module: "earn.onchain",
|
|
3777
|
-
description: "Cancel a pending staking/DeFi purchase order. [CAUTION]
|
|
4182
|
+
description: "Cancel a pending staking/DeFi purchase order. [CAUTION]",
|
|
3778
4183
|
isWrite: true,
|
|
3779
4184
|
inputSchema: {
|
|
3780
4185
|
type: "object",
|
|
@@ -3791,7 +4196,6 @@ function registerOnchainEarnTools() {
|
|
|
3791
4196
|
required: ["ordId", "protocolType"]
|
|
3792
4197
|
},
|
|
3793
4198
|
handler: async (rawArgs, context) => {
|
|
3794
|
-
assertNotDemo(context.config, "onchain_earn_cancel");
|
|
3795
4199
|
const args = asRecord(rawArgs);
|
|
3796
4200
|
const response = await context.client.privatePost(
|
|
3797
4201
|
"/api/v5/finance/staking-defi/cancel",
|
|
@@ -4084,7 +4488,6 @@ function registerDcdTools() {
|
|
|
4084
4488
|
required: ["productId", "notionalSz", "notionalCcy"]
|
|
4085
4489
|
},
|
|
4086
4490
|
handler: async (rawArgs, context) => {
|
|
4087
|
-
assertNotDemo(context.config, "dcd_subscribe");
|
|
4088
4491
|
const args = asRecord(rawArgs);
|
|
4089
4492
|
const productId = requireString(args, "productId");
|
|
4090
4493
|
const notionalSz = requireString(args, "notionalSz");
|
|
@@ -4256,13 +4659,30 @@ function registerAutoEarnTools() {
|
|
|
4256
4659
|
}
|
|
4257
4660
|
];
|
|
4258
4661
|
}
|
|
4662
|
+
var EARN_DEMO_MESSAGE = "Earn features (savings, DCD, on-chain staking, auto-earn) are not available in simulated trading mode.";
|
|
4663
|
+
var EARN_DEMO_SUGGESTION = "Switch to a live account to use Earn features.";
|
|
4664
|
+
var DEMO_GUARD_SKIP = /* @__PURE__ */ new Set(["dcd_redeem"]);
|
|
4665
|
+
function withDemoGuard(tool) {
|
|
4666
|
+
if (!tool.isWrite || DEMO_GUARD_SKIP.has(tool.name)) return tool;
|
|
4667
|
+
const originalHandler = tool.handler;
|
|
4668
|
+
return {
|
|
4669
|
+
...tool,
|
|
4670
|
+
handler: async (args, context) => {
|
|
4671
|
+
if (context.config.demo) {
|
|
4672
|
+
throw new ConfigError(EARN_DEMO_MESSAGE, EARN_DEMO_SUGGESTION);
|
|
4673
|
+
}
|
|
4674
|
+
return originalHandler(args, context);
|
|
4675
|
+
}
|
|
4676
|
+
};
|
|
4677
|
+
}
|
|
4259
4678
|
function registerAllEarnTools() {
|
|
4260
|
-
|
|
4679
|
+
const tools = [
|
|
4261
4680
|
...registerEarnTools(),
|
|
4262
4681
|
...registerOnchainEarnTools(),
|
|
4263
4682
|
...registerDcdTools(),
|
|
4264
4683
|
...registerAutoEarnTools()
|
|
4265
4684
|
];
|
|
4685
|
+
return tools.map(withDemoGuard);
|
|
4266
4686
|
}
|
|
4267
4687
|
function buildContractTradeTools(cfg) {
|
|
4268
4688
|
const { prefix, module, label, instTypes, instIdExample } = cfg;
|
|
@@ -5286,7 +5706,7 @@ function registerMarketTools() {
|
|
|
5286
5706
|
{
|
|
5287
5707
|
name: "market_get_stock_tokens",
|
|
5288
5708
|
module: "market",
|
|
5289
|
-
description: "Get all stock token instruments (instCategory=3). Stock tokens track real-world stock prices on OKX (e.g. AAPL-USDT-SWAP).
|
|
5709
|
+
description: '[Deprecated: use market_get_instruments_by_category with instCategory="3" instead] Get all stock token instruments (instCategory=3). Stock tokens track real-world stock prices on OKX (e.g. AAPL-USDT-SWAP).',
|
|
5290
5710
|
isWrite: false,
|
|
5291
5711
|
inputSchema: {
|
|
5292
5712
|
type: "object",
|
|
@@ -5316,110 +5736,448 @@ function registerMarketTools() {
|
|
|
5316
5736
|
const filtered = Array.isArray(data) ? data.filter((item) => item.instCategory === "3") : data;
|
|
5317
5737
|
return normalizeResponse({ ...response, data: filtered });
|
|
5318
5738
|
}
|
|
5319
|
-
}
|
|
5320
|
-
];
|
|
5321
|
-
}
|
|
5322
|
-
function registerOptionAlgoTools() {
|
|
5323
|
-
return [
|
|
5739
|
+
},
|
|
5324
5740
|
{
|
|
5325
|
-
name: "
|
|
5326
|
-
module: "
|
|
5327
|
-
description: "
|
|
5328
|
-
isWrite:
|
|
5741
|
+
name: "market_get_instruments_by_category",
|
|
5742
|
+
module: "market",
|
|
5743
|
+
description: "Discover tradeable instruments by asset category. Stock tokens (instCategory=3, e.g. AAPL-USDT-SWAP, TSLA-USDT-SWAP), Metals (4, e.g. XAUUSDT-USDT-SWAP for gold), Commodities (5, e.g. OIL-USDT-SWAP for crude oil), Forex (6, e.g. EURUSDT-USDT-SWAP for EUR/USD), Bonds (7, e.g. US30Y-USDT-SWAP). Use this to find instIds before querying prices or placing orders. Filters client-side by instCategory.",
|
|
5744
|
+
isWrite: false,
|
|
5329
5745
|
inputSchema: {
|
|
5330
5746
|
type: "object",
|
|
5331
5747
|
properties: {
|
|
5332
|
-
|
|
5333
|
-
type: "string",
|
|
5334
|
-
description: "e.g. BTC-USD-241227-50000-C"
|
|
5335
|
-
},
|
|
5336
|
-
tdMode: {
|
|
5337
|
-
type: "string",
|
|
5338
|
-
enum: ["cash", "cross", "isolated"],
|
|
5339
|
-
description: "cash=buyer full premium; cross/isolated=seller margin"
|
|
5340
|
-
},
|
|
5341
|
-
side: {
|
|
5342
|
-
type: "string",
|
|
5343
|
-
enum: ["buy", "sell"],
|
|
5344
|
-
description: "sell=close long, buy=close short"
|
|
5345
|
-
},
|
|
5346
|
-
ordType: {
|
|
5347
|
-
type: "string",
|
|
5348
|
-
enum: ["conditional", "oco"],
|
|
5349
|
-
description: "conditional=single TP/SL or both; oco=TP+SL pair (first trigger cancels other)"
|
|
5350
|
-
},
|
|
5351
|
-
sz: {
|
|
5352
|
-
type: "string",
|
|
5353
|
-
description: "Contracts count (NOT USDT). Use market_get_instruments for ctVal."
|
|
5354
|
-
},
|
|
5355
|
-
tpTriggerPx: {
|
|
5356
|
-
type: "string",
|
|
5357
|
-
description: "TP trigger price"
|
|
5358
|
-
},
|
|
5359
|
-
tpOrdPx: {
|
|
5360
|
-
type: "string",
|
|
5361
|
-
description: "TP order price; -1=market"
|
|
5362
|
-
},
|
|
5363
|
-
tpTriggerPxType: {
|
|
5364
|
-
type: "string",
|
|
5365
|
-
enum: ["last", "index", "mark"]
|
|
5366
|
-
},
|
|
5367
|
-
slTriggerPx: {
|
|
5368
|
-
type: "string",
|
|
5369
|
-
description: "SL trigger price"
|
|
5370
|
-
},
|
|
5371
|
-
slOrdPx: {
|
|
5372
|
-
type: "string",
|
|
5373
|
-
description: "SL order price; -1=market"
|
|
5374
|
-
},
|
|
5375
|
-
slTriggerPxType: {
|
|
5748
|
+
instCategory: {
|
|
5376
5749
|
type: "string",
|
|
5377
|
-
enum: ["
|
|
5750
|
+
enum: ["3", "4", "5", "6", "7"],
|
|
5751
|
+
description: "Asset category: 3=Stock tokens, 4=Metals, 5=Commodities, 6=Forex, 7=Bonds"
|
|
5378
5752
|
},
|
|
5379
|
-
|
|
5753
|
+
instType: {
|
|
5380
5754
|
type: "string",
|
|
5381
|
-
enum: ["
|
|
5382
|
-
description: "
|
|
5383
|
-
},
|
|
5384
|
-
reduceOnly: {
|
|
5385
|
-
type: "boolean",
|
|
5386
|
-
description: "Ensure order only reduces position"
|
|
5755
|
+
enum: ["SPOT", "SWAP"],
|
|
5756
|
+
description: "Instrument type. Default: SWAP"
|
|
5387
5757
|
},
|
|
5388
|
-
|
|
5758
|
+
instId: {
|
|
5389
5759
|
type: "string",
|
|
5390
|
-
description: "
|
|
5760
|
+
description: "Optional: filter by specific instrument ID"
|
|
5391
5761
|
}
|
|
5392
5762
|
},
|
|
5393
|
-
required: ["
|
|
5763
|
+
required: ["instCategory"]
|
|
5394
5764
|
},
|
|
5395
5765
|
handler: async (rawArgs, context) => {
|
|
5396
5766
|
const args = asRecord(rawArgs);
|
|
5397
|
-
const
|
|
5398
|
-
const
|
|
5399
|
-
|
|
5400
|
-
|
|
5401
|
-
|
|
5402
|
-
|
|
5403
|
-
|
|
5404
|
-
ordType: requireString(args, "ordType"),
|
|
5405
|
-
sz: requireString(args, "sz"),
|
|
5406
|
-
tgtCcy: readString(args, "tgtCcy"),
|
|
5407
|
-
tpTriggerPx: readString(args, "tpTriggerPx"),
|
|
5408
|
-
tpOrdPx: readString(args, "tpOrdPx"),
|
|
5409
|
-
tpTriggerPxType: readString(args, "tpTriggerPxType"),
|
|
5410
|
-
slTriggerPx: readString(args, "slTriggerPx"),
|
|
5411
|
-
slOrdPx: readString(args, "slOrdPx"),
|
|
5412
|
-
slTriggerPxType: readString(args, "slTriggerPxType"),
|
|
5413
|
-
reduceOnly: reduceOnly !== void 0 ? String(reduceOnly) : void 0,
|
|
5414
|
-
clOrdId: readString(args, "clOrdId"),
|
|
5415
|
-
tag: context.config.sourceTag
|
|
5416
|
-
}),
|
|
5417
|
-
privateRateLimit("option_place_algo_order", 20)
|
|
5767
|
+
const instCategory = requireString(args, "instCategory");
|
|
5768
|
+
const instType = readString(args, "instType") ?? "SWAP";
|
|
5769
|
+
const instId = readString(args, "instId");
|
|
5770
|
+
const response = await context.client.publicGet(
|
|
5771
|
+
"/api/v5/public/instruments",
|
|
5772
|
+
compactObject({ instType, instId }),
|
|
5773
|
+
publicRateLimit("market_get_instruments_by_category", 20)
|
|
5418
5774
|
);
|
|
5419
|
-
|
|
5775
|
+
const data = response.data;
|
|
5776
|
+
const filtered = Array.isArray(data) ? data.filter((item) => item.instCategory === instCategory) : data;
|
|
5777
|
+
return normalizeResponse({ ...response, data: filtered });
|
|
5420
5778
|
}
|
|
5421
|
-
}
|
|
5422
|
-
|
|
5779
|
+
}
|
|
5780
|
+
];
|
|
5781
|
+
}
|
|
5782
|
+
var NEWS_SEARCH = "/api/v5/orbit/news-search";
|
|
5783
|
+
var NEWS_DETAIL = "/api/v5/orbit/news-detail";
|
|
5784
|
+
var NEWS_DOMAINS = "/api/v5/orbit/news-platform";
|
|
5785
|
+
var SENTIMENT_QUERY = "/api/v5/orbit/currency-sentiment-query";
|
|
5786
|
+
var SENTIMENT_RANKING = "/api/v5/orbit/currency-sentiment-ranking";
|
|
5787
|
+
var NEWS_LANGUAGE = ["en_US", "zh_CN"];
|
|
5788
|
+
function langHeader(lang) {
|
|
5789
|
+
if (lang === "zh_CN" || lang === "en_US") return { "Accept-Language": lang };
|
|
5790
|
+
return void 0;
|
|
5791
|
+
}
|
|
5792
|
+
var NEWS_DETAIL_LVL = ["brief", "summary", "full"];
|
|
5793
|
+
var NEWS_IMPORTANCE = ["high", "medium", "low"];
|
|
5794
|
+
var NEWS_SENTIMENT = ["bullish", "bearish", "neutral"];
|
|
5795
|
+
var NEWS_SORT = ["latest", "relevant"];
|
|
5796
|
+
var SENTIMENT_PERIOD = ["1h", "4h", "24h"];
|
|
5797
|
+
var D_COINS_NEWS = 'Comma-separated uppercase ticker symbols (e.g. "BTC,ETH"). Normalize names/aliases to standard tickers.';
|
|
5798
|
+
var D_COINS_SENTIMENT = 'Comma-separated uppercase ticker symbols, max 20 (e.g. "BTC,ETH"). Normalize names/aliases to standard tickers.';
|
|
5799
|
+
var D_LANGUAGE = "Content language: zh_CN or en_US. Infer from user's message. No server default.";
|
|
5800
|
+
var D_BEGIN = "Start time, Unix epoch milliseconds. Parse relative time if given (e.g. 'yesterday', 'last 7 days').";
|
|
5801
|
+
var D_END = "End time, Unix epoch milliseconds. Parse relative time if given. Omit for no upper bound.";
|
|
5802
|
+
var D_IMPORTANCE = "Importance filter: high (server default), medium, low. Omit unless user wants broader coverage.";
|
|
5803
|
+
var D_LIMIT = "Number of results (default 10, max 50).";
|
|
5804
|
+
function registerNewsTools() {
|
|
5805
|
+
return [
|
|
5806
|
+
// -----------------------------------------------------------------------
|
|
5807
|
+
// News browsing tools
|
|
5808
|
+
// -----------------------------------------------------------------------
|
|
5809
|
+
{
|
|
5810
|
+
name: "news_get_latest",
|
|
5811
|
+
module: "news",
|
|
5812
|
+
description: "Get crypto news sorted by time. Omitting importance still returns only high-importance news (server default). Pass importance='medium' or 'low' explicitly to broaden results. Use when user asks 'what happened recently', 'latest news', 'any big news today', or wants to browse without a keyword.",
|
|
5813
|
+
isWrite: false,
|
|
5814
|
+
inputSchema: {
|
|
5815
|
+
type: "object",
|
|
5816
|
+
properties: {
|
|
5817
|
+
coins: { type: "string", description: D_COINS_NEWS + " Optional." },
|
|
5818
|
+
importance: { type: "string", enum: [...NEWS_IMPORTANCE], description: D_IMPORTANCE },
|
|
5819
|
+
begin: { type: "number", description: D_BEGIN },
|
|
5820
|
+
end: { type: "number", description: D_END },
|
|
5821
|
+
language: { type: "string", enum: [...NEWS_LANGUAGE], description: D_LANGUAGE },
|
|
5822
|
+
detailLvl: {
|
|
5823
|
+
type: "string",
|
|
5824
|
+
enum: [...NEWS_DETAIL_LVL],
|
|
5825
|
+
description: "Content level: summary (AI summary, default), full (original text), brief (title only)."
|
|
5826
|
+
},
|
|
5827
|
+
limit: { type: "number", description: D_LIMIT },
|
|
5828
|
+
after: { type: "string", description: "Pagination cursor from previous response nextCursor." }
|
|
5829
|
+
},
|
|
5830
|
+
required: []
|
|
5831
|
+
},
|
|
5832
|
+
handler: async (rawArgs, context) => {
|
|
5833
|
+
const args = asRecord(rawArgs);
|
|
5834
|
+
const response = await context.client.privateGet(
|
|
5835
|
+
NEWS_SEARCH,
|
|
5836
|
+
compactObject({
|
|
5837
|
+
sortBy: "latest",
|
|
5838
|
+
importance: readString(args, "importance"),
|
|
5839
|
+
ccyList: readString(args, "coins"),
|
|
5840
|
+
begin: readNumber(args, "begin"),
|
|
5841
|
+
end: readNumber(args, "end"),
|
|
5842
|
+
detailLvl: readString(args, "detailLvl"),
|
|
5843
|
+
limit: readNumber(args, "limit") ?? 10,
|
|
5844
|
+
cursor: readString(args, "after")
|
|
5845
|
+
}),
|
|
5846
|
+
publicRateLimit("news_get_latest", 20),
|
|
5847
|
+
langHeader(readString(args, "language"))
|
|
5848
|
+
);
|
|
5849
|
+
return normalizeResponse(response);
|
|
5850
|
+
}
|
|
5851
|
+
},
|
|
5852
|
+
{
|
|
5853
|
+
name: "news_get_by_coin",
|
|
5854
|
+
module: "news",
|
|
5855
|
+
description: "Get news for specific coins or tokens. Use when user mentions a coin: 'BTC news', 'any SOL updates'. Supports multiple coins (comma-separated).",
|
|
5856
|
+
isWrite: false,
|
|
5857
|
+
inputSchema: {
|
|
5858
|
+
type: "object",
|
|
5859
|
+
properties: {
|
|
5860
|
+
coins: { type: "string", description: D_COINS_NEWS + " Required." },
|
|
5861
|
+
importance: { type: "string", enum: [...NEWS_IMPORTANCE], description: D_IMPORTANCE },
|
|
5862
|
+
begin: { type: "number", description: D_BEGIN },
|
|
5863
|
+
end: { type: "number", description: D_END },
|
|
5864
|
+
language: { type: "string", enum: [...NEWS_LANGUAGE], description: D_LANGUAGE },
|
|
5865
|
+
detailLvl: { type: "string", enum: [...NEWS_DETAIL_LVL] },
|
|
5866
|
+
limit: { type: "number", description: D_LIMIT }
|
|
5867
|
+
},
|
|
5868
|
+
required: ["coins"]
|
|
5869
|
+
},
|
|
5870
|
+
handler: async (rawArgs, context) => {
|
|
5871
|
+
const args = asRecord(rawArgs);
|
|
5872
|
+
const coins = readString(args, "coins");
|
|
5873
|
+
if (!coins) {
|
|
5874
|
+
throw new Error(`Missing required parameter "coins".`);
|
|
5875
|
+
}
|
|
5876
|
+
const response = await context.client.privateGet(
|
|
5877
|
+
NEWS_SEARCH,
|
|
5878
|
+
compactObject({
|
|
5879
|
+
sortBy: "latest",
|
|
5880
|
+
ccyList: coins,
|
|
5881
|
+
importance: readString(args, "importance"),
|
|
5882
|
+
begin: readNumber(args, "begin"),
|
|
5883
|
+
end: readNumber(args, "end"),
|
|
5884
|
+
detailLvl: readString(args, "detailLvl"),
|
|
5885
|
+
limit: readNumber(args, "limit") ?? 10
|
|
5886
|
+
}),
|
|
5887
|
+
publicRateLimit("news_get_by_coin", 20),
|
|
5888
|
+
langHeader(readString(args, "language"))
|
|
5889
|
+
);
|
|
5890
|
+
return normalizeResponse(response);
|
|
5891
|
+
}
|
|
5892
|
+
},
|
|
5893
|
+
{
|
|
5894
|
+
name: "news_search",
|
|
5895
|
+
module: "news",
|
|
5896
|
+
description: "Search crypto news by keyword with optional filters. Use when user provides specific search terms: 'SEC ETF news', 'stablecoin regulation', 'Bitcoin halving'. For coin-only queries prefer news_get_by_coin.",
|
|
5897
|
+
isWrite: false,
|
|
5898
|
+
inputSchema: {
|
|
5899
|
+
type: "object",
|
|
5900
|
+
properties: {
|
|
5901
|
+
keyword: {
|
|
5902
|
+
type: "string",
|
|
5903
|
+
description: "Search keyword(s) extracted from user query (topic/entity). Multiple words are AND-combined. Omit to browse by filters only."
|
|
5904
|
+
},
|
|
5905
|
+
coins: { type: "string", description: D_COINS_NEWS + " Optional." },
|
|
5906
|
+
importance: { type: "string", enum: [...NEWS_IMPORTANCE], description: D_IMPORTANCE },
|
|
5907
|
+
sentiment: {
|
|
5908
|
+
type: "string",
|
|
5909
|
+
enum: [...NEWS_SENTIMENT],
|
|
5910
|
+
description: "Filter by sentiment if mentioned alongside the keyword."
|
|
5911
|
+
},
|
|
5912
|
+
sortBy: {
|
|
5913
|
+
type: "string",
|
|
5914
|
+
enum: [...NEWS_SORT],
|
|
5915
|
+
description: "Sort order: relevant (by relevance, default for keyword search), latest (by time)."
|
|
5916
|
+
},
|
|
5917
|
+
begin: { type: "number", description: D_BEGIN },
|
|
5918
|
+
end: { type: "number", description: D_END },
|
|
5919
|
+
language: { type: "string", enum: [...NEWS_LANGUAGE], description: D_LANGUAGE },
|
|
5920
|
+
detailLvl: { type: "string", enum: [...NEWS_DETAIL_LVL] },
|
|
5921
|
+
limit: { type: "number", description: D_LIMIT },
|
|
5922
|
+
after: { type: "string", description: "Pagination cursor from previous response nextCursor." }
|
|
5923
|
+
},
|
|
5924
|
+
required: []
|
|
5925
|
+
},
|
|
5926
|
+
handler: async (rawArgs, context) => {
|
|
5927
|
+
const args = asRecord(rawArgs);
|
|
5928
|
+
const response = await context.client.privateGet(
|
|
5929
|
+
NEWS_SEARCH,
|
|
5930
|
+
compactObject({
|
|
5931
|
+
keyword: readString(args, "keyword") || void 0,
|
|
5932
|
+
sortBy: readString(args, "sortBy") ?? "relevant",
|
|
5933
|
+
importance: readString(args, "importance"),
|
|
5934
|
+
ccyList: readString(args, "coins"),
|
|
5935
|
+
sentiment: readString(args, "sentiment"),
|
|
5936
|
+
begin: readNumber(args, "begin"),
|
|
5937
|
+
end: readNumber(args, "end"),
|
|
5938
|
+
detailLvl: readString(args, "detailLvl"),
|
|
5939
|
+
limit: readNumber(args, "limit") ?? 10,
|
|
5940
|
+
cursor: readString(args, "after")
|
|
5941
|
+
}),
|
|
5942
|
+
publicRateLimit("news_search", 20),
|
|
5943
|
+
langHeader(readString(args, "language"))
|
|
5944
|
+
);
|
|
5945
|
+
return normalizeResponse(response);
|
|
5946
|
+
}
|
|
5947
|
+
},
|
|
5948
|
+
{
|
|
5949
|
+
name: "news_get_detail",
|
|
5950
|
+
module: "news",
|
|
5951
|
+
description: "Get full article content by news ID (returns title + summary + full original text). Use when user says 'show full article', 'read more', or provides a specific news ID from a previous result.",
|
|
5952
|
+
isWrite: false,
|
|
5953
|
+
inputSchema: {
|
|
5954
|
+
type: "object",
|
|
5955
|
+
properties: {
|
|
5956
|
+
id: {
|
|
5957
|
+
type: "string",
|
|
5958
|
+
description: "News article ID from a previous news_get_latest / news_get_by_coin / news_search result. Required."
|
|
5959
|
+
},
|
|
5960
|
+
language: { type: "string", enum: [...NEWS_LANGUAGE], description: D_LANGUAGE }
|
|
5961
|
+
},
|
|
5962
|
+
required: ["id"]
|
|
5963
|
+
},
|
|
5964
|
+
handler: async (rawArgs, context) => {
|
|
5965
|
+
const args = asRecord(rawArgs);
|
|
5966
|
+
const id = readString(args, "id");
|
|
5967
|
+
if (!id) {
|
|
5968
|
+
throw new Error(`Missing required parameter "id".`);
|
|
5969
|
+
}
|
|
5970
|
+
const response = await context.client.privateGet(
|
|
5971
|
+
NEWS_DETAIL,
|
|
5972
|
+
{ id },
|
|
5973
|
+
publicRateLimit("news_get_detail", 20),
|
|
5974
|
+
langHeader(readString(args, "language"))
|
|
5975
|
+
);
|
|
5976
|
+
return normalizeResponse(response);
|
|
5977
|
+
}
|
|
5978
|
+
},
|
|
5979
|
+
{
|
|
5980
|
+
name: "news_get_domains",
|
|
5981
|
+
module: "news",
|
|
5982
|
+
description: "List available news source domains (e.g. coindesk, cointelegraph). Use when user asks what news sources are available or which platforms are covered.",
|
|
5983
|
+
isWrite: false,
|
|
5984
|
+
inputSchema: {
|
|
5985
|
+
type: "object",
|
|
5986
|
+
properties: {},
|
|
5987
|
+
required: []
|
|
5988
|
+
},
|
|
5989
|
+
handler: async (_rawArgs, context) => {
|
|
5990
|
+
const response = await context.client.privateGet(
|
|
5991
|
+
NEWS_DOMAINS,
|
|
5992
|
+
{},
|
|
5993
|
+
publicRateLimit("news_get_domains", 20)
|
|
5994
|
+
);
|
|
5995
|
+
return normalizeResponse(response);
|
|
5996
|
+
}
|
|
5997
|
+
},
|
|
5998
|
+
// -----------------------------------------------------------------------
|
|
5999
|
+
// Token sentiment tools
|
|
6000
|
+
// -----------------------------------------------------------------------
|
|
6001
|
+
{
|
|
6002
|
+
name: "news_get_coin_sentiment",
|
|
6003
|
+
module: "news",
|
|
6004
|
+
description: "Get sentiment snapshot or time-series trend for coins. Returns bullish/bearish ratios and mention counts. Pass trendPoints for trend data (1h\u219224 points, 4h\u21926, 24h\u21927). Use when user asks about coin sentiment, sentiment trend, or how bullish/bearish a coin is.",
|
|
6005
|
+
isWrite: false,
|
|
6006
|
+
inputSchema: {
|
|
6007
|
+
type: "object",
|
|
6008
|
+
properties: {
|
|
6009
|
+
coins: { type: "string", description: D_COINS_SENTIMENT + " Required." },
|
|
6010
|
+
period: {
|
|
6011
|
+
type: "string",
|
|
6012
|
+
enum: [...SENTIMENT_PERIOD],
|
|
6013
|
+
description: "Aggregation granularity: 1h, 4h, 24h. Snapshot default: 24h. Trend default: 1h."
|
|
6014
|
+
},
|
|
6015
|
+
trendPoints: {
|
|
6016
|
+
type: "number",
|
|
6017
|
+
description: "Trend data points. Pass for time-series trend; omit for snapshot. Guide: 1h\u219224, 4h\u21926, 24h\u21927."
|
|
6018
|
+
}
|
|
6019
|
+
},
|
|
6020
|
+
required: ["coins"]
|
|
6021
|
+
},
|
|
6022
|
+
handler: async (rawArgs, context) => {
|
|
6023
|
+
const args = asRecord(rawArgs);
|
|
6024
|
+
const coins = readString(args, "coins");
|
|
6025
|
+
if (!coins) {
|
|
6026
|
+
throw new Error(`Missing required parameter "coins".`);
|
|
6027
|
+
}
|
|
6028
|
+
const trendPoints = readNumber(args, "trendPoints");
|
|
6029
|
+
const inclTrend = trendPoints !== void 0;
|
|
6030
|
+
const response = await context.client.privateGet(
|
|
6031
|
+
SENTIMENT_QUERY,
|
|
6032
|
+
compactObject({
|
|
6033
|
+
ccy: coins,
|
|
6034
|
+
period: readString(args, "period") ?? (inclTrend ? "1h" : "24h"),
|
|
6035
|
+
...inclTrend ? { inclTrend: true, limit: trendPoints } : {}
|
|
6036
|
+
}),
|
|
6037
|
+
publicRateLimit("news_get_coin_sentiment", 20)
|
|
6038
|
+
);
|
|
6039
|
+
return normalizeResponse(response);
|
|
6040
|
+
}
|
|
6041
|
+
},
|
|
6042
|
+
{
|
|
6043
|
+
name: "news_get_sentiment_ranking",
|
|
6044
|
+
module: "news",
|
|
6045
|
+
description: "Get coin ranking by social hotness or sentiment direction. Use when user asks which coins are trending, most bullish/bearish coins. Sort by hot (mention count), bullish, or bearish.",
|
|
6046
|
+
isWrite: false,
|
|
6047
|
+
inputSchema: {
|
|
6048
|
+
type: "object",
|
|
6049
|
+
properties: {
|
|
6050
|
+
period: {
|
|
6051
|
+
type: "string",
|
|
6052
|
+
enum: [...SENTIMENT_PERIOD],
|
|
6053
|
+
description: "Aggregation granularity: 1h, 4h, 24h (default)."
|
|
6054
|
+
},
|
|
6055
|
+
sortBy: {
|
|
6056
|
+
type: "string",
|
|
6057
|
+
enum: ["hot", "bullish", "bearish"],
|
|
6058
|
+
description: "Sort: hot=by mentions (default), bullish=most bullish, bearish=most bearish."
|
|
6059
|
+
},
|
|
6060
|
+
limit: { type: "number", description: D_LIMIT }
|
|
6061
|
+
},
|
|
6062
|
+
required: []
|
|
6063
|
+
},
|
|
6064
|
+
handler: async (rawArgs, context) => {
|
|
6065
|
+
const args = asRecord(rawArgs);
|
|
6066
|
+
const response = await context.client.privateGet(
|
|
6067
|
+
SENTIMENT_RANKING,
|
|
6068
|
+
compactObject({
|
|
6069
|
+
period: readString(args, "period") ?? "24h",
|
|
6070
|
+
sortBy: readString(args, "sortBy") ?? "hot",
|
|
6071
|
+
limit: readNumber(args, "limit") ?? 10
|
|
6072
|
+
}),
|
|
6073
|
+
publicRateLimit("news_get_sentiment_ranking", 20)
|
|
6074
|
+
);
|
|
6075
|
+
return normalizeResponse(response);
|
|
6076
|
+
}
|
|
6077
|
+
}
|
|
6078
|
+
];
|
|
6079
|
+
}
|
|
6080
|
+
function registerOptionAlgoTools() {
|
|
6081
|
+
return [
|
|
6082
|
+
{
|
|
6083
|
+
name: "option_place_algo_order",
|
|
6084
|
+
module: "option",
|
|
6085
|
+
description: "Place OPTION TP/SL algo order (conditional/oco). [CAUTION] Executes real trades. conditional=single TP/SL; oco=TP+SL pair. -1=market.",
|
|
6086
|
+
isWrite: true,
|
|
6087
|
+
inputSchema: {
|
|
6088
|
+
type: "object",
|
|
6089
|
+
properties: {
|
|
6090
|
+
instId: {
|
|
6091
|
+
type: "string",
|
|
6092
|
+
description: "e.g. BTC-USD-241227-50000-C"
|
|
6093
|
+
},
|
|
6094
|
+
tdMode: {
|
|
6095
|
+
type: "string",
|
|
6096
|
+
enum: ["cash", "cross", "isolated"],
|
|
6097
|
+
description: "cash=buyer full premium; cross/isolated=seller margin"
|
|
6098
|
+
},
|
|
6099
|
+
side: {
|
|
6100
|
+
type: "string",
|
|
6101
|
+
enum: ["buy", "sell"],
|
|
6102
|
+
description: "sell=close long, buy=close short"
|
|
6103
|
+
},
|
|
6104
|
+
ordType: {
|
|
6105
|
+
type: "string",
|
|
6106
|
+
enum: ["conditional", "oco"],
|
|
6107
|
+
description: "conditional=single TP/SL or both; oco=TP+SL pair (first trigger cancels other)"
|
|
6108
|
+
},
|
|
6109
|
+
sz: {
|
|
6110
|
+
type: "string",
|
|
6111
|
+
description: "Contracts count (NOT USDT). Use market_get_instruments for ctVal."
|
|
6112
|
+
},
|
|
6113
|
+
tpTriggerPx: {
|
|
6114
|
+
type: "string",
|
|
6115
|
+
description: "TP trigger price"
|
|
6116
|
+
},
|
|
6117
|
+
tpOrdPx: {
|
|
6118
|
+
type: "string",
|
|
6119
|
+
description: "TP order price; -1=market"
|
|
6120
|
+
},
|
|
6121
|
+
tpTriggerPxType: {
|
|
6122
|
+
type: "string",
|
|
6123
|
+
enum: ["last", "index", "mark"]
|
|
6124
|
+
},
|
|
6125
|
+
slTriggerPx: {
|
|
6126
|
+
type: "string",
|
|
6127
|
+
description: "SL trigger price"
|
|
6128
|
+
},
|
|
6129
|
+
slOrdPx: {
|
|
6130
|
+
type: "string",
|
|
6131
|
+
description: "SL order price; -1=market"
|
|
6132
|
+
},
|
|
6133
|
+
slTriggerPxType: {
|
|
6134
|
+
type: "string",
|
|
6135
|
+
enum: ["last", "index", "mark"]
|
|
6136
|
+
},
|
|
6137
|
+
tgtCcy: {
|
|
6138
|
+
type: "string",
|
|
6139
|
+
enum: ["base_ccy", "quote_ccy"],
|
|
6140
|
+
description: "Size unit. base_ccy(default): sz in contracts, quote_ccy: sz in USDT (may not be supported for options)"
|
|
6141
|
+
},
|
|
6142
|
+
reduceOnly: {
|
|
6143
|
+
type: "boolean",
|
|
6144
|
+
description: "Ensure order only reduces position"
|
|
6145
|
+
},
|
|
6146
|
+
clOrdId: {
|
|
6147
|
+
type: "string",
|
|
6148
|
+
description: "Client order ID (max 32 chars)"
|
|
6149
|
+
}
|
|
6150
|
+
},
|
|
6151
|
+
required: ["instId", "tdMode", "side", "ordType", "sz"]
|
|
6152
|
+
},
|
|
6153
|
+
handler: async (rawArgs, context) => {
|
|
6154
|
+
const args = asRecord(rawArgs);
|
|
6155
|
+
const reduceOnly = readBoolean(args, "reduceOnly");
|
|
6156
|
+
const response = await context.client.privatePost(
|
|
6157
|
+
"/api/v5/trade/order-algo",
|
|
6158
|
+
compactObject({
|
|
6159
|
+
instId: requireString(args, "instId"),
|
|
6160
|
+
tdMode: requireString(args, "tdMode"),
|
|
6161
|
+
side: requireString(args, "side"),
|
|
6162
|
+
ordType: requireString(args, "ordType"),
|
|
6163
|
+
sz: requireString(args, "sz"),
|
|
6164
|
+
tgtCcy: readString(args, "tgtCcy"),
|
|
6165
|
+
tpTriggerPx: readString(args, "tpTriggerPx"),
|
|
6166
|
+
tpOrdPx: readString(args, "tpOrdPx"),
|
|
6167
|
+
tpTriggerPxType: readString(args, "tpTriggerPxType"),
|
|
6168
|
+
slTriggerPx: readString(args, "slTriggerPx"),
|
|
6169
|
+
slOrdPx: readString(args, "slOrdPx"),
|
|
6170
|
+
slTriggerPxType: readString(args, "slTriggerPxType"),
|
|
6171
|
+
reduceOnly: reduceOnly !== void 0 ? String(reduceOnly) : void 0,
|
|
6172
|
+
clOrdId: readString(args, "clOrdId"),
|
|
6173
|
+
tag: context.config.sourceTag
|
|
6174
|
+
}),
|
|
6175
|
+
privateRateLimit("option_place_algo_order", 20)
|
|
6176
|
+
);
|
|
6177
|
+
return normalizeResponse(response);
|
|
6178
|
+
}
|
|
6179
|
+
},
|
|
6180
|
+
{
|
|
5423
6181
|
name: "option_amend_algo_order",
|
|
5424
6182
|
module: "option",
|
|
5425
6183
|
description: "Amend a pending OPTION algo order (modify TP/SL prices or size).",
|
|
@@ -6792,9 +7550,11 @@ function allToolSpecs() {
|
|
|
6792
7550
|
...registerOptionAlgoTools(),
|
|
6793
7551
|
...registerAlgoTradeTools(),
|
|
6794
7552
|
...registerAccountTools(),
|
|
7553
|
+
...registerNewsTools(),
|
|
6795
7554
|
...registerBotTools(),
|
|
6796
7555
|
...registerAllEarnTools(),
|
|
6797
|
-
...registerAuditTools()
|
|
7556
|
+
...registerAuditTools(),
|
|
7557
|
+
...registerSkillsTools()
|
|
6798
7558
|
];
|
|
6799
7559
|
}
|
|
6800
7560
|
function createToolRunner(client, config) {
|
|
@@ -6809,12 +7569,12 @@ function createToolRunner(client, config) {
|
|
|
6809
7569
|
};
|
|
6810
7570
|
}
|
|
6811
7571
|
function configFilePath() {
|
|
6812
|
-
return
|
|
7572
|
+
return join4(homedir2(), ".okx", "config.toml");
|
|
6813
7573
|
}
|
|
6814
7574
|
function readFullConfig() {
|
|
6815
7575
|
const path42 = configFilePath();
|
|
6816
|
-
if (!
|
|
6817
|
-
const raw =
|
|
7576
|
+
if (!existsSync3(path42)) return { profiles: {} };
|
|
7577
|
+
const raw = readFileSync3(path42, "utf-8");
|
|
6818
7578
|
try {
|
|
6819
7579
|
return parse(raw);
|
|
6820
7580
|
} catch (err) {
|
|
@@ -6842,11 +7602,11 @@ var CONFIG_HEADER = `# OKX Trade Kit Configuration
|
|
|
6842
7602
|
`;
|
|
6843
7603
|
function writeFullConfig(config) {
|
|
6844
7604
|
const path42 = configFilePath();
|
|
6845
|
-
const dir =
|
|
6846
|
-
if (!
|
|
6847
|
-
|
|
7605
|
+
const dir = dirname3(path42);
|
|
7606
|
+
if (!existsSync3(dir)) {
|
|
7607
|
+
mkdirSync4(dir, { recursive: true });
|
|
6848
7608
|
}
|
|
6849
|
-
|
|
7609
|
+
writeFileSync3(path42, CONFIG_HEADER + stringify(config), "utf-8");
|
|
6850
7610
|
}
|
|
6851
7611
|
function expandShorthand(moduleId) {
|
|
6852
7612
|
if (moduleId === "all") return [...MODULES];
|
|
@@ -6915,10 +7675,21 @@ function resolveBaseUrl(site, tomlBaseUrl) {
|
|
|
6915
7675
|
}
|
|
6916
7676
|
return rawBaseUrl.replace(/\/+$/, "");
|
|
6917
7677
|
}
|
|
7678
|
+
function resolveDemo(cli, toml) {
|
|
7679
|
+
if (cli.demo && cli.live) {
|
|
7680
|
+
throw new ConfigError(
|
|
7681
|
+
"--demo and --live are mutually exclusive.",
|
|
7682
|
+
"Use --demo for simulated trading or --live to force live mode, not both."
|
|
7683
|
+
);
|
|
7684
|
+
}
|
|
7685
|
+
if (cli.live === true) return false;
|
|
7686
|
+
if (cli.demo === true) return true;
|
|
7687
|
+
return process.env.OKX_DEMO === "1" || process.env.OKX_DEMO === "true" || (toml.demo ?? false);
|
|
7688
|
+
}
|
|
6918
7689
|
function loadConfig(cli) {
|
|
6919
7690
|
const toml = readTomlProfile(cli.profile);
|
|
6920
7691
|
const creds = loadCredentials(toml);
|
|
6921
|
-
const demo = cli
|
|
7692
|
+
const demo = resolveDemo(cli, toml);
|
|
6922
7693
|
const site = resolveSite(cli.site, toml.site);
|
|
6923
7694
|
const baseUrl = resolveBaseUrl(site, toml.base_url);
|
|
6924
7695
|
const rawTimeout = process.env.OKX_TIMEOUT_MS ? Number(process.env.OKX_TIMEOUT_MS) : toml.timeout_ms ?? 15e3;
|
|
@@ -6949,12 +7720,12 @@ function loadConfig(cli) {
|
|
|
6949
7720
|
verbose: cli.verbose ?? false
|
|
6950
7721
|
};
|
|
6951
7722
|
}
|
|
6952
|
-
var CACHE_FILE =
|
|
7723
|
+
var CACHE_FILE = join5(homedir3(), ".okx", "update-check.json");
|
|
6953
7724
|
var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
6954
7725
|
function readCache() {
|
|
6955
7726
|
try {
|
|
6956
|
-
if (
|
|
6957
|
-
return JSON.parse(
|
|
7727
|
+
if (existsSync4(CACHE_FILE)) {
|
|
7728
|
+
return JSON.parse(readFileSync4(CACHE_FILE, "utf-8"));
|
|
6958
7729
|
}
|
|
6959
7730
|
} catch {
|
|
6960
7731
|
}
|
|
@@ -6962,8 +7733,8 @@ function readCache() {
|
|
|
6962
7733
|
}
|
|
6963
7734
|
function writeCache(cache) {
|
|
6964
7735
|
try {
|
|
6965
|
-
|
|
6966
|
-
|
|
7736
|
+
mkdirSync5(join5(homedir3(), ".okx"), { recursive: true });
|
|
7737
|
+
writeFileSync4(CACHE_FILE, JSON.stringify(cache, null, 2), "utf-8");
|
|
6967
7738
|
} catch {
|
|
6968
7739
|
}
|
|
6969
7740
|
}
|
|
@@ -7270,26 +8041,26 @@ var Report = class {
|
|
|
7270
8041
|
this.lines.push({ key, value });
|
|
7271
8042
|
}
|
|
7272
8043
|
print() {
|
|
7273
|
-
const
|
|
8044
|
+
const sep2 = "\u2500".repeat(52);
|
|
7274
8045
|
outputLine("");
|
|
7275
|
-
outputLine(` \u2500\u2500 Diagnostic Report (copy & share) ${
|
|
8046
|
+
outputLine(` \u2500\u2500 Diagnostic Report (copy & share) ${sep2.slice(35)}`);
|
|
7276
8047
|
for (const { key, value } of this.lines) {
|
|
7277
8048
|
outputLine(` ${key.padEnd(14)} ${value}`);
|
|
7278
8049
|
}
|
|
7279
|
-
outputLine(` ${
|
|
8050
|
+
outputLine(` ${sep2}`);
|
|
7280
8051
|
outputLine("");
|
|
7281
8052
|
}
|
|
7282
8053
|
/** Write report to a file path, returns true on success. */
|
|
7283
8054
|
writeToFile(filePath) {
|
|
7284
8055
|
try {
|
|
7285
|
-
const
|
|
8056
|
+
const sep2 = "-".repeat(52);
|
|
7286
8057
|
const lines = [
|
|
7287
|
-
`-- Diagnostic Report (copy & share) ${
|
|
8058
|
+
`-- Diagnostic Report (copy & share) ${sep2.slice(35)}`
|
|
7288
8059
|
];
|
|
7289
8060
|
for (const { key, value } of this.lines) {
|
|
7290
8061
|
lines.push(`${key.padEnd(14)} ${value}`);
|
|
7291
8062
|
}
|
|
7292
|
-
lines.push(
|
|
8063
|
+
lines.push(sep2, "");
|
|
7293
8064
|
fs2.writeFileSync(filePath, lines.join("\n"), "utf8");
|
|
7294
8065
|
return true;
|
|
7295
8066
|
} catch (_e) {
|
|
@@ -7665,13 +8436,13 @@ async function checkStdioHandshake(entryPath, report) {
|
|
|
7665
8436
|
clientInfo: { name: "okx-diagnose", version: "1.0" }
|
|
7666
8437
|
}
|
|
7667
8438
|
});
|
|
7668
|
-
return new Promise((
|
|
8439
|
+
return new Promise((resolve3) => {
|
|
7669
8440
|
let settled = false;
|
|
7670
8441
|
const settle = (passed) => {
|
|
7671
8442
|
if (settled) return;
|
|
7672
8443
|
settled = true;
|
|
7673
8444
|
clearTimeout(timer);
|
|
7674
|
-
|
|
8445
|
+
resolve3(passed);
|
|
7675
8446
|
};
|
|
7676
8447
|
const child = spawn(process.execPath, [entryPath], {
|
|
7677
8448
|
stdio: ["pipe", "pipe", "pipe"],
|
|
@@ -7792,7 +8563,7 @@ async function cmdDiagnoseMcp(options = {}) {
|
|
|
7792
8563
|
|
|
7793
8564
|
// src/commands/diagnose.ts
|
|
7794
8565
|
var CLI_VERSION = readCliVersion();
|
|
7795
|
-
var GIT_HASH = true ? "
|
|
8566
|
+
var GIT_HASH = true ? "f0a9e50" : "dev";
|
|
7796
8567
|
function maskKey2(key) {
|
|
7797
8568
|
if (!key) return "(not set)";
|
|
7798
8569
|
if (key.length <= 8) return "****";
|
|
@@ -7809,7 +8580,7 @@ async function checkDns(hostname) {
|
|
|
7809
8580
|
}
|
|
7810
8581
|
async function checkSocket(createFn, successEvent, timeoutMs) {
|
|
7811
8582
|
const t0 = Date.now();
|
|
7812
|
-
return new Promise((
|
|
8583
|
+
return new Promise((resolve3) => {
|
|
7813
8584
|
const socket = createFn();
|
|
7814
8585
|
const cleanup = () => {
|
|
7815
8586
|
socket.removeAllListeners();
|
|
@@ -7817,15 +8588,15 @@ async function checkSocket(createFn, successEvent, timeoutMs) {
|
|
|
7817
8588
|
};
|
|
7818
8589
|
socket.once(successEvent, () => {
|
|
7819
8590
|
cleanup();
|
|
7820
|
-
|
|
8591
|
+
resolve3({ ok: true, ms: Date.now() - t0 });
|
|
7821
8592
|
});
|
|
7822
8593
|
socket.once("timeout", () => {
|
|
7823
8594
|
cleanup();
|
|
7824
|
-
|
|
8595
|
+
resolve3({ ok: false, ms: Date.now() - t0, error: `timed out after ${timeoutMs}ms` });
|
|
7825
8596
|
});
|
|
7826
8597
|
socket.once("error", (err) => {
|
|
7827
8598
|
cleanup();
|
|
7828
|
-
|
|
8599
|
+
resolve3({ ok: false, ms: Date.now() - t0, error: err.message });
|
|
7829
8600
|
});
|
|
7830
8601
|
});
|
|
7831
8602
|
}
|
|
@@ -8089,24 +8860,24 @@ async function runCliChecks(config, profile, outputPath) {
|
|
|
8089
8860
|
|
|
8090
8861
|
// src/commands/upgrade.ts
|
|
8091
8862
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
8092
|
-
import { readFileSync as
|
|
8093
|
-
import { dirname as
|
|
8094
|
-
import { homedir as
|
|
8863
|
+
import { readFileSync as readFileSync6, writeFileSync as writeFileSync6, mkdirSync as mkdirSync7 } from "fs";
|
|
8864
|
+
import { dirname as dirname5, join as join7 } from "path";
|
|
8865
|
+
import { homedir as homedir5 } from "os";
|
|
8095
8866
|
var PACKAGES = ["@okx_ai/okx-trade-mcp", "@okx_ai/okx-trade-cli"];
|
|
8096
|
-
var CACHE_FILE2 =
|
|
8867
|
+
var CACHE_FILE2 = join7(homedir5(), ".okx", "last_check");
|
|
8097
8868
|
var THROTTLE_MS = 12 * 60 * 60 * 1e3;
|
|
8098
|
-
var NPM_BIN =
|
|
8869
|
+
var NPM_BIN = join7(dirname5(process.execPath), process.platform === "win32" ? "npm.cmd" : "npm");
|
|
8099
8870
|
function readLastCheck() {
|
|
8100
8871
|
try {
|
|
8101
|
-
return parseInt(
|
|
8872
|
+
return parseInt(readFileSync6(CACHE_FILE2, "utf-8").trim(), 10) || 0;
|
|
8102
8873
|
} catch {
|
|
8103
8874
|
return 0;
|
|
8104
8875
|
}
|
|
8105
8876
|
}
|
|
8106
8877
|
function writeLastCheck() {
|
|
8107
8878
|
try {
|
|
8108
|
-
|
|
8109
|
-
|
|
8879
|
+
mkdirSync7(join7(homedir5(), ".okx"), { recursive: true });
|
|
8880
|
+
writeFileSync6(CACHE_FILE2, String(Math.floor(Date.now() / 1e3)), "utf-8");
|
|
8110
8881
|
} catch {
|
|
8111
8882
|
}
|
|
8112
8883
|
}
|
|
@@ -8190,13 +8961,231 @@ async function cmdUpgrade(currentVersion, options, json) {
|
|
|
8190
8961
|
}
|
|
8191
8962
|
}
|
|
8192
8963
|
|
|
8964
|
+
// src/commands/news.ts
|
|
8965
|
+
function getData(result) {
|
|
8966
|
+
return result.data;
|
|
8967
|
+
}
|
|
8968
|
+
function formatTime(ts) {
|
|
8969
|
+
if (!ts) return "-";
|
|
8970
|
+
return new Date(Number(ts)).toLocaleString();
|
|
8971
|
+
}
|
|
8972
|
+
async function cmdNewsLatest(run, opts) {
|
|
8973
|
+
const result = await run("news_get_latest", {
|
|
8974
|
+
coins: opts.coins,
|
|
8975
|
+
importance: opts.importance,
|
|
8976
|
+
begin: opts.begin,
|
|
8977
|
+
end: opts.end,
|
|
8978
|
+
language: opts.language,
|
|
8979
|
+
detailLvl: opts.detailLvl,
|
|
8980
|
+
limit: opts.limit,
|
|
8981
|
+
after: opts.after
|
|
8982
|
+
});
|
|
8983
|
+
const raw = getData(result);
|
|
8984
|
+
const pageData = raw?.[0];
|
|
8985
|
+
if (opts.json) return printJson(pageData ?? null);
|
|
8986
|
+
const items = pageData?.["details"] ?? [];
|
|
8987
|
+
printTable(
|
|
8988
|
+
items.map((n) => ({
|
|
8989
|
+
id: n["id"],
|
|
8990
|
+
time: formatTime(n["cTime"] ?? n["createTime"]),
|
|
8991
|
+
platforms: n["platformList"]?.join(",") ?? "-",
|
|
8992
|
+
title: String(n["title"] ?? "").slice(0, 80)
|
|
8993
|
+
}))
|
|
8994
|
+
);
|
|
8995
|
+
const cursor = pageData?.["nextCursor"];
|
|
8996
|
+
if (cursor) outputLine(`[next] --after ${cursor}`);
|
|
8997
|
+
}
|
|
8998
|
+
async function cmdNewsImportant(run, opts) {
|
|
8999
|
+
const result = await run("news_get_latest", {
|
|
9000
|
+
coins: opts.coins,
|
|
9001
|
+
importance: "high",
|
|
9002
|
+
begin: opts.begin,
|
|
9003
|
+
end: opts.end,
|
|
9004
|
+
language: opts.language,
|
|
9005
|
+
detailLvl: opts.detailLvl,
|
|
9006
|
+
limit: opts.limit
|
|
9007
|
+
});
|
|
9008
|
+
const raw = getData(result);
|
|
9009
|
+
const pageData = raw?.[0];
|
|
9010
|
+
if (opts.json) return printJson(pageData ?? null);
|
|
9011
|
+
const items = pageData?.["details"] ?? [];
|
|
9012
|
+
printTable(
|
|
9013
|
+
items.map((n) => ({
|
|
9014
|
+
id: n["id"],
|
|
9015
|
+
time: formatTime(n["cTime"] ?? n["createTime"]),
|
|
9016
|
+
importance: n["importance"] ?? "-",
|
|
9017
|
+
platforms: n["platformList"]?.join(",") ?? "-",
|
|
9018
|
+
title: String(n["title"] ?? "").slice(0, 80)
|
|
9019
|
+
}))
|
|
9020
|
+
);
|
|
9021
|
+
}
|
|
9022
|
+
async function cmdNewsByCoin(run, coins, opts) {
|
|
9023
|
+
const result = await run("news_get_by_coin", {
|
|
9024
|
+
coins,
|
|
9025
|
+
importance: opts.importance,
|
|
9026
|
+
begin: opts.begin,
|
|
9027
|
+
end: opts.end,
|
|
9028
|
+
language: opts.language,
|
|
9029
|
+
detailLvl: opts.detailLvl,
|
|
9030
|
+
limit: opts.limit
|
|
9031
|
+
});
|
|
9032
|
+
const raw = getData(result);
|
|
9033
|
+
const pageData = raw?.[0];
|
|
9034
|
+
if (opts.json) return printJson(pageData ?? null);
|
|
9035
|
+
const items = pageData?.["details"] ?? [];
|
|
9036
|
+
printTable(
|
|
9037
|
+
items.map((n) => ({
|
|
9038
|
+
id: n["id"],
|
|
9039
|
+
time: formatTime(n["cTime"] ?? n["createTime"]),
|
|
9040
|
+
coins: n["ccyList"]?.join(",") ?? "-",
|
|
9041
|
+
platforms: n["platformList"]?.join(",") ?? "-",
|
|
9042
|
+
title: String(n["title"] ?? "").slice(0, 80)
|
|
9043
|
+
}))
|
|
9044
|
+
);
|
|
9045
|
+
}
|
|
9046
|
+
async function cmdNewsSearch(run, keyword, opts) {
|
|
9047
|
+
const result = await run("news_search", {
|
|
9048
|
+
keyword: keyword || void 0,
|
|
9049
|
+
coins: opts.coins,
|
|
9050
|
+
importance: opts.importance,
|
|
9051
|
+
sentiment: opts.sentiment,
|
|
9052
|
+
sortBy: opts.sortBy,
|
|
9053
|
+
begin: opts.begin,
|
|
9054
|
+
end: opts.end,
|
|
9055
|
+
language: opts.language,
|
|
9056
|
+
detailLvl: opts.detailLvl,
|
|
9057
|
+
limit: opts.limit,
|
|
9058
|
+
after: opts.after
|
|
9059
|
+
});
|
|
9060
|
+
const raw = getData(result);
|
|
9061
|
+
const pageData = raw?.[0];
|
|
9062
|
+
if (opts.json) return printJson(pageData ?? null);
|
|
9063
|
+
const items = pageData?.["details"] ?? [];
|
|
9064
|
+
printTable(
|
|
9065
|
+
items.map((n) => ({
|
|
9066
|
+
id: n["id"],
|
|
9067
|
+
time: formatTime(n["cTime"] ?? n["createTime"]),
|
|
9068
|
+
platforms: n["platformList"]?.join(",") ?? "-",
|
|
9069
|
+
title: String(n["title"] ?? "").slice(0, 80)
|
|
9070
|
+
}))
|
|
9071
|
+
);
|
|
9072
|
+
const cursor = pageData?.["nextCursor"];
|
|
9073
|
+
if (cursor) outputLine(`[next] --after ${cursor}`);
|
|
9074
|
+
}
|
|
9075
|
+
async function cmdNewsDetail(run, id, opts) {
|
|
9076
|
+
const result = await run("news_get_detail", { id, language: opts.language });
|
|
9077
|
+
const items = getData(result);
|
|
9078
|
+
if (opts.json) return printJson(items);
|
|
9079
|
+
const article = items?.[0];
|
|
9080
|
+
if (!article) {
|
|
9081
|
+
outputLine("Article not found.");
|
|
9082
|
+
return;
|
|
9083
|
+
}
|
|
9084
|
+
const rawContent = article["content"] ? String(article["content"]) : void 0;
|
|
9085
|
+
let content;
|
|
9086
|
+
if (rawContent && rawContent.length > 500) {
|
|
9087
|
+
content = rawContent.slice(0, 500) + "... (use --json for full text)";
|
|
9088
|
+
} else {
|
|
9089
|
+
content = rawContent;
|
|
9090
|
+
}
|
|
9091
|
+
printKv({
|
|
9092
|
+
id: article["id"],
|
|
9093
|
+
title: article["title"],
|
|
9094
|
+
platforms: article["platformList"]?.join(", ") ?? "-",
|
|
9095
|
+
time: formatTime(article["cTime"] ?? article["createTime"]),
|
|
9096
|
+
sourceUrl: article["sourceUrl"],
|
|
9097
|
+
coins: article["ccyList"]?.join(", ") ?? "-",
|
|
9098
|
+
importance: article["importance"] ?? "-",
|
|
9099
|
+
summary: article["summary"] ?? "(see content)",
|
|
9100
|
+
content
|
|
9101
|
+
});
|
|
9102
|
+
}
|
|
9103
|
+
async function cmdNewsDomains(run, opts) {
|
|
9104
|
+
const result = await run("news_get_domains", {});
|
|
9105
|
+
const raw = getData(result);
|
|
9106
|
+
const items = raw?.[0]?.["platform"] ?? [];
|
|
9107
|
+
if (opts.json) return printJson(items);
|
|
9108
|
+
outputLine("Available news source domains:");
|
|
9109
|
+
(items ?? []).forEach((d) => outputLine(` ${d}`));
|
|
9110
|
+
}
|
|
9111
|
+
async function cmdNewsCoinSentiment(run, coins, opts) {
|
|
9112
|
+
const result = await run("news_get_coin_sentiment", {
|
|
9113
|
+
coins,
|
|
9114
|
+
period: opts.period
|
|
9115
|
+
});
|
|
9116
|
+
const raw = getData(result);
|
|
9117
|
+
if (opts.json) return printJson(raw);
|
|
9118
|
+
const items = raw?.[0]?.["details"] ?? [];
|
|
9119
|
+
printTable(
|
|
9120
|
+
items.map((c) => {
|
|
9121
|
+
const snt = c["sentiment"];
|
|
9122
|
+
return {
|
|
9123
|
+
symbol: c["ccy"],
|
|
9124
|
+
label: snt?.["label"] ?? "-",
|
|
9125
|
+
bullish: snt?.["bullishRatio"] ?? "-",
|
|
9126
|
+
bearish: snt?.["bearishRatio"] ?? "-",
|
|
9127
|
+
mentions: c["mentionCnt"]
|
|
9128
|
+
};
|
|
9129
|
+
})
|
|
9130
|
+
);
|
|
9131
|
+
}
|
|
9132
|
+
async function cmdNewsCoinTrend(run, coin, opts) {
|
|
9133
|
+
const result = await run("news_get_coin_sentiment", {
|
|
9134
|
+
coins: coin,
|
|
9135
|
+
period: opts.period,
|
|
9136
|
+
trendPoints: opts.points
|
|
9137
|
+
});
|
|
9138
|
+
const raw = getData(result);
|
|
9139
|
+
if (opts.json) return printJson(raw);
|
|
9140
|
+
const items = raw?.[0]?.["details"] ?? [];
|
|
9141
|
+
const coinData = items?.[0];
|
|
9142
|
+
if (!coinData) {
|
|
9143
|
+
outputLine("No trend data.");
|
|
9144
|
+
return;
|
|
9145
|
+
}
|
|
9146
|
+
const trend = coinData["trend"] ?? [];
|
|
9147
|
+
outputLine(`Sentiment trend for ${coin} (period: ${opts.period ?? "1h"}):`);
|
|
9148
|
+
printTable(
|
|
9149
|
+
trend.map((t) => ({
|
|
9150
|
+
time: formatTime(t["ts"]),
|
|
9151
|
+
bullish: t["bullishRatio"],
|
|
9152
|
+
bearish: t["bearishRatio"],
|
|
9153
|
+
mentions: t["mentionCnt"]
|
|
9154
|
+
}))
|
|
9155
|
+
);
|
|
9156
|
+
}
|
|
9157
|
+
async function cmdNewsSentimentRank(run, opts) {
|
|
9158
|
+
const result = await run("news_get_sentiment_ranking", {
|
|
9159
|
+
period: opts.period,
|
|
9160
|
+
sortBy: opts.sortBy,
|
|
9161
|
+
limit: opts.limit
|
|
9162
|
+
});
|
|
9163
|
+
const raw = getData(result);
|
|
9164
|
+
if (opts.json) return printJson(raw);
|
|
9165
|
+
const items = raw?.[0]?.["details"] ?? [];
|
|
9166
|
+
printTable(
|
|
9167
|
+
items.map((c, i) => {
|
|
9168
|
+
const snt = c["sentiment"];
|
|
9169
|
+
return {
|
|
9170
|
+
rank: i + 1,
|
|
9171
|
+
symbol: c["ccy"],
|
|
9172
|
+
label: snt?.["label"] ?? "-",
|
|
9173
|
+
bullish: snt?.["bullishRatio"] ?? "-",
|
|
9174
|
+
bearish: snt?.["bearishRatio"] ?? "-",
|
|
9175
|
+
mentions: c["mentionCnt"]
|
|
9176
|
+
};
|
|
9177
|
+
})
|
|
9178
|
+
);
|
|
9179
|
+
}
|
|
9180
|
+
|
|
8193
9181
|
// src/config/loader.ts
|
|
8194
9182
|
function loadProfileConfig(opts) {
|
|
8195
9183
|
return loadConfig({
|
|
8196
9184
|
profile: opts.profile,
|
|
8197
9185
|
modules: opts.modules,
|
|
8198
9186
|
readOnly: opts.readOnly ?? false,
|
|
8199
|
-
demo: opts.demo
|
|
9187
|
+
demo: opts.demo,
|
|
9188
|
+
live: opts.live,
|
|
8200
9189
|
site: opts.site,
|
|
8201
9190
|
userAgent: opts.userAgent,
|
|
8202
9191
|
sourceTag: opts.sourceTag,
|
|
@@ -8286,7 +9275,11 @@ var HELP_TREE = {
|
|
|
8286
9275
|
},
|
|
8287
9276
|
"stock-tokens": {
|
|
8288
9277
|
usage: "okx market stock-tokens [--instType <SPOT|SWAP>] [--instId <id>]",
|
|
8289
|
-
description: "List all stock token instruments (instCategory=3, e.g. AAPL-USDT-SWAP)"
|
|
9278
|
+
description: "[Deprecated: use instruments-by-category --instCategory 3] List all stock token instruments (instCategory=3, e.g. AAPL-USDT-SWAP)"
|
|
9279
|
+
},
|
|
9280
|
+
"instruments-by-category": {
|
|
9281
|
+
usage: "okx market instruments-by-category --instCategory <4|5|6|7> [--instType <SPOT|SWAP>] [--instId <id>]",
|
|
9282
|
+
description: "List instruments by asset category: 4=Metals (gold/silver), 5=Commodities (oil/gas), 6=Forex (EUR/USD), 7=Bonds"
|
|
8290
9283
|
}
|
|
8291
9284
|
}
|
|
8292
9285
|
},
|
|
@@ -8791,21 +9784,67 @@ var HELP_TREE = {
|
|
|
8791
9784
|
},
|
|
8792
9785
|
diagnose: {
|
|
8793
9786
|
description: "Run network / MCP server diagnostics",
|
|
8794
|
-
usage: "okx diagnose [--cli | --mcp | --all] [--profile <name>] [--demo] [--output <file>]"
|
|
9787
|
+
usage: "okx diagnose [--cli | --mcp | --all] [--profile <name>] [--demo | --live] [--output <file>]"
|
|
8795
9788
|
},
|
|
8796
9789
|
upgrade: {
|
|
8797
9790
|
description: "Upgrade okx CLI and MCP server to the latest stable version",
|
|
8798
9791
|
usage: "okx upgrade [--check] [--beta] [--force] [--json]"
|
|
9792
|
+
},
|
|
9793
|
+
news: {
|
|
9794
|
+
description: "Orbit News \u2014 crypto news feed, coin sentiment, and trend analysis",
|
|
9795
|
+
commands: {
|
|
9796
|
+
latest: {
|
|
9797
|
+
usage: "okx news latest [--limit <n>] [--lang <zh_CN|en_US>]",
|
|
9798
|
+
description: "Get latest crypto news"
|
|
9799
|
+
},
|
|
9800
|
+
important: {
|
|
9801
|
+
usage: "okx news important [--limit <n>] [--lang <zh_CN|en_US>]",
|
|
9802
|
+
description: "Get important / high-impact crypto news"
|
|
9803
|
+
},
|
|
9804
|
+
"by-coin": {
|
|
9805
|
+
usage: "okx news by-coin --coins <BTC,ETH,...> [--limit <n>] [--lang <zh_CN|en_US>]",
|
|
9806
|
+
description: "Get news filtered by coin(s)"
|
|
9807
|
+
},
|
|
9808
|
+
"by-sentiment": {
|
|
9809
|
+
usage: "okx news by-sentiment --sentiment <bullish|bearish|neutral> [--limit <n>] [--lang <zh_CN|en_US>]",
|
|
9810
|
+
description: "Get news filtered by sentiment"
|
|
9811
|
+
},
|
|
9812
|
+
search: {
|
|
9813
|
+
usage: "okx news search --keyword <text> [--limit <n>] [--lang <zh_CN|en_US>]",
|
|
9814
|
+
description: "Search news by keyword"
|
|
9815
|
+
},
|
|
9816
|
+
detail: {
|
|
9817
|
+
usage: "okx news detail <articleId> [--lang <zh_CN|en_US>]",
|
|
9818
|
+
description: "Get full article detail by ID"
|
|
9819
|
+
},
|
|
9820
|
+
domains: {
|
|
9821
|
+
usage: "okx news domains",
|
|
9822
|
+
description: "List available news source domains"
|
|
9823
|
+
},
|
|
9824
|
+
"coin-sentiment": {
|
|
9825
|
+
usage: "okx news coin-sentiment --coins <BTC,ETH,...> [--period <1h|24h>]",
|
|
9826
|
+
description: "Get sentiment score for specific coin(s)"
|
|
9827
|
+
},
|
|
9828
|
+
"coin-trend": {
|
|
9829
|
+
usage: "okx news coin-trend <coin> [--period <1h|24h>] [--points <n>]",
|
|
9830
|
+
description: "Get sentiment trend data points for a coin"
|
|
9831
|
+
},
|
|
9832
|
+
"sentiment-rank": {
|
|
9833
|
+
usage: "okx news sentiment-rank [--period <1h|24h>] [--sort-by <hot|bullish|bearish>] [--limit <n>]",
|
|
9834
|
+
description: "Get coin ranking by social hotness or sentiment direction"
|
|
9835
|
+
}
|
|
9836
|
+
}
|
|
8799
9837
|
}
|
|
8800
9838
|
};
|
|
8801
9839
|
function printGlobalHelp() {
|
|
8802
9840
|
const lines = [
|
|
8803
9841
|
"",
|
|
8804
|
-
`Usage: okx [--profile <name>] [--demo] [--json] <module> <action> [args...]`,
|
|
9842
|
+
`Usage: okx [--profile <name>] [--demo | --live] [--json] <module> <action> [args...]`,
|
|
8805
9843
|
"",
|
|
8806
9844
|
"Global Options:",
|
|
8807
9845
|
` --profile <name> Use a named profile from ${configFilePath()}`,
|
|
8808
9846
|
" --demo Use simulated trading (demo) mode",
|
|
9847
|
+
" --live Force live trading mode (overrides profile demo=true; mutually exclusive with --demo)",
|
|
8809
9848
|
" --json Output raw JSON",
|
|
8810
9849
|
" --verbose Show detailed network request/response info (stderr)",
|
|
8811
9850
|
" --version, -v Show version",
|
|
@@ -8997,6 +10036,7 @@ var CLI_OPTIONS = {
|
|
|
8997
10036
|
live: { type: "boolean", default: false },
|
|
8998
10037
|
// market extras
|
|
8999
10038
|
instType: { type: "string" },
|
|
10039
|
+
instCategory: { type: "string" },
|
|
9000
10040
|
quoteCcy: { type: "string" },
|
|
9001
10041
|
// account extras
|
|
9002
10042
|
archive: { type: "boolean", default: false },
|
|
@@ -9077,6 +10117,19 @@ var CLI_OPTIONS = {
|
|
|
9077
10117
|
params: { type: "string" },
|
|
9078
10118
|
list: { type: "boolean", default: false },
|
|
9079
10119
|
"backtest-time": { type: "string" },
|
|
10120
|
+
// news
|
|
10121
|
+
coins: { type: "string" },
|
|
10122
|
+
sentiment: { type: "string" },
|
|
10123
|
+
importance: { type: "string" },
|
|
10124
|
+
keyword: { type: "string" },
|
|
10125
|
+
"detail-lvl": { type: "string" },
|
|
10126
|
+
period: { type: "string" },
|
|
10127
|
+
points: { type: "string" },
|
|
10128
|
+
"sort-by": { type: "string" },
|
|
10129
|
+
// skill marketplace
|
|
10130
|
+
categories: { type: "string" },
|
|
10131
|
+
dir: { type: "string" },
|
|
10132
|
+
page: { type: "string" },
|
|
9080
10133
|
// diagnostics — cli/mcp/all/output are diagnose-specific; verbose is shared
|
|
9081
10134
|
verbose: { type: "boolean", default: false },
|
|
9082
10135
|
mcp: { type: "boolean", default: false },
|
|
@@ -9112,12 +10165,12 @@ function parseCli(argv) {
|
|
|
9112
10165
|
}
|
|
9113
10166
|
|
|
9114
10167
|
// src/commands/market.ts
|
|
9115
|
-
function
|
|
10168
|
+
function getData2(result) {
|
|
9116
10169
|
return result.data;
|
|
9117
10170
|
}
|
|
9118
10171
|
async function cmdMarketInstruments(run, opts) {
|
|
9119
10172
|
const result = await run("market_get_instruments", { instType: opts.instType, instId: opts.instId });
|
|
9120
|
-
const items =
|
|
10173
|
+
const items = getData2(result);
|
|
9121
10174
|
if (opts.json) return printJson(items);
|
|
9122
10175
|
printTable(
|
|
9123
10176
|
(items ?? []).slice(0, 50).map((t) => ({
|
|
@@ -9132,7 +10185,7 @@ async function cmdMarketInstruments(run, opts) {
|
|
|
9132
10185
|
}
|
|
9133
10186
|
async function cmdMarketFundingRate(run, instId, opts) {
|
|
9134
10187
|
const result = await run("market_get_funding_rate", { instId, history: opts.history, limit: opts.limit });
|
|
9135
|
-
const items =
|
|
10188
|
+
const items = getData2(result);
|
|
9136
10189
|
if (opts.json) return printJson(items);
|
|
9137
10190
|
if (opts.history) {
|
|
9138
10191
|
printTable(
|
|
@@ -9160,7 +10213,7 @@ async function cmdMarketFundingRate(run, instId, opts) {
|
|
|
9160
10213
|
}
|
|
9161
10214
|
async function cmdMarketMarkPrice(run, opts) {
|
|
9162
10215
|
const result = await run("market_get_mark_price", { instType: opts.instType, instId: opts.instId });
|
|
9163
|
-
const items =
|
|
10216
|
+
const items = getData2(result);
|
|
9164
10217
|
if (opts.json) return printJson(items);
|
|
9165
10218
|
printTable(
|
|
9166
10219
|
(items ?? []).map((r) => ({
|
|
@@ -9173,7 +10226,7 @@ async function cmdMarketMarkPrice(run, opts) {
|
|
|
9173
10226
|
}
|
|
9174
10227
|
async function cmdMarketTrades(run, instId, opts) {
|
|
9175
10228
|
const result = await run("market_get_trades", { instId, limit: opts.limit });
|
|
9176
|
-
const items =
|
|
10229
|
+
const items = getData2(result);
|
|
9177
10230
|
if (opts.json) return printJson(items);
|
|
9178
10231
|
printTable(
|
|
9179
10232
|
(items ?? []).map((t) => ({
|
|
@@ -9187,7 +10240,7 @@ async function cmdMarketTrades(run, instId, opts) {
|
|
|
9187
10240
|
}
|
|
9188
10241
|
async function cmdMarketIndexTicker(run, opts) {
|
|
9189
10242
|
const result = await run("market_get_index_ticker", { instId: opts.instId, quoteCcy: opts.quoteCcy });
|
|
9190
|
-
const items =
|
|
10243
|
+
const items = getData2(result);
|
|
9191
10244
|
if (opts.json) return printJson(items);
|
|
9192
10245
|
printTable(
|
|
9193
10246
|
(items ?? []).map((t) => ({
|
|
@@ -9201,7 +10254,7 @@ async function cmdMarketIndexTicker(run, opts) {
|
|
|
9201
10254
|
}
|
|
9202
10255
|
async function cmdMarketIndexCandles(run, instId, opts) {
|
|
9203
10256
|
const result = await run("market_get_index_candles", { instId, bar: opts.bar, limit: opts.limit, history: opts.history });
|
|
9204
|
-
const candles =
|
|
10257
|
+
const candles = getData2(result);
|
|
9205
10258
|
if (opts.json) return printJson(candles);
|
|
9206
10259
|
printTable(
|
|
9207
10260
|
(candles ?? []).map(([ts, o, h, l, c]) => ({
|
|
@@ -9215,7 +10268,7 @@ async function cmdMarketIndexCandles(run, instId, opts) {
|
|
|
9215
10268
|
}
|
|
9216
10269
|
async function cmdMarketPriceLimit(run, instId, json) {
|
|
9217
10270
|
const result = await run("market_get_price_limit", { instId });
|
|
9218
|
-
const items =
|
|
10271
|
+
const items = getData2(result);
|
|
9219
10272
|
if (json) return printJson(items);
|
|
9220
10273
|
const r = items?.[0];
|
|
9221
10274
|
if (!r) {
|
|
@@ -9231,7 +10284,7 @@ async function cmdMarketPriceLimit(run, instId, json) {
|
|
|
9231
10284
|
}
|
|
9232
10285
|
async function cmdMarketOpenInterest(run, opts) {
|
|
9233
10286
|
const result = await run("market_get_open_interest", { instType: opts.instType, instId: opts.instId });
|
|
9234
|
-
const items =
|
|
10287
|
+
const items = getData2(result);
|
|
9235
10288
|
if (opts.json) return printJson(items);
|
|
9236
10289
|
printTable(
|
|
9237
10290
|
(items ?? []).map((r) => ({
|
|
@@ -9244,7 +10297,7 @@ async function cmdMarketOpenInterest(run, opts) {
|
|
|
9244
10297
|
}
|
|
9245
10298
|
async function cmdMarketTicker(run, instId, json) {
|
|
9246
10299
|
const result = await run("market_get_ticker", { instId });
|
|
9247
|
-
const items =
|
|
10300
|
+
const items = getData2(result);
|
|
9248
10301
|
if (json) return printJson(items);
|
|
9249
10302
|
if (!items?.length) {
|
|
9250
10303
|
outputLine("No data");
|
|
@@ -9268,7 +10321,7 @@ async function cmdMarketTicker(run, instId, json) {
|
|
|
9268
10321
|
}
|
|
9269
10322
|
async function cmdMarketTickers(run, instType, json) {
|
|
9270
10323
|
const result = await run("market_get_tickers", { instType });
|
|
9271
|
-
const items =
|
|
10324
|
+
const items = getData2(result);
|
|
9272
10325
|
if (json) return printJson(items);
|
|
9273
10326
|
printTable(
|
|
9274
10327
|
(items ?? []).map((t) => ({
|
|
@@ -9282,7 +10335,7 @@ async function cmdMarketTickers(run, instType, json) {
|
|
|
9282
10335
|
}
|
|
9283
10336
|
async function cmdMarketOrderbook(run, instId, sz, json) {
|
|
9284
10337
|
const result = await run("market_get_orderbook", { instId, sz });
|
|
9285
|
-
const data =
|
|
10338
|
+
const data = getData2(result);
|
|
9286
10339
|
if (json) return printJson(data);
|
|
9287
10340
|
const book = data[0];
|
|
9288
10341
|
if (!book) {
|
|
@@ -9299,7 +10352,7 @@ async function cmdMarketOrderbook(run, instId, sz, json) {
|
|
|
9299
10352
|
}
|
|
9300
10353
|
async function cmdMarketCandles(run, instId, opts) {
|
|
9301
10354
|
const result = await run("market_get_candles", { instId, bar: opts.bar, limit: opts.limit, after: opts.after, before: opts.before });
|
|
9302
|
-
const candles =
|
|
10355
|
+
const candles = getData2(result);
|
|
9303
10356
|
if (opts.json) return printJson(candles);
|
|
9304
10357
|
printTable(
|
|
9305
10358
|
(candles ?? []).map(([ts, o, h, l, c, vol]) => ({
|
|
@@ -9323,7 +10376,7 @@ async function cmdMarketIndicator(run, indicator, instId, opts) {
|
|
|
9323
10376
|
limit: opts.limit,
|
|
9324
10377
|
backtestTime: opts.backtestTime
|
|
9325
10378
|
});
|
|
9326
|
-
const outerArray =
|
|
10379
|
+
const outerArray = getData2(result);
|
|
9327
10380
|
if (opts.json) return printJson(outerArray);
|
|
9328
10381
|
if (!outerArray?.length) {
|
|
9329
10382
|
process.stdout.write("No data\n");
|
|
@@ -9360,9 +10413,40 @@ async function cmdMarketIndicator(run, indicator, instId, opts) {
|
|
|
9360
10413
|
}
|
|
9361
10414
|
}
|
|
9362
10415
|
}
|
|
10416
|
+
async function cmdMarketInstrumentsByCategory(run, opts) {
|
|
10417
|
+
const result = await run("market_get_instruments_by_category", {
|
|
10418
|
+
instCategory: opts.instCategory,
|
|
10419
|
+
instType: opts.instType,
|
|
10420
|
+
instId: opts.instId
|
|
10421
|
+
});
|
|
10422
|
+
const items = getData2(result);
|
|
10423
|
+
if (opts.json) return printJson(items);
|
|
10424
|
+
const CATEGORY_LABELS = {
|
|
10425
|
+
"3": "Stock tokens",
|
|
10426
|
+
"4": "Metals",
|
|
10427
|
+
"5": "Commodities",
|
|
10428
|
+
"6": "Forex",
|
|
10429
|
+
"7": "Bonds"
|
|
10430
|
+
};
|
|
10431
|
+
const label = CATEGORY_LABELS[opts.instCategory] ?? opts.instCategory;
|
|
10432
|
+
process.stdout.write(`instCategory=${opts.instCategory} (${label}) \u2014 ${items?.length ?? 0} instruments
|
|
10433
|
+
|
|
10434
|
+
`);
|
|
10435
|
+
printTable(
|
|
10436
|
+
(items ?? []).slice(0, 50).map((t) => ({
|
|
10437
|
+
instId: t["instId"],
|
|
10438
|
+
instCategory: t["instCategory"],
|
|
10439
|
+
ctVal: t["ctVal"],
|
|
10440
|
+
lotSz: t["lotSz"],
|
|
10441
|
+
minSz: t["minSz"],
|
|
10442
|
+
tickSz: t["tickSz"],
|
|
10443
|
+
state: t["state"]
|
|
10444
|
+
}))
|
|
10445
|
+
);
|
|
10446
|
+
}
|
|
9363
10447
|
async function cmdMarketStockTokens(run, opts) {
|
|
9364
10448
|
const result = await run("market_get_stock_tokens", { instType: opts.instType, instId: opts.instId });
|
|
9365
|
-
const items =
|
|
10449
|
+
const items = getData2(result);
|
|
9366
10450
|
if (opts.json) return printJson(items);
|
|
9367
10451
|
printTable(
|
|
9368
10452
|
(items ?? []).slice(0, 50).map((t) => ({
|
|
@@ -9381,12 +10465,12 @@ async function cmdMarketStockTokens(run, opts) {
|
|
|
9381
10465
|
import * as fs6 from "fs";
|
|
9382
10466
|
import * as path4 from "path";
|
|
9383
10467
|
import * as os5 from "os";
|
|
9384
|
-
function
|
|
10468
|
+
function getData3(result) {
|
|
9385
10469
|
return result.data;
|
|
9386
10470
|
}
|
|
9387
10471
|
async function cmdAccountBalance(run, ccy, json) {
|
|
9388
10472
|
const result = await run("account_get_balance", { ccy });
|
|
9389
|
-
const data =
|
|
10473
|
+
const data = getData3(result);
|
|
9390
10474
|
if (json) return printJson(data);
|
|
9391
10475
|
const details = data?.[0]?.["details"] ?? [];
|
|
9392
10476
|
const rows = details.filter((d) => Number(d["eq"]) > 0).map((d) => ({
|
|
@@ -9439,7 +10523,7 @@ async function cmdAccountAssetBalance(run, ccy, json, showValuation) {
|
|
|
9439
10523
|
}
|
|
9440
10524
|
async function cmdAccountPositions(run, opts) {
|
|
9441
10525
|
const result = await run("account_get_positions", { instType: opts.instType, instId: opts.instId });
|
|
9442
|
-
const positions =
|
|
10526
|
+
const positions = getData3(result);
|
|
9443
10527
|
if (opts.json) return printJson(positions);
|
|
9444
10528
|
const open = (positions ?? []).filter((p) => Number(p["pos"]) !== 0);
|
|
9445
10529
|
if (!open.length) {
|
|
@@ -9461,7 +10545,7 @@ async function cmdAccountPositions(run, opts) {
|
|
|
9461
10545
|
async function cmdAccountBills(run, opts) {
|
|
9462
10546
|
const toolName = opts.archive ? "account_get_bills_archive" : "account_get_bills";
|
|
9463
10547
|
const result = await run(toolName, { instType: opts.instType, ccy: opts.ccy, limit: opts.limit });
|
|
9464
|
-
const bills =
|
|
10548
|
+
const bills = getData3(result);
|
|
9465
10549
|
if (opts.json) return printJson(bills);
|
|
9466
10550
|
printTable(
|
|
9467
10551
|
(bills ?? []).map((b) => ({
|
|
@@ -9477,7 +10561,7 @@ async function cmdAccountBills(run, opts) {
|
|
|
9477
10561
|
}
|
|
9478
10562
|
async function cmdAccountFees(run, opts) {
|
|
9479
10563
|
const result = await run("account_get_trade_fee", { instType: opts.instType, instId: opts.instId });
|
|
9480
|
-
const data =
|
|
10564
|
+
const data = getData3(result);
|
|
9481
10565
|
if (opts.json) return printJson(data);
|
|
9482
10566
|
const fee = data?.[0];
|
|
9483
10567
|
if (!fee) {
|
|
@@ -9495,7 +10579,7 @@ async function cmdAccountFees(run, opts) {
|
|
|
9495
10579
|
}
|
|
9496
10580
|
async function cmdAccountConfig(run, json) {
|
|
9497
10581
|
const result = await run("account_get_config", {});
|
|
9498
|
-
const data =
|
|
10582
|
+
const data = getData3(result);
|
|
9499
10583
|
if (json) return printJson(data);
|
|
9500
10584
|
const cfg = data?.[0];
|
|
9501
10585
|
if (!cfg) {
|
|
@@ -9514,14 +10598,14 @@ async function cmdAccountConfig(run, json) {
|
|
|
9514
10598
|
}
|
|
9515
10599
|
async function cmdAccountSetPositionMode(run, posMode, json) {
|
|
9516
10600
|
const result = await run("account_set_position_mode", { posMode });
|
|
9517
|
-
const data =
|
|
10601
|
+
const data = getData3(result);
|
|
9518
10602
|
if (json) return printJson(data);
|
|
9519
10603
|
const r = data?.[0];
|
|
9520
10604
|
outputLine(`Position mode set: ${r?.["posMode"]}`);
|
|
9521
10605
|
}
|
|
9522
10606
|
async function cmdAccountMaxSize(run, opts) {
|
|
9523
10607
|
const result = await run("account_get_max_size", { instId: opts.instId, tdMode: opts.tdMode, px: opts.px });
|
|
9524
|
-
const data =
|
|
10608
|
+
const data = getData3(result);
|
|
9525
10609
|
if (opts.json) return printJson(data);
|
|
9526
10610
|
const r = data?.[0];
|
|
9527
10611
|
if (!r) {
|
|
@@ -9532,7 +10616,7 @@ async function cmdAccountMaxSize(run, opts) {
|
|
|
9532
10616
|
}
|
|
9533
10617
|
async function cmdAccountMaxAvailSize(run, opts) {
|
|
9534
10618
|
const result = await run("account_get_max_avail_size", { instId: opts.instId, tdMode: opts.tdMode });
|
|
9535
|
-
const data =
|
|
10619
|
+
const data = getData3(result);
|
|
9536
10620
|
if (opts.json) return printJson(data);
|
|
9537
10621
|
const r = data?.[0];
|
|
9538
10622
|
if (!r) {
|
|
@@ -9543,7 +10627,7 @@ async function cmdAccountMaxAvailSize(run, opts) {
|
|
|
9543
10627
|
}
|
|
9544
10628
|
async function cmdAccountMaxWithdrawal(run, ccy, json) {
|
|
9545
10629
|
const result = await run("account_get_max_withdrawal", { ccy });
|
|
9546
|
-
const data =
|
|
10630
|
+
const data = getData3(result);
|
|
9547
10631
|
if (json) return printJson(data);
|
|
9548
10632
|
printTable(
|
|
9549
10633
|
(data ?? []).map((r) => ({
|
|
@@ -9555,7 +10639,7 @@ async function cmdAccountMaxWithdrawal(run, ccy, json) {
|
|
|
9555
10639
|
}
|
|
9556
10640
|
async function cmdAccountPositionsHistory(run, opts) {
|
|
9557
10641
|
const result = await run("account_get_positions_history", { instType: opts.instType, instId: opts.instId, limit: opts.limit });
|
|
9558
|
-
const data =
|
|
10642
|
+
const data = getData3(result);
|
|
9559
10643
|
if (opts.json) return printJson(data);
|
|
9560
10644
|
printTable(
|
|
9561
10645
|
(data ?? []).map((p) => ({
|
|
@@ -9577,7 +10661,7 @@ async function cmdAccountTransfer(run, opts) {
|
|
|
9577
10661
|
type: opts.transferType,
|
|
9578
10662
|
subAcct: opts.subAcct
|
|
9579
10663
|
});
|
|
9580
|
-
const data =
|
|
10664
|
+
const data = getData3(result);
|
|
9581
10665
|
if (opts.json) return printJson(data);
|
|
9582
10666
|
const r = data?.[0];
|
|
9583
10667
|
outputLine(`Transfer: ${r?.["transId"]} (${r?.["ccy"]} ${r?.["amt"]})`);
|
|
@@ -9637,7 +10721,7 @@ function cmdAccountAudit(opts) {
|
|
|
9637
10721
|
}
|
|
9638
10722
|
|
|
9639
10723
|
// src/commands/spot.ts
|
|
9640
|
-
function
|
|
10724
|
+
function getData4(result) {
|
|
9641
10725
|
return result.data;
|
|
9642
10726
|
}
|
|
9643
10727
|
function emitWriteResult(item, label, idKey) {
|
|
@@ -9661,7 +10745,7 @@ function emitBatchResults(items) {
|
|
|
9661
10745
|
}
|
|
9662
10746
|
async function cmdSpotOrders(run, opts) {
|
|
9663
10747
|
const result = await run("spot_get_orders", { instId: opts.instId, status: opts.status });
|
|
9664
|
-
const orders =
|
|
10748
|
+
const orders = getData4(result);
|
|
9665
10749
|
if (opts.json) return printJson(orders);
|
|
9666
10750
|
printTable(
|
|
9667
10751
|
(orders ?? []).map((o) => ({
|
|
@@ -9690,7 +10774,7 @@ async function cmdSpotPlace(run, opts) {
|
|
|
9690
10774
|
slTriggerPx: opts.slTriggerPx,
|
|
9691
10775
|
slOrdPx: opts.slOrdPx
|
|
9692
10776
|
});
|
|
9693
|
-
const data =
|
|
10777
|
+
const data = getData4(result);
|
|
9694
10778
|
if (opts.json) return printJson(data);
|
|
9695
10779
|
emitWriteResult(data?.[0], "Order placed", "ordId");
|
|
9696
10780
|
}
|
|
@@ -9698,7 +10782,7 @@ async function cmdSpotCancel(run, opts) {
|
|
|
9698
10782
|
const { instId, ordId, clOrdId, json } = opts;
|
|
9699
10783
|
if (!ordId && !clOrdId) throw new Error("Either --ordId or --clOrdId is required");
|
|
9700
10784
|
const result = await run("spot_cancel_order", { instId, ...ordId ? { ordId } : { clOrdId } });
|
|
9701
|
-
const data =
|
|
10785
|
+
const data = getData4(result);
|
|
9702
10786
|
if (json) return printJson(data);
|
|
9703
10787
|
emitWriteResult(data?.[0], "Cancelled", "ordId");
|
|
9704
10788
|
}
|
|
@@ -9718,7 +10802,7 @@ async function cmdSpotAlgoPlace(run, opts) {
|
|
|
9718
10802
|
callbackSpread: opts.callbackSpread,
|
|
9719
10803
|
activePx: opts.activePx
|
|
9720
10804
|
});
|
|
9721
|
-
const data =
|
|
10805
|
+
const data = getData4(result);
|
|
9722
10806
|
if (opts.json) return printJson(data);
|
|
9723
10807
|
emitWriteResult(data?.[0], "Algo order placed", "algoId");
|
|
9724
10808
|
}
|
|
@@ -9732,19 +10816,19 @@ async function cmdSpotAlgoAmend(run, opts) {
|
|
|
9732
10816
|
newSlTriggerPx: opts.newSlTriggerPx,
|
|
9733
10817
|
newSlOrdPx: opts.newSlOrdPx
|
|
9734
10818
|
});
|
|
9735
|
-
const data =
|
|
10819
|
+
const data = getData4(result);
|
|
9736
10820
|
if (opts.json) return printJson(data);
|
|
9737
10821
|
emitWriteResult(data?.[0], "Algo order amended", "algoId");
|
|
9738
10822
|
}
|
|
9739
10823
|
async function cmdSpotAlgoCancel(run, instId, algoId, json) {
|
|
9740
10824
|
const result = await run("spot_cancel_algo_order", { instId, algoId });
|
|
9741
|
-
const data =
|
|
10825
|
+
const data = getData4(result);
|
|
9742
10826
|
if (json) return printJson(data);
|
|
9743
10827
|
emitWriteResult(data?.[0], "Algo order cancelled", "algoId");
|
|
9744
10828
|
}
|
|
9745
10829
|
async function cmdSpotGet(run, opts) {
|
|
9746
10830
|
const result = await run("spot_get_order", { instId: opts.instId, ordId: opts.ordId, clOrdId: opts.clOrdId });
|
|
9747
|
-
const data =
|
|
10831
|
+
const data = getData4(result);
|
|
9748
10832
|
if (opts.json) return printJson(data);
|
|
9749
10833
|
const o = data?.[0];
|
|
9750
10834
|
if (!o) {
|
|
@@ -9772,7 +10856,7 @@ async function cmdSpotAmend(run, opts) {
|
|
|
9772
10856
|
newSz: opts.newSz,
|
|
9773
10857
|
newPx: opts.newPx
|
|
9774
10858
|
});
|
|
9775
|
-
const data =
|
|
10859
|
+
const data = getData4(result);
|
|
9776
10860
|
if (opts.json) return printJson(data);
|
|
9777
10861
|
emitWriteResult(data?.[0], "Order amended", "ordId");
|
|
9778
10862
|
}
|
|
@@ -9782,7 +10866,7 @@ async function cmdSpotAlgoOrders(run, opts) {
|
|
|
9782
10866
|
status: opts.status,
|
|
9783
10867
|
ordType: opts.ordType
|
|
9784
10868
|
});
|
|
9785
|
-
const orders =
|
|
10869
|
+
const orders = getData4(result);
|
|
9786
10870
|
if (opts.json) return printJson(orders);
|
|
9787
10871
|
if (!(orders ?? []).length) {
|
|
9788
10872
|
outputLine("No algo orders");
|
|
@@ -9803,7 +10887,7 @@ async function cmdSpotAlgoOrders(run, opts) {
|
|
|
9803
10887
|
}
|
|
9804
10888
|
async function cmdSpotFills(run, opts) {
|
|
9805
10889
|
const result = await run("spot_get_fills", { instId: opts.instId, ordId: opts.ordId });
|
|
9806
|
-
const fills =
|
|
10890
|
+
const fills = getData4(result);
|
|
9807
10891
|
if (opts.json) return printJson(fills);
|
|
9808
10892
|
printTable(
|
|
9809
10893
|
(fills ?? []).map((f) => ({
|
|
@@ -9827,7 +10911,7 @@ async function cmdSpotAlgoTrailPlace(run, opts) {
|
|
|
9827
10911
|
callbackSpread: opts.callbackSpread,
|
|
9828
10912
|
activePx: opts.activePx
|
|
9829
10913
|
});
|
|
9830
|
-
const data =
|
|
10914
|
+
const data = getData4(result);
|
|
9831
10915
|
if (opts.json) return printJson(data);
|
|
9832
10916
|
emitWriteResult(data?.[0], "Trailing stop placed", "algoId");
|
|
9833
10917
|
}
|
|
@@ -9857,13 +10941,13 @@ async function cmdSpotBatch(run, opts) {
|
|
|
9857
10941
|
return;
|
|
9858
10942
|
}
|
|
9859
10943
|
const result = await run(tool, tool === "spot_batch_orders" ? { action: opts.action, orders: parsed } : { orders: parsed });
|
|
9860
|
-
const data =
|
|
10944
|
+
const data = getData4(result);
|
|
9861
10945
|
if (opts.json) return printJson(data);
|
|
9862
10946
|
emitBatchResults(data ?? []);
|
|
9863
10947
|
}
|
|
9864
10948
|
|
|
9865
10949
|
// src/commands/swap.ts
|
|
9866
|
-
function
|
|
10950
|
+
function getData5(result) {
|
|
9867
10951
|
return result.data;
|
|
9868
10952
|
}
|
|
9869
10953
|
function emitWriteResult2(item, label, idKey) {
|
|
@@ -9887,7 +10971,7 @@ function emitBatchResults2(items) {
|
|
|
9887
10971
|
}
|
|
9888
10972
|
async function cmdSwapPositions(run, instId, json) {
|
|
9889
10973
|
const result = await run("swap_get_positions", { instId });
|
|
9890
|
-
const positions =
|
|
10974
|
+
const positions = getData5(result);
|
|
9891
10975
|
if (json) return printJson(positions);
|
|
9892
10976
|
const open = (positions ?? []).filter((p) => Number(p["pos"]) !== 0);
|
|
9893
10977
|
if (!open.length) {
|
|
@@ -9908,7 +10992,7 @@ async function cmdSwapPositions(run, instId, json) {
|
|
|
9908
10992
|
}
|
|
9909
10993
|
async function cmdSwapOrders(run, opts) {
|
|
9910
10994
|
const result = await run("swap_get_orders", { instId: opts.instId, status: opts.status });
|
|
9911
|
-
const orders =
|
|
10995
|
+
const orders = getData5(result);
|
|
9912
10996
|
if (opts.json) return printJson(orders);
|
|
9913
10997
|
printTable(
|
|
9914
10998
|
(orders ?? []).map((o) => ({
|
|
@@ -9938,7 +11022,7 @@ async function cmdSwapPlace(run, opts) {
|
|
|
9938
11022
|
slTriggerPx: opts.slTriggerPx,
|
|
9939
11023
|
slOrdPx: opts.slOrdPx
|
|
9940
11024
|
});
|
|
9941
|
-
const data =
|
|
11025
|
+
const data = getData5(result);
|
|
9942
11026
|
if (opts.json) return printJson(data);
|
|
9943
11027
|
emitWriteResult2(data?.[0], "Order placed", "ordId");
|
|
9944
11028
|
}
|
|
@@ -9946,7 +11030,7 @@ async function cmdSwapCancel(run, opts) {
|
|
|
9946
11030
|
const { instId, ordId, clOrdId, json } = opts;
|
|
9947
11031
|
if (!ordId && !clOrdId) throw new Error("Either --ordId or --clOrdId is required");
|
|
9948
11032
|
const result = await run("swap_cancel_order", { instId, ...ordId ? { ordId } : { clOrdId } });
|
|
9949
|
-
const data =
|
|
11033
|
+
const data = getData5(result);
|
|
9950
11034
|
if (json) return printJson(data);
|
|
9951
11035
|
emitWriteResult2(data?.[0], "Cancelled", "ordId");
|
|
9952
11036
|
}
|
|
@@ -9968,7 +11052,7 @@ async function cmdSwapAlgoPlace(run, opts) {
|
|
|
9968
11052
|
callbackSpread: opts.callbackSpread,
|
|
9969
11053
|
activePx: opts.activePx
|
|
9970
11054
|
});
|
|
9971
|
-
const data =
|
|
11055
|
+
const data = getData5(result);
|
|
9972
11056
|
if (opts.json) return printJson(data);
|
|
9973
11057
|
emitWriteResult2(data?.[0], "Algo order placed", "algoId");
|
|
9974
11058
|
}
|
|
@@ -9982,7 +11066,7 @@ async function cmdSwapAlgoAmend(run, opts) {
|
|
|
9982
11066
|
newSlTriggerPx: opts.newSlTriggerPx,
|
|
9983
11067
|
newSlOrdPx: opts.newSlOrdPx
|
|
9984
11068
|
});
|
|
9985
|
-
const data =
|
|
11069
|
+
const data = getData5(result);
|
|
9986
11070
|
if (opts.json) return printJson(data);
|
|
9987
11071
|
emitWriteResult2(data?.[0], "Algo order amended", "algoId");
|
|
9988
11072
|
}
|
|
@@ -9998,13 +11082,13 @@ async function cmdSwapAlgoTrailPlace(run, opts) {
|
|
|
9998
11082
|
posSide: opts.posSide,
|
|
9999
11083
|
reduceOnly: opts.reduceOnly
|
|
10000
11084
|
});
|
|
10001
|
-
const data =
|
|
11085
|
+
const data = getData5(result);
|
|
10002
11086
|
if (opts.json) return printJson(data);
|
|
10003
11087
|
emitWriteResult2(data?.[0], "Trailing stop placed", "algoId");
|
|
10004
11088
|
}
|
|
10005
11089
|
async function cmdSwapAlgoCancel(run, instId, algoId, json) {
|
|
10006
11090
|
const result = await run("swap_cancel_algo_orders", { orders: [{ instId, algoId }] });
|
|
10007
|
-
const data =
|
|
11091
|
+
const data = getData5(result);
|
|
10008
11092
|
if (json) return printJson(data);
|
|
10009
11093
|
emitWriteResult2(data?.[0], "Algo order cancelled", "algoId");
|
|
10010
11094
|
}
|
|
@@ -10014,7 +11098,7 @@ async function cmdSwapAlgoOrders(run, opts) {
|
|
|
10014
11098
|
status: opts.status,
|
|
10015
11099
|
ordType: opts.ordType
|
|
10016
11100
|
});
|
|
10017
|
-
const orders =
|
|
11101
|
+
const orders = getData5(result);
|
|
10018
11102
|
if (opts.json) return printJson(orders);
|
|
10019
11103
|
if (!(orders ?? []).length) {
|
|
10020
11104
|
outputLine("No algo orders");
|
|
@@ -10035,7 +11119,7 @@ async function cmdSwapAlgoOrders(run, opts) {
|
|
|
10035
11119
|
}
|
|
10036
11120
|
async function cmdSwapFills(run, opts) {
|
|
10037
11121
|
const result = await run("swap_get_fills", { instId: opts.instId, ordId: opts.ordId, archive: opts.archive });
|
|
10038
|
-
const fills =
|
|
11122
|
+
const fills = getData5(result);
|
|
10039
11123
|
if (opts.json) return printJson(fills);
|
|
10040
11124
|
printTable(
|
|
10041
11125
|
(fills ?? []).map((f) => ({
|
|
@@ -10050,7 +11134,7 @@ async function cmdSwapFills(run, opts) {
|
|
|
10050
11134
|
}
|
|
10051
11135
|
async function cmdSwapGet(run, opts) {
|
|
10052
11136
|
const result = await run("swap_get_order", { instId: opts.instId, ordId: opts.ordId, clOrdId: opts.clOrdId });
|
|
10053
|
-
const data =
|
|
11137
|
+
const data = getData5(result);
|
|
10054
11138
|
if (opts.json) return printJson(data);
|
|
10055
11139
|
const o = data?.[0];
|
|
10056
11140
|
if (!o) {
|
|
@@ -10078,14 +11162,14 @@ async function cmdSwapClose(run, opts) {
|
|
|
10078
11162
|
posSide: opts.posSide,
|
|
10079
11163
|
autoCxl: opts.autoCxl
|
|
10080
11164
|
});
|
|
10081
|
-
const data =
|
|
11165
|
+
const data = getData5(result);
|
|
10082
11166
|
if (opts.json) return printJson(data);
|
|
10083
11167
|
const r = data?.[0];
|
|
10084
11168
|
outputLine(`Position closed: ${r?.["instId"]} ${r?.["posSide"] ?? ""}`);
|
|
10085
11169
|
}
|
|
10086
11170
|
async function cmdSwapGetLeverage(run, opts) {
|
|
10087
11171
|
const result = await run("swap_get_leverage", { instId: opts.instId, mgnMode: opts.mgnMode });
|
|
10088
|
-
const data =
|
|
11172
|
+
const data = getData5(result);
|
|
10089
11173
|
if (opts.json) return printJson(data);
|
|
10090
11174
|
printTable(
|
|
10091
11175
|
(data ?? []).map((r) => ({
|
|
@@ -10104,7 +11188,7 @@ async function cmdSwapAmend(run, opts) {
|
|
|
10104
11188
|
newSz: opts.newSz,
|
|
10105
11189
|
newPx: opts.newPx
|
|
10106
11190
|
});
|
|
10107
|
-
const data =
|
|
11191
|
+
const data = getData5(result);
|
|
10108
11192
|
if (opts.json) return printJson(data);
|
|
10109
11193
|
emitWriteResult2(data?.[0], "Order amended", "ordId");
|
|
10110
11194
|
}
|
|
@@ -10115,7 +11199,7 @@ async function cmdSwapSetLeverage(run, opts) {
|
|
|
10115
11199
|
mgnMode: opts.mgnMode,
|
|
10116
11200
|
posSide: opts.posSide
|
|
10117
11201
|
});
|
|
10118
|
-
const data =
|
|
11202
|
+
const data = getData5(result);
|
|
10119
11203
|
if (opts.json) return printJson(data);
|
|
10120
11204
|
const r = data?.[0];
|
|
10121
11205
|
outputLine(`Leverage set: ${r?.["lever"]}x ${r?.["instId"]}`);
|
|
@@ -10146,13 +11230,13 @@ async function cmdSwapBatch(run, opts) {
|
|
|
10146
11230
|
return;
|
|
10147
11231
|
}
|
|
10148
11232
|
const result = await run(tool, tool === "swap_batch_orders" ? { action: opts.action, orders: parsed } : { orders: parsed });
|
|
10149
|
-
const data =
|
|
11233
|
+
const data = getData5(result);
|
|
10150
11234
|
if (opts.json) return printJson(data);
|
|
10151
11235
|
emitBatchResults2(data ?? []);
|
|
10152
11236
|
}
|
|
10153
11237
|
|
|
10154
11238
|
// src/commands/futures.ts
|
|
10155
|
-
function
|
|
11239
|
+
function getData6(result) {
|
|
10156
11240
|
return result.data;
|
|
10157
11241
|
}
|
|
10158
11242
|
function emitWriteResult3(item, label, idKey) {
|
|
@@ -10176,7 +11260,7 @@ function emitBatchResults3(items) {
|
|
|
10176
11260
|
}
|
|
10177
11261
|
async function cmdFuturesOrders(run, opts) {
|
|
10178
11262
|
const result = await run("futures_get_orders", { instId: opts.instId, status: opts.status });
|
|
10179
|
-
const orders =
|
|
11263
|
+
const orders = getData6(result);
|
|
10180
11264
|
if (opts.json) return printJson(orders);
|
|
10181
11265
|
printTable(
|
|
10182
11266
|
(orders ?? []).map((o) => ({
|
|
@@ -10193,7 +11277,7 @@ async function cmdFuturesOrders(run, opts) {
|
|
|
10193
11277
|
}
|
|
10194
11278
|
async function cmdFuturesPositions(run, instId, json) {
|
|
10195
11279
|
const result = await run("futures_get_positions", { instId });
|
|
10196
|
-
const positions =
|
|
11280
|
+
const positions = getData6(result);
|
|
10197
11281
|
if (json) return printJson(positions);
|
|
10198
11282
|
const open = (positions ?? []).filter((p) => Number(p["pos"]) !== 0);
|
|
10199
11283
|
if (!open.length) {
|
|
@@ -10213,7 +11297,7 @@ async function cmdFuturesPositions(run, instId, json) {
|
|
|
10213
11297
|
}
|
|
10214
11298
|
async function cmdFuturesFills(run, opts) {
|
|
10215
11299
|
const result = await run("futures_get_fills", { instId: opts.instId, ordId: opts.ordId, archive: opts.archive });
|
|
10216
|
-
const fills =
|
|
11300
|
+
const fills = getData6(result);
|
|
10217
11301
|
if (opts.json) return printJson(fills);
|
|
10218
11302
|
printTable(
|
|
10219
11303
|
(fills ?? []).map((f) => ({
|
|
@@ -10242,7 +11326,7 @@ async function cmdFuturesPlace(run, opts) {
|
|
|
10242
11326
|
slTriggerPx: opts.slTriggerPx,
|
|
10243
11327
|
slOrdPx: opts.slOrdPx
|
|
10244
11328
|
});
|
|
10245
|
-
const data =
|
|
11329
|
+
const data = getData6(result);
|
|
10246
11330
|
if (opts.json) return printJson(data);
|
|
10247
11331
|
emitWriteResult3(data?.[0], "Order placed", "ordId");
|
|
10248
11332
|
}
|
|
@@ -10250,13 +11334,13 @@ async function cmdFuturesCancel(run, opts) {
|
|
|
10250
11334
|
const { instId, ordId, clOrdId, json } = opts;
|
|
10251
11335
|
if (!ordId && !clOrdId) throw new Error("Either --ordId or --clOrdId is required");
|
|
10252
11336
|
const result = await run("futures_cancel_order", { instId, ...ordId ? { ordId } : { clOrdId } });
|
|
10253
|
-
const data =
|
|
11337
|
+
const data = getData6(result);
|
|
10254
11338
|
if (json) return printJson(data);
|
|
10255
11339
|
emitWriteResult3(data?.[0], "Cancelled", "ordId");
|
|
10256
11340
|
}
|
|
10257
11341
|
async function cmdFuturesGet(run, opts) {
|
|
10258
11342
|
const result = await run("futures_get_order", { instId: opts.instId, ordId: opts.ordId });
|
|
10259
|
-
const data =
|
|
11343
|
+
const data = getData6(result);
|
|
10260
11344
|
if (opts.json) return printJson(data);
|
|
10261
11345
|
const o = data?.[0];
|
|
10262
11346
|
if (!o) {
|
|
@@ -10285,7 +11369,7 @@ async function cmdFuturesAmend(run, opts) {
|
|
|
10285
11369
|
newSz: opts.newSz,
|
|
10286
11370
|
newPx: opts.newPx
|
|
10287
11371
|
});
|
|
10288
|
-
const data =
|
|
11372
|
+
const data = getData6(result);
|
|
10289
11373
|
if (opts.json) return printJson(data);
|
|
10290
11374
|
emitWriteResult3(data?.[0], "Order amended", "ordId");
|
|
10291
11375
|
}
|
|
@@ -10296,7 +11380,7 @@ async function cmdFuturesClose(run, opts) {
|
|
|
10296
11380
|
posSide: opts.posSide,
|
|
10297
11381
|
autoCxl: opts.autoCxl
|
|
10298
11382
|
});
|
|
10299
|
-
const data =
|
|
11383
|
+
const data = getData6(result);
|
|
10300
11384
|
if (opts.json) return printJson(data);
|
|
10301
11385
|
const r = data?.[0];
|
|
10302
11386
|
outputLine(`Position closed: ${r?.["instId"]} ${r?.["posSide"] ?? ""}`);
|
|
@@ -10308,14 +11392,14 @@ async function cmdFuturesSetLeverage(run, opts) {
|
|
|
10308
11392
|
mgnMode: opts.mgnMode,
|
|
10309
11393
|
posSide: opts.posSide
|
|
10310
11394
|
});
|
|
10311
|
-
const data =
|
|
11395
|
+
const data = getData6(result);
|
|
10312
11396
|
if (opts.json) return printJson(data);
|
|
10313
11397
|
const r = data?.[0];
|
|
10314
11398
|
outputLine(`Leverage set: ${r?.["lever"]}x ${r?.["instId"]}`);
|
|
10315
11399
|
}
|
|
10316
11400
|
async function cmdFuturesGetLeverage(run, opts) {
|
|
10317
11401
|
const result = await run("futures_get_leverage", { instId: opts.instId, mgnMode: opts.mgnMode });
|
|
10318
|
-
const data =
|
|
11402
|
+
const data = getData6(result);
|
|
10319
11403
|
if (opts.json) return printJson(data);
|
|
10320
11404
|
printTable(
|
|
10321
11405
|
(data ?? []).map((r) => ({
|
|
@@ -10352,7 +11436,7 @@ async function cmdFuturesBatch(run, opts) {
|
|
|
10352
11436
|
return;
|
|
10353
11437
|
}
|
|
10354
11438
|
const result = await run(tool, { orders: parsed });
|
|
10355
|
-
const data =
|
|
11439
|
+
const data = getData6(result);
|
|
10356
11440
|
if (opts.json) return printJson(data);
|
|
10357
11441
|
emitBatchResults3(data ?? []);
|
|
10358
11442
|
}
|
|
@@ -10374,7 +11458,7 @@ async function cmdFuturesAlgoPlace(run, opts) {
|
|
|
10374
11458
|
callbackSpread: opts.callbackSpread,
|
|
10375
11459
|
activePx: opts.activePx
|
|
10376
11460
|
});
|
|
10377
|
-
const data =
|
|
11461
|
+
const data = getData6(result);
|
|
10378
11462
|
if (opts.json) return printJson(data);
|
|
10379
11463
|
emitWriteResult3(data?.[0], "Algo order placed", "algoId");
|
|
10380
11464
|
}
|
|
@@ -10390,7 +11474,7 @@ async function cmdFuturesAlgoTrailPlace(run, opts) {
|
|
|
10390
11474
|
posSide: opts.posSide,
|
|
10391
11475
|
reduceOnly: opts.reduceOnly
|
|
10392
11476
|
});
|
|
10393
|
-
const data =
|
|
11477
|
+
const data = getData6(result);
|
|
10394
11478
|
if (opts.json) return printJson(data);
|
|
10395
11479
|
emitWriteResult3(data?.[0], "Trailing stop placed", "algoId");
|
|
10396
11480
|
}
|
|
@@ -10404,13 +11488,13 @@ async function cmdFuturesAlgoAmend(run, opts) {
|
|
|
10404
11488
|
newSlTriggerPx: opts.newSlTriggerPx,
|
|
10405
11489
|
newSlOrdPx: opts.newSlOrdPx
|
|
10406
11490
|
});
|
|
10407
|
-
const data =
|
|
11491
|
+
const data = getData6(result);
|
|
10408
11492
|
if (opts.json) return printJson(data);
|
|
10409
11493
|
emitWriteResult3(data?.[0], "Algo order amended", "algoId");
|
|
10410
11494
|
}
|
|
10411
11495
|
async function cmdFuturesAlgoCancel(run, instId, algoId, json) {
|
|
10412
11496
|
const result = await run("futures_cancel_algo_orders", { orders: [{ instId, algoId }] });
|
|
10413
|
-
const data =
|
|
11497
|
+
const data = getData6(result);
|
|
10414
11498
|
if (json) return printJson(data);
|
|
10415
11499
|
emitWriteResult3(data?.[0], "Algo order cancelled", "algoId");
|
|
10416
11500
|
}
|
|
@@ -10420,7 +11504,7 @@ async function cmdFuturesAlgoOrders(run, opts) {
|
|
|
10420
11504
|
status: opts.status,
|
|
10421
11505
|
ordType: opts.ordType
|
|
10422
11506
|
});
|
|
10423
|
-
const orders =
|
|
11507
|
+
const orders = getData6(result);
|
|
10424
11508
|
if (opts.json) return printJson(orders);
|
|
10425
11509
|
if (!(orders ?? []).length) {
|
|
10426
11510
|
outputLine("No algo orders");
|
|
@@ -10441,7 +11525,7 @@ async function cmdFuturesAlgoOrders(run, opts) {
|
|
|
10441
11525
|
}
|
|
10442
11526
|
|
|
10443
11527
|
// src/commands/option.ts
|
|
10444
|
-
function
|
|
11528
|
+
function getData7(result) {
|
|
10445
11529
|
return result.data;
|
|
10446
11530
|
}
|
|
10447
11531
|
function emitWriteResult4(item, label, idKey) {
|
|
@@ -10469,7 +11553,7 @@ async function cmdOptionOrders(run, opts) {
|
|
|
10469
11553
|
uly: opts.uly,
|
|
10470
11554
|
status: opts.status
|
|
10471
11555
|
});
|
|
10472
|
-
const orders =
|
|
11556
|
+
const orders = getData7(result);
|
|
10473
11557
|
if (opts.json) return printJson(orders);
|
|
10474
11558
|
printTable(
|
|
10475
11559
|
(orders ?? []).map((o) => ({
|
|
@@ -10489,7 +11573,7 @@ async function cmdOptionGet(run, opts) {
|
|
|
10489
11573
|
ordId: opts.ordId,
|
|
10490
11574
|
clOrdId: opts.clOrdId
|
|
10491
11575
|
});
|
|
10492
|
-
const data =
|
|
11576
|
+
const data = getData7(result);
|
|
10493
11577
|
if (opts.json) return printJson(data);
|
|
10494
11578
|
const o = data?.[0];
|
|
10495
11579
|
if (!o) {
|
|
@@ -10514,7 +11598,7 @@ async function cmdOptionPositions(run, opts) {
|
|
|
10514
11598
|
instId: opts.instId,
|
|
10515
11599
|
uly: opts.uly
|
|
10516
11600
|
});
|
|
10517
|
-
const positions =
|
|
11601
|
+
const positions = getData7(result);
|
|
10518
11602
|
if (opts.json) return printJson(positions);
|
|
10519
11603
|
const open = (positions ?? []).filter((p) => Number(p["pos"]) !== 0);
|
|
10520
11604
|
if (!open.length) {
|
|
@@ -10541,7 +11625,7 @@ async function cmdOptionFills(run, opts) {
|
|
|
10541
11625
|
ordId: opts.ordId,
|
|
10542
11626
|
archive: opts.archive
|
|
10543
11627
|
});
|
|
10544
|
-
const fills =
|
|
11628
|
+
const fills = getData7(result);
|
|
10545
11629
|
if (opts.json) return printJson(fills);
|
|
10546
11630
|
printTable(
|
|
10547
11631
|
(fills ?? []).map((f) => ({
|
|
@@ -10559,7 +11643,7 @@ async function cmdOptionInstruments(run, opts) {
|
|
|
10559
11643
|
uly: opts.uly,
|
|
10560
11644
|
expTime: opts.expTime
|
|
10561
11645
|
});
|
|
10562
|
-
const instruments =
|
|
11646
|
+
const instruments = getData7(result);
|
|
10563
11647
|
if (opts.json) return printJson(instruments);
|
|
10564
11648
|
printTable(
|
|
10565
11649
|
(instruments ?? []).map((i) => ({
|
|
@@ -10577,7 +11661,7 @@ async function cmdOptionGreeks(run, opts) {
|
|
|
10577
11661
|
uly: opts.uly,
|
|
10578
11662
|
expTime: opts.expTime
|
|
10579
11663
|
});
|
|
10580
|
-
const greeks =
|
|
11664
|
+
const greeks = getData7(result);
|
|
10581
11665
|
if (opts.json) return printJson(greeks);
|
|
10582
11666
|
printTable(
|
|
10583
11667
|
(greeks ?? []).map((g) => ({
|
|
@@ -10606,7 +11690,7 @@ async function cmdOptionPlace(run, opts) {
|
|
|
10606
11690
|
slTriggerPx: opts.slTriggerPx,
|
|
10607
11691
|
slOrdPx: opts.slOrdPx
|
|
10608
11692
|
});
|
|
10609
|
-
const data =
|
|
11693
|
+
const data = getData7(result);
|
|
10610
11694
|
if (opts.json) return printJson(data);
|
|
10611
11695
|
emitWriteResult4(data?.[0], "Order placed", "ordId");
|
|
10612
11696
|
}
|
|
@@ -10616,7 +11700,7 @@ async function cmdOptionCancel(run, opts) {
|
|
|
10616
11700
|
ordId: opts.ordId,
|
|
10617
11701
|
clOrdId: opts.clOrdId
|
|
10618
11702
|
});
|
|
10619
|
-
const data =
|
|
11703
|
+
const data = getData7(result);
|
|
10620
11704
|
if (opts.json) return printJson(data);
|
|
10621
11705
|
emitWriteResult4(data?.[0], "Cancelled", "ordId");
|
|
10622
11706
|
}
|
|
@@ -10628,7 +11712,7 @@ async function cmdOptionAmend(run, opts) {
|
|
|
10628
11712
|
newSz: opts.newSz,
|
|
10629
11713
|
newPx: opts.newPx
|
|
10630
11714
|
});
|
|
10631
|
-
const data =
|
|
11715
|
+
const data = getData7(result);
|
|
10632
11716
|
if (opts.json) return printJson(data);
|
|
10633
11717
|
emitWriteResult4(data?.[0], "Amended", "ordId");
|
|
10634
11718
|
}
|
|
@@ -10647,7 +11731,7 @@ async function cmdOptionBatchCancel(run, opts) {
|
|
|
10647
11731
|
return;
|
|
10648
11732
|
}
|
|
10649
11733
|
const result = await run("option_batch_cancel", { orders: parsed });
|
|
10650
|
-
const data =
|
|
11734
|
+
const data = getData7(result);
|
|
10651
11735
|
if (opts.json) return printJson(data);
|
|
10652
11736
|
emitBatchResults4(data ?? []);
|
|
10653
11737
|
}
|
|
@@ -10666,7 +11750,7 @@ async function cmdOptionAlgoPlace(run, opts) {
|
|
|
10666
11750
|
reduceOnly: opts.reduceOnly,
|
|
10667
11751
|
clOrdId: opts.clOrdId
|
|
10668
11752
|
});
|
|
10669
|
-
const data =
|
|
11753
|
+
const data = getData7(result);
|
|
10670
11754
|
if (opts.json) return printJson(data);
|
|
10671
11755
|
emitWriteResult4(data?.[0], "Algo order placed", "algoId");
|
|
10672
11756
|
}
|
|
@@ -10680,13 +11764,13 @@ async function cmdOptionAlgoAmend(run, opts) {
|
|
|
10680
11764
|
newSlTriggerPx: opts.newSlTriggerPx,
|
|
10681
11765
|
newSlOrdPx: opts.newSlOrdPx
|
|
10682
11766
|
});
|
|
10683
|
-
const data =
|
|
11767
|
+
const data = getData7(result);
|
|
10684
11768
|
if (opts.json) return printJson(data);
|
|
10685
11769
|
emitWriteResult4(data?.[0], "Algo order amended", "algoId");
|
|
10686
11770
|
}
|
|
10687
11771
|
async function cmdOptionAlgoCancel(run, opts) {
|
|
10688
11772
|
const result = await run("option_cancel_algo_orders", { orders: [{ instId: opts.instId, algoId: opts.algoId }] });
|
|
10689
|
-
const data =
|
|
11773
|
+
const data = getData7(result);
|
|
10690
11774
|
if (opts.json) return printJson(data);
|
|
10691
11775
|
emitWriteResult4(data?.[0], "Algo order cancelled", "algoId");
|
|
10692
11776
|
}
|
|
@@ -10696,7 +11780,7 @@ async function cmdOptionAlgoOrders(run, opts) {
|
|
|
10696
11780
|
status: opts.status,
|
|
10697
11781
|
ordType: opts.ordType
|
|
10698
11782
|
});
|
|
10699
|
-
const orders =
|
|
11783
|
+
const orders = getData7(result);
|
|
10700
11784
|
if (opts.json) return printJson(orders);
|
|
10701
11785
|
if (!(orders ?? []).length) {
|
|
10702
11786
|
outputLine("No algo orders");
|
|
@@ -10795,7 +11879,7 @@ Config saved to ${p}
|
|
|
10795
11879
|
}
|
|
10796
11880
|
};
|
|
10797
11881
|
function prompt(rl, question) {
|
|
10798
|
-
return new Promise((
|
|
11882
|
+
return new Promise((resolve3) => rl.question(question, resolve3));
|
|
10799
11883
|
}
|
|
10800
11884
|
function cmdConfigShow(json) {
|
|
10801
11885
|
const config = readFullConfig();
|
|
@@ -11215,7 +12299,7 @@ function emitWriteResult5(item, label, idKey) {
|
|
|
11215
12299
|
outputLine(`${label}: ${item?.[idKey]} (OK)`);
|
|
11216
12300
|
}
|
|
11217
12301
|
}
|
|
11218
|
-
function
|
|
12302
|
+
function getData8(result) {
|
|
11219
12303
|
return result.data;
|
|
11220
12304
|
}
|
|
11221
12305
|
async function cmdGridOrders(run, opts) {
|
|
@@ -11225,7 +12309,7 @@ async function cmdGridOrders(run, opts) {
|
|
|
11225
12309
|
algoId: opts.algoId,
|
|
11226
12310
|
status: opts.status
|
|
11227
12311
|
});
|
|
11228
|
-
const orders =
|
|
12312
|
+
const orders = getData8(result) ?? [];
|
|
11229
12313
|
if (opts.json) return printJson(orders);
|
|
11230
12314
|
if (!orders.length) {
|
|
11231
12315
|
outputLine("No grid bots");
|
|
@@ -11250,7 +12334,7 @@ async function cmdGridDetails(run, opts) {
|
|
|
11250
12334
|
algoOrdType: opts.algoOrdType,
|
|
11251
12335
|
algoId: opts.algoId
|
|
11252
12336
|
});
|
|
11253
|
-
const detail = (
|
|
12337
|
+
const detail = (getData8(result) ?? [])[0];
|
|
11254
12338
|
if (!detail) {
|
|
11255
12339
|
outputLine("Bot not found");
|
|
11256
12340
|
return;
|
|
@@ -11278,7 +12362,7 @@ async function cmdGridSubOrders(run, opts) {
|
|
|
11278
12362
|
algoId: opts.algoId,
|
|
11279
12363
|
type: opts.type
|
|
11280
12364
|
});
|
|
11281
|
-
const orders =
|
|
12365
|
+
const orders = getData8(result) ?? [];
|
|
11282
12366
|
if (opts.json) return printJson(orders);
|
|
11283
12367
|
if (!orders.length) {
|
|
11284
12368
|
outputLine("No sub-orders");
|
|
@@ -11317,7 +12401,7 @@ async function cmdGridCreate(run, opts) {
|
|
|
11317
12401
|
slRatio: opts.slRatio,
|
|
11318
12402
|
algoClOrdId: opts.algoClOrdId
|
|
11319
12403
|
});
|
|
11320
|
-
const data =
|
|
12404
|
+
const data = getData8(result);
|
|
11321
12405
|
if (opts.json) return printJson(data);
|
|
11322
12406
|
emitWriteResult5(data?.[0], "Grid bot created", "algoId");
|
|
11323
12407
|
}
|
|
@@ -11328,7 +12412,7 @@ async function cmdGridStop(run, opts) {
|
|
|
11328
12412
|
instId: opts.instId,
|
|
11329
12413
|
stopType: opts.stopType
|
|
11330
12414
|
});
|
|
11331
|
-
const data =
|
|
12415
|
+
const data = getData8(result);
|
|
11332
12416
|
if (opts.json) return printJson(data);
|
|
11333
12417
|
emitWriteResult5(data?.[0], "Grid bot stopped", "algoId");
|
|
11334
12418
|
}
|
|
@@ -11358,7 +12442,7 @@ async function cmdDcaCreate(run, opts) {
|
|
|
11358
12442
|
reserveFunds: opts.reserveFunds,
|
|
11359
12443
|
tradeQuoteCcy: opts.tradeQuoteCcy
|
|
11360
12444
|
});
|
|
11361
|
-
const data =
|
|
12445
|
+
const data = getData8(result);
|
|
11362
12446
|
if (opts.json) return printJson(data);
|
|
11363
12447
|
emitWriteResult5(data?.[0], "DCA bot created", "algoId");
|
|
11364
12448
|
}
|
|
@@ -11368,7 +12452,7 @@ async function cmdDcaStop(run, opts) {
|
|
|
11368
12452
|
algoOrdType: opts.algoOrdType,
|
|
11369
12453
|
stopType: opts.stopType
|
|
11370
12454
|
});
|
|
11371
|
-
const data =
|
|
12455
|
+
const data = getData8(result);
|
|
11372
12456
|
if (opts.json) return printJson(data);
|
|
11373
12457
|
emitWriteResult5(data?.[0], "DCA bot stopped", "algoId");
|
|
11374
12458
|
}
|
|
@@ -11379,7 +12463,7 @@ async function cmdDcaOrders(run, opts) {
|
|
|
11379
12463
|
algoId: opts.algoId,
|
|
11380
12464
|
instId: opts.instId
|
|
11381
12465
|
});
|
|
11382
|
-
const orders =
|
|
12466
|
+
const orders = getData8(result) ?? [];
|
|
11383
12467
|
if (opts.json) return printJson(orders);
|
|
11384
12468
|
if (!orders.length) {
|
|
11385
12469
|
outputLine("No DCA bots");
|
|
@@ -11402,7 +12486,7 @@ async function cmdDcaDetails(run, opts) {
|
|
|
11402
12486
|
algoId: opts.algoId,
|
|
11403
12487
|
algoOrdType: opts.algoOrdType
|
|
11404
12488
|
});
|
|
11405
|
-
const detail = (
|
|
12489
|
+
const detail = (getData8(result) ?? [])[0];
|
|
11406
12490
|
if (!detail) {
|
|
11407
12491
|
outputLine("DCA bot not found");
|
|
11408
12492
|
return;
|
|
@@ -11431,7 +12515,7 @@ async function cmdDcaSubOrders(run, opts) {
|
|
|
11431
12515
|
algoOrdType: opts.algoOrdType,
|
|
11432
12516
|
cycleId: opts.cycleId
|
|
11433
12517
|
});
|
|
11434
|
-
const rows =
|
|
12518
|
+
const rows = getData8(result) ?? [];
|
|
11435
12519
|
if (opts.json) return printJson(rows);
|
|
11436
12520
|
if (!rows.length) {
|
|
11437
12521
|
outputLine("No sub-orders");
|
|
@@ -11760,10 +12844,194 @@ async function cmdDcdQuoteAndBuy(run, opts) {
|
|
|
11760
12844
|
}
|
|
11761
12845
|
}
|
|
11762
12846
|
|
|
12847
|
+
// src/commands/skill.ts
|
|
12848
|
+
import { tmpdir, homedir as homedir7 } from "os";
|
|
12849
|
+
import { join as join9, dirname as dirname6 } from "path";
|
|
12850
|
+
import { mkdirSync as mkdirSync8, rmSync, existsSync as existsSync7, copyFileSync as copyFileSync2 } from "fs";
|
|
12851
|
+
import { execFileSync as execFileSync2 } from "child_process";
|
|
12852
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
12853
|
+
function resolveNpx() {
|
|
12854
|
+
const sibling = join9(dirname6(process.execPath), "npx");
|
|
12855
|
+
if (existsSync7(sibling)) return sibling;
|
|
12856
|
+
return "npx";
|
|
12857
|
+
}
|
|
12858
|
+
async function cmdSkillSearch(run, opts) {
|
|
12859
|
+
const args = {};
|
|
12860
|
+
if (opts.keyword) args.keyword = opts.keyword;
|
|
12861
|
+
if (opts.categories) args.categories = opts.categories;
|
|
12862
|
+
if (opts.page) args.page = opts.page;
|
|
12863
|
+
if (opts.limit) args.limit = opts.limit;
|
|
12864
|
+
const result = await run("skills_search", args);
|
|
12865
|
+
const data = result.data;
|
|
12866
|
+
const totalPage = result.totalPage;
|
|
12867
|
+
if (opts.json) {
|
|
12868
|
+
outputLine(JSON.stringify(result, null, 2));
|
|
12869
|
+
return;
|
|
12870
|
+
}
|
|
12871
|
+
if (!Array.isArray(data) || data.length === 0) {
|
|
12872
|
+
outputLine("No skills found.");
|
|
12873
|
+
return;
|
|
12874
|
+
}
|
|
12875
|
+
outputLine("");
|
|
12876
|
+
outputLine(" NAME VERSION DESCRIPTION");
|
|
12877
|
+
for (const item of data) {
|
|
12878
|
+
const name = (item.name ?? "").padEnd(20);
|
|
12879
|
+
const ver = (item.latestVersion ?? "").padEnd(10);
|
|
12880
|
+
const desc = (item.description ?? "").slice(0, 50);
|
|
12881
|
+
outputLine(` ${name}${ver}${desc}`);
|
|
12882
|
+
}
|
|
12883
|
+
outputLine("");
|
|
12884
|
+
const page = opts.page ?? "1";
|
|
12885
|
+
const pageInfo = totalPage ? ` (page ${page}/${totalPage})` : "";
|
|
12886
|
+
outputLine(`${data.length} skills found${pageInfo}. Use \`okx skill add <name>\` to install.`);
|
|
12887
|
+
}
|
|
12888
|
+
async function cmdSkillCategories(run, json) {
|
|
12889
|
+
const result = await run("skills_get_categories", {});
|
|
12890
|
+
const data = result.data;
|
|
12891
|
+
if (json) {
|
|
12892
|
+
outputLine(JSON.stringify(result, null, 2));
|
|
12893
|
+
return;
|
|
12894
|
+
}
|
|
12895
|
+
if (!Array.isArray(data) || data.length === 0) {
|
|
12896
|
+
outputLine("No categories found.");
|
|
12897
|
+
return;
|
|
12898
|
+
}
|
|
12899
|
+
outputLine("");
|
|
12900
|
+
outputLine(" ID NAME");
|
|
12901
|
+
for (const cat of data) {
|
|
12902
|
+
outputLine(` ${(cat.categoryId ?? "").padEnd(20)}${cat.name ?? ""}`);
|
|
12903
|
+
}
|
|
12904
|
+
outputLine("");
|
|
12905
|
+
}
|
|
12906
|
+
async function cmdSkillAdd(name, config, json) {
|
|
12907
|
+
const tmpBase = join9(tmpdir(), `okx-skill-${randomUUID2()}`);
|
|
12908
|
+
mkdirSync8(tmpBase, { recursive: true });
|
|
12909
|
+
try {
|
|
12910
|
+
outputLine(`Downloading ${name}...`);
|
|
12911
|
+
const client = new OkxRestClient(config);
|
|
12912
|
+
const zipPath = await downloadSkillZip(client, name, tmpBase);
|
|
12913
|
+
const contentDir = await extractSkillZip(zipPath, join9(tmpBase, "content"));
|
|
12914
|
+
const meta = readMetaJson(contentDir);
|
|
12915
|
+
validateSkillMdExists(contentDir);
|
|
12916
|
+
outputLine("Installing to detected agents...");
|
|
12917
|
+
try {
|
|
12918
|
+
execFileSync2(resolveNpx(), ["skills", "add", contentDir, "-y", "-g"], {
|
|
12919
|
+
stdio: "inherit",
|
|
12920
|
+
timeout: 6e4
|
|
12921
|
+
});
|
|
12922
|
+
} catch (e) {
|
|
12923
|
+
const savedZip = join9(process.cwd(), `${name}.zip`);
|
|
12924
|
+
try {
|
|
12925
|
+
copyFileSync2(zipPath, savedZip);
|
|
12926
|
+
} catch {
|
|
12927
|
+
}
|
|
12928
|
+
errorLine(`npx skills add failed. The zip has been downloaded but not installed.`);
|
|
12929
|
+
errorLine(`You can manually install from: ${savedZip}`);
|
|
12930
|
+
throw e;
|
|
12931
|
+
}
|
|
12932
|
+
upsertSkillRecord(meta);
|
|
12933
|
+
if (json) {
|
|
12934
|
+
outputLine(JSON.stringify({ name: meta.name, version: meta.version, status: "installed" }, null, 2));
|
|
12935
|
+
} else {
|
|
12936
|
+
outputLine(`\u2713 Skill "${meta.name}" v${meta.version} installed`);
|
|
12937
|
+
}
|
|
12938
|
+
} finally {
|
|
12939
|
+
rmSync(tmpBase, { recursive: true, force: true });
|
|
12940
|
+
}
|
|
12941
|
+
}
|
|
12942
|
+
async function cmdSkillDownload(name, targetDir, config, json) {
|
|
12943
|
+
outputLine(`Downloading ${name}...`);
|
|
12944
|
+
const client = new OkxRestClient(config);
|
|
12945
|
+
const filePath = await downloadSkillZip(client, name, targetDir);
|
|
12946
|
+
if (json) {
|
|
12947
|
+
outputLine(JSON.stringify({ name, filePath }, null, 2));
|
|
12948
|
+
} else {
|
|
12949
|
+
outputLine(`\u2713 Downloaded ${name}.zip`);
|
|
12950
|
+
outputLine(` Path: ${filePath}`);
|
|
12951
|
+
}
|
|
12952
|
+
}
|
|
12953
|
+
function cmdSkillRemove(name, json) {
|
|
12954
|
+
const removed = removeSkillRecord(name);
|
|
12955
|
+
if (!removed) {
|
|
12956
|
+
errorLine(`Skill "${name}" is not installed.`);
|
|
12957
|
+
process.exitCode = 1;
|
|
12958
|
+
return;
|
|
12959
|
+
}
|
|
12960
|
+
try {
|
|
12961
|
+
execFileSync2(resolveNpx(), ["skills", "remove", name, "-y", "-g"], {
|
|
12962
|
+
stdio: "inherit",
|
|
12963
|
+
timeout: 6e4
|
|
12964
|
+
});
|
|
12965
|
+
} catch {
|
|
12966
|
+
const agentsPath = join9(homedir7(), ".agents", "skills", name);
|
|
12967
|
+
try {
|
|
12968
|
+
rmSync(agentsPath, { recursive: true, force: true });
|
|
12969
|
+
} catch {
|
|
12970
|
+
}
|
|
12971
|
+
}
|
|
12972
|
+
if (json) {
|
|
12973
|
+
outputLine(JSON.stringify({ name, status: "removed" }, null, 2));
|
|
12974
|
+
} else {
|
|
12975
|
+
outputLine(`\u2713 Skill "${name}" removed`);
|
|
12976
|
+
}
|
|
12977
|
+
}
|
|
12978
|
+
async function cmdSkillCheck(run, name, json) {
|
|
12979
|
+
const local = getSkillRecord(name);
|
|
12980
|
+
if (!local) {
|
|
12981
|
+
errorLine(`Skill "${name}" is not installed.`);
|
|
12982
|
+
process.exitCode = 1;
|
|
12983
|
+
return;
|
|
12984
|
+
}
|
|
12985
|
+
const result = await run("skills_search", { keyword: name });
|
|
12986
|
+
const data = result.data;
|
|
12987
|
+
const remote = data?.find((s) => s.name === name);
|
|
12988
|
+
if (!remote) {
|
|
12989
|
+
errorLine(`Skill "${name}" not found in marketplace.`);
|
|
12990
|
+
process.exitCode = 1;
|
|
12991
|
+
return;
|
|
12992
|
+
}
|
|
12993
|
+
const upToDate = local.version === remote.latestVersion;
|
|
12994
|
+
if (json) {
|
|
12995
|
+
outputLine(JSON.stringify({
|
|
12996
|
+
name,
|
|
12997
|
+
installedVersion: local.version,
|
|
12998
|
+
latestVersion: remote.latestVersion,
|
|
12999
|
+
upToDate
|
|
13000
|
+
}, null, 2));
|
|
13001
|
+
} else if (upToDate) {
|
|
13002
|
+
outputLine(`${name}: installed v${local.version} \u2192 latest v${remote.latestVersion} (up to date)`);
|
|
13003
|
+
} else {
|
|
13004
|
+
outputLine(`${name}: installed v${local.version} \u2192 latest v${remote.latestVersion} (update available)`);
|
|
13005
|
+
outputLine(` Use \`okx skill add ${name}\` to update.`);
|
|
13006
|
+
}
|
|
13007
|
+
}
|
|
13008
|
+
function cmdSkillList(json) {
|
|
13009
|
+
const registry = readRegistry();
|
|
13010
|
+
const skills = Object.values(registry.skills);
|
|
13011
|
+
if (json) {
|
|
13012
|
+
outputLine(JSON.stringify(registry, null, 2));
|
|
13013
|
+
return;
|
|
13014
|
+
}
|
|
13015
|
+
if (skills.length === 0) {
|
|
13016
|
+
outputLine("No skills installed.");
|
|
13017
|
+
return;
|
|
13018
|
+
}
|
|
13019
|
+
outputLine("");
|
|
13020
|
+
outputLine(" NAME VERSION INSTALLED AT");
|
|
13021
|
+
for (const s of skills) {
|
|
13022
|
+
const name = s.name.padEnd(20);
|
|
13023
|
+
const ver = s.version.padEnd(10);
|
|
13024
|
+
const date = s.installedAt.slice(0, 19).replace("T", " ");
|
|
13025
|
+
outputLine(` ${name}${ver}${date}`);
|
|
13026
|
+
}
|
|
13027
|
+
outputLine("");
|
|
13028
|
+
outputLine(`${skills.length} skills installed.`);
|
|
13029
|
+
}
|
|
13030
|
+
|
|
11763
13031
|
// src/index.ts
|
|
11764
13032
|
var _require3 = createRequire3(import.meta.url);
|
|
11765
13033
|
var CLI_VERSION2 = _require3("../package.json").version;
|
|
11766
|
-
var GIT_HASH2 = true ? "
|
|
13034
|
+
var GIT_HASH2 = true ? "f0a9e50" : "dev";
|
|
11767
13035
|
function handleConfigCommand(action, rest, json, lang, force) {
|
|
11768
13036
|
if (action === "init") return cmdConfigInit(lang === "zh" ? "zh" : "en");
|
|
11769
13037
|
if (action === "show") return cmdConfigShow(json);
|
|
@@ -11806,6 +13074,13 @@ function handleMarketPublicCommand(run, action, rest, v, json) {
|
|
|
11806
13074
|
return cmdMarketOpenInterest(run, { instType: v.instType, instId: v.instId, json });
|
|
11807
13075
|
if (action === "stock-tokens")
|
|
11808
13076
|
return cmdMarketStockTokens(run, { instType: v.instType, instId: v.instId, json });
|
|
13077
|
+
if (action === "instruments-by-category")
|
|
13078
|
+
return cmdMarketInstrumentsByCategory(run, {
|
|
13079
|
+
instCategory: v.instCategory,
|
|
13080
|
+
instType: v.instType,
|
|
13081
|
+
instId: v.instId,
|
|
13082
|
+
json
|
|
13083
|
+
});
|
|
11809
13084
|
if (action === "indicator") {
|
|
11810
13085
|
const limit = v.limit !== void 0 ? Number(v.limit) : void 0;
|
|
11811
13086
|
const backtestTime = v["backtest-time"] !== void 0 ? Number(v["backtest-time"]) : void 0;
|
|
@@ -12522,6 +13797,72 @@ function handleEarnDcdCommand(run, action, v, json) {
|
|
|
12522
13797
|
errorLine("Valid: pairs, products, quote-and-buy, redeem-execute, order, orders");
|
|
12523
13798
|
process.exitCode = 1;
|
|
12524
13799
|
}
|
|
13800
|
+
function handleNewsCommand(run, action, rest, v, json) {
|
|
13801
|
+
const limit = v.limit !== void 0 ? Number(v.limit) : void 0;
|
|
13802
|
+
const begin = v.begin !== void 0 ? Number(v.begin) : void 0;
|
|
13803
|
+
const end = v.end !== void 0 ? Number(v.end) : void 0;
|
|
13804
|
+
const language = v.lang;
|
|
13805
|
+
const detailLvl = v["detail-lvl"];
|
|
13806
|
+
const after = v.after;
|
|
13807
|
+
const period = v.period;
|
|
13808
|
+
const points = v.points !== void 0 ? Number(v.points) : 24;
|
|
13809
|
+
const sortBy = v["sort-by"];
|
|
13810
|
+
const searchOpts = { coins: v.coins, importance: v.importance, sentiment: v.sentiment, sortBy, begin, end, language, detailLvl, limit, after, json };
|
|
13811
|
+
const listOpts = { coins: v.coins, importance: v.importance, begin, end, language, detailLvl, limit, after, json };
|
|
13812
|
+
const dispatch = {
|
|
13813
|
+
latest: () => cmdNewsLatest(run, listOpts),
|
|
13814
|
+
important: () => cmdNewsImportant(run, { coins: v.coins, begin, end, language, detailLvl, limit, json }),
|
|
13815
|
+
"by-coin": () => cmdNewsByCoin(run, v.coins ?? rest[0], { importance: v.importance, begin, end, language, detailLvl, limit, json }),
|
|
13816
|
+
search: () => cmdNewsSearch(run, v.keyword ?? rest[0], searchOpts),
|
|
13817
|
+
detail: () => cmdNewsDetail(run, rest[0], { language, json }),
|
|
13818
|
+
domains: () => cmdNewsDomains(run, { json }),
|
|
13819
|
+
"coin-sentiment": () => cmdNewsCoinSentiment(run, v.coins ?? rest[0], { period, json }),
|
|
13820
|
+
"coin-trend": () => cmdNewsCoinTrend(run, rest[0], { period, points, json }),
|
|
13821
|
+
"by-sentiment": () => cmdNewsSearch(run, "", { ...searchOpts, sentiment: v.sentiment ?? rest[0], sortBy: sortBy ?? "latest" }),
|
|
13822
|
+
"sentiment-rank": () => cmdNewsSentimentRank(run, { period, sortBy, limit, json })
|
|
13823
|
+
};
|
|
13824
|
+
const handler = dispatch[action];
|
|
13825
|
+
if (handler) return handler();
|
|
13826
|
+
process.stderr.write(`Unknown news command: ${action}
|
|
13827
|
+
`);
|
|
13828
|
+
process.exitCode = 1;
|
|
13829
|
+
}
|
|
13830
|
+
function requireSkillName(rest, usage) {
|
|
13831
|
+
const name = rest[0];
|
|
13832
|
+
if (!name) {
|
|
13833
|
+
errorLine(usage);
|
|
13834
|
+
process.exitCode = 1;
|
|
13835
|
+
}
|
|
13836
|
+
return name;
|
|
13837
|
+
}
|
|
13838
|
+
function handleSkillAdd(rest, config, json) {
|
|
13839
|
+
const n = requireSkillName(rest, "Usage: okx skill add <name>");
|
|
13840
|
+
if (n) return cmdSkillAdd(n, config, json);
|
|
13841
|
+
}
|
|
13842
|
+
function handleSkillDownload(rest, v, config, json) {
|
|
13843
|
+
const n = requireSkillName(rest, "Usage: okx skill download <name> [--dir <path>]");
|
|
13844
|
+
if (n) return cmdSkillDownload(n, v.dir ?? process.cwd(), config, json);
|
|
13845
|
+
}
|
|
13846
|
+
function handleSkillRemove(rest, json) {
|
|
13847
|
+
const n = requireSkillName(rest, "Usage: okx skill remove <name>");
|
|
13848
|
+
if (n) cmdSkillRemove(n, json);
|
|
13849
|
+
}
|
|
13850
|
+
function handleSkillCheck(run, rest, json) {
|
|
13851
|
+
const n = requireSkillName(rest, "Usage: okx skill check <name>");
|
|
13852
|
+
if (n) return cmdSkillCheck(run, n, json);
|
|
13853
|
+
}
|
|
13854
|
+
function handleSkillCommand(run, action, rest, v, json, config) {
|
|
13855
|
+
if (action === "search") return cmdSkillSearch(run, { keyword: rest[0] ?? v.keyword, categories: v.categories, page: v.page, limit: v.limit, json });
|
|
13856
|
+
if (action === "categories") return cmdSkillCategories(run, json);
|
|
13857
|
+
if (action === "list") return cmdSkillList(json);
|
|
13858
|
+
if (action === "add") return handleSkillAdd(rest, config, json);
|
|
13859
|
+
if (action === "download") return handleSkillDownload(rest, v, config, json);
|
|
13860
|
+
if (action === "remove") return handleSkillRemove(rest, json);
|
|
13861
|
+
if (action === "check") return handleSkillCheck(run, rest, json);
|
|
13862
|
+
errorLine(`Unknown skill command: ${action}`);
|
|
13863
|
+
errorLine("Valid: search, categories, add, download, remove, check, list");
|
|
13864
|
+
process.exitCode = 1;
|
|
13865
|
+
}
|
|
12525
13866
|
function outputResult(result, json) {
|
|
12526
13867
|
if (json) {
|
|
12527
13868
|
outputLine(JSON.stringify(result, null, 2));
|
|
@@ -12559,12 +13900,12 @@ async function main() {
|
|
|
12559
13900
|
if (module === "diagnose") {
|
|
12560
13901
|
let config2;
|
|
12561
13902
|
try {
|
|
12562
|
-
config2 = loadProfileConfig({ profile: v.profile, demo: v.demo, verbose: v.verbose, userAgent: `okx-trade-cli/${CLI_VERSION2}`, sourceTag: "CLI" });
|
|
13903
|
+
config2 = loadProfileConfig({ profile: v.profile, demo: v.demo, live: v.live, verbose: v.verbose, userAgent: `okx-trade-cli/${CLI_VERSION2}`, sourceTag: "CLI" });
|
|
12563
13904
|
} catch {
|
|
12564
13905
|
}
|
|
12565
13906
|
return cmdDiagnose(config2, v.profile ?? "default", { mcp: v.mcp, cli: v.cli, all: v.all, output: v.output });
|
|
12566
13907
|
}
|
|
12567
|
-
const config = loadProfileConfig({ profile: v.profile, demo: v.demo, verbose: v.verbose, userAgent: `okx-trade-cli/${CLI_VERSION2}`, sourceTag: "CLI" });
|
|
13908
|
+
const config = loadProfileConfig({ profile: v.profile, demo: v.demo, live: v.live, verbose: v.verbose, userAgent: `okx-trade-cli/${CLI_VERSION2}`, sourceTag: "CLI" });
|
|
12568
13909
|
const client = new OkxRestClient(config);
|
|
12569
13910
|
const baseRunner = createToolRunner(client, config);
|
|
12570
13911
|
const writeToolNames = new Set(allToolSpecs().filter((t) => t.isWrite).map((t) => t.name));
|
|
@@ -12582,8 +13923,10 @@ async function main() {
|
|
|
12582
13923
|
swap: () => handleSwapCommand(run, action, rest, v, json),
|
|
12583
13924
|
futures: () => handleFuturesCommand(run, action, rest, v, json),
|
|
12584
13925
|
option: () => handleOptionCommand(run, action, rest, v, json),
|
|
13926
|
+
news: () => handleNewsCommand(run, action, rest, v, json),
|
|
12585
13927
|
bot: () => handleBotCommand(run, action, rest, v, json),
|
|
12586
|
-
earn: () => handleEarnCommand(run, action, rest, v, json)
|
|
13928
|
+
earn: () => handleEarnCommand(run, action, rest, v, json),
|
|
13929
|
+
skill: () => handleSkillCommand(run, action, rest, v, json, config)
|
|
12587
13930
|
};
|
|
12588
13931
|
const handler = moduleHandlers[module];
|
|
12589
13932
|
if (handler) return handler();
|
|
@@ -12610,9 +13953,11 @@ export {
|
|
|
12610
13953
|
handleMarketCommand,
|
|
12611
13954
|
handleMarketDataCommand,
|
|
12612
13955
|
handleMarketPublicCommand,
|
|
13956
|
+
handleNewsCommand,
|
|
12613
13957
|
handleOptionAlgoCommand,
|
|
12614
13958
|
handleOptionCommand,
|
|
12615
13959
|
handleSetupCommand,
|
|
13960
|
+
handleSkillCommand,
|
|
12616
13961
|
handleSpotAlgoCommand,
|
|
12617
13962
|
handleSpotCommand,
|
|
12618
13963
|
handleSwapAlgoCommand,
|