@okx_ai/okx-trade-cli 1.3.2-beta.4 → 1.3.2-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
@@ -21,26 +21,23 @@ 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";
27
24
  import fs from "fs";
28
25
  import path from "path";
29
26
  import os from "os";
30
27
  import { writeFileSync as writeFileSync2, renameSync as renameSync2, unlinkSync as unlinkSync2, mkdirSync as mkdirSync2 } from "fs";
31
- import { join as join4, resolve, basename, sep } from "path";
28
+ import { join as join3, resolve, basename, sep } from "path";
32
29
  import { randomUUID } from "crypto";
33
30
  import yauzl from "yauzl";
34
31
  import { createWriteStream, mkdirSync as mkdirSync3 } from "fs";
35
32
  import { resolve as resolve2, dirname as dirname2 } from "path";
36
33
  import { readFileSync as readFileSync2, existsSync } from "fs";
37
- import { join as join5 } from "path";
34
+ import { join as join4 } from "path";
38
35
  import { readFileSync as readFileSync3, writeFileSync as writeFileSync3, mkdirSync as mkdirSync4, existsSync as existsSync2 } from "fs";
39
- import { join as join6, dirname as dirname3 } from "path";
40
- import { homedir as homedir4 } from "os";
36
+ import { join as join5, dirname as dirname3 } from "path";
37
+ import { homedir as homedir3 } from "os";
41
38
  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";
39
+ import { join as join6, dirname as dirname4 } from "path";
40
+ import { homedir as homedir4 } from "os";
44
41
 
45
42
  // ../../node_modules/.pnpm/smol-toml@1.6.0/node_modules/smol-toml/dist/error.js
