@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 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 { readFileSync, writeFileSync, mkdirSync, existsSync } from "fs";
13
- import { join, dirname } from "path";
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, sep, end, banNewLines = false) {
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 === sep) {
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 readFileSync2, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2, existsSync as existsSync2 } from "fs";
844
- import { join as join2 } from "path";
845
- import { homedir as homedir2 } from "os";
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((resolve) => {
931
- setTimeout(resolve, ms);
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
- async request(reqConfig) {
1217
- const queryString = buildQueryString(reqConfig.query);
1218
- const requestPath = queryString.length > 0 ? `${reqConfig.path}?${queryString}` : reqConfig.path;
1219
- const url = `${this.config.baseUrl}${requestPath}`;
1220
- const bodyJson = reqConfig.body ? JSON.stringify(reqConfig.body) : "";
1221
- const timestamp = getNow();
1222
- this.logRequest(reqConfig.method, url, reqConfig.auth);
1223
- if (reqConfig.rateLimit) {
1224
- await this.rateLimiter.consume(reqConfig.rateLimit);
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
- return normalizeResponse(response);
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. Not available in demo mode.",
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. Not available in demo mode.",
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] Not available in demo mode.",
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
- return [
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). Filters client-side by instCategory=3.",
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: "option_place_algo_order",
5326
- module: "option",
5327
- description: "Place OPTION TP/SL algo order (conditional/oco). [CAUTION] Executes real trades. conditional=single TP/SL; oco=TP+SL pair. -1=market.",
5328
- isWrite: true,
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
- instId: {
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: ["last", "index", "mark"]
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
- tgtCcy: {
5753
+ instType: {
5380
5754
  type: "string",
5381
- enum: ["base_ccy", "quote_ccy"],
5382
- description: "Size unit. base_ccy(default): sz in contracts, quote_ccy: sz in USDT (may not be supported for options)"
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
- clOrdId: {
5758
+ instId: {
5389
5759
  type: "string",
5390
- description: "Client order ID (max 32 chars)"
5760
+ description: "Optional: filter by specific instrument ID"
5391
5761
  }
5392
5762
  },
5393
- required: ["instId", "tdMode", "side", "ordType", "sz"]
5763
+ required: ["instCategory"]
5394
5764
  },
5395
5765
  handler: async (rawArgs, context) => {
5396
5766
  const args = asRecord(rawArgs);
5397
- const reduceOnly = readBoolean(args, "reduceOnly");
5398
- const response = await context.client.privatePost(
5399
- "/api/v5/trade/order-algo",
5400
- compactObject({
5401
- instId: requireString(args, "instId"),
5402
- tdMode: requireString(args, "tdMode"),
5403
- side: requireString(args, "side"),
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
- return normalizeResponse(response);
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 join(homedir(), ".okx", "config.toml");
7572
+ return join4(homedir2(), ".okx", "config.toml");
6813
7573
  }
6814
7574
  function readFullConfig() {
6815
7575
  const path42 = configFilePath();
6816
- if (!existsSync(path42)) return { profiles: {} };
6817
- const raw = readFileSync(path42, "utf-8");
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 = dirname(path42);
6846
- if (!existsSync(dir)) {
6847
- mkdirSync(dir, { recursive: true });
7605
+ const dir = dirname3(path42);
7606
+ if (!existsSync3(dir)) {
7607
+ mkdirSync4(dir, { recursive: true });
6848
7608
  }
6849
- writeFileSync(path42, CONFIG_HEADER + stringify(config), "utf-8");
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.demo || process.env.OKX_DEMO === "1" || process.env.OKX_DEMO === "true" || (toml.demo ?? false);
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 = join2(homedir2(), ".okx", "update-check.json");
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 (existsSync2(CACHE_FILE)) {
6957
- return JSON.parse(readFileSync2(CACHE_FILE, "utf-8"));
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
- mkdirSync2(join2(homedir2(), ".okx"), { recursive: true });
6966
- writeFileSync2(CACHE_FILE, JSON.stringify(cache, null, 2), "utf-8");
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 sep = "\u2500".repeat(52);
8044
+ const sep2 = "\u2500".repeat(52);
7274
8045
  outputLine("");
7275
- outputLine(` \u2500\u2500 Diagnostic Report (copy & share) ${sep.slice(35)}`);
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(` ${sep}`);
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 sep = "-".repeat(52);
8056
+ const sep2 = "-".repeat(52);
7286
8057
  const lines = [
7287
- `-- Diagnostic Report (copy & share) ${sep.slice(35)}`
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(sep, "");
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((resolve) => {
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
- resolve(passed);
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 ? "43f321d" : "dev";
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((resolve) => {
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
- resolve({ ok: true, ms: Date.now() - t0 });
8591
+ resolve3({ ok: true, ms: Date.now() - t0 });
7821
8592
  });
7822
8593
  socket.once("timeout", () => {
7823
8594
  cleanup();
7824
- resolve({ ok: false, ms: Date.now() - t0, error: `timed out after ${timeoutMs}ms` });
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
- resolve({ ok: false, ms: Date.now() - t0, error: err.message });
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 readFileSync4, writeFileSync as writeFileSync4, mkdirSync as mkdirSync4 } from "fs";
8093
- import { dirname as dirname3, join as join4 } from "path";
8094
- import { homedir as homedir4 } from "os";
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 = join4(homedir4(), ".okx", "last_check");
8867
+ var CACHE_FILE2 = join7(homedir5(), ".okx", "last_check");
8097
8868
  var THROTTLE_MS = 12 * 60 * 60 * 1e3;
8098
- var NPM_BIN = join4(dirname3(process.execPath), process.platform === "win32" ? "npm.cmd" : "npm");
8869
+ var NPM_BIN = join7(dirname5(process.execPath), process.platform === "win32" ? "npm.cmd" : "npm");
8099
8870
  function readLastCheck() {
8100
8871
  try {
8101
- return parseInt(readFileSync4(CACHE_FILE2, "utf-8").trim(), 10) || 0;
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
- mkdirSync4(join4(homedir4(), ".okx"), { recursive: true });
8109
- writeFileSync4(CACHE_FILE2, String(Math.floor(Date.now() / 1e3)), "utf-8");
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 ?? false,
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 getData(result) {
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 = getData(result);
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 = getData(result);
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 = getData(result);
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 = getData(result);
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 = getData(result);
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 = getData(result);
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 = getData(result);
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 = getData(result);
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 = getData(result);
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 = getData(result);
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 = getData(result);
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 = getData(result);
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 = getData(result);
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 = getData(result);
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 getData2(result) {
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 = getData2(result);
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 = getData2(result);
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 = getData2(result);
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 = getData2(result);
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 = getData2(result);
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 = getData2(result);
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 = getData2(result);
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 = getData2(result);
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 = getData2(result);
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 = getData2(result);
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 = getData2(result);
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 getData3(result) {
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 = getData3(result);
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 = getData3(result);
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 = getData3(result);
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 = getData3(result);
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 = getData3(result);
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 = getData3(result);
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 = getData3(result);
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 = getData3(result);
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 = getData3(result);
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 = getData3(result);
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 = getData3(result);
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 = getData3(result);
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 getData4(result) {
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 = getData4(result);
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 = getData4(result);
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 = getData4(result);
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 = getData4(result);
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 = getData4(result);
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 = getData4(result);
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 = getData4(result);
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 = getData4(result);
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 = getData4(result);
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 = getData4(result);
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 = getData4(result);
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 = getData4(result);
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 = getData4(result);
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 = getData4(result);
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 = getData4(result);
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 = getData4(result);
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 getData5(result) {
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 = getData5(result);
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 = getData5(result);
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 = getData5(result);
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 = getData5(result);
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 = getData5(result);
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 = getData5(result);
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 = getData5(result);
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 = getData5(result);
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 = getData5(result);
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 = getData5(result);
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 = getData5(result);
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 = getData5(result);
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 = getData5(result);
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 = getData5(result);
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 = getData5(result);
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 = getData5(result);
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 getData6(result) {
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 = getData6(result);
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 = getData6(result);
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 = getData6(result);
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 = getData6(result);
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 = getData6(result);
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 = getData6(result);
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 = getData6(result);
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 = getData6(result);
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 = getData6(result);
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 = getData6(result);
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 = getData6(result);
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 = getData6(result);
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 = getData6(result);
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 = getData6(result);
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((resolve) => rl.question(question, resolve));
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 getData7(result) {
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 = getData7(result) ?? [];
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 = (getData7(result) ?? [])[0];
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 = getData7(result) ?? [];
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 = getData7(result);
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 = getData7(result);
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 = getData7(result);
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 = getData7(result);
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 = getData7(result) ?? [];
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 = (getData7(result) ?? [])[0];
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 = getData7(result) ?? [];
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 ? "43f321d" : "dev";
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,