@okx_ai/okx-trade-cli 1.3.1-beta.12 → 1.3.1-beta.14

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
@@ -21,25 +21,28 @@ import {
21
21
  import { homedir as homedir2 } from "os";
22
22
  import { join as join2, dirname } from "path";
23
23
  import { createHmac } from "crypto";
24
+ import { spawn, execFile as execFile2 } from "child_process";
25
+ import { homedir as homedir3 } from "os";
26
+ import { join as join3 } from "path";
24
27
  import fs from "fs";
25
28
  import path from "path";
26
29
  import os from "os";
27
30
  import { writeFileSync as writeFileSync2, renameSync as renameSync2, unlinkSync as unlinkSync2, mkdirSync as mkdirSync2 } from "fs";
28
- import { join as join3, resolve, basename, sep } from "path";
31
+ import { join as join4, resolve, basename, sep } from "path";
29
32
  import { randomUUID } from "crypto";
30
33
  import yauzl from "yauzl";
31
34
  import { createWriteStream, mkdirSync as mkdirSync3 } from "fs";
32
35
  import { resolve as resolve2, dirname as dirname2 } from "path";
33
36
  import { readFileSync as readFileSync2, existsSync } from "fs";
34
- import { join as join4 } from "path";
37
+ import { join as join5 } from "path";
35
38
  import { readFileSync as readFileSync3, writeFileSync as writeFileSync3, mkdirSync as mkdirSync4, existsSync as existsSync2 } from "fs";
36
- import { join as join5, dirname as dirname3 } from "path";
37
- import { homedir as homedir3 } from "os";
38
- import { readFileSync as readFileSync4, writeFileSync as writeFileSync4, mkdirSync as mkdirSync5, existsSync as existsSync3 } from "fs";
39
- import { join as join6, dirname as dirname4 } from "path";
39
+ import { join as join6, dirname as dirname3 } from "path";
40
40
  import { homedir as homedir4 } from "os";
41
+ import { readFileSync as readFileSync4, writeFileSync as writeFileSync4, mkdirSync as mkdirSync5, existsSync as existsSync3 } from "fs";
42
+ import { join as join7, dirname as dirname4 } from "path";
43
+ import { homedir as homedir5 } from "os";
41
44
 
42
- // ../../node_modules/.pnpm/smol-toml@1.6.0/node_modules/smol-toml/dist/error.js
45
+ // ../../node_modules/smol-toml/dist/error.js
43
46
  function getLineColFromPtr(string, ptr) {
44
47
  let lines = string.slice(0, ptr).split(/\r\n|\n|\r/g);
45
48
  return [lines.length, lines.pop().length + 1];
@@ -79,7 +82,7 @@ ${codeblock}`, options);
79
82
  }
80
83
  };
81
84
 
82
- // ../../node_modules/.pnpm/smol-toml@1.6.0/node_modules/smol-toml/dist/util.js
85
+ // ../../node_modules/smol-toml/dist/util.js
83
86
  function isEscaped(str, ptr) {
84
87
  let i = 0;
85
88
  while (str[ptr - ++i] === "\\")
@@ -110,9 +113,14 @@ function skipComment(str, ptr) {
110
113
  }
111
114
  function skipVoid(str, ptr, banNewLines, banComments) {
112
115
  let c;
113
- while ((c = str[ptr]) === " " || c === " " || !banNewLines && (c === "\n" || c === "\r" && str[ptr + 1] === "\n"))
114
- ptr++;
115
- return banComments || c !== "#" ? ptr : skipVoid(str, skipComment(str, ptr), banNewLines);
116
+ while (1) {
117
+ while ((c = str[ptr]) === " " || c === " " || !banNewLines && (c === "\n" || c === "\r" && str[ptr + 1] === "\n"))
118
+ ptr++;
119
+ if (banComments || c !== "#")
120
+ break;
121
+ ptr = skipComment(str, ptr);
122
+ }
123
+ return ptr;
116
124
  }
117
125
  function skipUntil(str, ptr, sep2, end, banNewLines = false) {
118
126
  if (!end) {
@@ -153,7 +161,7 @@ function getStringEnd(str, seek) {
153
161
  return seek;
154
162
  }
155
163
 
156
- // ../../node_modules/.pnpm/smol-toml@1.6.0/node_modules/smol-toml/dist/date.js
164
+ // ../../node_modules/smol-toml/dist/date.js
157
165
  var DATE_TIME_RE = /^(\d{4}-\d{2}-\d{2})?[T ]?(?:(\d{2}):\d{2}(?::\d{2}(?:\.\d+)?)?)?(Z|[-+]\d{2}:\d{2})?$/i;
158
166
  var TomlDate = class _TomlDate extends Date {
159
167
  #hasDate = false;
@@ -245,7 +253,7 @@ var TomlDate = class _TomlDate extends Date {
245
253
  }
246
254
  };
247
255
 
248
- // ../../node_modules/.pnpm/smol-toml@1.6.0/node_modules/smol-toml/dist/primitive.js
256
+ // ../../node_modules/smol-toml/dist/primitive.js
249
257
  var INT_REGEX = /^((0x[0-9a-fA-F](_?[0-9a-fA-F])*)|(([+-]|0[ob])?\d(_?\d)*))$/;
250
258
  var FLOAT_REGEX = /^[+-]?\d(_?\d)*(\.\d(_?\d)*)?([eE][+-]?\d(_?\d)*)?$/;
251
259
  var LEADING_ZERO = /^[+-]?0[0-9_]/;
@@ -384,7 +392,7 @@ function parseValue(value, toml, ptr, integersAsBigInt) {
384
392
  return date;
385
393
  }
386
394
 
387
- // ../../node_modules/.pnpm/smol-toml@1.6.0/node_modules/smol-toml/dist/extract.js
395
+ // ../../node_modules/smol-toml/dist/extract.js
388
396
  function sliceAndTrimEndOf(str, startPtr, endPtr) {
389
397
  let value = str.slice(startPtr, endPtr);
390
398
  let commentIdx = value.indexOf("#");
@@ -451,7 +459,7 @@ function extractValue(str, ptr, end, depth, integersAsBigInt) {
451
459
  ];
452
460
  }
453
461
 
454
- // ../../node_modules/.pnpm/smol-toml@1.6.0/node_modules/smol-toml/dist/struct.js
462
+ // ../../node_modules/smol-toml/dist/struct.js
455
463
  var KEY_PART_RE = /^[a-zA-Z0-9-_]+[ \t]*$/;
456
464
  function parseKey(str, ptr, end = "=") {
457
465
  let dot = ptr - 1;
@@ -599,7 +607,7 @@ function parseArray(str, ptr, depth, integersAsBigInt) {
599
607
  return [res, ptr];
600
608
  }
601
609
 
602
- // ../../node_modules/.pnpm/smol-toml@1.6.0/node_modules/smol-toml/dist/parse.js
610
+ // ../../node_modules/smol-toml/dist/parse.js
603
611
  function peekTable(key, table, meta, type) {
604
612
  let t = table;
605
613
  let m = meta;
@@ -724,7 +732,7 @@ function parse(toml, { maxDepth = 1e3, integersAsBigInt } = {}) {
724
732
  return res;
725
733
  }
726
734
 
727
- // ../../node_modules/.pnpm/smol-toml@1.6.0/node_modules/smol-toml/dist/stringify.js
735
+ // ../../node_modules/smol-toml/dist/stringify.js
728
736
  var BARE_KEY = /^[a-z0-9-_]+$/i;
729
737
  function extendedTypeOf(obj) {
730
738
  let type = typeof obj;
@@ -867,8 +875,8 @@ function stringify(obj, { maxDepth = 1e3, numbersAsFloat = false } = {}) {
867
875
 
868
876
  // ../core/dist/index.js
869
877
  import { readFileSync as readFileSync5, writeFileSync as writeFileSync5, mkdirSync as mkdirSync6, existsSync as existsSync4 } from "fs";
870
- import { join as join7 } from "path";
871
- import { homedir as homedir5 } from "os";
878
+ import { join as join8 } from "path";
879
+ import { homedir as homedir6 } from "os";
872
880
  import fs2 from "fs";
873
881
  import path2 from "path";
874
882
  import os2 from "os";
@@ -876,6 +884,18 @@ import * as fs3 from "fs";
876
884
  import * as path3 from "path";
877
885
  import * as os3 from "os";
878
886
  import { execFileSync } from "child_process";
887
+ import {
888
+ createWriteStream as createWriteStream3,
889
+ mkdirSync as mkdirSync9,
890
+ chmodSync as chmodSync2,
891
+ existsSync as existsSync7,
892
+ unlinkSync as unlinkSync4,
893
+ renameSync as renameSync4
894
+ } from "fs";
895
+ import { homedir as homedir9, platform as platform2 } from "os";
896
+ import { join as join11, dirname as dirname7 } from "path";
897
+ import { get as httpsGet2 } from "https";
898
+ import { get as httpGet2 } from "http";
879
899
  import {
880
900
  readFileSync as readFileSync7,
881
901
  createWriteStream as createWriteStream2,
@@ -886,10 +906,13 @@ import {
886
906
  renameSync as renameSync3
887
907
  } from "fs";
888
908
  import { createHash } from "crypto";
889
- import { homedir as homedir7, platform, arch } from "os";
890
- import { join as join9, dirname as dirname6 } from "path";
909
+ import { homedir as homedir8, platform, arch } from "os";
910
+ import { join as join10, dirname as dirname6 } from "path";
891
911
  import { get as httpsGet } from "https";
892
912
  import { get as httpGet } from "http";
913
+ import { readFileSync as readFileSync8, writeFileSync as writeFileSync7, mkdirSync as mkdirSync10, unlinkSync as unlinkSync5, existsSync as existsSync8 } from "fs";
914
+ import { join as join12 } from "path";
915
+ import { homedir as homedir10 } from "os";
893
916
  var EXEC_TIMEOUT_MS = 3e4;
894
917
  var ALLOWED_DOMAIN_RE = /^[\w.-]+\.okx\.com$/;
895
918
  var DOH_BIN_DIR = join(homedir(), ".okx", "bin");
@@ -898,7 +921,7 @@ function getDohBinaryPath() {
898
921
  return process.env.OKX_DOH_BINARY_PATH;
899
922
  }
900
923
  const ext = process.platform === "win32" ? ".exe" : "";
901
- return join(DOH_BIN_DIR, `okx-pilot${ext}`);
924
+ return join(DOH_BIN_DIR, `okx-doh-resolver${ext}`);
902
925
  }
903
926
  function execDohBinary(domain, exclude = [], userAgent) {
904
927
  if (!ALLOWED_DOMAIN_RE.test(domain)) {
@@ -969,7 +992,7 @@ function classifyAndCache(node, hostname, failedNodes, cachePath) {
969
992
  if (!node) {
970
993
  return { mode: null, node: null };
971
994
  }
972
- if (node.ip === hostname && node.host === hostname) {
995
+ if (node.ip === hostname || node.host === hostname) {
973
996
  writeCache(hostname, {
974
997
  mode: "direct",
975
998
  node: null,
@@ -1201,6 +1224,11 @@ var ConfigError = class extends OkxMcpError {
1201
1224
  super("ConfigError", message, { suggestion });
1202
1225
  }
1203
1226
  };
1227
+ var NotLoggedInError = class extends ConfigError {
1228
+ constructor(suggestion = "Run `okx auth login` to authenticate.") {
1229
+ super("Not logged in.", suggestion);
1230
+ }
1231
+ };
1204
1232
  var ValidationError = class extends OkxMcpError {
1205
1233
  constructor(message, suggestion) {
1206
1234
  super("ValidationError", message, { suggestion });
@@ -1253,6 +1281,97 @@ function toToolErrorPayload(error, fallbackEndpoint) {
1253
1281
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
1254
1282
  };
1255
1283
  }
1284
+ var EXIT_CODES = {
1285
+ SUCCESS: 0,
1286
+ UNAUTHORIZED_CALLER: 1,
1287
+ NOT_LOGGED_IN: 2,
1288
+ REFRESH_FAILED: 3
1289
+ };
1290
+ var EXEC_TIMEOUT_MS2 = 5e3;
1291
+ var AUTH_BIN_DIR = join3(homedir3(), ".okx", "bin");
1292
+ function getAuthBinaryPath() {
1293
+ if (process.env.OKX_AUTH_BIN) {
1294
+ return process.env.OKX_AUTH_BIN;
1295
+ }
1296
+ const ext = process.platform === "win32" ? ".exe" : "";
1297
+ return join3(AUTH_BIN_DIR, `okx-auth${ext}`);
1298
+ }
1299
+ function execAuthToken() {
1300
+ const binPath = getAuthBinaryPath();
1301
+ return new Promise((resolve3, reject) => {
1302
+ const child = spawn(binPath, ["token"], {
1303
+ stdio: ["ignore", "ignore", "inherit", "pipe"]
1304
+ // stdin stdout stderr fd3 (pipe)
1305
+ });
1306
+ const chunks = [];
1307
+ const fd3 = child.stdio[3];
1308
+ fd3.on("data", (chunk) => chunks.push(chunk));
1309
+ child.on("error", (err) => {
1310
+ reject(new ConfigError(
1311
+ `Failed to spawn okx-auth: ${err.message}`,
1312
+ "Ensure the okx-auth binary exists and is executable."
1313
+ ));
1314
+ });
1315
+ child.on("close", (code) => {
1316
+ if (code === EXIT_CODES.SUCCESS) {
1317
+ const token = Buffer.concat(chunks).toString("utf-8").trim();
1318
+ if (!token) {
1319
+ reject(new AuthenticationError(
1320
+ "okx-auth returned empty token.",
1321
+ "Run `okx auth login` to re-authenticate."
1322
+ ));
1323
+ return;
1324
+ }
1325
+ resolve3(token);
1326
+ return;
1327
+ }
1328
+ if (code === EXIT_CODES.NOT_LOGGED_IN) {
1329
+ reject(new NotLoggedInError());
1330
+ return;
1331
+ }
1332
+ if (code === EXIT_CODES.UNAUTHORIZED_CALLER) {
1333
+ reject(new AuthenticationError(
1334
+ "okx-auth rejected the caller (unauthorized).",
1335
+ "Ensure you are running from a trusted OKX tool."
1336
+ ));
1337
+ return;
1338
+ }
1339
+ if (code === EXIT_CODES.REFRESH_FAILED) {
1340
+ reject(new AuthenticationError(
1341
+ "Token refresh failed.",
1342
+ "Run `okx auth login` to re-authenticate."
1343
+ ));
1344
+ return;
1345
+ }
1346
+ reject(new AuthenticationError(
1347
+ `okx-auth token exited with code ${code}.`,
1348
+ "Run `okx auth login` to re-authenticate."
1349
+ ));
1350
+ });
1351
+ });
1352
+ }
1353
+ function execAuthStatus() {
1354
+ const binPath = getAuthBinaryPath();
1355
+ return new Promise((resolve3) => {
1356
+ execFile2(
1357
+ binPath,
1358
+ ["status", "--json"],
1359
+ { timeout: EXEC_TIMEOUT_MS2, encoding: "utf-8" },
1360
+ (error, stdout) => {
1361
+ if (error) {
1362
+ resolve3(null);
1363
+ return;
1364
+ }
1365
+ try {
1366
+ const result = JSON.parse(stdout);
1367
+ resolve3(result);
1368
+ } catch {
1369
+ resolve3(null);
1370
+ }
1371
+ }
1372
+ );
1373
+ });
1374
+ }
1256
1375
  function sleep(ms) {
1257
1376
  return new Promise((resolve3) => {
1258
1377
  setTimeout(resolve3, ms);
@@ -1331,10 +1450,10 @@ var OKX_CODE_BEHAVIORS = {
1331
1450
  "50011": { retry: true, suggestion: "Rate limited. Back off and retry after a delay." },
1332
1451
  "50061": { retry: true, suggestion: "Too many connections. Reduce request frequency and retry." },
1333
1452
  // Server temporarily unavailable → retryable
1334
- "50001": { retry: true, suggestion: "Service temporarily unavailable. Retry in a few minutes." },
1335
- "50004": { retry: true, suggestion: "Endpoint request timeout. Retry later." },
1453
+ "50001": { retry: true, suggestion: "OKX system upgrade in progress. Retry in a few minutes." },
1454
+ "50004": { retry: true, suggestion: "Endpoint temporarily unavailable. Retry later." },
1336
1455
  "50013": { retry: true, suggestion: "System busy. Retry after 1-2 seconds." },
1337
- "50026": { retry: true, suggestion: "System error. Retry in a few minutes." },
1456
+ "50026": { retry: true, suggestion: "Order book system upgrading. Retry in a few minutes." },
1338
1457
  // Region / compliance restriction → do not retry
1339
1458
  "51155": { retry: false, suggestion: "Feature unavailable in your region (site: {site}). Verify your site setting matches your account registration region. Available sites: global, eea, us. Do not retry." },
1340
1459
  "51734": { retry: false, suggestion: "Feature not supported for your KYC country (site: {site}). Verify your site setting matches your account registration region. Available sites: global, eea, us. Do not retry." },
@@ -1384,6 +1503,7 @@ function maskKey(key) {
1384
1503
  if (key.length <= 8) return "***";
1385
1504
  return `${key.slice(0, 3)}***${key.slice(-3)}`;
1386
1505
  }
1506
+ var TOKEN_CACHE_TTL_MS = 6e4;
1387
1507
  function vlog2(message) {
1388
1508
  process.stderr.write(`[verbose] ${message}
1389
1509
  `);
@@ -1392,6 +1512,8 @@ var OkxRestClient = class _OkxRestClient {
1392
1512
  config;
1393
1513
  rateLimiter;
1394
1514
  dispatcher;
1515
+ cachedAccessToken;
1516
+ cachedAccessTokenAt = 0;
1395
1517
  doh;
1396
1518
  constructor(config) {
1397
1519
  this.config = config;
@@ -1406,6 +1528,51 @@ var OkxRestClient = class _OkxRestClient {
1406
1528
  hasCustomProxy: !!config.proxyUrl
1407
1529
  });
1408
1530
  }
1531
+ /**
1532
+ * Resolve OAuth access token via the okx-auth binary (fd3 pipe).
1533
+ * Caches the token for 60 s to avoid spawning the binary on every
1534
+ * request. The binary handles refresh internally (300s TTL lead),
1535
+ * so periodic re-calls let it serve a fresh token when needed.
1536
+ * Returns null when not logged in.
1537
+ */
1538
+ async resolveAccessToken() {
1539
+ if (this.cachedAccessToken && Date.now() - this.cachedAccessTokenAt < TOKEN_CACHE_TTL_MS) {
1540
+ return this.cachedAccessToken;
1541
+ }
1542
+ try {
1543
+ const token = await execAuthToken();
1544
+ this.cachedAccessToken = token;
1545
+ this.cachedAccessTokenAt = Date.now();
1546
+ return token;
1547
+ } catch (e) {
1548
+ if (e instanceof NotLoggedInError) {
1549
+ return null;
1550
+ }
1551
+ throw e;
1552
+ }
1553
+ }
1554
+ /**
1555
+ * Dynamic auth — determines auth method per request.
1556
+ *
1557
+ * 1. API key in config → HMAC signing (no OAuth fallback)
1558
+ * 2. OAuth token via okx-auth binary → Bearer token
1559
+ * 3. Neither → throw ConfigError
1560
+ */
1561
+ async applyAuth(headers, method, requestPath, bodyJson, timestamp) {
1562
+ if (this.config.apiKey && this.config.secretKey && this.config.passphrase) {
1563
+ this.setAuthHeaders(headers, method, requestPath, bodyJson, timestamp);
1564
+ return;
1565
+ }
1566
+ const accessToken = await this.resolveAccessToken();
1567
+ if (accessToken) {
1568
+ headers.set("Authorization", `Bearer ${accessToken}`);
1569
+ return;
1570
+ }
1571
+ throw new ConfigError(
1572
+ "No credentials found.",
1573
+ "Run `okx auth login` to authenticate, or configure API key credentials."
1574
+ );
1575
+ }
1409
1576
  /** The canonical base URL for this client (e.g. https://www.okx.com). */
1410
1577
  get baseUrl() {
1411
1578
  return this.config.baseUrl;
@@ -1463,18 +1630,6 @@ var OkxRestClient = class _OkxRestClient {
1463
1630
  });
1464
1631
  }
1465
1632
  setAuthHeaders(headers, method, requestPath, bodyJson, timestamp) {
1466
- if (!this.config.hasAuth) {
1467
- throw new ConfigError(
1468
- "Private endpoint requires API credentials.",
1469
- "Configure OKX_API_KEY, OKX_SECRET_KEY and OKX_PASSPHRASE."
1470
- );
1471
- }
1472
- if (!this.config.apiKey || !this.config.secretKey || !this.config.passphrase) {
1473
- throw new ConfigError(
1474
- "Invalid private API credentials state.",
1475
- "Ensure all OKX credentials are set."
1476
- );
1477
- }
1478
1633
  const payload = `${timestamp}${method.toUpperCase()}${requestPath}${bodyJson}`;
1479
1634
  const signature = signOkxPayload(payload, this.config.secretKey);
1480
1635
  headers.set("OK-ACCESS-KEY", this.config.apiKey);
@@ -1589,7 +1744,7 @@ var OkxRestClient = class _OkxRestClient {
1589
1744
  const conn = this.doh.getConnectionParams();
1590
1745
  this.logRequest("POST", `${conn.baseUrl}${path42}`, "private");
1591
1746
  const reqConfig = { method: "POST", path: path42, auth: "private" };
1592
- const headers = this.buildHeaders(reqConfig, path42, bodyJson, getNow());
1747
+ const headers = await this.buildHeaders(reqConfig, path42, bodyJson, getNow());
1593
1748
  if (conn.userAgent) {
1594
1749
  headers.set("User-Agent", conn.userAgent);
1595
1750
  }
@@ -1706,7 +1861,7 @@ var OkxRestClient = class _OkxRestClient {
1706
1861
  // Header building
1707
1862
  // ---------------------------------------------------------------------------
1708
1863
  /** Build HTTP headers. reqConfig.extraHeaders must NOT contain auth keys (OK-ACCESS-*). */
1709
- buildHeaders(reqConfig, requestPath, bodyJson, timestamp) {
1864
+ async buildHeaders(reqConfig, requestPath, bodyJson, timestamp) {
1710
1865
  const headers = new Headers({
1711
1866
  "Content-Type": "application/json",
1712
1867
  Accept: "application/json"
@@ -1715,7 +1870,7 @@ var OkxRestClient = class _OkxRestClient {
1715
1870
  headers.set("User-Agent", this.config.userAgent);
1716
1871
  }
1717
1872
  if (reqConfig.auth === "private") {
1718
- this.setAuthHeaders(headers, reqConfig.method, requestPath, bodyJson, timestamp);
1873
+ await this.applyAuth(headers, reqConfig.method, requestPath, bodyJson, timestamp);
1719
1874
  }
1720
1875
  const useSimulated = reqConfig.simulatedTrading !== void 0 ? reqConfig.simulatedTrading : this.config.demo;
1721
1876
  if (useSimulated) {
@@ -1769,7 +1924,7 @@ var OkxRestClient = class _OkxRestClient {
1769
1924
  if (reqConfig.rateLimit) {
1770
1925
  await this.rateLimiter.consume(reqConfig.rateLimit);
1771
1926
  }
1772
- const headers = this.buildHeaders(reqConfig, requestPath, bodyJson, timestamp);
1927
+ const headers = await this.buildHeaders(reqConfig, requestPath, bodyJson, timestamp);
1773
1928
  if (conn.userAgent) {
1774
1929
  headers.set("User-Agent", conn.userAgent);
1775
1930
  }
@@ -2192,7 +2347,7 @@ var OKX_SITES = {
2192
2347
  },
2193
2348
  us: {
2194
2349
  label: "US",
2195
- apiBaseUrl: "https://app.okx.com",
2350
+ apiBaseUrl: "https://us.okx.com",
2196
2351
  webUrl: "https://app.okx.com"
2197
2352
  }
2198
2353
  };
@@ -3722,7 +3877,7 @@ function safeWriteFile(targetDir, fileName, data) {
3722
3877
  throw new Error(`Invalid file name: "${fileName}"`);
3723
3878
  }
3724
3879
  const resolvedDir = resolve(targetDir);
3725
- const filePath = join3(resolvedDir, safeName);
3880
+ const filePath = join4(resolvedDir, safeName);
3726
3881
  const resolvedPath = resolve(filePath);
3727
3882
  if (!resolvedPath.startsWith(resolvedDir + sep)) {
3728
3883
  throw new Error(`Path traversal detected: "${fileName}" resolves outside target directory`);
@@ -3850,7 +4005,7 @@ async function extractSkillZip(zipPath, targetDir, limits) {
3850
4005
  });
3851
4006
  }
3852
4007
  function readMetaJson(contentDir) {
3853
- const metaPath = join4(contentDir, "_meta.json");
4008
+ const metaPath = join5(contentDir, "_meta.json");
3854
4009
  if (!existsSync(metaPath)) {
3855
4010
  throw new Error(`_meta.json not found in ${contentDir}. Invalid skill package.`);
3856
4011
  }
@@ -3876,12 +4031,12 @@ function readMetaJson(contentDir) {
3876
4031
  };
3877
4032
  }
3878
4033
  function validateSkillMdExists(contentDir) {
3879
- const skillMdPath = join4(contentDir, "SKILL.md");
4034
+ const skillMdPath = join5(contentDir, "SKILL.md");
3880
4035
  if (!existsSync(skillMdPath)) {
3881
4036
  throw new Error(`SKILL.md not found in ${contentDir}. Invalid skill package.`);
3882
4037
  }
3883
4038
  }
3884
- var DEFAULT_REGISTRY_PATH = join5(homedir3(), ".okx", "skills", "registry.json");
4039
+ var DEFAULT_REGISTRY_PATH = join6(homedir4(), ".okx", "skills", "registry.json");
3885
4040
  function readRegistry(registryPath = DEFAULT_REGISTRY_PATH) {
3886
4041
  if (!existsSync2(registryPath)) {
3887
4042
  return { version: 1, skills: {} };
@@ -5219,7 +5374,7 @@ function registerOnchainEarnTools() {
5219
5374
  ];
5220
5375
  }
5221
5376
  var DCD_CODE_BEHAVIORS = {
5222
- "50001": { retry: true, suggestion: "Service temporarily unavailable. Retry in a few minutes." },
5377
+ "50001": { retry: true, suggestion: "DCD service is down. Retry in a few minutes." },
5223
5378
  "50002": { retry: false, suggestion: "Invalid JSON in request body. This is likely a bug \u2014 check request parameters." },
5224
5379
  "50014": { retry: false, suggestion: "Missing required parameter. Check that all required fields are provided." },
5225
5380
  "50016": { retry: false, suggestion: "notionalCcy does not match productId option type. Use baseCcy for CALL, quoteCcy for PUT." },
@@ -7837,40 +7992,38 @@ var NEWS_DETAIL = "/api/v5/orbit/news-detail";
7837
7992
  var NEWS_DOMAINS = "/api/v5/orbit/news-platform";
7838
7993
  var SENTIMENT_QUERY = "/api/v5/orbit/currency-sentiment-query";
7839
7994
  var SENTIMENT_RANKING = "/api/v5/orbit/currency-sentiment-ranking";
7840
- var NEWS_LANGUAGE = ["en-US", "zh-CN"];
7995
+ var NEWS_LANGUAGE = ["en_US", "zh_CN"];
7841
7996
  function langHeader(lang) {
7842
- if (lang === "zh-CN" || lang === "zh_CN") return { "Accept-Language": "zh-CN" };
7843
- return { "Accept-Language": "en-US" };
7997
+ const resolved = lang === "zh_CN" ? "zh_CN" : "en_US";
7998
+ return { "Accept-Language": resolved };
7844
7999
  }
7845
8000
  var NEWS_DETAIL_LVL = ["brief", "summary", "full"];
7846
- var NEWS_IMPORTANCE = ["high", "low"];
8001
+ var NEWS_IMPORTANCE = ["high", "medium", "low"];
7847
8002
  var NEWS_SENTIMENT = ["bullish", "bearish", "neutral"];
7848
8003
  var NEWS_SORT = ["latest", "relevant"];
7849
8004
  var SENTIMENT_PERIOD = ["1h", "4h", "24h"];
7850
8005
  var D_COINS_NEWS = 'Comma-separated uppercase ticker symbols (e.g. "BTC,ETH"). Normalize names/aliases to standard tickers.';
7851
8006
  var D_COINS_SENTIMENT = 'Comma-separated uppercase ticker symbols, max 20 (e.g. "BTC,ETH"). Normalize names/aliases to standard tickers.';
7852
- var D_LANGUAGE = "Content language: zh-CN or en-US. Infer from user's message. No server default.";
7853
- var D_BEGIN = "Start time, Unix epoch milliseconds. API defaults to 72 hours ago when omitted. Pass explicitly for older topics (e.g. 'last 30 days'). Max range: 180 days. Parse relative time if given.";
8007
+ var D_LANGUAGE = "Content language: zh_CN or en_US. Infer from user's message. No server default.";
8008
+ var D_BEGIN = "Start time, Unix epoch milliseconds. Parse relative time if given (e.g. 'yesterday', 'last 7 days').";
7854
8009
  var D_END = "End time, Unix epoch milliseconds. Parse relative time if given. Omit for no upper bound.";
7855
- var D_IMPORTANCE = "Importance filter: high (server default) or low. Omit unless user wants broader coverage.";
7856
- var D_PLATFORM = "Filter by news source. Use values from news_get_domains (e.g. blockbeats, odaily_flash). Omit for all sources.";
8010
+ var D_IMPORTANCE = "Importance filter: high (server default), medium, low. Omit unless user wants broader coverage.";
7857
8011
  var D_LIMIT = "Number of results (default 10, max 50).";
7858
8012
  function registerNewsTools() {
7859
- const tools = [
8013
+ return [
7860
8014
  // -----------------------------------------------------------------------
7861
8015
  // News browsing tools
7862
8016
  // -----------------------------------------------------------------------
7863
8017
  {
7864
8018
  name: "news_get_latest",
7865
8019
  module: "news",
7866
- description: "Get crypto news sorted by time. Omitting importance still returns only high-importance news (server default). Pass importance='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. For coin-specific news, use news_get_by_coin instead.",
8020
+ 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. For coin-specific news, use news_get_by_coin instead.",
7867
8021
  isWrite: false,
7868
8022
  inputSchema: {
7869
8023
  type: "object",
7870
8024
  properties: {
7871
8025
  coins: { type: "string", description: D_COINS_NEWS + " Optional." },
7872
8026
  importance: { type: "string", enum: [...NEWS_IMPORTANCE], description: D_IMPORTANCE },
7873
- platform: { type: "string", description: D_PLATFORM },
7874
8027
  begin: { type: "number", description: D_BEGIN },
7875
8028
  end: { type: "number", description: D_END },
7876
8029
  language: { type: "string", enum: [...NEWS_LANGUAGE], description: D_LANGUAGE },
@@ -7891,7 +8044,6 @@ function registerNewsTools() {
7891
8044
  compactObject({
7892
8045
  sortBy: "latest",
7893
8046
  importance: readString(args, "importance"),
7894
- platform: readString(args, "platform"),
7895
8047
  ccyList: readString(args, "coins"),
7896
8048
  begin: readNumber(args, "begin"),
7897
8049
  end: readNumber(args, "end"),
@@ -7915,7 +8067,6 @@ function registerNewsTools() {
7915
8067
  properties: {
7916
8068
  coins: { type: "string", description: D_COINS_NEWS + " Required." },
7917
8069
  importance: { type: "string", enum: [...NEWS_IMPORTANCE], description: D_IMPORTANCE },
7918
- platform: { type: "string", description: D_PLATFORM },
7919
8070
  begin: { type: "number", description: D_BEGIN },
7920
8071
  end: { type: "number", description: D_END },
7921
8072
  language: { type: "string", enum: [...NEWS_LANGUAGE], description: D_LANGUAGE },
@@ -7936,7 +8087,6 @@ function registerNewsTools() {
7936
8087
  sortBy: "latest",
7937
8088
  ccyList: coins,
7938
8089
  importance: readString(args, "importance"),
7939
- platform: readString(args, "platform"),
7940
8090
  begin: readNumber(args, "begin"),
7941
8091
  end: readNumber(args, "end"),
7942
8092
  detailLvl: readString(args, "detailLvl"),
@@ -7962,7 +8112,6 @@ function registerNewsTools() {
7962
8112
  },
7963
8113
  coins: { type: "string", description: D_COINS_NEWS + " Optional." },
7964
8114
  importance: { type: "string", enum: [...NEWS_IMPORTANCE], description: D_IMPORTANCE },
7965
- platform: { type: "string", description: D_PLATFORM },
7966
8115
  sentiment: {
7967
8116
  type: "string",
7968
8117
  enum: [...NEWS_SENTIMENT],
@@ -7990,7 +8139,6 @@ function registerNewsTools() {
7990
8139
  keyword: readString(args, "keyword") || void 0,
7991
8140
  sortBy: readString(args, "sortBy") ?? "relevant",
7992
8141
  importance: readString(args, "importance"),
7993
- platform: readString(args, "platform"),
7994
8142
  ccyList: readString(args, "coins"),
7995
8143
  sentiment: readString(args, "sentiment"),
7996
8144
  begin: readNumber(args, "begin"),
@@ -8136,24 +8284,6 @@ function registerNewsTools() {
8136
8284
  }
8137
8285
  }
8138
8286
  ];
8139
- const domainsIdx = tools.findIndex((t) => t.name === "news_get_domains");
8140
- if (domainsIdx === -1) throw new Error("news_get_domains not found in tools list");
8141
- const [domainsTool] = tools.splice(domainsIdx, 1);
8142
- return [...tools.map(withNewsDemoGuard), domainsTool];
8143
- }
8144
- var NEWS_DEMO_MESSAGE = "News features are not available in demo/simulated trading mode.";
8145
- var NEWS_DEMO_SUGGESTION = "Switch to a live profile to use News features.";
8146
- function withNewsDemoGuard(tool) {
8147
- const originalHandler = tool.handler;
8148
- return {
8149
- ...tool,
8150
- handler: async (args, context) => {
8151
- if (context.config.demo) {
8152
- throw new ConfigError(NEWS_DEMO_MESSAGE, NEWS_DEMO_SUGGESTION);
8153
- }
8154
- return originalHandler(args, context);
8155
- }
8156
- };
8157
8287
  }
8158
8288
  function registerOptionAlgoTools() {
8159
8289
  return [
@@ -9671,7 +9801,7 @@ function createToolRunner(client, config) {
9671
9801
  };
9672
9802
  }
9673
9803
  function configFilePath() {
9674
- return join6(homedir4(), ".okx", "config.toml");
9804
+ return join7(homedir5(), ".okx", "config.toml");
9675
9805
  }
9676
9806
  function readFullConfig() {
9677
9807
  const path42 = configFilePath();
@@ -9690,11 +9820,6 @@ Or re-run: okx config init`
9690
9820
  );
9691
9821
  }