46
43
  function getLineColFromPtr(string, ptr) {
@@ -870,8 +867,8 @@ function stringify(obj, { maxDepth = 1e3, numbersAsFloat = false } = {}) {
870
867
 
871
868
  // ../core/dist/index.js
872
869
  import { readFileSync as readFileSync5, writeFileSync as writeFileSync5, mkdirSync as mkdirSync6, existsSync as existsSync4 } from "fs";
873
- import { join as join8 } from "path";
874
- import { homedir as homedir6 } from "os";
870
+ import { join as join7 } from "path";
871
+ import { homedir as homedir5 } from "os";
875
872
  import fs2 from "fs";
876
873
  import path2 from "path";
877
874
  import os2 from "os";
@@ -879,18 +876,6 @@ import * as fs3 from "fs";
879
876
  import * as path3 from "path";
880
877
  import * as os3 from "os";
881
878
  import { execFileSync } from "child_process";
882
- import {
883
- createWriteStream as createWriteStream3,
884
- mkdirSync as mkdirSync9,
885
- chmodSync as chmodSync2,
886
- existsSync as existsSync7,
887
- unlinkSync as unlinkSync4,
888
- renameSync as renameSync4
889
- } from "fs";
890
- import { homedir as homedir9, platform as platform2 } from "os";
891
- import { join as join11, dirname as dirname7 } from "path";
892
- import { get as httpsGet2 } from "https";
893
- import { get as httpGet2 } from "http";
894
879
  import {
895
880
  readFileSync as readFileSync7,
896
881
  createWriteStream as createWriteStream2,
@@ -901,13 +886,10 @@ import {
901
886
  renameSync as renameSync3
902
887
  } from "fs";
903
888
  import { createHash } from "crypto";
904
- import { homedir as homedir8, platform, arch } from "os";
905
- import { join as join10, dirname as dirname6 } from "path";
889
+ import { homedir as homedir7, platform, arch } from "os";
890
+ import { join as join9, dirname as dirname6 } from "path";
906
891
  import { get as httpsGet } from "https";
907
892
  import { get as httpGet } from "http";
908
- import { readFileSync as readFileSync8, writeFileSync as writeFileSync7, mkdirSync as mkdirSync10, unlinkSync as unlinkSync5, existsSync as existsSync8 } from "fs";
909
- import { join as join12 } from "path";
910
- import { homedir as homedir10 } from "os";
911
893
  var EXEC_TIMEOUT_MS = 3e4;
912
894
  var ALLOWED_DOMAIN_RE = /^[\w.-]+\.okx\.com$/;
913
895
  var PILOT_BIN_DIR = join(homedir(), ".okx", "bin");
@@ -1219,11 +1201,6 @@ var ConfigError = class extends OkxMcpError {
1219
1201
  super("ConfigError", message, { suggestion });
1220
1202
  }
1221
1203
  };
1222
- var NotLoggedInError = class extends ConfigError {
1223
- constructor(suggestion = "Run `okx auth login` to authenticate.") {
1224
- super("Not logged in.", suggestion);
1225
- }
1226
- };
1227
1204
  var ValidationError = class extends OkxMcpError {
1228
1205
  constructor(message, suggestion) {
1229
1206
  super("ValidationError", message, { suggestion });
@@ -1276,97 +1253,6 @@ function toToolErrorPayload(error, fallbackEndpoint) {
1276
1253
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
1277
1254
  };
1278
1255
  }
1279
- var EXIT_CODES = {
1280
- SUCCESS: 0,
1281
- UNAUTHORIZED_CALLER: 1,
1282
- NOT_LOGGED_IN: 2,
1283
- REFRESH_FAILED: 3
1284
- };
1285
- var EXEC_TIMEOUT_MS2 = 5e3;
1286
- var AUTH_BIN_DIR = join3(homedir3(), ".okx", "bin");
1287
- function getAuthBinaryPath() {
1288
- if (process.env.OKX_AUTH_BIN) {
1289
- return process.env.OKX_AUTH_BIN;
1290
- }
1291
- const ext = process.platform === "win32" ? ".exe" : "";
1292
- return join3(AUTH_BIN_DIR, `okx-auth${ext}`);
1293
- }
1294
- function execAuthToken() {
1295
- const binPath = getAuthBinaryPath();
1296
- return new Promise((resolve3, reject) => {
1297
- const child = spawn(binPath, ["token"], {
1298
- stdio: ["ignore", "ignore", "inherit", "pipe"]
1299
- // stdin stdout stderr fd3 (pipe)
1300
- });
1301
- const chunks = [];
1302
- const fd3 = child.stdio[3];
1303
- fd3.on("data", (chunk) => chunks.push(chunk));
1304
- child.on("error", (err) => {
1305
- reject(new ConfigError(
1306
- `Failed to spawn okx-auth: ${err.message}`,
1307
- "Ensure the okx-auth binary exists and is executable."
1308
- ));
1309
- });
1310
- child.on("close", (code) => {
1311
- if (code === EXIT_CODES.SUCCESS) {
1312
- const token = Buffer.concat(chunks).toString("utf-8").trim();
1313
- if (!token) {
1314
- reject(new AuthenticationError(
1315
- "okx-auth returned empty token.",
1316
- "Run `okx auth login` to re-authenticate."
1317
- ));
1318
- return;
1319
- }
1320
- resolve3(token);
1321
- return;
1322
- }
1323
- if (code === EXIT_CODES.NOT_LOGGED_IN) {
1324
- reject(new NotLoggedInError());
1325
- return;
1326
- }
1327
- if (code === EXIT_CODES.UNAUTHORIZED_CALLER) {
1328
- reject(new AuthenticationError(
1329
- "okx-auth rejected the caller (unauthorized).",
1330
- "Ensure you are running from a trusted OKX tool."
1331
- ));
1332
- return;
1333
- }
1334
- if (code === EXIT_CODES.REFRESH_FAILED) {
1335
- reject(new AuthenticationError(
1336
- "Token refresh failed.",
1337
- "Run `okx auth login` to re-authenticate."
1338
- ));
1339
- return;
1340
- }
1341
- reject(new AuthenticationError(
1342
- `okx-auth token exited with code ${code}.`,
1343
- "Run `okx auth login` to re-authenticate."
1344
- ));
1345
- });
1346
- });
1347
- }
1348
- function execAuthStatus() {
1349
- const binPath = getAuthBinaryPath();
1350
- return new Promise((resolve3) => {
1351
- execFile2(
1352
- binPath,
1353
- ["status", "--json"],
1354
- { timeout: EXEC_TIMEOUT_MS2, encoding: "utf-8" },
1355
- (error, stdout) => {
1356
- if (error) {
1357
- resolve3(null);
1358
- return;
1359
- }
1360
- try {
1361
- const result = JSON.parse(stdout);
1362
- resolve3(result);
1363
- } catch {
1364
- resolve3(null);
1365
- }
1366
- }
1367
- );
1368
- });
1369
- }
1370
1256
  function sleep(ms) {
1371
1257
  return new Promise((resolve3) => {
1372
1258
  setTimeout(resolve3, ms);
@@ -1498,7 +1384,6 @@ function maskKey(key) {
1498
1384
  if (key.length <= 8) return "***";
1499
1385
  return `${key.slice(0, 3)}***${key.slice(-3)}`;
1500
1386
  }
1501
- var TOKEN_CACHE_TTL_MS = 6e4;
1502
1387
  function vlog2(message) {
1503
1388
  process.stderr.write(`[verbose] ${message}
1504
1389
  `);
@@ -1507,8 +1392,6 @@ var OkxRestClient = class _OkxRestClient {
1507
1392
  config;
1508
1393
  rateLimiter;
1509
1394
  dispatcher;
1510
- cachedAccessToken;
1511
- cachedAccessTokenAt = 0;
1512
1395
  pilot;
1513
1396
  constructor(config) {
1514
1397
  this.config = config;
@@ -1523,51 +1406,6 @@ var OkxRestClient = class _OkxRestClient {
1523
1406
  hasCustomProxy: !!config.proxyUrl
1524
1407
  });
1525
1408
  }
1526
- /**
1527
- * Resolve OAuth access token via the okx-auth binary (fd3 pipe).
1528
- * Caches the token for 60 s to avoid spawning the binary on every
1529
- * request. The binary handles refresh internally (300s TTL lead),
1530
- * so periodic re-calls let it serve a fresh token when needed.
1531
- * Returns null when not logged in.
1532
- */
1533
- async resolveAccessToken() {
1534
- if (this.cachedAccessToken && Date.now() - this.cachedAccessTokenAt < TOKEN_CACHE_TTL_MS) {
1535
- return this.cachedAccessToken;
1536
- }
1537
- try {
1538
- const token = await execAuthToken();
1539
- this.cachedAccessToken = token;
1540
- this.cachedAccessTokenAt = Date.now();
1541
- return token;
1542
- } catch (e) {
1543
- if (e instanceof NotLoggedInError) {
1544
- return null;
1545
- }
1546
- throw e;
1547
- }
1548
- }
1549
- /**
1550
- * Dynamic auth — determines auth method per request.
1551
- *
1552
- * 1. API key in config → HMAC signing (no OAuth fallback)
1553
- * 2. OAuth token via okx-auth binary → Bearer token
1554
- * 3. Neither → throw ConfigError
1555
- */
1556
- async applyAuth(headers, method, requestPath, bodyJson, timestamp) {
1557
- if (this.config.apiKey && this.config.secretKey && this.config.passphrase) {
1558
- this.setAuthHeaders(headers, method, requestPath, bodyJson, timestamp);
1559
- return;
1560
- }
1561
- const accessToken = await this.resolveAccessToken();
1562
- if (accessToken) {
1563
- headers.set("Authorization", `Bearer ${accessToken}`);
1564
- return;
1565
- }
1566
- throw new ConfigError(
1567
- "No credentials found.",
1568
- "Run `okx auth login` to authenticate, or configure API key credentials."
1569
- );
1570
- }
1571
1409
  /** The canonical base URL for this client (e.g. https://www.okx.com). */
1572
1410
  get baseUrl() {
1573
1411
  return this.config.baseUrl;
@@ -1626,6 +1464,18 @@ var OkxRestClient = class _OkxRestClient {
1626
1464
  });
1627
1465
  }
1628
1466
  setAuthHeaders(headers, method, requestPath, bodyJson, timestamp) {
1467
+ if (!this.config.hasAuth) {
1468
+ throw new ConfigError(
1469
+ "Private endpoint requires API credentials.",
1470
+ "Configure OKX_API_KEY, OKX_SECRET_KEY and OKX_PASSPHRASE."
1471
+ );
1472
+ }
1473
+ if (!this.config.apiKey || !this.config.secretKey || !this.config.passphrase) {
1474
+ throw new ConfigError(
1475
+ "Invalid private API credentials state.",
1476
+ "Ensure all OKX credentials are set."
1477
+ );
1478
+ }
1629
1479
  const payload = `${timestamp}${method.toUpperCase()}${requestPath}${bodyJson}`;
1630
1480
  const signature = signOkxPayload(payload, this.config.secretKey);
1631
1481
  headers.set("OK-ACCESS-KEY", this.config.apiKey);
@@ -1740,7 +1590,7 @@ var OkxRestClient = class _OkxRestClient {
1740
1590
  const conn = this.pilot.getConnectionParams();
1741
1591
  this.logRequest("POST", `${conn.baseUrl}${path42}`, "private");
1742
1592
  const reqConfig = { method: "POST", path: path42, auth: "private" };
1743
- const headers = await this.buildHeaders(reqConfig, path42, bodyJson, getNow());
1593
+ const headers = this.buildHeaders(reqConfig, path42, bodyJson, getNow());
1744
1594
  if (conn.userAgent) {
1745
1595
  headers.set("User-Agent", conn.userAgent);
1746
1596
  }
@@ -1861,7 +1711,7 @@ var OkxRestClient = class _OkxRestClient {
1861
1711
  // Header building
1862
1712
  // ---------------------------------------------------------------------------
1863
1713
  /** Build HTTP headers. reqConfig.extraHeaders must NOT contain auth keys (OK-ACCESS-*). */
1864
- async buildHeaders(reqConfig, requestPath, bodyJson, timestamp) {
1714
+ buildHeaders(reqConfig, requestPath, bodyJson, timestamp) {
1865
1715
  const headers = new Headers({
1866
1716
  "Content-Type": "application/json",
1867
1717
  Accept: "application/json"
@@ -1870,7 +1720,7 @@ var OkxRestClient = class _OkxRestClient {
1870
1720
  headers.set("User-Agent", this.config.userAgent);
1871
1721
  }
1872
1722
  if (reqConfig.auth === "private") {
1873
- await this.applyAuth(headers, reqConfig.method, requestPath, bodyJson, timestamp);
1723
+ this.setAuthHeaders(headers, reqConfig.method, requestPath, bodyJson, timestamp);
1874
1724
  }
1875
1725
  const useSimulated = reqConfig.simulatedTrading !== void 0 ? reqConfig.simulatedTrading : this.config.demo;
1876
1726
  if (useSimulated) {
@@ -1924,7 +1774,7 @@ var OkxRestClient = class _OkxRestClient {
1924
1774
  if (reqConfig.rateLimit) {
1925
1775
  await this.rateLimiter.consume(reqConfig.rateLimit);
1926
1776
  }
1927
- const headers = await this.buildHeaders(reqConfig, requestPath, bodyJson, timestamp);
1777
+ const headers = this.buildHeaders(reqConfig, requestPath, bodyJson, timestamp);
1928
1778
  if (conn.userAgent) {
1929
1779
  headers.set("User-Agent", conn.userAgent);
1930
1780
  }
@@ -2351,6 +2201,8 @@ function registerIndicatorTools() {
2351
2201
  ];
2352
2202
  }
2353
2203
  var DEFAULT_SOURCE_TAG = "MCP";
2204
+ var TOKEN_REGEX = /^[A-Z2-7]{12}$/;
2205
+ var TAG_JOIN = "";
2354
2206
  var OKX_SITES = {
2355
2207
  global: {
2356
2208
  label: "Global",
@@ -2364,7 +2216,7 @@ var OKX_SITES = {
2364
2216
  },
2365
2217
  us: {
2366
2218
  label: "US",
2367
- apiBaseUrl: "https://us.okx.com",
2219
+ apiBaseUrl: "https://app.okx.com",
2368
2220
  webUrl: "https://app.okx.com"
2369
2221
  }
2370
2222
  };
@@ -3308,7 +3160,8 @@ function registerAlgoTradeTools() {
3308
3160
  callBackSpread: readString(args, "callbackSpread"),
3309
3161
  activePx: readString(args, "activePx"),
3310
3162
  reduceOnly: typeof reduceOnly === "boolean" ? String(reduceOnly) : void 0,
3311
- clOrdId: readString(args, "clOrdId")
3163
+ clOrdId: readString(args, "clOrdId"),
3164
+ tag: context.config.sourceTag
3312
3165
  }),
3313
3166
  privateRateLimit("swap_place_move_stop_order", 20)
3314
3167
  );
@@ -3653,7 +3506,8 @@ function registerFuturesAlgoTools() {
3653
3506
  callBackSpread: readString(args, "callbackSpread"),
3654
3507
  activePx: readString(args, "activePx"),
3655
3508
  reduceOnly: typeof reduceOnly === "boolean" ? String(reduceOnly) : void 0,
3656
- clOrdId: readString(args, "clOrdId")
3509
+ clOrdId: readString(args, "clOrdId"),
3510
+ tag: context.config.sourceTag
3657
3511
  }),
3658
3512
  privateRateLimit("futures_place_move_stop_order", 20)
3659
3513
  );
@@ -3911,7 +3765,7 @@ function safeWriteFile(targetDir, fileName, data) {
3911
3765
  throw new Error(`Invalid file name: "${fileName}"`);
3912
3766
  }
3913
3767
  const resolvedDir = resolve(targetDir);
3914
- const filePath = join4(resolvedDir, safeName);
3768
+ const filePath = join3(resolvedDir, safeName);
3915
3769
  const resolvedPath = resolve(filePath);
3916
3770
  if (!resolvedPath.startsWith(resolvedDir + sep)) {
3917
3771
  throw new Error(`Path traversal detected: "${fileName}" resolves outside target directory`);
@@ -4039,7 +3893,7 @@ async function extractSkillZip(zipPath, targetDir, limits) {
4039
3893
  });
4040
3894
  }
4041
3895
  function readMetaJson(contentDir) {
4042
- const metaPath = join5(contentDir, "_meta.json");
3896
+ const metaPath = join4(contentDir, "_meta.json");
4043
3897
  if (!existsSync(metaPath)) {
4044
3898
  throw new Error(`_meta.json not found in ${contentDir}. Invalid skill package.`);
4045
3899
  }
@@ -4065,12 +3919,12 @@ function readMetaJson(contentDir) {
4065
3919
  };
4066
3920
  }
4067
3921
  function validateSkillMdExists(contentDir) {
4068
- const skillMdPath = join5(contentDir, "SKILL.md");
3922
+ const skillMdPath = join4(contentDir, "SKILL.md");
4069
3923
  if (!existsSync(skillMdPath)) {
4070
3924
  throw new Error(`SKILL.md not found in ${contentDir}. Invalid skill package.`);
4071
3925
  }
4072
3926
  }
4073
- var DEFAULT_REGISTRY_PATH = join6(homedir4(), ".okx", "skills", "registry.json");
3927
+ var DEFAULT_REGISTRY_PATH = join5(homedir3(), ".okx", "skills", "registry.json");
4074
3928
  function readRegistry(registryPath = DEFAULT_REGISTRY_PATH) {
4075
3929
  if (!existsSync2(registryPath)) {
4076
3930
  return { version: 1, skills: {} };
@@ -10485,7 +10339,7 @@ function createToolRunner(client, config) {
10485
10339
  };
10486
10340
  }
10487
10341
  function configFilePath() {
10488
- return join7(homedir5(), ".okx", "config.toml");
10342
+ return join6(homedir4(), ".okx", "config.toml");
10489
10343
  }
10490
10344
  function readFullConfig() {
10491
10345
  const path42 = configFilePath();
@@ -10504,6 +10358,11 @@ Or re-run: okx config init`
10504
10358
  );
10505
10359
  }
10506
10360
  }
10361
+ function readTomlProfile(profileName) {
10362
+ const config = readFullConfig();
10363
+ const name = profileName ?? config.default_profile ?? "default";
10364
+ return config.profiles?.[name] ?? {};
10365
+ }
10507
10366
  var CONFIG_HEADER = `# OKX Trade Kit Configuration
10508
10367
  # If editing manually, wrap values containing special chars in quotes:
10509
10368
  # passphrase = 'value' (if value contains # \\ ")
@@ -10552,24 +10411,18 @@ function parseModuleList(rawModules) {
10552
10411
  }
10553
10412
  return Array.from(deduped);
10554
10413
  }
10555
- async function loadCredentials(toml) {
10414
+ function loadCredentials(toml) {
10556
10415
  const apiKey = process.env.OKX_API_KEY?.trim() ?? toml.api_key;
10557
10416
  const secretKey = process.env.OKX_SECRET_KEY?.trim() ?? toml.secret_key;
10558
10417
  const passphrase = process.env.OKX_PASSPHRASE?.trim() ?? toml.passphrase;
10559
- const hasApiKey = Boolean(apiKey && secretKey && passphrase);
10418
+ const hasAuth = Boolean(apiKey && secretKey && passphrase);
10560
10419
  const partialAuth = Boolean(apiKey) || Boolean(secretKey) || Boolean(passphrase);
10561
- if (partialAuth && !hasApiKey) {
10420
+ if (partialAuth && !hasAuth) {
10562
10421
  throw new ConfigError(
10563
10422
  "Partial API credentials detected.",
10564
10423
  "Set OKX_API_KEY, OKX_SECRET_KEY and OKX_PASSPHRASE together (env vars or config.toml profile)."
10565
10424
  );
10566
10425
  }
10567
- let hasOAuth = false;
10568
- if (!hasApiKey) {
10569
- const status = await execAuthStatus();
10570
- hasOAuth = status?.status === "logged_in";
10571
- }
10572
- const hasAuth = hasOAuth || hasApiKey;
10573
10426
  return { apiKey, secretKey, passphrase, hasAuth };
10574
10427
  }
10575
10428
  function resolveSite(cliSite, tomlSite) {
@@ -10592,6 +10445,30 @@ function resolveBaseUrl(site, tomlBaseUrl) {
10592
10445
  }
10593
10446
  return rawBaseUrl.replace(/\/+$/, "");
10594
10447
  }
10448
+ function resolveSourceTag(cli, defaultTag, opts = {}) {
10449
+ const readEnv = opts.readEnv !== false;
10450
+ const cliSkill = cli.skill?.trim();
10451
+ if (cliSkill) {
10452
+ if (isValidToken(cliSkill)) return composeTag(defaultTag, cliSkill);
10453
+ console.warn("--skill value format invalid (expected 12-char Base32), ignoring");
10454
+ }
10455
+ if (readEnv) {
10456
+ const envToken = process.env.OKX_SKILL_TOKEN?.trim();
10457
+ if (envToken) {
10458
+ if (isValidToken(envToken)) return composeTag(defaultTag, envToken);
10459
+ console.warn("OKX_SKILL_TOKEN format invalid, ignoring");
10460
+ }
10461
+ }
10462
+ const sourceTag = cli.sourceTag?.trim();
10463
+ if (sourceTag) return sourceTag;
10464
+ return defaultTag;
10465
+ }
10466
+ function composeTag(defaultTag, token) {
10467
+ return `${defaultTag}${TAG_JOIN}${token}`;
10468
+ }
10469
+ function isValidToken(s) {
10470
+ return TOKEN_REGEX.test(s);
10471
+ }
10595
10472
  function resolveDemo(cli, toml) {
10596
10473
  if (cli.demo && cli.live) {
10597
10474
  throw new ConfigError(
@@ -10603,11 +10480,9 @@ function resolveDemo(cli, toml) {
10603
10480
  if (cli.demo === true) return true;
10604
10481
  return process.env.OKX_DEMO === "1" || process.env.OKX_DEMO === "true" || (toml.demo ?? false);
10605
10482
  }
10606
- async function loadConfig(cli) {
10607
- const config = readFullConfig();
10608
- const profileName = cli.profile ?? config.default_profile ?? "default";
10609
- const toml = config.profiles?.[profileName] ?? {};
10610
- const creds = await loadCredentials(toml);
10483
+ function loadConfig(cli, opts = {}) {
10484
+ const toml = readTomlProfile(cli.profile);
10485
+ const creds = loadCredentials(toml);
10611
10486
  const demo = resolveDemo(cli, toml);
10612
10487
  const site = resolveSite(cli.site, toml.site);
10613
10488
  const baseUrl = resolveBaseUrl(site, toml.base_url);
@@ -10625,9 +10500,9 @@ async function loadConfig(cli) {
10625
10500
  "proxy_url must start with http:// or https://. SOCKS proxies are not supported."
10626
10501
  );
10627
10502
  }
10503
+ const defaultTag = opts.entryPrefix ?? DEFAULT_SOURCE_TAG;
10628
10504
  return {
10629
10505
  ...creds,
10630
- profile: profileName,
10631
10506
  baseUrl,
10632
10507
  timeoutMs: Math.floor(rawTimeout),
10633
10508
  modules: parseModuleList(cli.modules),
@@ -10635,12 +10510,14 @@ async function loadConfig(cli) {
10635
10510
  demo,
10636
10511
  site,
10637
10512
  userAgent: cli.userAgent,
10638
- sourceTag: cli.sourceTag ?? DEFAULT_SOURCE_TAG,
10513
+ // sourceTag resolution: --skill > OKX_SKILL_TOKEN > --source-tag > defaultTag
10514
+ // MCP entry passes { readEnv: false } to honour the "MCP path not attributed" contract.
10515
+ sourceTag: resolveSourceTag(cli, defaultTag, { readEnv: opts.readEnv }),
10639
10516
  proxyUrl: rawProxyUrl || void 0,
10640
10517
  verbose: cli.verbose ?? false
10641
10518
  };
10642
10519
  }
10643
- var CACHE_FILE = join8(homedir6(), ".okx", "update-check.json");
10520
+ var CACHE_FILE = join7(homedir5(), ".okx", "update-check.json");
10644
10521
  var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
10645
10522
  function readCache2() {
10646
10523
  try {
@@ -10653,7 +10530,7 @@ function readCache2() {
10653
10530
  }
10654
10531
  function writeCache2(cache) {
10655
10532
  try {
10656
- mkdirSync6(join8(homedir6(), ".okx"), { recursive: true });
10533
+ mkdirSync6(join7(homedir5(), ".okx"), { recursive: true });
10657
10534
  writeFileSync5(CACHE_FILE, JSON.stringify(cache, null, 2), "utf-8");
10658
10535
  } catch {
10659
10536
  }
@@ -10823,13 +10700,13 @@ function findMsStoreClaudePath() {
10823
10700
  }
10824
10701
  function getConfigPath(client) {
10825
10702
  const home = os3.homedir();
10826
- const platform3 = process.platform;
10703
+ const platform2 = process.platform;
10827
10704
  switch (client) {
10828
10705
  case "claude-desktop":
10829
- if (platform3 === "win32") {
10706
+ if (platform2 === "win32") {
10830
10707
  return findMsStoreClaudePath() ?? path3.join(appData(), "Claude", CLAUDE_CONFIG_FILE);
10831
10708
  }
10832
- if (platform3 === "darwin") {
10709
+ if (platform2 === "darwin") {
10833
10710
  return path3.join(home, "Library", "Application Support", "Claude", CLAUDE_CONFIG_FILE);
10834
10711
  }
10835
10712
  return path3.join(process.env.XDG_CONFIG_HOME ?? path3.join(home, ".config"), "Claude", CLAUDE_CONFIG_FILE);
@@ -10941,7 +10818,7 @@ var DOWNLOAD_TIMEOUT_MS = 3e4;
10941
10818
  var PLATFORM_MAP = {
10942
10819
  "darwin-arm64": "darwin-arm64",
10943
10820
  "darwin-x64": "darwin-x64",
10944
- "linux-arm64": "linux-x64",
10821
+ "linux-arm64": "linux-arm64",
10945
10822
  "linux-x64": "linux-x64",
10946
10823
  "win32-arm64": "win32-arm64",
10947
10824
  "win32-x64": "win32-x64"
@@ -11073,7 +10950,7 @@ async function installPilotBinary(destPath, sources = CDN_SOURCES, onProgress) {
11073
10950
  if (earlyResult) return earlyResult;
11074
10951
  const platformDir = getPlatformDir();
11075
10952
  const binaryName = getBinaryName();
11076
- const resolvedDest = destPath ?? join10(homedir8(), ".okx", "bin", binaryName);
10953
+ const resolvedDest = destPath ?? join9(homedir7(), ".okx", "bin", binaryName);
11077
10954
  const tmpPath = resolvedDest + ".tmp";
11078
10955
  mkdirSync8(dirname6(resolvedDest), { recursive: true });
11079
10956
  const localHash = existsSync6(resolvedDest) ? hashFile(resolvedDest) : null;
@@ -11196,276 +11073,16 @@ function downloadText(url, timeoutMs) {
11196
11073
  })
11197
11074
  );
11198
11075
  }
11199
- var AUTH_CDN_PATH_PREFIX = "/upgradeapp/tools/oauth";
11200
- function getAuthBinaryName() {
11201
- return platform2() === "win32" ? "okx-auth.exe" : "okx-auth";
11202
- }
11203
- function getAuthStatus(binaryPath, opts) {
11204
- const resolvedPath = binaryPath ?? getAuthBinaryPath();
11205
- const platformDir = getPlatformDir();
11206
- if (!existsSync7(resolvedPath)) {
11207
- return { binaryPath: resolvedPath, exists: false, platform: platformDir };
11208
- }
11209
- if (opts?.skipHash) {
11210
- return { binaryPath: resolvedPath, exists: true, platform: platformDir };
11211
- }
11212
- const { size, sha256 } = hashFile(resolvedPath);
11213
- return { binaryPath: resolvedPath, exists: true, platform: platformDir, fileSize: size, sha256 };
11214
- }
11215
- async function fetchAuthCdnChecksum(sources = CDN_SOURCES, timeoutMs = DOWNLOAD_TIMEOUT_MS) {
11216
- const platformDir = getPlatformDir();
11217
- if (!platformDir) return null;
11218
- const checksumPath = `${AUTH_CDN_PATH_PREFIX}/${platformDir}/checksum.json`;
11219
- for (const { host, protocol } of sources) {
11220
- try {
11221
- const url = `${protocol}://${host}${checksumPath}`;
11222
- const raw = await downloadText2(url, timeoutMs);
11223
- const data = JSON.parse(raw);
11224
- if (typeof data.sha256 !== "string" || typeof data.size !== "number" || typeof data.target !== "string") {
11225
- continue;
11226
- }
11227
- return { sha256: data.sha256, size: data.size, target: data.target, source: host };
11228
- } catch {
11229
- }
11230
- }
11231
- return null;
11232
- }
11233
- async function fetchAndValidateChecksum2(host, protocol, checksumPath, platformDir, timeoutMs, onProgress) {
11234
- const checksumUrl = `${protocol}://${host}${checksumPath}`;
11235
- onProgress?.(`Fetching checksum from ${host}...`);
11236
- const raw = await downloadText2(checksumUrl, timeoutMs);
11237
- const checksum = JSON.parse(raw);
11238
- if (typeof checksum.sha256 !== "string" || typeof checksum.size !== "number" || typeof checksum.target !== "string") {
11239
- throw new Error("Invalid checksum.json: missing sha256, size, or target");
11240
- }
11241
- if (checksum.target !== platformDir) {
11242
- throw new Error(`Target mismatch: expected ${platformDir}, got ${checksum.target}`);
11243
- }
11244
- return { sha256: checksum.sha256, size: checksum.size, target: checksum.target };
11245
- }
11246
- async function downloadAndVerify2(host, protocol, binaryPath, tmpPath, checksum, timeoutMs, onProgress) {
11247
- const binaryUrl = `${protocol}://${host}${binaryPath}`;
11248
- onProgress?.(`Downloading binary from ${host}...`);
11249
- await download2(binaryUrl, tmpPath, timeoutMs);
11250
- const actual = hashFile(tmpPath);
11251
- if (actual.size !== checksum.size) {
11252
- throw new Error(`Size mismatch: expected ${checksum.size}, got ${actual.size}`);
11253
- }
11254
- if (actual.sha256 !== checksum.sha256) {
11255
- throw new Error(`SHA-256 mismatch: expected ${checksum.sha256}, got ${actual.sha256}`);
11256
- }
11257
- }
11258
- function atomicReplace2(tmpPath, resolvedDest) {
11259
- if (platform2() === "win32") {
11260
- try {
11261
- unlinkSync4(resolvedDest);
11262
- } catch {
11263
- }
11264
- }
11265
- renameSync4(tmpPath, resolvedDest);
11266
- if (platform2() !== "win32") {
11267
- chmodSync2(resolvedDest, 493);
11268
- }
11269
- }
11270
- function installPreChecks2(destPath, sources) {
11271
- if (!destPath && process.env.OKX_AUTH_BIN) {
11272
- return { status: "up-to-date", source: "(env override)" };
11273
- }
11274
- if (!getPlatformDir()) {
11275
- return { status: "failed", error: "Unsupported platform" };
11276
- }
11277
- if (sources.length === 0) {
11278
- return { status: "failed", error: "No CDN sources available" };
11279
- }
11280
- return null;
11281
- }
11282
- function isLocalUpToDate2(localHash, checksum) {
11283
- return localHash !== null && localHash.size === checksum.size && localHash.sha256 === checksum.sha256;
11284
- }
11285
- async function installAuthBinary(destPath, sources = CDN_SOURCES, onProgress) {
11286
- const earlyResult = installPreChecks2(destPath, sources);
11287
- if (earlyResult) return earlyResult;
11288
- const platformDir = getPlatformDir();
11289
- const binaryName = getAuthBinaryName();
11290
- const resolvedDest = destPath ?? join11(homedir9(), ".okx", "bin", binaryName);
11291
- const tmpPath = resolvedDest + ".tmp";
11292
- mkdirSync9(dirname7(resolvedDest), { recursive: true });
11293
- const localHash = existsSync7(resolvedDest) ? hashFile(resolvedDest) : null;
11294
- const checksumPath = `${AUTH_CDN_PATH_PREFIX}/${platformDir}/checksum.json`;
11295
- const binaryPath = `${AUTH_CDN_PATH_PREFIX}/${platformDir}/${binaryName}`;
11296
- const errors = [];
11297
- for (const { host, protocol } of sources) {
11298
- try {
11299
- const checksum = await fetchAndValidateChecksum2(
11300
- host,
11301
- protocol,
11302
- checksumPath,
11303
- platformDir,
11304
- DOWNLOAD_TIMEOUT_MS,
11305
- onProgress
11306
- );
11307
- if (isLocalUpToDate2(localHash, checksum)) {
11308
- onProgress?.("Already up to date (checksum match)");
11309
- return { status: "up-to-date", source: host };
11310
- }
11311
- await downloadAndVerify2(host, protocol, binaryPath, tmpPath, checksum, DOWNLOAD_TIMEOUT_MS, onProgress);
11312
- atomicReplace2(tmpPath, resolvedDest);
11313
- onProgress?.(`Downloaded and verified from ${host}`);
11314
- return { status: "installed", source: host };
11315
- } catch (err) {
11316
- try {
11317
- unlinkSync4(tmpPath);
11318
- } catch {
11319
- }
11320
- const msg = err instanceof Error ? err.message : String(err);
11321
- errors.push(`${host}: ${msg}`);
11322
- onProgress?.(`${host} failed: ${msg}`);
11323
- }
11324
- }
11325
- return { status: "failed", error: `All CDN sources failed:
11326
- ${errors.join("\n")}` };
11327
- }
11328
- function removeAuthBinary(binaryPath) {
11329
- const resolvedPath = binaryPath ?? getAuthBinaryPath();
11330
- try {
11331
- unlinkSync4(resolvedPath);
11332
- return { status: "removed" };
11333
- } catch (err) {
11334
- if (err.code === "ENOENT") {
11335
- return { status: "not-found" };
11336
- }
11337
- const msg = err instanceof Error ? err.message : String(err);
11338
- throw new Error(`Failed to remove ${resolvedPath}: ${msg}`);
11339
- }
11340
- }
11341
- function isRedirect2(statusCode) {
11342
- return statusCode !== void 0 && statusCode >= 300 && statusCode < 400;
11343
- }
11344
- function validateRedirect2(res, requestUrl, redirectCount, maxRedirects) {
11345
- if (redirectCount > maxRedirects) {
11346
- throw new Error(`Too many redirects (${maxRedirects})`);
11347
- }
11348
- const location = res.headers.location;
11349
- if (requestUrl.startsWith("https") && !location.startsWith("https")) {
11350
- throw new Error("Refused HTTPS \u2192 HTTP redirect downgrade");
11351
- }
11352
- return location;
11353
- }
11354
- function fetchResponse2(url, timeoutMs) {
11355
- return new Promise((resolve3, reject) => {
11356
- let redirects = 0;
11357
- const maxRedirects = 5;
11358
- function doRequest(requestUrl) {
11359
- const reqFn = requestUrl.startsWith("https") ? httpsGet2 : httpGet2;
11360
- const req = reqFn(requestUrl, { timeout: timeoutMs }, (res) => {
11361
- if (isRedirect2(res.statusCode) && res.headers.location) {
11362
- redirects++;
11363
- try {
11364
- const location = validateRedirect2(res, requestUrl, redirects, maxRedirects);
11365
- res.resume();
11366
- doRequest(location);
11367
- } catch (err) {
11368
- reject(err);
11369
- }
11370
- return;
11371
- }
11372
- if (res.statusCode !== 200) {
11373
- reject(new Error(`HTTP ${res.statusCode ?? "unknown"}`));
11374
- return;
11375
- }
11376
- resolve3(res);
11377
- });
11378
- req.on("error", reject);
11379
- req.on("timeout", () => {
11380
- req.destroy();
11381
- reject(new Error("Download timed out"));
11382
- });
11383
- }
11384
- doRequest(url);
11385
- });
11386
- }
11387
- function download2(url, destPath, timeoutMs) {
11388
- return fetchResponse2(url, timeoutMs).then(
11389
- (res) => new Promise((resolve3, reject) => {
11390
- const file = createWriteStream3(destPath);
11391
- res.pipe(file);
11392
- file.on("finish", () => file.close(() => resolve3()));
11393
- file.on("error", (err) => {
11394
- try {
11395
- unlinkSync4(destPath);
11396
- } catch {
11397
- }
11398
- reject(err);
11399
- });
11400
- })
11401
- );
11402
- }
11403
- function downloadText2(url, timeoutMs) {
11404
- return fetchResponse2(url, timeoutMs).then(
11405
- (res) => new Promise((resolve3, reject) => {
11406
- const chunks = [];
11407
- res.on("data", (chunk) => chunks.push(chunk));
11408
- res.on("end", () => resolve3(Buffer.concat(chunks).toString("utf8")));
11409
- res.on("error", reject);
11410
- })
11411
- );
11412
- }
11413
- var CACHE_PATH = join12(homedir10(), ".okx", "auth-binary-check.json");
11414
- var CHECK_INTERVAL_MS2 = 2 * 60 * 60 * 1e3;
11415
- function readCache3() {
11416
- try {
11417
- if (!existsSync8(CACHE_PATH)) return null;
11418
- const data = JSON.parse(readFileSync8(CACHE_PATH, "utf-8"));
11419
- if (typeof data.cdnSha256 !== "string" || typeof data.checkedAt !== "number") return null;
11420
- return { cdnSha256: data.cdnSha256, checkedAt: data.checkedAt };
11421
- } catch {
11422
- return null;
11423
- }
11424
- }
11425
- function writeCache3(cdnSha256) {
11426
- try {
11427
- mkdirSync10(join12(homedir10(), ".okx"), { recursive: true });
11428
- writeFileSync7(CACHE_PATH, JSON.stringify({ cdnSha256, checkedAt: Date.now() }, null, 2), "utf-8");
11429
- } catch {
11430
- }
11431
- }
11432
- async function ensureAuthBinaryLatest(onProgress) {
11433
- if (process.env.OKX_AUTH_BIN) return;
11434
- const cache = readCache3();
11435
- if (cache && Date.now() - cache.checkedAt < CHECK_INTERVAL_MS2) return;
11436
- try {
11437
- const cdn = await fetchAuthCdnChecksum(void 0, 5e3);
11438
- if (!cdn) return;
11439
- const local = getAuthStatus();
11440
- if (local.exists && local.sha256 === cdn.sha256) {
11441
- writeCache3(cdn.sha256);
11442
- return;
11443
- }
11444
- onProgress?.("Updating okx-auth binary...");
11445
- const result = await installAuthBinary(void 0, void 0, onProgress);
11446
- if (result.status === "installed" || result.status === "up-to-date") {
11447
- const updated = getAuthStatus();
11448
- if (updated.sha256) writeCache3(updated.sha256);
11449
- if (result.status === "installed") {
11450
- onProgress?.("\u2713 okx-auth updated successfully");
11451
- }
11452
- }
11453
- } catch {
11454
- }
11455
- }
11456
- function updateAuthBinaryCache(sha256) {
11457
- writeCache3(sha256);
11458
- }
11459
- function clearAuthBinaryCache() {
11460
- try {
11461
- unlinkSync5(CACHE_PATH);
11462
- } catch {
11463
- }
11464
- }
11465
11076
 
11466
- // src/commands/auth.ts
11467
- import { spawn as spawn2 } from "child_process";
11468
- import readline from "readline";
11077
+ // src/commands/diagnose.ts
11078
+ import dns from "dns/promises";
11079
+ import net from "net";
11080
+ import os5 from "os";
11081
+ import tls from "tls";
11082
+
11083
+ // src/commands/diagnose-utils.ts
11084
+ import fs4 from "fs";
11085
+ import { createRequire } from "module";
11469
11086
 
11470
11087
  // src/formatter.ts
11471
11088
  import { EOL } from "os";
@@ -11556,286 +11173,7 @@ function markFailedIfSCodeError(data) {
11556
11173
  }
11557
11174
  }
11558
11175
 
11559
- // src/commands/auth.ts
11560
- function runOkxAuth(args) {
11561
- const binPath = getAuthBinaryPath();
11562
- return new Promise((resolve3, reject) => {
11563
- const child = spawn2(binPath, args, {
11564
- stdio: "inherit"
11565
- });
11566
- child.on("error", (err) => {
11567
- reject(new Error(`Failed to spawn okx-auth: ${err.message}`));
11568
- });
11569
- child.on("close", (code) => {
11570
- resolve3(code ?? 1);
11571
- });
11572
- });
11573
- }
11574
- function runOkxAuthCapture(args) {
11575
- const binPath = getAuthBinaryPath();
11576
- return new Promise((resolve3, reject) => {
11577
- const child = spawn2(binPath, args, {
11578
- stdio: ["inherit", "pipe", "inherit"]
11579
- });
11580
- const chunks = [];
11581
- child.stdout.on("data", (chunk) => chunks.push(chunk));
11582
- child.on("error", (err) => {
11583
- reject(new Error(`Failed to spawn okx-auth: ${err.message}`));
11584
- });
11585
- child.on("close", (code) => {
11586
- resolve3({
11587
- code: code ?? 1,
11588
- stdout: Buffer.concat(chunks).toString("utf-8")
11589
- });
11590
- });
11591
- });
11592
- }
11593
- function findApiKeyProfile() {
11594
- let config;
11595
- try {
11596
- config = readFullConfig();
11597
- } catch {
11598
- return null;
11599
- }
11600
- for (const [name, profile] of Object.entries(config.profiles ?? {})) {
11601
- if (profile?.api_key) return name;
11602
- }
11603
- return null;
11604
- }
11605
- async function cmdAuthLogin(args) {
11606
- const apiKeyProfile = findApiKeyProfile();
11607
- if (apiKeyProfile) {
11608
- if (args.manual) {
11609
- outputLine(JSON.stringify({
11610
- status: "skipped",
11611
- reason: "api_key_configured",
11612
- profile: apiKeyProfile,
11613
- message: `API key already configured (profile: ${apiKeyProfile}). OAuth login skipped \u2014 API key will be used automatically.`
11614
- }));
11615
- } else {
11616
- outputLine(`API key already configured (profile: ${apiKeyProfile}). OAuth login skipped \u2014 API key will be used automatically.`);
11617
- }
11618
- return;
11619
- }
11620
- const cliArgs = ["login"];
11621
- if (args.site) cliArgs.push("--site", args.site);
11622
- if (args.manual) cliArgs.push("--manual");
11623
- const code = await runOkxAuth(cliArgs);
11624
- if (code !== 0) {
11625
- process.exitCode = code;
11626
- }
11627
- }
11628
- async function cmdAuthLogout() {
11629
- const code = await runOkxAuth(["logout"]);
11630
- if (code !== 0) {
11631
- process.exitCode = code;
11632
- }
11633
- }
11634
- async function cmdAuthStatus(args) {
11635
- const cliArgs = ["status"];
11636
- if (args.json) cliArgs.push("--json");
11637
- const result = await runOkxAuthCapture(cliArgs);
11638
- if (result.stdout) {
11639
- process.stdout.write(result.stdout);
11640
- }
11641
- if (result.code !== 0) {
11642
- process.exitCode = result.code;
11643
- }
11644
- }
11645
- function resolveChecksumMatch(local, cdnChecksum, cdnError) {
11646
- if (!local.exists) return "not-installed";
11647
- if (cdnError || !cdnChecksum) return "unavailable";
11648
- if (cdnChecksum.sha256 === local.sha256) return "match";
11649
- return "mismatch";
11650
- }
11651
- function checksumMatchLabel(match) {
11652
- if (match === "match") return "\u2713 match";
11653
- if (match === "mismatch") return "\u2717 mismatch (update available)";
11654
- return "CDN unreachable";
11655
- }
11656
- function formatBytes(bytes) {
11657
- if (bytes < 1024) return `${bytes} B`;
11658
- if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
11659
- return `${(bytes / (1024 * 1024)).toFixed(2)} MB`;
11660
- }
11661
- async function cmdAuthInstallStatus(json) {
11662
- const local = getAuthStatus();
11663
- let cdnChecksum = null;
11664
- let cdnError = null;
11665
- if (local.exists) {
11666
- try {
11667
- cdnChecksum = await fetchAuthCdnChecksum(void 0, 5e3);
11668
- } catch (err) {
11669
- cdnError = err instanceof Error ? err.message : String(err);
11670
- }
11671
- }
11672
- const checksumMatch = resolveChecksumMatch(local, cdnChecksum, cdnError);
11673
- if (json) {
11674
- outputLine(
11675
- JSON.stringify({
11676
- binaryPath: local.binaryPath,
11677
- exists: local.exists,
11678
- platform: local.platform,
11679
- fileSize: local.fileSize ?? null,
11680
- sha256: local.sha256 ?? null,
11681
- cdnMatch: checksumMatch,
11682
- cdnSha256: cdnChecksum?.sha256 ?? null,
11683
- cdnSource: cdnChecksum?.source ?? null
11684
- })
11685
- );
11686
- return;
11687
- }
11688
- outputLine("");
11689
- outputLine(" okx-auth Binary Status");
11690
- outputLine(" " + "\u2500".repeat(40));
11691
- outputLine(` Binary path : ${local.binaryPath}`);
11692
- outputLine(` Installed : ${local.exists ? "yes" : "no"}`);
11693
- outputLine(` Platform : ${local.platform ?? "(unsupported)"}`);
11694
- if (local.exists) {
11695
- outputLine(` File size : ${formatBytes(local.fileSize)}`);
11696
- outputLine(` SHA-256 : ${local.sha256 ?? "(unknown)"}`);
11697
- outputLine(` CDN check : ${checksumMatchLabel(checksumMatch)}`);
11698
- if (cdnChecksum) {
11699
- outputLine(` CDN source : ${cdnChecksum.source}`);
11700
- }
11701
- }
11702
- outputLine("");
11703
- }
11704
- async function cmdAuthInstall(json) {
11705
- const messages2 = [];
11706
- const onProgress = (msg) => {
11707
- if (!json) {
11708
- outputLine(` ${msg}`);
11709
- }
11710
- messages2.push(msg);
11711
- };
11712
- if (!json) {
11713
- outputLine("");
11714
- outputLine(" Installing okx-auth...");
11715
- }
11716
- const result = await installAuthBinary(void 0, void 0, onProgress);
11717
- if (json) {
11718
- outputLine(JSON.stringify({ status: result.status, source: result.source ?? null, error: result.error ?? null, messages: messages2 }));
11719
- if (result.status === "failed") {
11720
- process.exitCode = 1;
11721
- }
11722
- return;
11723
- }
11724
- if (result.status === "installed" || result.status === "up-to-date") {
11725
- const local = getAuthStatus();
11726
- if (local.sha256) updateAuthBinaryCache(local.sha256);
11727
- }
11728
- if (result.status === "installed") {
11729
- outputLine(` \u2713 okx-auth installed successfully (${result.source ?? ""})`);
11730
- } else if (result.status === "up-to-date") {
11731
- outputLine(" \u2713 okx-auth is already up to date");
11732
- } else {
11733
- errorLine(` \u2717 Installation failed: ${result.error ?? "unknown error"}`);
11734
- errorLine(" Hint: check network connectivity or try again later");
11735
- process.exitCode = 1;
11736
- }
11737
- outputLine("");
11738
- }
11739
- async function cmdAuthRemove(force, json) {
11740
- const local = getAuthStatus(void 0, { skipHash: true });
11741
- if (!local.exists) {
11742
- if (json) {
11743
- outputLine(JSON.stringify({ status: "not-installed" }));
11744
- } else {
11745
- outputLine(" okx-auth is not installed.");
11746
- }
11747
- return;
11748
- }
11749
- if (!await confirmRemoval(force, local.binaryPath)) return;
11750
- let result;
11751
- try {
11752
- result = removeAuthBinary();
11753
- } catch (err) {
11754
- const msg = err instanceof Error ? err.message : String(err);
11755
- if (json) {
11756
- outputLine(JSON.stringify({ status: "failed", error: msg }));
11757
- } else {
11758
- errorLine(` \u2717 Failed to remove: ${msg}`);
11759
- }
11760
- process.exitCode = 1;
11761
- return;
11762
- }
11763
- if (json) {
11764
- outputLine(JSON.stringify({ status: result.status, path: local.binaryPath }));
11765
- return;
11766
- }
11767
- if (result.status === "removed") {
11768
- clearAuthBinaryCache();
11769
- outputLine(` \u2713 Removed: ${local.binaryPath}`);
11770
- } else {
11771
- outputLine(" okx-auth is not installed.");
11772
- }
11773
- }
11774
- async function confirmRemoval(force, binaryPath) {
11775
- if (force) return true;
11776
- if (!process.stdin.isTTY) {
11777
- errorLine(" Error: stdin is not a TTY. Use --force to skip confirmation.");
11778
- process.exitCode = 1;
11779
- return false;
11780
- }
11781
- const confirmed = await askConfirmation(
11782
- ` Remove okx-auth at ${binaryPath}? [y/N] `
11783
- );
11784
- if (!confirmed) {
11785
- outputLine(" Cancelled.");
11786
- return false;
11787
- }
11788
- return true;
11789
- }
11790
- function askConfirmation(prompt2) {
11791
- return new Promise((resolve3) => {
11792
- const rl = readline.createInterface({
11793
- input: process.stdin,
11794
- output: process.stdout
11795
- });
11796
- rl.question(prompt2, (answer) => {
11797
- rl.close();
11798
- resolve3(answer.trim().toLowerCase() === "y");
11799
- });
11800
- });
11801
- }
11802
- async function handleAuthCommand(action, _rest, v) {
11803
- const site = v.site;
11804
- const json = v.json;
11805
- const manual = v.manual;
11806
- const force = v.force;
11807
- if (["login", "logout", "status"].includes(action)) {
11808
- await ensureAuthBinaryLatest((msg) => errorLine(` ${msg}`));
11809
- }
11810
- switch (action) {
11811
- case "login":
11812
- return cmdAuthLogin({ site, manual });
11813
- case "logout":
11814
- return cmdAuthLogout();
11815
- case "status":
11816
- return cmdAuthStatus({ json });
11817
- case "install":
11818
- return cmdAuthInstall(json ?? false);
11819
- case "install-status":
11820
- return cmdAuthInstallStatus(json ?? false);
11821
- case "remove":
11822
- return cmdAuthRemove(force ?? false, json ?? false);
11823
- default:
11824
- errorLine(`Unknown auth command: ${action}`);
11825
- errorLine("Available: login, logout, status, install, install-status, remove");
11826
- process.exitCode = 1;
11827
- }
11828
- }
11829
-
11830
- // src/commands/diagnose.ts
11831
- import dns from "dns/promises";
11832
- import net from "net";
11833
- import os5 from "os";
11834
- import tls from "tls";
11835
-
11836
11176
  // src/commands/diagnose-utils.ts
11837
- import fs4 from "fs";
11838
- import { createRequire } from "module";
11839
11177
  var _require = createRequire(import.meta.url);
11840
11178
  function readCliVersion() {
11841
11179
  for (const rel of ["../package.json", "../../package.json"]) {
@@ -11921,7 +11259,7 @@ function sanitize2(value) {
11921
11259
  import fs5 from "fs";
11922
11260
  import path4 from "path";
11923
11261
  import os4 from "os";
11924
- import { spawnSync, spawn as spawn3 } from "child_process";
11262
+ import { spawnSync, spawn } from "child_process";
11925
11263
  import { createRequire as createRequire2 } from "module";
11926
11264
  import { fileURLToPath } from "url";
11927
11265
  var _require2 = createRequire2(import.meta.url);
@@ -12255,7 +11593,7 @@ async function checkStdioHandshake(entryPath, report) {
12255
11593
  clearTimeout(timer);
12256
11594
  resolve3(passed);
12257
11595
  };
12258
- const child = spawn3(process.execPath, [entryPath], {
11596
+ const child = spawn(process.execPath, [entryPath], {
12259
11597
  stdio: ["pipe", "pipe", "pipe"],
12260
11598
  env: { ...process.env }
12261
11599
  });
@@ -12374,7 +11712,7 @@ async function cmdDiagnoseMcp(options = {}) {
12374
11712
 
12375
11713
  // src/commands/diagnose.ts
12376
11714
  var CLI_VERSION = readCliVersion();
12377
- var GIT_HASH = true ? "a4277b3" : "dev";
11715
+ var GIT_HASH = true ? "7acf49fb" : "dev";
12378
11716
  function maskKey2(key) {
12379
11717
  if (!key) return "(not set)";
12380
11718
  if (key.length <= 8) return "****";
@@ -12735,24 +12073,24 @@ function suggestSubcommand(action, knownActions) {
12735
12073
 
12736
12074
  // src/commands/upgrade.ts
12737
12075
  import { spawnSync as spawnSync2 } from "child_process";
12738
- import { readFileSync as readFileSync9, writeFileSync as writeFileSync8, mkdirSync as mkdirSync11 } from "fs";
12739
- import { dirname as dirname8, join as join13 } from "path";
12740
- import { homedir as homedir11 } from "os";
12076
+ import { readFileSync as readFileSync8, writeFileSync as writeFileSync7, mkdirSync as mkdirSync9 } from "fs";
12077
+ import { dirname as dirname7, join as join10 } from "path";
12078
+ import { homedir as homedir8 } from "os";
12741
12079
  var PACKAGES = ["@okx_ai/okx-trade-mcp", "@okx_ai/okx-trade-cli"];
12742
- var CACHE_FILE2 = join13(homedir11(), ".okx", "last_check");
12080
+ var CACHE_FILE2 = join10(homedir8(), ".okx", "last_check");
12743
12081
  var THROTTLE_MS = 12 * 60 * 60 * 1e3;
12744
- var NPM_BIN = join13(dirname8(process.execPath), process.platform === "win32" ? "npm.cmd" : "npm");
12082
+ var NPM_BIN = join10(dirname7(process.execPath), process.platform === "win32" ? "npm.cmd" : "npm");
12745
12083
  function readLastCheck() {
12746
12084
  try {
12747
- return parseInt(readFileSync9(CACHE_FILE2, "utf-8").trim(), 10) || 0;
12085
+ return parseInt(readFileSync8(CACHE_FILE2, "utf-8").trim(), 10) || 0;
12748
12086
  } catch {
12749
12087
  return 0;
12750
12088
  }
12751
12089
  }
12752
12090
  function writeLastCheck() {
12753
12091
  try {
12754
- mkdirSync11(join13(homedir11(), ".okx"), { recursive: true });
12755
- writeFileSync8(CACHE_FILE2, String(Math.floor(Date.now() / 1e3)), "utf-8");
12092
+ mkdirSync9(join10(homedir8(), ".okx"), { recursive: true });
12093
+ writeFileSync7(CACHE_FILE2, String(Math.floor(Date.now() / 1e3)), "utf-8");
12756
12094
  } catch {
12757
12095
  }
12758
12096
  }
@@ -14217,18 +13555,22 @@ async function cmdNewsSentimentRank(run, opts) {
14217
13555
  }
14218
13556
 
14219
13557
  // src/config/loader.ts
14220
- async function loadProfileConfig(opts) {
14221
- return loadConfig({
14222
- profile: opts.profile,
14223
- modules: opts.modules,
14224
- readOnly: opts.readOnly ?? false,
14225
- demo: opts.demo,
14226
- live: opts.live,
14227
- site: opts.site,
14228
- userAgent: opts.userAgent,
14229
- sourceTag: opts.sourceTag,
14230
- verbose: opts.verbose
14231
- });
13558
+ function loadProfileConfig(opts) {
13559
+ return loadConfig(
13560
+ {
13561
+ profile: opts.profile,
13562
+ modules: opts.modules,
13563
+ readOnly: opts.readOnly ?? false,
13564
+ demo: opts.demo,
13565
+ live: opts.live,
13566
+ site: opts.site,
13567
+ userAgent: opts.userAgent,
13568
+ sourceTag: opts.sourceTag,
13569
+ skill: opts.skill,
13570
+ verbose: opts.verbose
13571
+ },
13572
+ { entryPrefix: opts.entryPrefix, readEnv: opts.readEnv }
13573
+ );
14232
13574
  }
14233
13575
 
14234
13576
  // src/help.ts
@@ -14237,12 +13579,13 @@ var HELP_TREE = generateHelpTree();
14237
13579
  function printGlobalHelp() {
14238
13580
  const lines = [
14239
13581
  "",
14240
- `Usage: okx [--profile <name>] [--demo | --live] [--json] <module> <action> [args...]`,
13582
+ `Usage: okx [--profile <name>] [--demo | --live] [--skill <token>] [--json] <module> <action> [args...]`,
14241
13583
  "",
14242
13584
  "Global Options:",
14243
13585
  ` --profile <name> Use a named profile from ${configFilePath()}`,
14244
13586
  " --demo Use simulated trading (demo) mode",
14245
13587
  " --live Force live trading mode (overrides profile demo=true; mutually exclusive with --demo)",
13588
+ " --skill <token> OKX skill token for order tagging (from SKILL.md)",
14246
13589
  " --json Output raw JSON",
14247
13590
  " --env With --json, wrap output as {env, profile, data}",
14248
13591
  " --verbose Show detailed network request/response info (stderr)",
@@ -14375,6 +13718,7 @@ import { parseArgs } from "util";
14375
13718
  var CLI_OPTIONS = {
14376
13719
  profile: { type: "string" },
14377
13720
  demo: { type: "boolean", default: false },
13721
+ skill: { type: "string" },
14378
13722
  json: { type: "boolean", default: false },
14379
13723
  env: { type: "boolean", default: false },
14380
13724
  help: { type: "boolean", default: false },
@@ -14554,9 +13898,6 @@ var CLI_OPTIONS = {
14554
13898
  dir: { type: "string" },
14555
13899
  page: { type: "string" },
14556
13900
  format: { type: "string" },
14557
- // auth
14558
- site: { type: "string" },
14559
- manual: { type: "boolean", default: false },
14560
13901
  // event contract
14561
13902
  underlying: { type: "string" },
14562
13903
  seriesId: { type: "string" },
@@ -17780,14 +17121,14 @@ async function cmdDcdQuoteAndBuy(run, opts) {
17780
17121
  }
17781
17122
 
17782
17123
  // src/commands/skill.ts
17783
- import { tmpdir, homedir as homedir13 } from "os";
17784
- import { join as join15, dirname as dirname9 } from "path";
17785
- import { mkdirSync as mkdirSync12, rmSync, existsSync as existsSync10, copyFileSync as copyFileSync2 } from "fs";
17124
+ import { tmpdir, homedir as homedir10 } from "os";
17125
+ import { join as join12, dirname as dirname8 } from "path";
17126
+ import { mkdirSync as mkdirSync10, rmSync, existsSync as existsSync8, copyFileSync as copyFileSync2 } from "fs";
17786
17127
  import { execFileSync as execFileSync2 } from "child_process";
17787
17128
  import { randomUUID as randomUUID2 } from "crypto";
17788
17129
  function resolveNpx() {
17789
- const sibling = join15(dirname9(process.execPath), "npx");
17790
- if (existsSync10(sibling)) return sibling;
17130
+ const sibling = join12(dirname8(process.execPath), "npx");
17131
+ if (existsSync8(sibling)) return sibling;
17791
17132
  return "npx";
17792
17133
  }
17793
17134
  var THIRD_PARTY_INSTALL_NOTICE = "Note: This skill was created by a third-party developer, not by OKX. Review SKILL.md before use.";
@@ -17840,13 +17181,13 @@ async function cmdSkillCategories(run, json) {
17840
17181
  outputLine("");
17841
17182
  }
17842
17183
  async function cmdSkillAdd(name, config, json) {
17843
- const tmpBase = join15(tmpdir(), `okx-skill-${randomUUID2()}`);
17844
- mkdirSync12(tmpBase, { recursive: true });
17184
+ const tmpBase = join12(tmpdir(), `okx-skill-${randomUUID2()}`);
17185
+ mkdirSync10(tmpBase, { recursive: true });
17845
17186
  try {
17846
17187
  outputLine(`Downloading ${name}...`);
17847
17188
  const client = new OkxRestClient(config);
17848
17189
  const zipPath = await downloadSkillZip(client, name, tmpBase);
17849
- const contentDir = await extractSkillZip(zipPath, join15(tmpBase, "content"));
17190
+ const contentDir = await extractSkillZip(zipPath, join12(tmpBase, "content"));
17850
17191
  const meta = readMetaJson(contentDir);
17851
17192
  validateSkillMdExists(contentDir);
17852
17193
  outputLine("Installing to detected agents...");
@@ -17856,7 +17197,7 @@ async function cmdSkillAdd(name, config, json) {
17856
17197
  timeout: 6e4
17857
17198
  });
17858
17199
  } catch (e) {
17859
- const savedZip = join15(process.cwd(), `${name}.zip`);
17200
+ const savedZip = join12(process.cwd(), `${name}.zip`);
17860
17201
  try {
17861
17202
  copyFileSync2(zipPath, savedZip);
17862
17203
  } catch {
@@ -17895,7 +17236,7 @@ function cmdSkillRemove(name, json) {
17895
17236
  timeout: 6e4
17896
17237
  });
17897
17238
  } catch {
17898
- const agentsPath = join15(homedir13(), ".agents", "skills", name);
17239
+ const agentsPath = join12(homedir10(), ".agents", "skills", name);
17899
17240
  try {
17900
17241
  rmSync(agentsPath, { recursive: true, force: true });
17901
17242
  } catch {
@@ -17969,14 +17310,14 @@ function printSkillInstallResult(meta, json) {
17969
17310
  }
17970
17311
 
17971
17312
  // src/commands/pilot.ts
17972
- import readline2 from "readline";
17973
- function resolveChecksumMatch2(local, cdnChecksum, cdnError) {
17313
+ import readline from "readline";
17314
+ function resolveChecksumMatch(local, cdnChecksum, cdnError) {
17974
17315
  if (!local.exists) return "not-installed";
17975
17316
  if (cdnError || !cdnChecksum) return "unavailable";
17976
17317
  if (cdnChecksum.sha256 === local.sha256) return "match";
17977
17318
  return "mismatch";
17978
17319
  }
17979
- function checksumMatchLabel2(match) {
17320
+ function checksumMatchLabel(match) {
17980
17321
  if (match === "match") return "\u2713 match";
17981
17322
  if (match === "mismatch") return "\u2717 mismatch (update available)";
17982
17323
  return "CDN unreachable";
@@ -17989,9 +17330,9 @@ function formatStatusText(local, checksumMatch, cdnChecksum, runtimeMode) {
17989
17330
  outputLine(` Installed : ${local.exists ? "yes" : "no"}`);
17990
17331
  outputLine(` Platform : ${local.platform ?? "(unsupported)"}`);
17991
17332
  if (local.exists) {
17992
- outputLine(` File size : ${formatBytes2(local.fileSize)}`);
17333
+ outputLine(` File size : ${formatBytes(local.fileSize)}`);
17993
17334
  outputLine(` SHA-256 : ${local.sha256 ?? "(unknown)"}`);
17994
- outputLine(` CDN check : ${checksumMatchLabel2(checksumMatch)}`);
17335
+ outputLine(` CDN check : ${checksumMatchLabel(checksumMatch)}`);
17995
17336
  if (cdnChecksum) {
17996
17337
  outputLine(` CDN source : ${cdnChecksum.source}`);
17997
17338
  }
@@ -18018,7 +17359,7 @@ async function cmdPilotStatus(json, binaryPath) {
18018
17359
  cdnError = err instanceof Error ? err.message : String(err);
18019
17360
  }
18020
17361
  }
18021
- const checksumMatch = resolveChecksumMatch2(local, cdnChecksum, cdnError);
17362
+ const checksumMatch = resolveChecksumMatch(local, cdnChecksum, cdnError);
18022
17363
  if (json) {
18023
17364
  outputLine(
18024
17365
  JSON.stringify({
@@ -18084,7 +17425,7 @@ async function cmdPilotRemove(force, json, binaryPath) {
18084
17425
  process.exitCode = 1;
18085
17426
  return;
18086
17427
  }
18087
- const confirmed = await askConfirmation2(
17428
+ const confirmed = await askConfirmation(
18088
17429
  ` Remove Pilot at ${local.binaryPath}? [y/N] `
18089
17430
  );
18090
17431
  if (!confirmed) {
@@ -18103,14 +17444,14 @@ async function cmdPilotRemove(force, json, binaryPath) {
18103
17444
  outputLine(" Pilot is not installed.");
18104
17445
  }
18105
17446
  }
18106
- function formatBytes2(bytes) {
17447
+ function formatBytes(bytes) {
18107
17448
  if (bytes < 1024) return `${bytes} B`;
18108
17449
  if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
18109
17450
  return `${(bytes / (1024 * 1024)).toFixed(2)} MB`;
18110
17451
  }
18111
- function askConfirmation2(prompt2) {
17452
+ function askConfirmation(prompt2) {
18112
17453
  return new Promise((resolve3) => {
18113
- const rl = readline2.createInterface({
17454
+ const rl = readline.createInterface({
18114
17455
  input: process.stdin,
18115
17456
  output: process.stdout
18116
17457
  });
@@ -18579,7 +17920,7 @@ async function cmdEventCancel(run, opts) {
18579
17920
  // src/index.ts
18580
17921
  var _require3 = createRequire3(import.meta.url);
18581
17922
  var CLI_VERSION2 = _require3("../package.json").version;
18582
- var GIT_HASH2 = true ? "a4277b3" : "dev";
17923
+ var GIT_HASH2 = true ? "7acf49fb" : "dev";
18583
17924
  function handlePilotCommand(action, json, force, binaryPath) {
18584
17925
  if (action === "status") return cmdPilotStatus(json, binaryPath);
18585
17926
  if (action === "install") return cmdPilotInstall(json, binaryPath);
@@ -19622,13 +18963,13 @@ function handleNewsCommand(run, action, rest, v, json) {
19622
18963
  const period = v.period;
19623
18964
  const points = v.points !== void 0 ? Number(v.points) : 24;
19624
18965
  const sortBy = v["sort-by"];
19625
- const platform3 = v.platform;
19626
- const searchOpts = { coins: v.coins, importance: v.importance, platform: platform3, sentiment: v.sentiment, sortBy, begin, end, language, detailLvl, limit, after, json };
19627
- const listOpts = { coins: v.coins, importance: v.importance, platform: platform3, begin, end, language, detailLvl, limit, after, json };
18966
+ const platform2 = v.platform;
18967
+ const searchOpts = { coins: v.coins, importance: v.importance, platform: platform2, sentiment: v.sentiment, sortBy, begin, end, language, detailLvl, limit, after, json };
18968
+ const listOpts = { coins: v.coins, importance: v.importance, platform: platform2, begin, end, language, detailLvl, limit, after, json };
19628
18969
  const dispatch = {
19629
18970
  latest: () => cmdNewsLatest(run, listOpts),
19630
- important: () => cmdNewsImportant(run, { coins: v.coins, platform: platform3, begin, end, language, detailLvl, limit, json }),
19631
- "by-coin": () => cmdNewsByCoin(run, v.coins ?? rest[0], { importance: v.importance, platform: platform3, begin, end, language, detailLvl, limit, json }),
18971
+ important: () => cmdNewsImportant(run, { coins: v.coins, platform: platform2, begin, end, language, detailLvl, limit, json }),
18972
+ "by-coin": () => cmdNewsByCoin(run, v.coins ?? rest[0], { importance: v.importance, platform: platform2, begin, end, language, detailLvl, limit, json }),
19632
18973
  search: () => cmdNewsSearch(run, v.keyword ?? rest[0], searchOpts),
19633
18974
  detail: () => cmdNewsDetail(run, rest[0], { language, json }),
19634
18975
  platforms: () => cmdNewsPlatforms(run, { json }),
@@ -19769,7 +19110,7 @@ function wrapRunnerWithLogger(baseRunner, logger, verbose = false) {
19769
19110
  async function runDiagnose(v) {
19770
19111
  let config;
19771
19112
  try {
19772
- config = await loadProfileConfig({ profile: v.profile, demo: v.demo, live: v.live, verbose: v.verbose, userAgent: `okx-trade-cli/${CLI_VERSION2}`, sourceTag: "CLI" });
19113
+ config = loadProfileConfig({ profile: v.profile, demo: v.demo, live: v.live, verbose: v.verbose, userAgent: `okx-trade-cli/${CLI_VERSION2}`, sourceTag: "CLI", skill: v.skill, entryPrefix: "CLI" });
19773
19114
  } catch {
19774
19115
  }
19775
19116
  return cmdDiagnose(config, v.profile ?? "default", { mcp: v.mcp, cli: v.cli, all: v.all, output: v.output });
@@ -19792,7 +19133,6 @@ function routeManagementCommand(module, action, rest, json, v) {
19792
19133
  handleSetupCommand(v);
19793
19134
  return true;
19794
19135
  }
19795
- if (module === "auth") return handleAuthCommand(action, rest, v);
19796
19136
  if (module === "upgrade") return cmdUpgrade(CLI_VERSION2, { beta: v.beta, check: v.check, force: v.force }, json);
19797
19137
  if (module === "pilot") {
19798
19138
  const r = handlePilotCommand(action, json, v.force ?? false);
@@ -19825,7 +19165,7 @@ async function main() {
19825
19165
  const json = v.json ?? false;
19826
19166
  const mgmt = routeManagementCommand(module, action, rest, json, v);
19827
19167
  if (mgmt !== void 0) return mgmt === true ? void 0 : mgmt;
19828
- const config = await loadProfileConfig({ profile: v.profile, demo: v.demo, live: v.live, verbose: v.verbose, userAgent: `okx-trade-cli/${CLI_VERSION2}`, sourceTag: "CLI" });
19168
+ const config = loadProfileConfig({ profile: v.profile, demo: v.demo, live: v.live, verbose: v.verbose, userAgent: `okx-trade-cli/${CLI_VERSION2}`, sourceTag: "CLI", skill: v.skill, entryPrefix: "CLI" });
19829
19169
  setEnvContext({ demo: config.demo, profile: v.profile ?? "default" });
19830
19170
  setJsonEnvEnabled(v.env ?? false);
19831
19171
  const client = new OkxRestClient(config);