9692
9822
  }
9693
- function readTomlProfile(profileName) {
9694
- const config = readFullConfig();
9695
- const name = profileName ?? config.default_profile ?? "default";
9696
- return config.profiles?.[name] ?? {};
9697
- }
9698
9823
  var CONFIG_HEADER = `# OKX Trade Kit Configuration
9699
9824
  # If editing manually, wrap values containing special chars in quotes:
9700
9825
  # passphrase = 'value' (if value contains # \\ ")
@@ -9743,18 +9868,24 @@ function parseModuleList(rawModules) {
9743
9868
  }
9744
9869
  return Array.from(deduped);
9745
9870
  }
9746
- function loadCredentials(toml) {
9871
+ async function loadCredentials(toml) {
9747
9872
  const apiKey = process.env.OKX_API_KEY?.trim() ?? toml.api_key;
9748
9873
  const secretKey = process.env.OKX_SECRET_KEY?.trim() ?? toml.secret_key;
9749
9874
  const passphrase = process.env.OKX_PASSPHRASE?.trim() ?? toml.passphrase;
9750
- const hasAuth = Boolean(apiKey && secretKey && passphrase);
9875
+ const hasApiKey = Boolean(apiKey && secretKey && passphrase);
9751
9876
  const partialAuth = Boolean(apiKey) || Boolean(secretKey) || Boolean(passphrase);
9752
- if (partialAuth && !hasAuth) {
9877
+ if (partialAuth && !hasApiKey) {
9753
9878
  throw new ConfigError(
9754
9879
  "Partial API credentials detected.",
9755
9880
  "Set OKX_API_KEY, OKX_SECRET_KEY and OKX_PASSPHRASE together (env vars or config.toml profile)."
9756
9881
  );
9757
9882
  }
9883
+ let hasOAuth = false;
9884
+ if (!hasApiKey) {
9885
+ const status = await execAuthStatus();
9886
+ hasOAuth = status?.status === "logged_in";
9887
+ }
9888
+ const hasAuth = hasOAuth || hasApiKey;
9758
9889
  return { apiKey, secretKey, passphrase, hasAuth };
9759
9890
  }
9760
9891
  function resolveSite(cliSite, tomlSite) {
@@ -9788,9 +9919,11 @@ function resolveDemo(cli, toml) {
9788
9919
  if (cli.demo === true) return true;
9789
9920
  return process.env.OKX_DEMO === "1" || process.env.OKX_DEMO === "true" || (toml.demo ?? false);
9790
9921
  }
9791
- function loadConfig(cli) {
9792
- const toml = readTomlProfile(cli.profile);
9793
- const creds = loadCredentials(toml);
9922
+ async function loadConfig(cli) {
9923
+ const config = readFullConfig();
9924
+ const profileName = cli.profile ?? config.default_profile ?? "default";
9925
+ const toml = config.profiles?.[profileName] ?? {};
9926
+ const creds = await loadCredentials(toml);
9794
9927
  const demo = resolveDemo(cli, toml);
9795
9928
  const site = resolveSite(cli.site, toml.site);
9796
9929
  const baseUrl = resolveBaseUrl(site, toml.base_url);
@@ -9810,6 +9943,7 @@ function loadConfig(cli) {
9810
9943
  }
9811
9944
  return {
9812
9945
  ...creds,
9946
+ profile: profileName,
9813
9947
  baseUrl,
9814
9948
  timeoutMs: Math.floor(rawTimeout),
9815
9949
  modules: parseModuleList(cli.modules),
@@ -9822,7 +9956,7 @@ function loadConfig(cli) {
9822
9956
  verbose: cli.verbose ?? false
9823
9957
  };
9824
9958
  }
9825
- var CACHE_FILE = join7(homedir5(), ".okx", "update-check.json");
9959
+ var CACHE_FILE = join8(homedir6(), ".okx", "update-check.json");
9826
9960
  var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
9827
9961
  function readCache2() {
9828
9962
  try {
@@ -9835,7 +9969,7 @@ function readCache2() {
9835
9969
  }
9836
9970
  function writeCache2(cache) {
9837
9971
  try {
9838
- mkdirSync6(join7(homedir5(), ".okx"), { recursive: true });
9972
+ mkdirSync6(join8(homedir6(), ".okx"), { recursive: true });
9839
9973
  writeFileSync5(CACHE_FILE, JSON.stringify(cache, null, 2), "utf-8");
9840
9974
  } catch {
9841
9975
  }
@@ -10005,13 +10139,13 @@ function findMsStoreClaudePath() {
10005
10139
  }
10006
10140
  function getConfigPath(client) {
10007
10141
  const home = os3.homedir();
10008
- const platform2 = process.platform;
10142
+ const platform3 = process.platform;
10009
10143
  switch (client) {
10010
10144
  case "claude-desktop":
10011
- if (platform2 === "win32") {
10145
+ if (platform3 === "win32") {
10012
10146
  return findMsStoreClaudePath() ?? path3.join(appData(), "Claude", CLAUDE_CONFIG_FILE);
10013
10147
  }
10014
- if (platform2 === "darwin") {
10148
+ if (platform3 === "darwin") {
10015
10149
  return path3.join(home, "Library", "Application Support", "Claude", CLAUDE_CONFIG_FILE);
10016
10150
  }
10017
10151
  return path3.join(process.env.XDG_CONFIG_HOME ?? path3.join(home, ".config"), "Claude", CLAUDE_CONFIG_FILE);
@@ -10127,12 +10261,13 @@ function getPlatformDir() {
10127
10261
  "darwin-arm64": "darwin-arm64",
10128
10262
  "darwin-x64": "darwin-x64",
10129
10263
  "linux-x64": "linux-x64",
10264
+ "linux-arm64": "linux-x64",
10130
10265
  "win32-x64": "win32-x64"
10131
10266
  };
10132
10267
  return map[`${p}-${a}`] ?? null;
10133
10268
  }
10134
10269
  function getBinaryName() {
10135
- return platform() === "win32" ? "okx-pilot.exe" : "okx-pilot";
10270
+ return platform() === "win32" ? "okx-doh-resolver.exe" : "okx-doh-resolver";
10136
10271
  }
10137
10272
  function hashFile(filePath) {
10138
10273
  const buf = readFileSync7(filePath);
@@ -10253,7 +10388,7 @@ async function installDohBinary(destPath, sources = CDN_SOURCES, onProgress) {
10253
10388
  if (earlyResult) return earlyResult;
10254
10389
  const platformDir = getPlatformDir();
10255
10390
  const binaryName = getBinaryName();
10256
- const resolvedDest = destPath ?? join9(homedir7(), ".okx", "bin", binaryName);
10391
+ const resolvedDest = destPath ?? join10(homedir8(), ".okx", "bin", binaryName);
10257
10392
  const tmpPath = resolvedDest + ".tmp";
10258
10393
  mkdirSync8(dirname6(resolvedDest), { recursive: true });
10259
10394
  const localHash = existsSync6(resolvedDest) ? hashFile(resolvedDest) : null;
@@ -10376,16 +10511,276 @@ function downloadText(url, timeoutMs) {
10376
10511
  })
10377
10512
  );
10378
10513
  }
10514
+ var AUTH_CDN_PATH_PREFIX = "/upgradeapp/tools/oauth";
10515
+ function getAuthBinaryName() {
10516
+ return platform2() === "win32" ? "okx-auth.exe" : "okx-auth";
10517
+ }
10518
+ function getAuthStatus(binaryPath, opts) {
10519
+ const resolvedPath = binaryPath ?? getAuthBinaryPath();
10520
+ const platformDir = getPlatformDir();
10521
+ if (!existsSync7(resolvedPath)) {
10522
+ return { binaryPath: resolvedPath, exists: false, platform: platformDir };
10523
+ }
10524
+ if (opts?.skipHash) {
10525
+ return { binaryPath: resolvedPath, exists: true, platform: platformDir };
10526
+ }
10527
+ const { size, sha256 } = hashFile(resolvedPath);
10528
+ return { binaryPath: resolvedPath, exists: true, platform: platformDir, fileSize: size, sha256 };
10529
+ }
10530
+ async function fetchAuthCdnChecksum(sources = CDN_SOURCES, timeoutMs = DOWNLOAD_TIMEOUT_MS) {
10531
+ const platformDir = getPlatformDir();
10532
+ if (!platformDir) return null;
10533
+ const checksumPath = `${AUTH_CDN_PATH_PREFIX}/${platformDir}/checksum.json`;
10534
+ for (const { host, protocol } of sources) {
10535
+ try {
10536
+ const url = `${protocol}://${host}${checksumPath}`;
10537
+ const raw = await downloadText2(url, timeoutMs);
10538
+ const data = JSON.parse(raw);
10539
+ if (typeof data.sha256 !== "string" || typeof data.size !== "number" || typeof data.target !== "string") {
10540
+ continue;
10541
+ }
10542
+ return { sha256: data.sha256, size: data.size, target: data.target, source: host };
10543
+ } catch {
10544
+ }
10545
+ }
10546
+ return null;
10547
+ }
10548
+ async function fetchAndValidateChecksum2(host, protocol, checksumPath, platformDir, timeoutMs, onProgress) {
10549
+ const checksumUrl = `${protocol}://${host}${checksumPath}`;
10550
+ onProgress?.(`Fetching checksum from ${host}...`);
10551
+ const raw = await downloadText2(checksumUrl, timeoutMs);
10552
+ const checksum = JSON.parse(raw);
10553
+ if (typeof checksum.sha256 !== "string" || typeof checksum.size !== "number" || typeof checksum.target !== "string") {
10554
+ throw new Error("Invalid checksum.json: missing sha256, size, or target");
10555
+ }
10556
+ if (checksum.target !== platformDir) {
10557
+ throw new Error(`Target mismatch: expected ${platformDir}, got ${checksum.target}`);
10558
+ }
10559
+ return { sha256: checksum.sha256, size: checksum.size, target: checksum.target };
10560
+ }
10561
+ async function downloadAndVerify2(host, protocol, binaryPath, tmpPath, checksum, timeoutMs, onProgress) {
10562
+ const binaryUrl = `${protocol}://${host}${binaryPath}`;
10563
+ onProgress?.(`Downloading binary from ${host}...`);
10564
+ await download2(binaryUrl, tmpPath, timeoutMs);
10565
+ const actual = hashFile(tmpPath);
10566
+ if (actual.size !== checksum.size) {
10567
+ throw new Error(`Size mismatch: expected ${checksum.size}, got ${actual.size}`);
10568
+ }
10569
+ if (actual.sha256 !== checksum.sha256) {
10570
+ throw new Error(`SHA-256 mismatch: expected ${checksum.sha256}, got ${actual.sha256}`);
10571
+ }
10572
+ }
10573
+ function atomicReplace2(tmpPath, resolvedDest) {
10574
+ if (platform2() === "win32") {
10575
+ try {
10576
+ unlinkSync4(resolvedDest);
10577
+ } catch {
10578
+ }
10579
+ }
10580
+ renameSync4(tmpPath, resolvedDest);
10581
+ if (platform2() !== "win32") {
10582
+ chmodSync2(resolvedDest, 493);
10583
+ }
10584
+ }
10585
+ function installPreChecks2(destPath, sources) {
10586
+ if (!destPath && process.env.OKX_AUTH_BIN) {
10587
+ return { status: "up-to-date", source: "(env override)" };
10588
+ }
10589
+ if (!getPlatformDir()) {
10590
+ return { status: "failed", error: "Unsupported platform" };
10591
+ }
10592
+ if (sources.length === 0) {
10593
+ return { status: "failed", error: "No CDN sources available" };
10594
+ }
10595
+ return null;
10596
+ }
10597
+ function isLocalUpToDate2(localHash, checksum) {
10598
+ return localHash !== null && localHash.size === checksum.size && localHash.sha256 === checksum.sha256;
10599
+ }
10600
+ async function installAuthBinary(destPath, sources = CDN_SOURCES, onProgress) {
10601
+ const earlyResult = installPreChecks2(destPath, sources);
10602
+ if (earlyResult) return earlyResult;
10603
+ const platformDir = getPlatformDir();
10604
+ const binaryName = getAuthBinaryName();
10605
+ const resolvedDest = destPath ?? join11(homedir9(), ".okx", "bin", binaryName);
10606
+ const tmpPath = resolvedDest + ".tmp";
10607
+ mkdirSync9(dirname7(resolvedDest), { recursive: true });
10608
+ const localHash = existsSync7(resolvedDest) ? hashFile(resolvedDest) : null;
10609
+ const checksumPath = `${AUTH_CDN_PATH_PREFIX}/${platformDir}/checksum.json`;
10610
+ const binaryPath = `${AUTH_CDN_PATH_PREFIX}/${platformDir}/${binaryName}`;
10611
+ const errors = [];
10612
+ for (const { host, protocol } of sources) {
10613
+ try {
10614
+ const checksum = await fetchAndValidateChecksum2(
10615
+ host,
10616
+ protocol,
10617
+ checksumPath,
10618
+ platformDir,
10619
+ DOWNLOAD_TIMEOUT_MS,
10620
+ onProgress
10621
+ );
10622
+ if (isLocalUpToDate2(localHash, checksum)) {
10623
+ onProgress?.("Already up to date (checksum match)");
10624
+ return { status: "up-to-date", source: host };
10625
+ }
10626
+ await downloadAndVerify2(host, protocol, binaryPath, tmpPath, checksum, DOWNLOAD_TIMEOUT_MS, onProgress);
10627
+ atomicReplace2(tmpPath, resolvedDest);
10628
+ onProgress?.(`Downloaded and verified from ${host}`);
10629
+ return { status: "installed", source: host };
10630
+ } catch (err) {
10631
+ try {
10632
+ unlinkSync4(tmpPath);
10633
+ } catch {
10634
+ }
10635
+ const msg = err instanceof Error ? err.message : String(err);
10636
+ errors.push(`${host}: ${msg}`);
10637
+ onProgress?.(`${host} failed: ${msg}`);
10638
+ }
10639
+ }
10640
+ return { status: "failed", error: `All CDN sources failed:
10641
+ ${errors.join("\n")}` };
10642
+ }
10643
+ function removeAuthBinary(binaryPath) {
10644
+ const resolvedPath = binaryPath ?? getAuthBinaryPath();
10645
+ try {
10646
+ unlinkSync4(resolvedPath);
10647
+ return { status: "removed" };
10648
+ } catch (err) {
10649
+ if (err.code === "ENOENT") {
10650
+ return { status: "not-found" };
10651
+ }
10652
+ const msg = err instanceof Error ? err.message : String(err);
10653
+ throw new Error(`Failed to remove ${resolvedPath}: ${msg}`);
10654
+ }
10655
+ }
10656
+ function isRedirect2(statusCode) {
10657
+ return statusCode !== void 0 && statusCode >= 300 && statusCode < 400;
10658
+ }
10659
+ function validateRedirect2(res, requestUrl, redirectCount, maxRedirects) {
10660
+ if (redirectCount > maxRedirects) {
10661
+ throw new Error(`Too many redirects (${maxRedirects})`);
10662
+ }
10663
+ const location = res.headers.location;
10664
+ if (requestUrl.startsWith("https") && !location.startsWith("https")) {
10665
+ throw new Error("Refused HTTPS \u2192 HTTP redirect downgrade");
10666
+ }
10667
+ return location;
10668
+ }
10669
+ function fetchResponse2(url, timeoutMs) {
10670
+ return new Promise((resolve3, reject) => {
10671
+ let redirects = 0;
10672
+ const maxRedirects = 5;
10673
+ function doRequest(requestUrl) {
10674
+ const reqFn = requestUrl.startsWith("https") ? httpsGet2 : httpGet2;
10675
+ const req = reqFn(requestUrl, { timeout: timeoutMs }, (res) => {
10676
+ if (isRedirect2(res.statusCode) && res.headers.location) {
10677
+ redirects++;
10678
+ try {
10679
+ const location = validateRedirect2(res, requestUrl, redirects, maxRedirects);
10680
+ res.resume();
10681
+ doRequest(location);
10682
+ } catch (err) {
10683
+ reject(err);
10684
+ }
10685
+ return;
10686
+ }
10687
+ if (res.statusCode !== 200) {
10688
+ reject(new Error(`HTTP ${res.statusCode ?? "unknown"}`));
10689
+ return;
10690
+ }
10691
+ resolve3(res);
10692
+ });
10693
+ req.on("error", reject);
10694
+ req.on("timeout", () => {
10695
+ req.destroy();
10696
+ reject(new Error("Download timed out"));
10697
+ });
10698
+ }
10699
+ doRequest(url);
10700
+ });
10701
+ }
10702
+ function download2(url, destPath, timeoutMs) {
10703
+ return fetchResponse2(url, timeoutMs).then(
10704
+ (res) => new Promise((resolve3, reject) => {
10705
+ const file = createWriteStream3(destPath);
10706
+ res.pipe(file);
10707
+ file.on("finish", () => file.close(() => resolve3()));
10708
+ file.on("error", (err) => {
10709
+ try {
10710
+ unlinkSync4(destPath);
10711
+ } catch {
10712
+ }
10713
+ reject(err);
10714
+ });
10715
+ })
10716
+ );
10717
+ }
10718
+ function downloadText2(url, timeoutMs) {
10719
+ return fetchResponse2(url, timeoutMs).then(
10720
+ (res) => new Promise((resolve3, reject) => {
10721
+ const chunks = [];
10722
+ res.on("data", (chunk) => chunks.push(chunk));
10723
+ res.on("end", () => resolve3(Buffer.concat(chunks).toString("utf8")));
10724
+ res.on("error", reject);
10725
+ })
10726
+ );
10727
+ }
10728
+ var CACHE_PATH = join12(homedir10(), ".okx", "auth-binary-check.json");
10729
+ var CHECK_INTERVAL_MS2 = 2 * 60 * 60 * 1e3;
10730
+ function readCache3() {
10731
+ try {
10732
+ if (!existsSync8(CACHE_PATH)) return null;
10733
+ const data = JSON.parse(readFileSync8(CACHE_PATH, "utf-8"));
10734
+ if (typeof data.cdnSha256 !== "string" || typeof data.checkedAt !== "number") return null;
10735
+ return { cdnSha256: data.cdnSha256, checkedAt: data.checkedAt };
10736
+ } catch {
10737
+ return null;
10738
+ }
10739
+ }
10740
+ function writeCache3(cdnSha256) {
10741
+ try {
10742
+ mkdirSync10(join12(homedir10(), ".okx"), { recursive: true });
10743
+ writeFileSync7(CACHE_PATH, JSON.stringify({ cdnSha256, checkedAt: Date.now() }, null, 2), "utf-8");
10744
+ } catch {
10745
+ }
10746
+ }
10747
+ async function ensureAuthBinaryLatest(onProgress) {
10748
+ if (process.env.OKX_AUTH_BIN) return;
10749
+ const cache = readCache3();
10750
+ if (cache && Date.now() - cache.checkedAt < CHECK_INTERVAL_MS2) return;
10751
+ try {
10752
+ const cdn = await fetchAuthCdnChecksum(void 0, 5e3);
10753
+ if (!cdn) return;
10754
+ const local = getAuthStatus();
10755
+ if (local.exists && local.sha256 === cdn.sha256) {
10756
+ writeCache3(cdn.sha256);
10757
+ return;
10758
+ }
10759
+ onProgress?.("Updating okx-auth binary...");
10760
+ const result = await installAuthBinary(void 0, void 0, onProgress);
10761
+ if (result.status === "installed" || result.status === "up-to-date") {
10762
+ const updated = getAuthStatus();
10763
+ if (updated.sha256) writeCache3(updated.sha256);
10764
+ if (result.status === "installed") {
10765
+ onProgress?.("\u2713 okx-auth updated successfully");
10766
+ }
10767
+ }
10768
+ } catch {
10769
+ }
10770
+ }
10771
+ function updateAuthBinaryCache(sha256) {
10772
+ writeCache3(sha256);
10773
+ }
10774
+ function clearAuthBinaryCache() {
10775
+ try {
10776
+ unlinkSync5(CACHE_PATH);
10777
+ } catch {
10778
+ }
10779
+ }
10379
10780
 
10380
- // src/commands/diagnose.ts
10381
- import dns from "dns/promises";
10382
- import net from "net";
10383
- import os5 from "os";
10384
- import tls from "tls";
10385
-
10386
- // src/commands/diagnose-utils.ts
10387
- import fs4 from "fs";
10388
- import { createRequire } from "module";
10781
+ // src/commands/auth.ts
10782
+ import { spawn as spawn2 } from "child_process";
10783
+ import readline from "readline";
10389
10784
 
10390
10785
  // src/formatter.ts
10391
10786
  import { EOL } from "os";
@@ -10476,7 +10871,282 @@ function markFailedIfSCodeError(data) {
10476
10871
  }
10477
10872
  }
10478
10873
 
10874
+ // src/commands/auth.ts
10875
+ function runOkxAuth(args) {
10876
+ const binPath = getAuthBinaryPath();
10877
+ return new Promise((resolve3, reject) => {
10878
+ const child = spawn2(binPath, args, {
10879
+ stdio: "inherit"
10880
+ });
10881
+ child.on("error", (err) => {
10882
+ reject(new Error(`Failed to spawn okx-auth: ${err.message}`));
10883
+ });
10884
+ child.on("close", (code) => {
10885
+ resolve3(code ?? 1);
10886
+ });
10887
+ });
10888
+ }
10889
+ function runOkxAuthCapture(args) {
10890
+ const binPath = getAuthBinaryPath();
10891
+ return new Promise((resolve3, reject) => {
10892
+ const child = spawn2(binPath, args, {
10893
+ stdio: ["inherit", "pipe", "inherit"]
10894
+ });
10895
+ const chunks = [];
10896
+ child.stdout.on("data", (chunk) => chunks.push(chunk));
10897
+ child.on("error", (err) => {
10898
+ reject(new Error(`Failed to spawn okx-auth: ${err.message}`));
10899
+ });
10900
+ child.on("close", (code) => {
10901
+ resolve3({
10902
+ code: code ?? 1,
10903
+ stdout: Buffer.concat(chunks).toString("utf-8")
10904
+ });
10905
+ });
10906
+ });
10907
+ }
10908
+ async function cmdAuthLogin(args) {
10909
+ let site = args.site?.trim();
10910
+ if (!site) site = process.env.OKX_SITE?.trim();
10911
+ if (!site) {
10912
+ try {
10913
+ const cfg = readFullConfig();
10914
+ const profileName = cfg.default_profile ?? "default";
10915
+ site = cfg.profiles?.[profileName]?.site?.trim();
10916
+ } catch {
10917
+ }
10918
+ }
10919
+ const cliArgs = ["login"];
10920
+ if (site) cliArgs.push("--site", site);
10921
+ if (args.manual) cliArgs.push("--manual");
10922
+ const code = await runOkxAuth(cliArgs);
10923
+ if (code !== 0) {
10924
+ process.exitCode = code;
10925
+ return;
10926
+ }
10927
+ const explicit = args.site?.trim();
10928
+ if (explicit) {
10929
+ try {
10930
+ const cfg = readFullConfig();
10931
+ const profileName = cfg.default_profile ?? "default";
10932
+ const profiles = { ...cfg.profiles ?? {} };
10933
+ profiles[profileName] = { ...profiles[profileName] ?? {}, site: explicit };
10934
+ writeFullConfig({ ...cfg, default_profile: profileName, profiles });
10935
+ } catch {
10936
+ }
10937
+ }
10938
+ }
10939
+ async function cmdAuthLogout() {
10940
+ const code = await runOkxAuth(["logout"]);
10941
+ if (code !== 0) {
10942
+ process.exitCode = code;
10943
+ }
10944
+ }
10945
+ async function cmdAuthStatus(args) {
10946
+ const cliArgs = ["status"];
10947
+ if (args.json) cliArgs.push("--json");
10948
+ const result = await runOkxAuthCapture(cliArgs);
10949
+ if (result.stdout) {
10950
+ process.stdout.write(result.stdout);
10951
+ }
10952
+ if (result.code !== 0) {
10953
+ process.exitCode = result.code;
10954
+ }
10955
+ }
10956
+ function resolveChecksumMatch(local, cdnChecksum, cdnError) {
10957
+ if (!local.exists) return "not-installed";
10958
+ if (cdnError || !cdnChecksum) return "unavailable";
10959
+ if (cdnChecksum.sha256 === local.sha256) return "match";
10960
+ return "mismatch";
10961
+ }
10962
+ function checksumMatchLabel(match) {
10963
+ if (match === "match") return "\u2713 match";
10964
+ if (match === "mismatch") return "\u2717 mismatch (update available)";
10965
+ return "CDN unreachable";
10966
+ }
10967
+ function formatBytes(bytes) {
10968
+ if (bytes < 1024) return `${bytes} B`;
10969
+ if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
10970
+ return `${(bytes / (1024 * 1024)).toFixed(2)} MB`;
10971
+ }
10972
+ async function cmdAuthInstallStatus(json) {
10973
+ const local = getAuthStatus();
10974
+ let cdnChecksum = null;
10975
+ let cdnError = null;
10976
+ if (local.exists) {
10977
+ try {
10978
+ cdnChecksum = await fetchAuthCdnChecksum(void 0, 5e3);
10979
+ } catch (err) {
10980
+ cdnError = err instanceof Error ? err.message : String(err);
10981
+ }
10982
+ }
10983
+ const checksumMatch = resolveChecksumMatch(local, cdnChecksum, cdnError);
10984
+ if (json) {
10985
+ outputLine(
10986
+ JSON.stringify({
10987
+ binaryPath: local.binaryPath,
10988
+ exists: local.exists,
10989
+ platform: local.platform,
10990
+ fileSize: local.fileSize ?? null,
10991
+ sha256: local.sha256 ?? null,
10992
+ cdnMatch: checksumMatch,
10993
+ cdnSha256: cdnChecksum?.sha256 ?? null,
10994
+ cdnSource: cdnChecksum?.source ?? null
10995
+ })
10996
+ );
10997
+ return;
10998
+ }
10999
+ outputLine("");
11000
+ outputLine(" okx-auth Binary Status");
11001
+ outputLine(" " + "\u2500".repeat(40));
11002
+ outputLine(` Binary path : ${local.binaryPath}`);
11003
+ outputLine(` Installed : ${local.exists ? "yes" : "no"}`);
11004
+ outputLine(` Platform : ${local.platform ?? "(unsupported)"}`);
11005
+ if (local.exists) {
11006
+ outputLine(` File size : ${formatBytes(local.fileSize)}`);
11007
+ outputLine(` SHA-256 : ${local.sha256 ?? "(unknown)"}`);
11008
+ outputLine(` CDN check : ${checksumMatchLabel(checksumMatch)}`);
11009
+ if (cdnChecksum) {
11010
+ outputLine(` CDN source : ${cdnChecksum.source}`);
11011
+ }
11012
+ }
11013
+ outputLine("");
11014
+ }
11015
+ async function cmdAuthInstall(json) {
11016
+ const messages2 = [];
11017
+ const onProgress = (msg) => {
11018
+ if (!json) {
11019
+ outputLine(` ${msg}`);
11020
+ }
11021
+ messages2.push(msg);
11022
+ };
11023
+ if (!json) {
11024
+ outputLine("");
11025
+ outputLine(" Installing okx-auth...");
11026
+ }
11027
+ const result = await installAuthBinary(void 0, void 0, onProgress);
11028
+ if (json) {
11029
+ outputLine(JSON.stringify({ status: result.status, source: result.source ?? null, error: result.error ?? null, messages: messages2 }));
11030
+ if (result.status === "failed") {
11031
+ process.exitCode = 1;
11032
+ }
11033
+ return;
11034
+ }
11035
+ if (result.status === "installed" || result.status === "up-to-date") {
11036
+ const local = getAuthStatus();
11037
+ if (local.sha256) updateAuthBinaryCache(local.sha256);
11038
+ }
11039
+ if (result.status === "installed") {
11040
+ outputLine(` \u2713 okx-auth installed successfully (${result.source ?? ""})`);
11041
+ } else if (result.status === "up-to-date") {
11042
+ outputLine(" \u2713 okx-auth is already up to date");
11043
+ } else {
11044
+ errorLine(` \u2717 Installation failed: ${result.error ?? "unknown error"}`);
11045
+ errorLine(" Hint: check network connectivity or try again later");
11046
+ process.exitCode = 1;
11047
+ }
11048
+ outputLine("");
11049
+ }
11050
+ async function cmdAuthRemove(force, json) {
11051
+ const local = getAuthStatus(void 0, { skipHash: true });
11052
+ if (!local.exists) {
11053
+ if (json) {
11054
+ outputLine(JSON.stringify({ status: "not-installed" }));
11055
+ } else {
11056
+ outputLine(" okx-auth is not installed.");
11057
+ }
11058
+ return;
11059
+ }
11060
+ if (!await confirmRemoval(force, local.binaryPath)) return;
11061
+ let result;
11062
+ try {
11063
+ result = removeAuthBinary();
11064
+ } catch (err) {
11065
+ const msg = err instanceof Error ? err.message : String(err);
11066
+ if (json) {
11067
+ outputLine(JSON.stringify({ status: "failed", error: msg }));
11068
+ } else {
11069
+ errorLine(` \u2717 Failed to remove: ${msg}`);
11070
+ }
11071
+ process.exitCode = 1;
11072
+ return;
11073
+ }
11074
+ if (json) {
11075
+ outputLine(JSON.stringify({ status: result.status, path: local.binaryPath }));
11076
+ return;
11077
+ }
11078
+ if (result.status === "removed") {
11079
+ clearAuthBinaryCache();
11080
+ outputLine(` \u2713 Removed: ${local.binaryPath}`);
11081
+ } else {
11082
+ outputLine(" okx-auth is not installed.");
11083
+ }
11084
+ }
11085
+ async function confirmRemoval(force, binaryPath) {
11086
+ if (force) return true;
11087
+ if (!process.stdin.isTTY) {
11088
+ errorLine(" Error: stdin is not a TTY. Use --force to skip confirmation.");
11089
+ process.exitCode = 1;
11090
+ return false;
11091
+ }
11092
+ const confirmed = await askConfirmation(
11093
+ ` Remove okx-auth at ${binaryPath}? [y/N] `
11094
+ );
11095
+ if (!confirmed) {
11096
+ outputLine(" Cancelled.");
11097
+ return false;
11098
+ }
11099
+ return true;
11100
+ }
11101
+ function askConfirmation(prompt2) {
11102
+ return new Promise((resolve3) => {
11103
+ const rl = readline.createInterface({
11104
+ input: process.stdin,
11105
+ output: process.stdout
11106
+ });
11107
+ rl.question(prompt2, (answer) => {
11108
+ rl.close();
11109
+ resolve3(answer.trim().toLowerCase() === "y");
11110
+ });
11111
+ });
11112
+ }
11113
+ async function handleAuthCommand(action, _rest, v) {
11114
+ const site = v.site;
11115
+ const json = v.json;
11116
+ const manual = v.manual;
11117
+ const force = v.force;
11118
+ if (["login", "logout", "status"].includes(action)) {
11119
+ await ensureAuthBinaryLatest((msg) => errorLine(` ${msg}`));
11120
+ }
11121
+ switch (action) {
11122
+ case "login":
11123
+ return cmdAuthLogin({ site, manual });
11124
+ case "logout":
11125
+ return cmdAuthLogout();
11126
+ case "status":
11127
+ return cmdAuthStatus({ json });
11128
+ case "install":
11129
+ return cmdAuthInstall(json ?? false);
11130
+ case "install-status":
11131
+ return cmdAuthInstallStatus(json ?? false);
11132
+ case "remove":
11133
+ return cmdAuthRemove(force ?? false, json ?? false);
11134
+ default:
11135
+ errorLine(`Unknown auth command: ${action}`);
11136
+ errorLine("Available: login, logout, status, install, install-status, remove");
11137
+ process.exitCode = 1;
11138
+ }
11139
+ }
11140
+
11141
+ // src/commands/diagnose.ts
11142
+ import dns from "dns/promises";
11143
+ import net from "net";
11144
+ import os5 from "os";
11145
+ import tls from "tls";
11146
+
10479
11147
  // src/commands/diagnose-utils.ts
11148
+ import fs4 from "fs";
11149
+ import { createRequire } from "module";
10480
11150
  var _require = createRequire(import.meta.url);
10481
11151
  function readCliVersion() {
10482
11152
  for (const rel of ["../package.json", "../../package.json"]) {
@@ -10562,7 +11232,7 @@ function sanitize2(value) {
10562
11232
  import fs5 from "fs";
10563
11233
  import path4 from "path";
10564
11234
  import os4 from "os";
10565
- import { spawnSync, spawn } from "child_process";
11235
+ import { spawnSync, spawn as spawn3 } from "child_process";
10566
11236
  import { createRequire as createRequire2 } from "module";
10567
11237
  import { fileURLToPath } from "url";
10568
11238
  var _require2 = createRequire2(import.meta.url);
@@ -10896,7 +11566,7 @@ async function checkStdioHandshake(entryPath, report) {
10896
11566
  clearTimeout(timer);
10897
11567
  resolve3(passed);
10898
11568
  };
10899
- const child = spawn(process.execPath, [entryPath], {
11569
+ const child = spawn3(process.execPath, [entryPath], {
10900
11570
  stdio: ["pipe", "pipe", "pipe"],
10901
11571
  env: { ...process.env }
10902
11572
  });
@@ -11015,7 +11685,7 @@ async function cmdDiagnoseMcp(options = {}) {
11015
11685
 
11016
11686
  // src/commands/diagnose.ts
11017
11687
  var CLI_VERSION = readCliVersion();
11018
- var GIT_HASH = true ? "f19045a" : "dev";
11688
+ var GIT_HASH = true ? "17e06f4" : "dev";
11019
11689
  function maskKey2(key) {
11020
11690
  if (!key) return "(not set)";
11021
11691
  if (key.length <= 8) return "****";
@@ -11351,24 +12021,24 @@ async function runCliChecks(config, profile, outputPath) {
11351
12021
 
11352
12022
  // src/commands/upgrade.ts
11353
12023
  import { spawnSync as spawnSync2 } from "child_process";
11354
- import { readFileSync as readFileSync8, writeFileSync as writeFileSync7, mkdirSync as mkdirSync9 } from "fs";
11355
- import { dirname as dirname7, join as join10 } from "path";
11356
- import { homedir as homedir8 } from "os";
12024
+ import { readFileSync as readFileSync9, writeFileSync as writeFileSync8, mkdirSync as mkdirSync11 } from "fs";
12025
+ import { dirname as dirname8, join as join13 } from "path";
12026
+ import { homedir as homedir11 } from "os";
11357
12027
  var PACKAGES = ["@okx_ai/okx-trade-mcp", "@okx_ai/okx-trade-cli"];
11358
- var CACHE_FILE2 = join10(homedir8(), ".okx", "last_check");
12028
+ var CACHE_FILE2 = join13(homedir11(), ".okx", "last_check");
11359
12029
  var THROTTLE_MS = 12 * 60 * 60 * 1e3;
11360
- var NPM_BIN = join10(dirname7(process.execPath), process.platform === "win32" ? "npm.cmd" : "npm");
12030
+ var NPM_BIN = join13(dirname8(process.execPath), process.platform === "win32" ? "npm.cmd" : "npm");
11361
12031
  function readLastCheck() {
11362
12032
  try {
11363
- return parseInt(readFileSync8(CACHE_FILE2, "utf-8").trim(), 10) || 0;
12033
+ return parseInt(readFileSync9(CACHE_FILE2, "utf-8").trim(), 10) || 0;
11364
12034
  } catch {
11365
12035
  return 0;
11366
12036
  }
11367
12037
  }
11368
12038
  function writeLastCheck() {
11369
12039
  try {
11370
- mkdirSync9(join10(homedir8(), ".okx"), { recursive: true });
11371
- writeFileSync7(CACHE_FILE2, String(Math.floor(Date.now() / 1e3)), "utf-8");
12040
+ mkdirSync11(join13(homedir11(), ".okx"), { recursive: true });
12041
+ writeFileSync8(CACHE_FILE2, String(Math.floor(Date.now() / 1e3)), "utf-8");
11372
12042
  } catch {
11373
12043
  }
11374
12044
  }
@@ -12339,29 +13009,29 @@ var CLI_REGISTRY = {
12339
13009
  commands: {
12340
13010
  latest: {
12341
13011
  toolName: "news_get_latest",
12342
- usage: "okx news latest [--coins BTC,ETH] [--platform blockbeats] [--lang zh-CN] [--limit 20]"
13012
+ usage: "okx news latest [--coins BTC,ETH] [--lang zh_CN] [--limit 20]"
12343
13013
  },
12344
13014
  important: {
12345
13015
  toolName: "news_get_latest",
12346
- usage: "okx news important [--coins BTC,ETH] [--lang zh-CN] [--limit 20]",
13016
+ usage: "okx news important [--coins BTC,ETH] [--lang zh_CN] [--limit 20]",
12347
13017
  description: "Get important/high-impact crypto news"
12348
13018
  },
12349
13019
  "by-coin": {
12350
13020
  toolName: "news_get_by_coin",
12351
- usage: "okx news by-coin --coins BTC [--importance high] [--platform blockbeats] [--lang zh-CN]"
13021
+ usage: "okx news by-coin --coins BTC [--importance high] [--lang zh_CN]"
12352
13022
  },
12353
13023
  search: {
12354
13024
  toolName: "news_search",
12355
- usage: "okx news search --keyword <term> [--coins BTC] [--sentiment bullish] [--platform blockbeats] [--lang zh-CN]"
13025
+ usage: "okx news search --keyword <term> [--coins BTC] [--sentiment bullish] [--lang zh_CN]"
12356
13026
  },
12357
13027
  detail: {
12358
13028
  toolName: "news_get_detail",
12359
- usage: "okx news detail <id> [--lang zh-CN]"
13029
+ usage: "okx news detail <id> [--lang zh_CN]"
12360
13030
  },
12361
- platforms: {
13031
+ domains: {
12362
13032
  toolName: "news_get_domains",
12363
- usage: "okx news platforms",
12364
- description: "List available news platforms"
13033
+ usage: "okx news domains",
13034
+ description: "List available news source domains"
12365
13035
  },
12366
13036
  "coin-sentiment": {
12367
13037
  toolName: "news_get_coin_sentiment",
@@ -12374,7 +13044,7 @@ var CLI_REGISTRY = {
12374
13044
  },
12375
13045
  "by-sentiment": {
12376
13046
  toolName: "news_search",
12377
- usage: "okx news by-sentiment --sentiment bullish [--coins BTC] [--importance high] [--platform <source>] [--sort-by latest] [--begin <ms>] [--end <ms>]",
13047
+ usage: "okx news by-sentiment --sentiment bullish [--coins BTC] [--sort-by latest]",
12378
13048
  description: "Browse news filtered by sentiment direction"
12379
13049
  },
12380
13050
  "sentiment-rank": {
@@ -12582,7 +13252,6 @@ async function cmdNewsLatest(run, opts) {
12582
13252
  const result = await run("news_get_latest", {
12583
13253
  coins: opts.coins,
12584
13254
  importance: opts.importance,
12585
- platform: opts.platform,
12586
13255
  begin: opts.begin,
12587
13256
  end: opts.end,
12588
13257
  language: opts.language,
@@ -12609,7 +13278,6 @@ async function cmdNewsImportant(run, opts) {
12609
13278
  const result = await run("news_get_latest", {
12610
13279
  coins: opts.coins,
12611
13280
  importance: "high",
12612
- platform: opts.platform,
12613
13281
  begin: opts.begin,
12614
13282
  end: opts.end,
12615
13283
  language: opts.language,
@@ -12634,7 +13302,6 @@ async function cmdNewsByCoin(run, coins, opts) {
12634
13302
  const result = await run("news_get_by_coin", {
12635
13303
  coins,
12636
13304
  importance: opts.importance,
12637
- platform: opts.platform,
12638
13305
  begin: opts.begin,
12639
13306
  end: opts.end,
12640
13307
  language: opts.language,
@@ -12660,7 +13327,6 @@ async function cmdNewsSearch(run, keyword, opts) {
12660
13327
  keyword: keyword || void 0,
12661
13328
  coins: opts.coins,
12662
13329
  importance: opts.importance,
12663
- platform: opts.platform,
12664
13330
  sentiment: opts.sentiment,
12665
13331
  sortBy: opts.sortBy,
12666
13332
  begin: opts.begin,
@@ -12713,12 +13379,12 @@ async function cmdNewsDetail(run, id, opts) {
12713
13379
  content
12714
13380
  });
12715
13381
  }
12716
- async function cmdNewsPlatforms(run, opts) {
13382
+ async function cmdNewsDomains(run, opts) {
12717
13383
  const result = await run("news_get_domains", {});
12718
13384
  const raw = getData(result);
12719
13385
  const items = raw?.[0]?.["platform"] ?? [];
12720
13386
  if (opts.json) return printJson(items);
12721
- outputLine("Available news platforms:");
13387
+ outputLine("Available news source domains:");
12722
13388
  items.forEach((d) => outputLine(` ${d}`));
12723
13389
  }
12724
13390
  async function cmdNewsCoinSentiment(run, coins, opts) {
@@ -12792,7 +13458,7 @@ async function cmdNewsSentimentRank(run, opts) {
12792
13458
  }
12793
13459
 
12794
13460
  // src/config/loader.ts
12795
- function loadProfileConfig(opts) {
13461
+ async function loadProfileConfig(opts) {
12796
13462
  return loadConfig({
12797
13463
  profile: opts.profile,
12798
13464
  modules: opts.modules,
@@ -13100,7 +13766,6 @@ var CLI_OPTIONS = {
13100
13766
  coins: { type: "string" },
13101
13767
  sentiment: { type: "string" },
13102
13768
  importance: { type: "string" },
13103
- platform: { type: "string" },
13104
13769
  keyword: { type: "string" },
13105
13770
  "detail-lvl": { type: "string" },
13106
13771
  period: { type: "string" },
@@ -13111,6 +13776,9 @@ var CLI_OPTIONS = {
13111
13776
  dir: { type: "string" },
13112
13777
  page: { type: "string" },
13113
13778
  format: { type: "string" },
13779
+ // auth
13780
+ site: { type: "string" },
13781
+ manual: { type: "boolean", default: false },
13114
13782
  // event contract
13115
13783
  underlying: { type: "string" },
13116
13784
  seriesId: { type: "string" },
@@ -16125,14 +16793,14 @@ async function cmdDcdQuoteAndBuy(run, opts) {
16125
16793
  }
16126
16794
 
16127
16795
  // src/commands/skill.ts
16128
- import { tmpdir, homedir as homedir10 } from "os";
16129
- import { join as join12, dirname as dirname8 } from "path";
16130
- import { mkdirSync as mkdirSync10, rmSync, existsSync as existsSync8, copyFileSync as copyFileSync2 } from "fs";
16796
+ import { tmpdir, homedir as homedir13 } from "os";
16797
+ import { join as join15, dirname as dirname9 } from "path";
16798
+ import { mkdirSync as mkdirSync12, rmSync, existsSync as existsSync10, copyFileSync as copyFileSync2 } from "fs";
16131
16799
  import { execFileSync as execFileSync2 } from "child_process";
16132
16800
  import { randomUUID as randomUUID2 } from "crypto";
16133
16801
  function resolveNpx() {
16134
- const sibling = join12(dirname8(process.execPath), "npx");
16135
- if (existsSync8(sibling)) return sibling;
16802
+ const sibling = join15(dirname9(process.execPath), "npx");
16803
+ if (existsSync10(sibling)) return sibling;
16136
16804
  return "npx";
16137
16805
  }
16138
16806
  var THIRD_PARTY_INSTALL_NOTICE = "Note: This skill was created by a third-party developer, not by OKX. Review SKILL.md before use.";
@@ -16185,13 +16853,13 @@ async function cmdSkillCategories(run, json) {
16185
16853
  outputLine("");
16186
16854
  }
16187
16855
  async function cmdSkillAdd(name, config, json) {
16188
- const tmpBase = join12(tmpdir(), `okx-skill-${randomUUID2()}`);
16189
- mkdirSync10(tmpBase, { recursive: true });
16856
+ const tmpBase = join15(tmpdir(), `okx-skill-${randomUUID2()}`);
16857
+ mkdirSync12(tmpBase, { recursive: true });
16190
16858
  try {
16191
16859
  outputLine(`Downloading ${name}...`);
16192
16860
  const client = new OkxRestClient(config);
16193
16861
  const zipPath = await downloadSkillZip(client, name, tmpBase);
16194
- const contentDir = await extractSkillZip(zipPath, join12(tmpBase, "content"));
16862
+ const contentDir = await extractSkillZip(zipPath, join15(tmpBase, "content"));
16195
16863
  const meta = readMetaJson(contentDir);
16196
16864
  validateSkillMdExists(contentDir);
16197
16865
  outputLine("Installing to detected agents...");
@@ -16201,7 +16869,7 @@ async function cmdSkillAdd(name, config, json) {
16201
16869
  timeout: 6e4
16202
16870
  });
16203
16871
  } catch (e) {
16204
- const savedZip = join12(process.cwd(), `${name}.zip`);
16872
+ const savedZip = join15(process.cwd(), `${name}.zip`);
16205
16873
  try {
16206
16874
  copyFileSync2(zipPath, savedZip);
16207
16875
  } catch {
@@ -16240,7 +16908,7 @@ function cmdSkillRemove(name, json) {
16240
16908
  timeout: 6e4
16241
16909
  });
16242
16910
  } catch {
16243
- const agentsPath = join12(homedir10(), ".agents", "skills", name);
16911
+ const agentsPath = join15(homedir13(), ".agents", "skills", name);
16244
16912
  try {
16245
16913
  rmSync(agentsPath, { recursive: true, force: true });
16246
16914
  } catch {
@@ -16314,14 +16982,14 @@ function printSkillInstallResult(meta, json) {
16314
16982
  }
16315
16983
 
16316
16984
  // src/commands/doh.ts
16317
- import readline from "readline";
16318
- function resolveChecksumMatch(local, cdnChecksum, cdnError) {
16985
+ import readline2 from "readline";
16986
+ function resolveChecksumMatch2(local, cdnChecksum, cdnError) {
16319
16987
  if (!local.exists) return "not-installed";
16320
16988
  if (cdnError || !cdnChecksum) return "unavailable";
16321
16989
  if (cdnChecksum.sha256 === local.sha256) return "match";
16322
16990
  return "mismatch";
16323
16991
  }
16324
- function checksumMatchLabel(match) {
16992
+ function checksumMatchLabel2(match) {
16325
16993
  if (match === "match") return "\u2713 match";
16326
16994
  if (match === "mismatch") return "\u2717 mismatch (update available)";
16327
16995
  return "CDN unreachable";
@@ -16334,9 +17002,9 @@ function formatStatusText(local, checksumMatch, cdnChecksum, runtimeMode) {
16334
17002
  outputLine(` Installed : ${local.exists ? "yes" : "no"}`);
16335
17003
  outputLine(` Platform : ${local.platform ?? "(unsupported)"}`);
16336
17004
  if (local.exists) {
16337
- outputLine(` File size : ${formatBytes(local.fileSize)}`);
17005
+ outputLine(` File size : ${formatBytes2(local.fileSize)}`);
16338
17006
  outputLine(` SHA-256 : ${local.sha256 ?? "(unknown)"}`);
16339
- outputLine(` CDN check : ${checksumMatchLabel(checksumMatch)}`);
17007
+ outputLine(` CDN check : ${checksumMatchLabel2(checksumMatch)}`);
16340
17008
  if (cdnChecksum) {
16341
17009
  outputLine(` CDN source : ${cdnChecksum.source}`);
16342
17010
  }
@@ -16363,7 +17031,7 @@ async function cmdDohStatus(json, binaryPath) {
16363
17031
  cdnError = err instanceof Error ? err.message : String(err);
16364
17032
  }
16365
17033
  }
16366
- const checksumMatch = resolveChecksumMatch(local, cdnChecksum, cdnError);
17034
+ const checksumMatch = resolveChecksumMatch2(local, cdnChecksum, cdnError);
16367
17035
  if (json) {
16368
17036
  outputLine(
16369
17037
  JSON.stringify({
@@ -16429,7 +17097,7 @@ async function cmdDohRemove(force, json, binaryPath) {
16429
17097
  process.exitCode = 1;
16430
17098
  return;
16431
17099
  }
16432
- const confirmed = await askConfirmation(
17100
+ const confirmed = await askConfirmation2(
16433
17101
  ` Remove DoH resolver at ${local.binaryPath}? [y/N] `
16434
17102
  );
16435
17103
  if (!confirmed) {
@@ -16448,14 +17116,14 @@ async function cmdDohRemove(force, json, binaryPath) {
16448
17116
  outputLine(" DoH resolver is not installed.");
16449
17117
  }
16450
17118
  }
16451
- function formatBytes(bytes) {
17119
+ function formatBytes2(bytes) {
16452
17120
  if (bytes < 1024) return `${bytes} B`;
16453
17121
  if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
16454
17122
  return `${(bytes / (1024 * 1024)).toFixed(2)} MB`;
16455
17123
  }
16456
- function askConfirmation(prompt2) {
17124
+ function askConfirmation2(prompt2) {
16457
17125
  return new Promise((resolve3) => {
16458
- const rl = readline.createInterface({
17126
+ const rl = readline2.createInterface({
16459
17127
  input: process.stdin,
16460
17128
  output: process.stdout
16461
17129
  });
@@ -16912,7 +17580,7 @@ async function cmdEventCancel(run, opts) {
16912
17580
  // src/index.ts
16913
17581
  var _require3 = createRequire3(import.meta.url);
16914
17582
  var CLI_VERSION2 = _require3("../package.json").version;
16915
- var GIT_HASH2 = true ? "f19045a" : "dev";
17583
+ var GIT_HASH2 = true ? "17e06f4" : "dev";
16916
17584
  function handleDohCommand(action, json, force, binaryPath) {
16917
17585
  if (action === "status") return cmdDohStatus(json, binaryPath);
16918
17586
  if (action === "install") return cmdDohInstall(json, binaryPath);
@@ -17756,22 +18424,21 @@ function handleNewsCommand(run, action, rest, v, json) {
17756
18424
  const limit = v.limit !== void 0 ? Number(v.limit) : void 0;
17757
18425
  const begin = v.begin !== void 0 ? Number(v.begin) : void 0;
17758
18426
  const end = v.end !== void 0 ? Number(v.end) : void 0;
17759
- const language = v.lang ?? "en-US";
18427
+ const language = v.lang ?? "en_US";
17760
18428
  const detailLvl = v["detail-lvl"];
17761
18429
  const after = v.after;
17762
18430
  const period = v.period;
17763
18431
  const points = v.points !== void 0 ? Number(v.points) : 24;
17764
18432
  const sortBy = v["sort-by"];
17765
- const platform2 = v.platform;
17766
- const searchOpts = { coins: v.coins, importance: v.importance, platform: platform2, sentiment: v.sentiment, sortBy, begin, end, language, detailLvl, limit, after, json };
17767
- const listOpts = { coins: v.coins, importance: v.importance, platform: platform2, begin, end, language, detailLvl, limit, after, json };
18433
+ const searchOpts = { coins: v.coins, importance: v.importance, sentiment: v.sentiment, sortBy, begin, end, language, detailLvl, limit, after, json };
18434
+ const listOpts = { coins: v.coins, importance: v.importance, begin, end, language, detailLvl, limit, after, json };
17768
18435
  const dispatch = {
17769
18436
  latest: () => cmdNewsLatest(run, listOpts),
17770
- important: () => cmdNewsImportant(run, { coins: v.coins, platform: platform2, begin, end, language, detailLvl, limit, json }),
17771
- "by-coin": () => cmdNewsByCoin(run, v.coins ?? rest[0], { importance: v.importance, platform: platform2, begin, end, language, detailLvl, limit, json }),
18437
+ important: () => cmdNewsImportant(run, { coins: v.coins, begin, end, language, detailLvl, limit, json }),
18438
+ "by-coin": () => cmdNewsByCoin(run, v.coins ?? rest[0], { importance: v.importance, begin, end, language, detailLvl, limit, json }),
17772
18439
  search: () => cmdNewsSearch(run, v.keyword ?? rest[0], searchOpts),
17773
18440
  detail: () => cmdNewsDetail(run, rest[0], { language, json }),
17774
- platforms: () => cmdNewsPlatforms(run, { json }),
18441
+ domains: () => cmdNewsDomains(run, { json }),
17775
18442
  "coin-sentiment": () => cmdNewsCoinSentiment(run, v.coins ?? rest[0], { period, json }),
17776
18443
  "coin-trend": () => cmdNewsCoinTrend(run, v.coins ?? rest[0], { period, points, json }),
17777
18444
  // by-sentiment is a convenience wrapper over news_search (no keyword, sentiment filter only)
@@ -17888,7 +18555,7 @@ function wrapRunnerWithLogger(baseRunner, logger, verbose = false) {
17888
18555
  async function runDiagnose(v) {
17889
18556
  let config;
17890
18557
  try {
17891
- config = loadProfileConfig({ profile: v.profile, demo: v.demo, live: v.live, verbose: v.verbose, userAgent: `okx-trade-cli/${CLI_VERSION2}`, sourceTag: "CLI" });
18558
+ config = await loadProfileConfig({ profile: v.profile, demo: v.demo, live: v.live, verbose: v.verbose, userAgent: `okx-trade-cli/${CLI_VERSION2}`, sourceTag: "CLI" });
17892
18559
  } catch {
17893
18560
  }
17894
18561
  return cmdDiagnose(config, v.profile ?? "default", { mcp: v.mcp, cli: v.cli, all: v.all, output: v.output });
@@ -17903,24 +18570,13 @@ function printVersion() {
17903
18570
  }
17904
18571
  }
17905
18572
  function routeManagementCommand(module, action, rest, json, v) {
17906
- if (module === "config") {
17907
- const r = handleConfigCommand(action, rest, json, v.lang, v.force);
17908
- return r ?? true;
17909
- }
17910
- if (module === "setup") {
17911
- handleSetupCommand(v);
17912
- return true;
17913
- }
18573
+ if (module === "config") return handleConfigCommand(action, rest, json, v.lang, v.force);
18574
+ if (module === "setup") return handleSetupCommand(v);
18575
+ if (module === "auth") return handleAuthCommand(action, rest, v);
17914
18576
  if (module === "upgrade") return cmdUpgrade(CLI_VERSION2, { beta: v.beta, check: v.check, force: v.force }, json);
17915
- if (module === "doh") {
17916
- const r = handleDohCommand(action, json, v.force ?? false);
17917
- return r ?? true;
17918
- }
18577
+ if (module === "doh") return handleDohCommand(action, json, v.force ?? false);
17919
18578
  if (module === "diagnose") return runDiagnose(v);
17920
- if (module === "list-tools") {
17921
- cmdListTools(json);
17922
- return true;
17923
- }
18579
+ if (module === "list-tools") return cmdListTools(json);
17924
18580
  return void 0;
17925
18581
  }
17926
18582
  async function main() {
@@ -17942,8 +18598,8 @@ async function main() {
17942
18598
  const v = values;
17943
18599
  const json = v.json ?? false;
17944
18600
  const mgmt = routeManagementCommand(module, action, rest, json, v);
17945
- if (mgmt !== void 0) return mgmt === true ? void 0 : mgmt;
17946
- const config = loadProfileConfig({ profile: v.profile, demo: v.demo, live: v.live, verbose: v.verbose, userAgent: `okx-trade-cli/${CLI_VERSION2}`, sourceTag: "CLI" });
18601
+ if (mgmt) return mgmt;
18602
+ const config = await loadProfileConfig({ profile: v.profile, demo: v.demo, live: v.live, verbose: v.verbose, userAgent: `okx-trade-cli/${CLI_VERSION2}`, sourceTag: "CLI" });
17947
18603
  setEnvContext({ demo: config.demo, profile: v.profile ?? "default" });
17948
18604
  setJsonEnvEnabled(v.env ?? false);
17949
18605
  const client = new OkxRestClient(config);