@okx_ai/okx-trade-cli 1.3.1-beta.3 → 1.3.1-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
@@ -876,6 +876,20 @@ import * as fs3 from "fs";
876
876
  import * as path3 from "path";
877
877
  import * as os3 from "os";
878
878
  import { execFileSync } from "child_process";
879
+ import {
880
+ readFileSync as readFileSync7,
881
+ createWriteStream as createWriteStream2,
882
+ mkdirSync as mkdirSync8,
883
+ chmodSync,
884
+ existsSync as existsSync6,
885
+ unlinkSync as unlinkSync3,
886
+ renameSync as renameSync3
887
+ } from "fs";
888
+ import { createHash } from "crypto";
889
+ import { homedir as homedir7, platform, arch } from "os";
890
+ import { join as join9, dirname as dirname6 } from "path";
891
+ import { get as httpsGet } from "https";
892
+ import { get as httpGet } from "http";
879
893
  var EXEC_TIMEOUT_MS = 3e4;
880
894
  var ALLOWED_DOMAIN_RE = /^[\w.-]+\.okx\.com$/;
881
895
  var DOH_BIN_DIR = join(homedir(), ".okx", "bin");
@@ -922,8 +936,10 @@ function execDohBinary(domain, exclude = [], userAgent) {
922
936
  );
923
937
  });
924
938
  }
925
- var DOH_CACHE_PATH = join2(homedir2(), ".okx", "doh-cache.json");
926
- function readCache(hostname, cachePath = DOH_CACHE_PATH) {
939
+ function getDefaultCachePath() {
940
+ return process.env.OKX_DOH_CACHE_PATH || join2(homedir2(), ".okx", "doh-cache.json");
941
+ }
942
+ function readCache(hostname, cachePath = getDefaultCachePath()) {
927
943
  try {
928
944
  const raw = readFileSync(cachePath, "utf-8");
929
945
  const file = JSON.parse(raw);
@@ -932,7 +948,7 @@ function readCache(hostname, cachePath = DOH_CACHE_PATH) {
932
948
  return null;
933
949
  }
934
950
  }
935
- function writeCache(hostname, entry, cachePath = DOH_CACHE_PATH) {
951
+ function writeCache(hostname, entry, cachePath = getDefaultCachePath()) {
936
952
  try {
937
953
  const dir = dirname(cachePath);
938
954
  mkdirSync(dir, { recursive: true });
@@ -1573,9 +1589,17 @@ var OkxRestClient = class _OkxRestClient {
1573
1589
  headers.set("User-Agent", conn.userAgent);
1574
1590
  }
1575
1591
  const t0 = Date.now();
1576
- const response = await this.fetchBinary(path42, endpoint, headers, bodyJson, t0);
1592
+ let response;
1593
+ try {
1594
+ response = await this.fetchBinary(path42, endpoint, headers, bodyJson, t0);
1595
+ } catch (error) {
1596
+ this.doh.handleNetworkFailure().catch(() => {
1597
+ });
1598
+ throw error;
1599
+ }
1577
1600
  const elapsed = Date.now() - t0;
1578
1601
  const traceId = extractTraceId(response.headers);
1602
+ this.doh.cacheDirectIfNeeded();
1579
1603
  if (!response.ok) {
1580
1604
  const text = await response.text();
1581
1605
  this.logResponse(response.status, text.length, elapsed, traceId, String(response.status));
@@ -2106,6 +2130,30 @@ var MODULES = [
2106
2130
  "skills"
2107
2131
  ];
2108
2132
  var DEFAULT_MODULES = ["spot", "swap", "option", "account", ...BOT_DEFAULT_SUB_MODULES, "skills"];
2133
+ var SKILLS_MARKETPLACE_DESC = "OKX Skills Marketplace \u2014 search, install, and manage agent skills";
2134
+ var MODULE_DESCRIPTIONS = {
2135
+ market: "Market data (ticker, orderbook, candles, trades)",
2136
+ spot: "Spot trading (orders, algo orders)",
2137
+ swap: "Perpetual swap trading (orders, algo orders)",
2138
+ futures: "Futures trading (orders, positions, algo orders, leverage)",
2139
+ option: "Options trading (orders, positions, greeks)",
2140
+ account: "Account balance, positions, bills, and configuration",
2141
+ "earn.savings": "Simple Earn \u2014 flexible savings, fixed-term, and lending",
2142
+ "earn.onchain": "On-chain Earn \u2014 staking and DeFi products",
2143
+ "earn.dcd": "DCD (Dual Currency Deposit) \u2014 structured products with fixed yield",
2144
+ "earn.autoearn": "Auto-earn \u2014 automatically lend, stake, or earn on idle assets",
2145
+ "bot.grid": "Grid trading bot \u2014 create, monitor, and stop grid orders",
2146
+ "bot.dca": "DCA (Martingale) bot \u2014 spot or contract recurring buys",
2147
+ skills: SKILLS_MARKETPLACE_DESC,
2148
+ earn: "Earn products \u2014 Simple Earn, On-chain Earn, and DCD (Dual Currency Deposit)",
2149
+ bot: "Trading bot strategies (grid, dca)",
2150
+ config: "Manage CLI configuration profiles",
2151
+ setup: "Set up client integrations (Cursor, Windsurf, Claude, etc.)",
2152
+ doh: "Manage DoH (DNS-over-HTTPS) resolver binary",
2153
+ diagnose: "Run network / MCP server diagnostics",
2154
+ upgrade: "Upgrade okx CLI and MCP server to the latest stable version",
2155
+ skill: SKILLS_MARKETPLACE_DESC
2156
+ };
2109
2157
  function registerAccountTools() {
2110
2158
  return [
2111
2159
  {
@@ -8439,13 +8487,13 @@ function findMsStoreClaudePath() {
8439
8487
  }
8440
8488
  function getConfigPath(client) {
8441
8489
  const home = os3.homedir();
8442
- const platform = process.platform;
8490
+ const platform2 = process.platform;
8443
8491
  switch (client) {
8444
8492
  case "claude-desktop":
8445
- if (platform === "win32") {
8493
+ if (platform2 === "win32") {
8446
8494
  return findMsStoreClaudePath() ?? path3.join(appData(), "Claude", CLAUDE_CONFIG_FILE);
8447
8495
  }
8448
- if (platform === "darwin") {
8496
+ if (platform2 === "darwin") {
8449
8497
  return path3.join(home, "Library", "Application Support", "Claude", CLAUDE_CONFIG_FILE);
8450
8498
  }
8451
8499
  return path3.join(process.env.XDG_CONFIG_HOME ?? path3.join(home, ".config"), "Claude", CLAUDE_CONFIG_FILE);
@@ -8547,6 +8595,269 @@ function runSetup(options) {
8547
8595
  `);
8548
8596
  }
8549
8597
  }
8598
+ var CDN_SOURCES = [
8599
+ { host: "static.jingyunyilian.com", protocol: "https" },
8600
+ { host: "static.okx.com", protocol: "https" },
8601
+ { host: "static.coinall.ltd", protocol: "https" }
8602
+ ];
8603
+ var CDN_PATH_PREFIX = "/upgradeapp/doh";
8604
+ var DOWNLOAD_TIMEOUT_MS = 3e4;
8605
+ function getPlatformDir() {
8606
+ const p = platform();
8607
+ const a = arch();
8608
+ const map = {
8609
+ "darwin-arm64": "darwin-arm64",
8610
+ "darwin-x64": "darwin-x64",
8611
+ "linux-x64": "linux-x64",
8612
+ "win32-x64": "win32-x64"
8613
+ };
8614
+ return map[`${p}-${a}`] ?? null;
8615
+ }
8616
+ function getBinaryName() {
8617
+ return platform() === "win32" ? "okx-doh-resolver.exe" : "okx-doh-resolver";
8618
+ }
8619
+ function hashFile(filePath) {
8620
+ const buf = readFileSync7(filePath);
8621
+ return {
8622
+ size: buf.byteLength,
8623
+ sha256: createHash("sha256").update(buf).digest("hex")
8624
+ };
8625
+ }
8626
+ function getDohStatus(binaryPath, opts) {
8627
+ const resolvedPath = binaryPath ?? getDohBinaryPath();
8628
+ const platformDir = getPlatformDir();
8629
+ if (!existsSync6(resolvedPath)) {
8630
+ return {
8631
+ binaryPath: resolvedPath,
8632
+ exists: false,
8633
+ platform: platformDir
8634
+ };
8635
+ }
8636
+ if (opts?.skipHash) {
8637
+ return {
8638
+ binaryPath: resolvedPath,
8639
+ exists: true,
8640
+ platform: platformDir
8641
+ };
8642
+ }
8643
+ const { size, sha256 } = hashFile(resolvedPath);
8644
+ return {
8645
+ binaryPath: resolvedPath,
8646
+ exists: true,
8647
+ platform: platformDir,
8648
+ fileSize: size,
8649
+ sha256
8650
+ };
8651
+ }
8652
+ async function fetchCdnChecksum(sources = CDN_SOURCES, timeoutMs = DOWNLOAD_TIMEOUT_MS) {
8653
+ const platformDir = getPlatformDir();
8654
+ if (!platformDir) return null;
8655
+ const checksumPath = `${CDN_PATH_PREFIX}/${platformDir}/checksum.json`;
8656
+ for (const { host, protocol } of sources) {
8657
+ try {
8658
+ const url = `${protocol}://${host}${checksumPath}`;
8659
+ const raw = await downloadText(url, timeoutMs);
8660
+ const data = JSON.parse(raw);
8661
+ if (typeof data.sha256 !== "string" || typeof data.size !== "number" || typeof data.target !== "string") {
8662
+ continue;
8663
+ }
8664
+ return {
8665
+ sha256: data.sha256,
8666
+ size: data.size,
8667
+ target: data.target,
8668
+ source: host
8669
+ };
8670
+ } catch {
8671
+ }
8672
+ }
8673
+ return null;
8674
+ }
8675
+ async function fetchAndValidateChecksum(host, protocol, checksumPath, platformDir, timeoutMs, onProgress) {
8676
+ const checksumUrl = `${protocol}://${host}${checksumPath}`;
8677
+ onProgress?.(`Fetching checksum from ${host}...`);
8678
+ const raw = await downloadText(checksumUrl, timeoutMs);
8679
+ const checksum = JSON.parse(raw);
8680
+ if (typeof checksum.sha256 !== "string" || typeof checksum.size !== "number" || typeof checksum.target !== "string") {
8681
+ throw new Error("Invalid checksum.json: missing sha256, size, or target");
8682
+ }
8683
+ if (checksum.target !== platformDir) {
8684
+ throw new Error(
8685
+ `Target mismatch: expected ${platformDir}, got ${checksum.target}`
8686
+ );
8687
+ }
8688
+ return { sha256: checksum.sha256, size: checksum.size, target: checksum.target };
8689
+ }
8690
+ async function downloadAndVerify(host, protocol, binaryPath, tmpPath, checksum, timeoutMs, onProgress) {
8691
+ const binaryUrl = `${protocol}://${host}${binaryPath}`;
8692
+ onProgress?.(`Downloading binary from ${host}...`);
8693
+ await download(binaryUrl, tmpPath, timeoutMs);
8694
+ const actual = hashFile(tmpPath);
8695
+ if (actual.size !== checksum.size) {
8696
+ throw new Error(
8697
+ `Size mismatch: expected ${checksum.size}, got ${actual.size}`
8698
+ );
8699
+ }
8700
+ if (actual.sha256 !== checksum.sha256) {
8701
+ throw new Error(
8702
+ `SHA-256 mismatch: expected ${checksum.sha256}, got ${actual.sha256}`
8703
+ );
8704
+ }
8705
+ }
8706
+ function atomicReplace(tmpPath, resolvedDest) {
8707
+ if (platform() === "win32") {
8708
+ try {
8709
+ unlinkSync3(resolvedDest);
8710
+ } catch {
8711
+ }
8712
+ }
8713
+ renameSync3(tmpPath, resolvedDest);
8714
+ if (platform() !== "win32") {
8715
+ chmodSync(resolvedDest, 493);
8716
+ }
8717
+ }
8718
+ function installPreChecks(destPath, sources) {
8719
+ if (!destPath && process.env.OKX_DOH_BINARY_PATH) {
8720
+ return { status: "up-to-date", source: "(env override)" };
8721
+ }
8722
+ if (!getPlatformDir()) {
8723
+ return { status: "failed", error: "Unsupported platform" };
8724
+ }
8725
+ if (sources.length === 0) {
8726
+ return { status: "failed", error: "No CDN sources available" };
8727
+ }
8728
+ return null;
8729
+ }
8730
+ function isLocalUpToDate(localHash, checksum) {
8731
+ return localHash !== null && localHash.size === checksum.size && localHash.sha256 === checksum.sha256;
8732
+ }
8733
+ async function installDohBinary(destPath, sources = CDN_SOURCES, onProgress) {
8734
+ const earlyResult = installPreChecks(destPath, sources);
8735
+ if (earlyResult) return earlyResult;
8736
+ const platformDir = getPlatformDir();
8737
+ const binaryName = getBinaryName();
8738
+ const resolvedDest = destPath ?? join9(homedir7(), ".okx", "bin", binaryName);
8739
+ const tmpPath = resolvedDest + ".tmp";
8740
+ mkdirSync8(dirname6(resolvedDest), { recursive: true });
8741
+ const localHash = existsSync6(resolvedDest) ? hashFile(resolvedDest) : null;
8742
+ const checksumPath = `${CDN_PATH_PREFIX}/${platformDir}/checksum.json`;
8743
+ const binaryPath = `${CDN_PATH_PREFIX}/${platformDir}/${binaryName}`;
8744
+ const errors = [];
8745
+ for (const { host, protocol } of sources) {
8746
+ try {
8747
+ const checksum = await fetchAndValidateChecksum(
8748
+ host,
8749
+ protocol,
8750
+ checksumPath,
8751
+ platformDir,
8752
+ DOWNLOAD_TIMEOUT_MS,
8753
+ onProgress
8754
+ );
8755
+ if (isLocalUpToDate(localHash, checksum)) {
8756
+ onProgress?.("Already up to date (checksum match)");
8757
+ return { status: "up-to-date", source: host };
8758
+ }
8759
+ await downloadAndVerify(host, protocol, binaryPath, tmpPath, checksum, DOWNLOAD_TIMEOUT_MS, onProgress);
8760
+ atomicReplace(tmpPath, resolvedDest);
8761
+ onProgress?.(`Downloaded and verified from ${host}`);
8762
+ return { status: "installed", source: host };
8763
+ } catch (err) {
8764
+ try {
8765
+ unlinkSync3(tmpPath);
8766
+ } catch {
8767
+ }
8768
+ const msg = err instanceof Error ? err.message : String(err);
8769
+ errors.push(`${host}: ${msg}`);
8770
+ onProgress?.(`${host} failed: ${msg}`);
8771
+ }
8772
+ }
8773
+ return { status: "failed", error: `All CDN sources failed:
8774
+ ${errors.join("\n")}` };
8775
+ }
8776
+ function removeDohBinary(binaryPath) {
8777
+ const resolvedPath = binaryPath ?? getDohBinaryPath();
8778
+ try {
8779
+ unlinkSync3(resolvedPath);
8780
+ return { status: "removed" };
8781
+ } catch (err) {
8782
+ if (err.code === "ENOENT") {
8783
+ return { status: "not-found" };
8784
+ }
8785
+ const msg = err instanceof Error ? err.message : String(err);
8786
+ throw new Error(`Failed to remove ${resolvedPath}: ${msg}`);
8787
+ }
8788
+ }
8789
+ function isRedirect(statusCode) {
8790
+ return statusCode !== void 0 && statusCode >= 300 && statusCode < 400;
8791
+ }
8792
+ function validateRedirect(res, requestUrl, redirectCount, maxRedirects) {
8793
+ if (redirectCount > maxRedirects) {
8794
+ throw new Error(`Too many redirects (${maxRedirects})`);
8795
+ }
8796
+ const location = res.headers.location;
8797
+ if (requestUrl.startsWith("https") && !location.startsWith("https")) {
8798
+ throw new Error("Refused HTTPS \u2192 HTTP redirect downgrade");
8799
+ }
8800
+ return location;
8801
+ }
8802
+ function fetchResponse(url, timeoutMs) {
8803
+ return new Promise((resolve3, reject) => {
8804
+ let redirects = 0;
8805
+ const maxRedirects = 5;
8806
+ function doRequest(requestUrl) {
8807
+ const reqFn = requestUrl.startsWith("https") ? httpsGet : httpGet;
8808
+ const req = reqFn(requestUrl, { timeout: timeoutMs }, (res) => {
8809
+ if (isRedirect(res.statusCode) && res.headers.location) {
8810
+ redirects++;
8811
+ try {
8812
+ const location = validateRedirect(res, requestUrl, redirects, maxRedirects);
8813
+ res.resume();
8814
+ doRequest(location);
8815
+ } catch (err) {
8816
+ reject(err);
8817
+ }
8818
+ return;
8819
+ }
8820
+ if (res.statusCode !== 200) {
8821
+ reject(new Error(`HTTP ${res.statusCode ?? "unknown"}`));
8822
+ return;
8823
+ }
8824
+ resolve3(res);
8825
+ });
8826
+ req.on("error", reject);
8827
+ req.on("timeout", () => {
8828
+ req.destroy();
8829
+ reject(new Error("Download timed out"));
8830
+ });
8831
+ }
8832
+ doRequest(url);
8833
+ });
8834
+ }
8835
+ function download(url, destPath, timeoutMs) {
8836
+ return fetchResponse(url, timeoutMs).then(
8837
+ (res) => new Promise((resolve3, reject) => {
8838
+ const file = createWriteStream2(destPath);
8839
+ res.pipe(file);
8840
+ file.on("finish", () => file.close(() => resolve3()));
8841
+ file.on("error", (err) => {
8842
+ try {
8843
+ unlinkSync3(destPath);
8844
+ } catch {
8845
+ }
8846
+ reject(err);
8847
+ });
8848
+ })
8849
+ );
8850
+ }
8851
+ function downloadText(url, timeoutMs) {
8852
+ return fetchResponse(url, timeoutMs).then(
8853
+ (res) => new Promise((resolve3, reject) => {
8854
+ const chunks = [];
8855
+ res.on("data", (chunk) => chunks.push(chunk));
8856
+ res.on("end", () => resolve3(Buffer.concat(chunks).toString("utf8")));
8857
+ res.on("error", reject);
8858
+ })
8859
+ );
8860
+ }
8550
8861
 
8551
8862
  // src/commands/diagnose.ts
8552
8863
  import dns from "dns/promises";
@@ -9179,7 +9490,7 @@ async function cmdDiagnoseMcp(options = {}) {
9179
9490
 
9180
9491
  // src/commands/diagnose.ts
9181
9492
  var CLI_VERSION = readCliVersion();
9182
- var GIT_HASH = true ? "8ae72a0" : "dev";
9493
+ var GIT_HASH = true ? "63a5613" : "dev";
9183
9494
  function maskKey2(key) {
9184
9495
  if (!key) return "(not set)";
9185
9496
  if (key.length <= 8) return "****";
@@ -9421,6 +9732,44 @@ async function cmdDiagnose(config, profile, options = {}) {
9421
9732
  }
9422
9733
  return runCliChecks(config, profile, options.output);
9423
9734
  }
9735
+ async function checkDoh(report) {
9736
+ section("DoH Resolver");
9737
+ const local = getDohStatus();
9738
+ if (!local.exists) {
9739
+ fail("DoH binary", "not installed", [
9740
+ "Run: okx doh install",
9741
+ "Or wait for the next npm install to auto-download"
9742
+ ]);
9743
+ report.add("doh_binary", "not installed");
9744
+ return;
9745
+ }
9746
+ ok("DoH binary", local.binaryPath);
9747
+ report.add("doh_binary", `installed (${local.platform ?? "unknown"})`);
9748
+ const cdnChecksum = await fetchCdnChecksum(void 0, 5e3);
9749
+ if (!cdnChecksum) {
9750
+ warn("DoH checksum", "CDN unreachable \u2014 cannot verify");
9751
+ report.add("doh_checksum", "CDN unreachable");
9752
+ } else if (cdnChecksum.sha256 === local.sha256) {
9753
+ ok("DoH checksum", `match (${cdnChecksum.source})`);
9754
+ report.add("doh_checksum", `match (${cdnChecksum.source})`);
9755
+ } else {
9756
+ warn("DoH checksum", "mismatch \u2014 update available", ["Run: okx doh install"]);
9757
+ report.add("doh_checksum", "mismatch");
9758
+ }
9759
+ try {
9760
+ const cacheEntry = readCache("www.okx.com");
9761
+ if (cacheEntry) {
9762
+ ok("DoH mode", cacheEntry.mode);
9763
+ report.add("doh_mode", cacheEntry.mode);
9764
+ } else {
9765
+ ok("DoH mode", "no cache (will auto-detect on next request)");
9766
+ report.add("doh_mode", "no cache");
9767
+ }
9768
+ } catch {
9769
+ ok("DoH mode", "no cache");
9770
+ report.add("doh_mode", "no cache");
9771
+ }
9772
+ }
9424
9773
  function checkConfigFile(report) {
9425
9774
  section("Config File");
9426
9775
  const path6 = configFilePath();
@@ -9449,6 +9798,7 @@ async function runCliChecks(config, profile, outputPath) {
9449
9798
  report.add("ts", (/* @__PURE__ */ new Date()).toISOString());
9450
9799
  const configFilePassed = checkConfigFile(report);
9451
9800
  const envPassed = checkEnvironment(report);
9801
+ await checkDoh(report);
9452
9802
  if (!config) {
9453
9803
  fail("Config", "Could not load config (see Config File check above)", []);
9454
9804
  report.add("result", "FAIL");
@@ -9476,23 +9826,23 @@ async function runCliChecks(config, profile, outputPath) {
9476
9826
 
9477
9827
  // src/commands/upgrade.ts
9478
9828
  import { spawnSync as spawnSync2 } from "child_process";
9479
- import { readFileSync as readFileSync7, writeFileSync as writeFileSync7, mkdirSync as mkdirSync8 } from "fs";
9480
- import { dirname as dirname6, join as join9 } from "path";
9481
- import { homedir as homedir7 } from "os";
9829
+ import { readFileSync as readFileSync8, writeFileSync as writeFileSync7, mkdirSync as mkdirSync9 } from "fs";
9830
+ import { dirname as dirname7, join as join10 } from "path";
9831
+ import { homedir as homedir8 } from "os";
9482
9832
  var PACKAGES = ["@okx_ai/okx-trade-mcp", "@okx_ai/okx-trade-cli"];
9483
- var CACHE_FILE2 = join9(homedir7(), ".okx", "last_check");
9833
+ var CACHE_FILE2 = join10(homedir8(), ".okx", "last_check");
9484
9834
  var THROTTLE_MS = 12 * 60 * 60 * 1e3;
9485
- var NPM_BIN = join9(dirname6(process.execPath), process.platform === "win32" ? "npm.cmd" : "npm");
9835
+ var NPM_BIN = join10(dirname7(process.execPath), process.platform === "win32" ? "npm.cmd" : "npm");
9486
9836
  function readLastCheck() {
9487
9837
  try {
9488
- return parseInt(readFileSync7(CACHE_FILE2, "utf-8").trim(), 10) || 0;
9838
+ return parseInt(readFileSync8(CACHE_FILE2, "utf-8").trim(), 10) || 0;
9489
9839
  } catch {
9490
9840
  return 0;
9491
9841
  }
9492
9842
  }
9493
9843
  function writeLastCheck() {
9494
9844
  try {
9495
- mkdirSync8(join9(homedir7(), ".okx"), { recursive: true });
9845
+ mkdirSync9(join10(homedir8(), ".okx"), { recursive: true });
9496
9846
  writeFileSync7(CACHE_FILE2, String(Math.floor(Date.now() / 1e3)), "utf-8");
9497
9847
  } catch {
9498
9848
  }
@@ -9577,24 +9927,6 @@ async function cmdUpgrade(currentVersion, options, json) {
9577
9927
  }
9578
9928
  }
9579
9929
 
9580
- // src/config/loader.ts
9581
- function loadProfileConfig(opts) {
9582
- return loadConfig({
9583
- profile: opts.profile,
9584
- modules: opts.modules,
9585
- readOnly: opts.readOnly ?? false,
9586
- demo: opts.demo,
9587
- live: opts.live,
9588
- site: opts.site,
9589
- userAgent: opts.userAgent,
9590
- sourceTag: opts.sourceTag,
9591
- verbose: opts.verbose
9592
- });
9593
- }
9594
-
9595
- // src/help.ts
9596
- import { EOL as EOL2 } from "os";
9597
-
9598
9930
  // src/commands/client-setup.ts
9599
9931
  import * as fs6 from "fs";
9600
9932
  var DETECTABLE_CLIENTS = ["claude-desktop", "cursor", "windsurf"];
@@ -9619,154 +9951,213 @@ function cmdSetupClients() {
9619
9951
  printSetupUsage();
9620
9952
  }
9621
9953
 
9622
- // src/help.ts
9623
- var HELP_TREE = {
9954
+ // src/cli-registry.ts
9955
+ var CLI_REGISTRY = {
9956
+ // ── market ─────────────────────────────────────────────────────────────────
9624
9957
  market: {
9625
9958
  description: "Market data (ticker, orderbook, candles, trades)",
9626
9959
  commands: {
9627
9960
  ticker: {
9961
+ toolName: "market_get_ticker",
9628
9962
  usage: "okx market ticker <instId>",
9629
9963
  description: "Get latest ticker data for an instrument"
9630
9964
  },
9631
9965
  tickers: {
9966
+ toolName: "market_get_tickers",
9632
9967
  usage: "okx market tickers <instType>",
9633
9968
  description: "Get all tickers for an instrument type (SPOT|SWAP|FUTURES|OPTION)"
9634
9969
  },
9635
9970
  orderbook: {
9971
+ toolName: "market_get_orderbook",
9636
9972
  usage: "okx market orderbook <instId> [--sz <n>]",
9637
9973
  description: "Get order book depth for an instrument"
9638
9974
  },
9639
9975
  candles: {
9976
+ toolName: "market_get_candles",
9640
9977
  usage: "okx market candles <instId> [--bar <bar>] [--limit <n>]",
9641
9978
  description: "Get candlestick (OHLCV) data"
9642
9979
  },
9643
9980
  instruments: {
9981
+ toolName: "market_get_instruments",
9644
9982
  usage: "okx market instruments --instType <type> [--instId <id>]",
9645
9983
  description: "List tradable instruments of a given type"
9646
9984
  },
9647
9985
  "funding-rate": {
9986
+ toolName: "market_get_funding_rate",
9648
9987
  usage: "okx market funding-rate <instId> [--history] [--limit <n>]",
9649
9988
  description: "Get current or historical funding rate for perpetual swaps"
9650
9989
  },
9651
9990
  "mark-price": {
9991
+ toolName: "market_get_mark_price",
9652
9992
  usage: "okx market mark-price --instType <MARGIN|SWAP|FUTURES|OPTION> [--instId <id>]",
9653
9993
  description: "Get mark price for instruments"
9654
9994
  },
9655
9995
  trades: {
9996
+ toolName: "market_get_trades",
9656
9997
  usage: "okx market trades <instId> [--limit <n>]",
9657
9998
  description: "Get recent trades for an instrument"
9658
9999
  },
9659
10000
  "index-ticker": {
10001
+ toolName: "market_get_index_ticker",
9660
10002
  usage: "okx market index-ticker [--instId <id>] [--quoteCcy <ccy>]",
9661
10003
  description: "Get index ticker data"
9662
10004
  },
9663
10005
  "index-candles": {
10006
+ toolName: "market_get_index_candles",
9664
10007
  usage: "okx market index-candles <instId> [--bar <bar>] [--limit <n>] [--history]",
9665
10008
  description: "Get index candlestick data"
9666
10009
  },
9667
10010
  "price-limit": {
10011
+ toolName: "market_get_price_limit",
9668
10012
  usage: "okx market price-limit <instId>",
9669
10013
  description: "Get price limit for an instrument"
9670
10014
  },
9671
10015
  "open-interest": {
10016
+ toolName: "market_get_open_interest",
9672
10017
  usage: "okx market open-interest --instType <SWAP|FUTURES|OPTION> [--instId <id>]",
9673
10018
  description: "Get open interest for instruments"
9674
10019
  },
9675
10020
  "stock-tokens": {
10021
+ toolName: "market_get_stock_tokens",
9676
10022
  usage: "okx market stock-tokens [--instType <SPOT|SWAP>] [--instId <id>]",
9677
10023
  description: "[Deprecated: use instruments-by-category --instCategory 3] List all stock token instruments (instCategory=3, e.g. AAPL-USDT-SWAP)"
9678
10024
  },
9679
10025
  "instruments-by-category": {
10026
+ toolName: "market_get_instruments_by_category",
9680
10027
  usage: "okx market instruments-by-category --instCategory <4|5|6|7> [--instType <SPOT|SWAP>] [--instId <id>]",
9681
10028
  description: "List instruments by asset category: 4=Metals (gold/silver), 5=Commodities (oil/gas), 6=Forex (EUR/USD), 7=Bonds"
9682
10029
  }
10030
+ },
10031
+ subgroups: {
10032
+ indicator: {
10033
+ description: "Technical indicators and chart patterns",
10034
+ commands: {
10035
+ list: {
10036
+ toolName: "market_list_indicators",
10037
+ usage: "okx market indicator list",
10038
+ description: "List all supported technical indicators"
10039
+ },
10040
+ "<instId> <indicator>": {
10041
+ toolName: "market_get_indicator",
10042
+ usage: "okx market indicator <instId> <indicator> [--bar <bar>] [--limit <n>] [--backtest-time <ts>] [--params <json>]",
10043
+ description: "Get indicator values for an instrument (e.g. okx market indicator BTC-USDT-SWAP rsi)"
10044
+ }
10045
+ }
10046
+ }
9683
10047
  }
9684
10048
  },
10049
+ // ── account ────────────────────────────────────────────────────────────────
9685
10050
  account: {
9686
10051
  description: "Account balance, positions, bills, and configuration",
9687
10052
  commands: {
9688
10053
  balance: {
10054
+ toolName: "account_get_balance",
9689
10055
  usage: "okx account balance [<ccy>]",
9690
10056
  description: "Get trading account balance"
9691
10057
  },
9692
10058
  "asset-balance": {
10059
+ toolName: "account_get_asset_balance",
9693
10060
  usage: "okx account asset-balance [--ccy <ccy>]",
9694
10061
  description: "Get funding account asset balance"
9695
10062
  },
9696
10063
  positions: {
10064
+ toolName: "account_get_positions",
9697
10065
  usage: "okx account positions [--instType <type>] [--instId <id>]",
9698
10066
  description: "Get current open positions"
9699
10067
  },
9700
10068
  "positions-history": {
10069
+ toolName: "account_get_positions_history",
9701
10070
  usage: "okx account positions-history [--instType <type>] [--instId <id>] [--limit <n>]",
9702
10071
  description: "Get historical positions"
9703
10072
  },
9704
10073
  bills: {
10074
+ toolName: "account_get_bills",
10075
+ alternateTools: ["account_get_bills_archive"],
9705
10076
  usage: "okx account bills [--instType <type>] [--ccy <ccy>] [--limit <n>] [--archive]",
9706
10077
  description: "Get account bill history"
9707
10078
  },
9708
10079
  fees: {
10080
+ toolName: "account_get_trade_fee",
9709
10081
  usage: "okx account fees --instType <type> [--instId <id>]",
9710
10082
  description: "Get trading fee rates"
9711
10083
  },
9712
10084
  config: {
10085
+ toolName: "account_get_config",
9713
10086
  usage: "okx account config",
9714
10087
  description: "Get account configuration"
9715
10088
  },
9716
10089
  "set-position-mode": {
10090
+ toolName: "account_set_position_mode",
9717
10091
  usage: "okx account set-position-mode --posMode <long_short_mode|net_mode>",
9718
10092
  description: "Set position mode (long/short or net)"
9719
10093
  },
9720
10094
  "max-size": {
10095
+ toolName: "account_get_max_size",
9721
10096
  usage: "okx account max-size --instId <id> --tdMode <cross|isolated> [--px <price>]",
9722
10097
  description: "Get maximum order size for an instrument"
9723
10098
  },
9724
10099
  "max-avail-size": {
10100
+ toolName: "account_get_max_avail_size",
9725
10101
  usage: "okx account max-avail-size --instId <id> --tdMode <cross|isolated|cash>",
9726
10102
  description: "Get maximum available tradable amount"
9727
10103
  },
9728
10104
  "max-withdrawal": {
10105
+ toolName: "account_get_max_withdrawal",
9729
10106
  usage: "okx account max-withdrawal [--ccy <ccy>]",
9730
10107
  description: "Get maximum withdrawable amount"
9731
10108
  },
9732
10109
  transfer: {
10110
+ toolName: "account_transfer",
9733
10111
  usage: "okx account transfer --ccy <ccy> --amt <n> --from <acct> --to <acct> [--transferType <0|1|2|3>]",
9734
10112
  description: "Transfer funds between accounts"
9735
10113
  },
9736
10114
  audit: {
10115
+ // trade_get_history ToolSpec reads the same local log files as cmdAccountAudit.
10116
+ // CLI reads files directly without routing through ToolRunner for performance,
10117
+ // but conceptually this command is the CLI representation of the ToolSpec.
10118
+ toolName: "trade_get_history",
9737
10119
  usage: "okx account audit [--tool <name>] [--since <ISO-date>] [--limit <n>]",
9738
10120
  description: "Audit account activity and tool call history"
9739
10121
  }
9740
10122
  }
9741
10123
  },
10124
+ // ── spot ───────────────────────────────────────────────────────────────────
9742
10125
  spot: {
9743
10126
  description: "Spot trading (orders, algo orders)",
9744
10127
  commands: {
9745
10128
  orders: {
10129
+ toolName: "spot_get_orders",
9746
10130
  usage: "okx spot orders [--instId <id>] [--history]",
9747
10131
  description: "List open or historical spot orders"
9748
10132
  },
9749
10133
  get: {
10134
+ toolName: "spot_get_order",
9750
10135
  usage: "okx spot get --instId <id> --ordId <id>",
9751
10136
  description: "Get details of a specific spot order"
9752
10137
  },
9753
10138
  fills: {
10139
+ toolName: "spot_get_fills",
9754
10140
  usage: "okx spot fills [--instId <id>] [--ordId <id>]",
9755
10141
  description: "Get trade fill history for spot orders"
9756
10142
  },
9757
10143
  place: {
10144
+ toolName: "spot_place_order",
9758
10145
  usage: "okx spot place --instId <id> --side <buy|sell> --ordType <type> --sz <n> [--px <price>] [--tdMode <cash|cross|isolated>] [--tpTriggerPx <price>] [--tpOrdPx <price|-1>] [--slTriggerPx <price>] [--slOrdPx <price|-1>]",
9759
10146
  description: "Place a new spot order (supports attached TP/SL)"
9760
10147
  },
9761
10148
  amend: {
10149
+ toolName: "spot_amend_order",
9762
10150
  usage: "okx spot amend --instId <id> --ordId <id> [--newSz <n>] [--newPx <price>]",
9763
10151
  description: "Amend a pending spot order"
9764
10152
  },
9765
10153
  cancel: {
10154
+ toolName: "spot_cancel_order",
9766
10155
  usage: "okx spot cancel <instId> --ordId <id>",
9767
10156
  description: "Cancel a pending spot order"
9768
10157
  },
9769
10158
  batch: {
10159
+ toolName: "spot_batch_orders",
10160
+ alternateTools: ["spot_batch_amend", "spot_batch_cancel"],
9770
10161
  usage: "okx spot batch --action <place|amend|cancel> --orders '<json>'",
9771
10162
  description: "Batch place, amend, or cancel spot orders"
9772
10163
  }
@@ -9776,18 +10167,27 @@ var HELP_TREE = {
9776
10167
  description: "Spot algo orders (conditional, OCO, take-profit/stop-loss)",
9777
10168
  commands: {
9778
10169
  orders: {
10170
+ toolName: "spot_get_algo_orders",
9779
10171
  usage: "okx spot algo orders [--instId <id>] [--history] [--ordType <conditional|oco>]",
9780
10172
  description: "List spot algo orders"
9781
10173
  },
9782
10174
  place: {
10175
+ toolName: "spot_place_algo_order",
9783
10176
  usage: "okx spot algo place --instId <id> --side <buy|sell> --sz <n> [--ordType <conditional|oco>]\n [--tpTriggerPx <price>] [--tpOrdPx <price|-1>]\n [--slTriggerPx <price>] [--slOrdPx <price|-1>] [--tdMode <cash|cross|isolated>]",
9784
10177
  description: "Place a spot algo order (take-profit/stop-loss)"
9785
10178
  },
10179
+ trail: {
10180
+ toolName: "spot_place_algo_order",
10181
+ usage: "okx spot algo trail --instId <id> --side <buy|sell> --sz <n> --callbackRatio <ratio>\n [--activePx <price>] [--tdMode <cash|cross|isolated>]",
10182
+ description: "Place a trailing stop algo order for spot"
10183
+ },
9786
10184
  amend: {
10185
+ toolName: "spot_amend_algo_order",
9787
10186
  usage: "okx spot algo amend --instId <id> --algoId <id> [--newSz <n>]\n [--newTpTriggerPx <price>] [--newTpOrdPx <price|-1>]\n [--newSlTriggerPx <price>] [--newSlOrdPx <price|-1>]",
9788
10187
  description: "Amend a pending spot algo order"
9789
10188
  },
9790
10189
  cancel: {
10190
+ toolName: "spot_cancel_algo_order",
9791
10191
  usage: "okx spot algo cancel --instId <id> --algoId <id>",
9792
10192
  description: "Cancel a pending spot algo order"
9793
10193
  }
@@ -9795,50 +10195,64 @@ var HELP_TREE = {
9795
10195
  }
9796
10196
  }
9797
10197
  },
10198
+ // ── swap ───────────────────────────────────────────────────────────────────
9798
10199
  swap: {
9799
10200
  description: "Perpetual swap trading (orders, algo orders)",
9800
10201
  commands: {
9801
10202
  positions: {
10203
+ toolName: "swap_get_positions",
9802
10204
  usage: "okx swap positions [<instId>]",
9803
10205
  description: "Get current perpetual swap positions"
9804
10206
  },
9805
10207
  orders: {
10208
+ toolName: "swap_get_orders",
9806
10209
  usage: "okx swap orders [--instId <id>] [--history] [--archive]",
9807
10210
  description: "List open or historical swap orders"
9808
10211
  },
9809
10212
  get: {
10213
+ toolName: "swap_get_order",
9810
10214
  usage: "okx swap get --instId <id> --ordId <id>",
9811
10215
  description: "Get details of a specific swap order"
9812
10216
  },
9813
10217
  fills: {
10218
+ toolName: "swap_get_fills",
9814
10219
  usage: "okx swap fills [--instId <id>] [--ordId <id>] [--archive]",
9815
10220
  description: "Get trade fill history for swap orders"
9816
10221
  },
9817
10222
  place: {
10223
+ toolName: "swap_place_order",
9818
10224
  usage: "okx swap place --instId <id> --side <buy|sell> --ordType <type> --sz <n> [--posSide <side>] [--px <price>] [--tdMode <cross|isolated>] [--tpTriggerPx <price>] [--tpOrdPx <price|-1>] [--slTriggerPx <price>] [--slOrdPx <price|-1>]",
9819
10225
  description: "Place a new perpetual swap order (supports attached TP/SL)"
9820
10226
  },
9821
10227
  cancel: {
10228
+ toolName: "swap_cancel_order",
9822
10229
  usage: "okx swap cancel <instId> --ordId <id>",
9823
10230
  description: "Cancel a pending swap order"
9824
10231
  },
9825
10232
  amend: {
10233
+ // swap amend uses spot_amend_order (same OKX /trade/amend-order endpoint works for all types)
10234
+ toolName: "spot_amend_order",
9826
10235
  usage: "okx swap amend --instId <id> --ordId <id> [--newSz <n>] [--newPx <price>]",
9827
10236
  description: "Amend a pending swap order"
9828
10237
  },
9829
10238
  close: {
10239
+ toolName: "swap_close_position",
9830
10240
  usage: "okx swap close --instId <id> --mgnMode <cross|isolated> [--posSide <net|long|short>] [--autoCxl]",
9831
10241
  description: "Close a swap position"
9832
10242
  },
9833
10243
  leverage: {
10244
+ toolName: "swap_set_leverage",
9834
10245
  usage: "okx swap leverage --instId <id> --lever <n> --mgnMode <cross|isolated> [--posSide <side>]",
9835
10246
  description: "Set leverage for a swap instrument"
9836
10247
  },
9837
10248
  "get-leverage": {
10249
+ toolName: "swap_get_leverage",
9838
10250
  usage: "okx swap get-leverage --instId <id> --mgnMode <cross|isolated>",
9839
10251
  description: "Get current leverage setting for a swap instrument"
9840
10252
  },
9841
10253
  batch: {
10254
+ toolName: "swap_batch_orders",
10255
+ alternateTools: ["swap_batch_amend", "swap_batch_cancel"],
9842
10256
  usage: "okx swap batch --action <place|amend|cancel> --orders '<json>'",
9843
10257
  description: "Batch place, amend, or cancel swap orders"
9844
10258
  }
@@ -9848,22 +10262,27 @@ var HELP_TREE = {
9848
10262
  description: "Perpetual swap algo orders (trailing stop, conditional, OCO)",
9849
10263
  commands: {
9850
10264
  orders: {
10265
+ toolName: "swap_get_algo_orders",
9851
10266
  usage: "okx swap algo orders [--instId <id>] [--history] [--ordType <conditional|oco>]",
9852
10267
  description: "List swap algo orders"
9853
10268
  },
9854
10269
  trail: {
10270
+ toolName: "swap_place_move_stop_order",
9855
10271
  usage: "okx swap algo trail --instId <id> --side <buy|sell> --sz <n> --callbackRatio <ratio>\n [--activePx <price>] [--posSide <net|long|short>] [--tdMode <cross|isolated>] [--reduceOnly]",
9856
10272
  description: "Place a trailing stop algo order for perpetual swap"
9857
10273
  },
9858
10274
  place: {
10275
+ toolName: "swap_place_algo_order",
9859
10276
  usage: "okx swap algo place --instId <id> --side <buy|sell> --sz <n> [--ordType <conditional|oco>]\n [--tpTriggerPx <price>] [--tpOrdPx <price|-1>]\n [--slTriggerPx <price>] [--slOrdPx <price|-1>]\n [--posSide <net|long|short>] [--tdMode <cross|isolated>] [--reduceOnly]",
9860
10277
  description: "Place a swap algo order (take-profit/stop-loss)"
9861
10278
  },
9862
10279
  amend: {
10280
+ toolName: "swap_amend_algo_order",
9863
10281
  usage: "okx swap algo amend --instId <id> --algoId <id> [--newSz <n>]\n [--newTpTriggerPx <price>] [--newTpOrdPx <price|-1>]\n [--newSlTriggerPx <price>] [--newSlOrdPx <price|-1>]",
9864
10282
  description: "Amend a pending swap algo order"
9865
10283
  },
9866
10284
  cancel: {
10285
+ toolName: "swap_cancel_algo_orders",
9867
10286
  usage: "okx swap algo cancel --instId <id> --algoId <id>",
9868
10287
  description: "Cancel a pending swap algo order"
9869
10288
  }
@@ -9871,50 +10290,63 @@ var HELP_TREE = {
9871
10290
  }
9872
10291
  }
9873
10292
  },
10293
+ // ── futures ────────────────────────────────────────────────────────────────
9874
10294
  futures: {
9875
10295
  description: "Futures trading (orders, positions, algo orders, leverage)",
9876
10296
  commands: {
9877
10297
  orders: {
10298
+ toolName: "futures_get_orders",
9878
10299
  usage: "okx futures orders [--instId <id>] [--history] [--archive]",
9879
10300
  description: "List open or historical futures orders"
9880
10301
  },
9881
10302
  positions: {
10303
+ toolName: "futures_get_positions",
9882
10304
  usage: "okx futures positions [--instId <id>]",
9883
10305
  description: "Get current futures positions"
9884
10306
  },
9885
10307
  fills: {
10308
+ toolName: "futures_get_fills",
9886
10309
  usage: "okx futures fills [--instId <id>] [--ordId <id>] [--archive]",
9887
10310
  description: "Get trade fill history for futures orders"
9888
10311
  },
9889
10312
  place: {
10313
+ toolName: "futures_place_order",
9890
10314
  usage: "okx futures place --instId <id> --side <buy|sell> --ordType <type> --sz <n>\n [--tdMode <cross|isolated>] [--posSide <net|long|short>] [--px <price>] [--reduceOnly]\n [--tpTriggerPx <price>] [--tpOrdPx <price|-1>] [--slTriggerPx <price>] [--slOrdPx <price|-1>]",
9891
10315
  description: "Place a new futures order (supports attached TP/SL)"
9892
10316
  },
9893
10317
  cancel: {
10318
+ toolName: "futures_cancel_order",
9894
10319
  usage: "okx futures cancel <instId> --ordId <id>",
9895
10320
  description: "Cancel a pending futures order"
9896
10321
  },
9897
10322
  amend: {
10323
+ toolName: "futures_amend_order",
9898
10324
  usage: "okx futures amend --instId <id> [--ordId <id>] [--clOrdId <id>] [--newSz <n>] [--newPx <price>]",
9899
10325
  description: "Amend a pending futures order"
9900
10326
  },
9901
10327
  get: {
10328
+ toolName: "futures_get_order",
9902
10329
  usage: "okx futures get --instId <id> --ordId <id>",
9903
10330
  description: "Get details of a specific futures order"
9904
10331
  },
9905
10332
  close: {
10333
+ toolName: "futures_close_position",
9906
10334
  usage: "okx futures close --instId <id> --mgnMode <cross|isolated> [--posSide <net|long|short>] [--autoCxl]",
9907
10335
  description: "Close a futures position"
9908
10336
  },
9909
10337
  "get-leverage": {
10338
+ toolName: "futures_get_leverage",
9910
10339
  usage: "okx futures get-leverage --instId <id> --mgnMode <cross|isolated>",
9911
10340
  description: "Get current leverage for a futures instrument"
9912
10341
  },
9913
10342
  leverage: {
10343
+ toolName: "futures_set_leverage",
9914
10344
  usage: "okx futures leverage --instId <id> --lever <n> --mgnMode <cross|isolated> [--posSide <net|long|short>]",
9915
10345
  description: "Set leverage for a futures instrument"
9916
10346
  },
9917
10347
  batch: {
10348
+ toolName: "futures_batch_orders",
10349
+ alternateTools: ["futures_batch_amend", "futures_batch_cancel"],
9918
10350
  usage: "okx futures batch --action <place|amend|cancel> --orders '<json>'",
9919
10351
  description: "Batch place, amend, or cancel futures orders"
9920
10352
  }
@@ -9924,22 +10356,27 @@ var HELP_TREE = {
9924
10356
  description: "Futures algo orders (trailing stop, conditional, OCO)",
9925
10357
  commands: {
9926
10358
  orders: {
10359
+ toolName: "futures_get_algo_orders",
9927
10360
  usage: "okx futures algo orders [--instId <id>] [--history] [--ordType <conditional|oco>]",
9928
10361
  description: "List futures algo orders"
9929
10362
  },
9930
10363
  trail: {
10364
+ toolName: "futures_place_move_stop_order",
9931
10365
  usage: "okx futures algo trail --instId <id> --side <buy|sell> --sz <n> --callbackRatio <ratio>\n [--activePx <price>] [--posSide <net|long|short>] [--tdMode <cross|isolated>] [--reduceOnly]",
9932
10366
  description: "Place a trailing stop algo order for futures"
9933
10367
  },
9934
10368
  place: {
10369
+ toolName: "futures_place_algo_order",
9935
10370
  usage: "okx futures algo place --instId <id> --side <buy|sell> --sz <n> [--ordType <conditional|oco>]\n [--tpTriggerPx <price>] [--tpOrdPx <price|-1>]\n [--slTriggerPx <price>] [--slOrdPx <price|-1>]\n [--posSide <net|long|short>] [--tdMode <cross|isolated>] [--reduceOnly]",
9936
10371
  description: "Place a futures algo order (take-profit/stop-loss)"
9937
10372
  },
9938
10373
  amend: {
10374
+ toolName: "futures_amend_algo_order",
9939
10375
  usage: "okx futures algo amend --instId <id> --algoId <id> [--newSz <n>]\n [--newTpTriggerPx <price>] [--newTpOrdPx <price|-1>]\n [--newSlTriggerPx <price>] [--newSlOrdPx <price|-1>]",
9940
10376
  description: "Amend a pending futures algo order"
9941
10377
  },
9942
10378
  cancel: {
10379
+ toolName: "futures_cancel_algo_orders",
9943
10380
  usage: "okx futures algo cancel --instId <id> --algoId <id>",
9944
10381
  description: "Cancel a pending futures algo order"
9945
10382
  }
@@ -9947,51 +10384,90 @@ var HELP_TREE = {
9947
10384
  }
9948
10385
  }
9949
10386
  },
10387
+ // ── option ─────────────────────────────────────────────────────────────────
9950
10388
  option: {
9951
10389
  description: "Options trading (orders, positions, greeks)",
9952
10390
  commands: {
9953
10391
  orders: {
10392
+ toolName: "option_get_orders",
9954
10393
  usage: "okx option orders [--instId <id>] [--uly <uly>] [--history] [--archive]",
9955
10394
  description: "List open or historical option orders"
9956
10395
  },
9957
10396
  get: {
10397
+ toolName: "option_get_order",
9958
10398
  usage: "okx option get --instId <id> [--ordId <id>] [--clOrdId <id>]",
9959
10399
  description: "Get details of a specific option order"
9960
10400
  },
9961
10401
  positions: {
10402
+ toolName: "option_get_positions",
9962
10403
  usage: "okx option positions [--instId <id>] [--uly <uly>]",
9963
10404
  description: "Get current option positions"
9964
10405
  },
9965
10406
  fills: {
10407
+ toolName: "option_get_fills",
9966
10408
  usage: "okx option fills [--instId <id>] [--ordId <id>] [--archive]",
9967
10409
  description: "Get trade fill history for option orders"
9968
10410
  },
9969
10411
  instruments: {
10412
+ toolName: "option_get_instruments",
9970
10413
  usage: "okx option instruments --uly <uly> [--expTime <date>]",
9971
10414
  description: "List tradable option instruments for an underlying"
9972
10415
  },
9973
10416
  greeks: {
10417
+ toolName: "option_get_greeks",
9974
10418
  usage: "okx option greeks --uly <uly> [--expTime <date>]",
9975
10419
  description: "Get option greeks (delta, gamma, theta, vega)"
9976
10420
  },
9977
10421
  place: {
10422
+ toolName: "option_place_order",
9978
10423
  usage: "okx option place --instId <id> --tdMode <cash|cross|isolated> --side <buy|sell> --ordType <type> --sz <n>\n [--px <price>] [--reduceOnly] [--clOrdId <id>]",
9979
10424
  description: "Place a new option order"
9980
10425
  },
9981
10426
  cancel: {
10427
+ toolName: "option_cancel_order",
9982
10428
  usage: "okx option cancel --instId <id> [--ordId <id>] [--clOrdId <id>]",
9983
10429
  description: "Cancel a pending option order"
9984
10430
  },
9985
10431
  amend: {
10432
+ toolName: "option_amend_order",
9986
10433
  usage: "okx option amend --instId <id> [--ordId <id>] [--clOrdId <id>] [--newSz <n>] [--newPx <price>]",
9987
10434
  description: "Amend a pending option order"
9988
10435
  },
9989
10436
  "batch-cancel": {
10437
+ toolName: "option_batch_cancel",
9990
10438
  usage: "okx option batch-cancel --orders '<json>'",
9991
10439
  description: "Batch cancel option orders"
9992
10440
  }
10441
+ },
10442
+ subgroups: {
10443
+ algo: {
10444
+ description: "Option algo orders (conditional, TP/SL)",
10445
+ commands: {
10446
+ orders: {
10447
+ toolName: "option_get_algo_orders",
10448
+ usage: "okx option algo orders [--instId <id>] [--history] [--ordType <conditional|oco>]",
10449
+ description: "List option algo orders"
10450
+ },
10451
+ place: {
10452
+ toolName: "option_place_algo_order",
10453
+ usage: "okx option algo place --instId <id> --tdMode <cash|cross|isolated> --side <buy|sell> --sz <n>\n [--ordType <conditional|oco>] [--tpTriggerPx <price>] [--tpOrdPx <price|-1>]\n [--slTriggerPx <price>] [--slOrdPx <price|-1>] [--reduceOnly] [--clOrdId <id>]",
10454
+ description: "Place an option algo order (take-profit/stop-loss)"
10455
+ },
10456
+ amend: {
10457
+ toolName: "option_amend_algo_order",
10458
+ usage: "okx option algo amend --instId <id> --algoId <id> [--newSz <n>]\n [--newTpTriggerPx <price>] [--newTpOrdPx <price|-1>]\n [--newSlTriggerPx <price>] [--newSlOrdPx <price|-1>]",
10459
+ description: "Amend a pending option algo order"
10460
+ },
10461
+ cancel: {
10462
+ toolName: "option_cancel_algo_orders",
10463
+ usage: "okx option algo cancel --instId <id> --algoId <id>",
10464
+ description: "Cancel a pending option algo order"
10465
+ }
10466
+ }
10467
+ }
9993
10468
  }
9994
10469
  },
10470
+ // ── earn ───────────────────────────────────────────────────────────────────
9995
10471
  earn: {
9996
10472
  description: "Earn products \u2014 Simple Earn, On-chain Earn, and DCD (Dual Currency Deposit)",
9997
10473
  subgroups: {
@@ -9999,38 +10475,47 @@ var HELP_TREE = {
9999
10475
  description: "Simple Earn \u2014 flexible savings, fixed-term, and lending",
10000
10476
  commands: {
10001
10477
  balance: {
10478
+ toolName: "earn_get_savings_balance",
10002
10479
  usage: "okx earn savings balance [<ccy>]",
10003
10480
  description: "Get savings balance (optionally filter by currency)"
10004
10481
  },
10005
10482
  purchase: {
10483
+ toolName: "earn_savings_purchase",
10006
10484
  usage: "okx earn savings purchase --ccy <ccy> --amt <n> [--rate <rate>]",
10007
10485
  description: "Purchase Simple Earn (flexible savings). Rate defaults to 0.01 (1%)"
10008
10486
  },
10009
10487
  redeem: {
10488
+ toolName: "earn_savings_redeem",
10010
10489
  usage: "okx earn savings redeem --ccy <ccy> --amt <n>",
10011
10490
  description: "Redeem Simple Earn (flexible savings)"
10012
10491
  },
10013
10492
  "set-rate": {
10493
+ toolName: "earn_set_lending_rate",
10014
10494
  usage: "okx earn savings set-rate --ccy <ccy> --rate <rate>",
10015
10495
  description: "Set lending rate for a currency"
10016
10496
  },
10017
10497
  "lending-history": {
10498
+ toolName: "earn_get_lending_history",
10018
10499
  usage: "okx earn savings lending-history [--ccy <ccy>] [--limit <n>]",
10019
10500
  description: "Get personal lending records (requires auth)"
10020
10501
  },
10021
10502
  "rate-history": {
10503
+ toolName: "earn_get_lending_rate_history",
10022
10504
  usage: "okx earn savings rate-history [--ccy <ccy>] [--limit <n>]",
10023
10505
  description: "Query Simple Earn lending rates and fixed-term offers (requires auth)"
10024
10506
  },
10025
10507
  "fixed-orders": {
10508
+ toolName: "earn_get_fixed_order_list",
10026
10509
  usage: "okx earn savings fixed-orders [--ccy <ccy>] [--state <pending|earning|expired|settled|cancelled>]",
10027
10510
  description: "List fixed-term earn orders"
10028
10511
  },
10029
10512
  "fixed-purchase": {
10513
+ toolName: "earn_fixed_purchase",
10030
10514
  usage: "okx earn savings fixed-purchase --ccy <ccy> --amt <n> --term <term> [--confirm]",
10031
10515
  description: "Purchase Simple Earn Fixed (\u5B9A\u671F). Preview by default; add --confirm to execute. Funds locked until maturity"
10032
10516
  },
10033
10517
  "fixed-redeem": {
10518
+ toolName: "earn_fixed_redeem",
10034
10519
  usage: "okx earn savings fixed-redeem <reqId>",
10035
10520
  description: "Redeem a fixed-term earn order (full amount)"
10036
10521
  }
@@ -10040,26 +10525,32 @@ var HELP_TREE = {
10040
10525
  description: "On-chain Earn \u2014 staking and DeFi products",
10041
10526
  commands: {
10042
10527
  offers: {
10528
+ toolName: "onchain_earn_get_offers",
10043
10529
  usage: "okx earn onchain offers [--productId <id>] [--protocolType <type>] [--ccy <ccy>]",
10044
10530
  description: "Browse available on-chain earn products (staking, DeFi)"
10045
10531
  },
10046
10532
  purchase: {
10533
+ toolName: "onchain_earn_purchase",
10047
10534
  usage: "okx earn onchain purchase --productId <id> --ccy <ccy> --amt <n> [--term <term>] [--tag <tag>]",
10048
10535
  description: "Purchase an on-chain earn product (stake/deposit)"
10049
10536
  },
10050
10537
  redeem: {
10538
+ toolName: "onchain_earn_redeem",
10051
10539
  usage: "okx earn onchain redeem --ordId <id> --protocolType <type> [--allowEarlyRedeem]",
10052
10540
  description: "Redeem an on-chain earn position"
10053
10541
  },
10054
10542
  cancel: {
10543
+ toolName: "onchain_earn_cancel",
10055
10544
  usage: "okx earn onchain cancel --ordId <id> --protocolType <type>",
10056
10545
  description: "Cancel a pending on-chain earn order"
10057
10546
  },
10058
10547
  orders: {
10548
+ toolName: "onchain_earn_get_active_orders",
10059
10549
  usage: "okx earn onchain orders [--productId <id>] [--protocolType <type>] [--ccy <ccy>] [--state <state>]",
10060
10550
  description: "List active on-chain earn orders"
10061
10551
  },
10062
10552
  history: {
10553
+ toolName: "onchain_earn_get_order_history",
10063
10554
  usage: "okx earn onchain history [--productId <id>] [--protocolType <type>] [--ccy <ccy>]",
10064
10555
  description: "Get on-chain earn order history"
10065
10556
  }
@@ -10069,14 +10560,19 @@ var HELP_TREE = {
10069
10560
  description: "Auto-earn \u2014 automatically lend, stake, or earn on idle assets",
10070
10561
  commands: {
10071
10562
  status: {
10563
+ // CLI reads from account_get_balance; earn_auto_set is covered by 'on' command below
10564
+ toolName: null,
10072
10565
  usage: "okx earn auto-earn status [<ccy>]",
10073
10566
  description: "Query auto-earn status for all or a specific currency"
10074
10567
  },
10075
10568
  on: {
10569
+ toolName: "earn_auto_set",
10076
10570
  usage: "okx earn auto-earn on <ccy>",
10077
10571
  description: "Enable auto-earn for a currency (auto-detects lend/stake vs USDG earn)"
10078
10572
  },
10079
10573
  off: {
10574
+ // same tool as 'on'; earn_auto_set already registered above
10575
+ toolName: "earn_auto_set",
10080
10576
  usage: "okx earn auto-earn off <ccy>",
10081
10577
  description: "Disable auto-earn for a currency"
10082
10578
  }
@@ -10086,26 +10582,32 @@ var HELP_TREE = {
10086
10582
  description: "DCD (Dual Currency Deposit) \u2014 structured products with fixed yield",
10087
10583
  commands: {
10088
10584
  pairs: {
10585
+ toolName: "dcd_get_currency_pairs",
10089
10586
  usage: "okx earn dcd pairs",
10090
10587
  description: "List available DCD currency pairs"
10091
10588
  },
10092
10589
  products: {
10590
+ toolName: "dcd_get_products",
10093
10591
  usage: "okx earn dcd products --baseCcy <ccy> --quoteCcy <ccy> --optType <C|P>\n [--minYield <n>] [--strikeNear <price>]\n [--termDays <n>] [--minTermDays <n>] [--maxTermDays <n>]\n [--expDate <YYYY-MM-DD|YYYY-MM-DDTHH:mm>]",
10094
10592
  description: "List active DCD products (baseCcy, quoteCcy, optType required). Client-side filters: minYield (e.g. 0.05=5%), strikeNear (\xB110%), term range, expDate"
10095
10593
  },
10096
10594
  "quote-and-buy": {
10595
+ toolName: "dcd_subscribe",
10097
10596
  usage: "okx earn dcd quote-and-buy --productId <id> --sz <n> --notionalCcy <ccy> [--clOrdId <id>] [--minAnnualizedYield <pct>]",
10098
10597
  description: "[CAUTION] Subscribe to a DCD product atomically (quote + execute in one step)"
10099
10598
  },
10100
10599
  "redeem-execute": {
10600
+ toolName: "dcd_redeem",
10101
10601
  usage: "okx earn dcd redeem-execute --ordId <id>",
10102
10602
  description: "[CAUTION] Re-quote and execute early redemption in one step (recommended for AI agent use)"
10103
10603
  },
10104
10604
  order: {
10605
+ toolName: "dcd_get_order_state",
10105
10606
  usage: "okx earn dcd order --ordId <id>",
10106
10607
  description: "Query current state of a DCD order"
10107
10608
  },
10108
10609
  orders: {
10610
+ toolName: "dcd_get_orders",
10109
10611
  usage: "okx earn dcd orders [--ordId <id>] [--productId <id>] [--uly <uly>] [--state <state>] [--limit <n>]",
10110
10612
  description: "Get DCD order history. State: initial|live|pending_settle|settled|pending_redeem|redeemed|rejected"
10111
10613
  }
@@ -10113,6 +10615,7 @@ var HELP_TREE = {
10113
10615
  }
10114
10616
  }
10115
10617
  },
10618
+ // ── bot ────────────────────────────────────────────────────────────────────
10116
10619
  bot: {
10117
10620
  description: "Trading bot strategies (grid, dca)",
10118
10621
  subgroups: {
@@ -10120,22 +10623,27 @@ var HELP_TREE = {
10120
10623
  description: "Grid trading bot \u2014 create, monitor, and stop grid orders",
10121
10624
  commands: {
10122
10625
  orders: {
10626
+ toolName: "grid_get_orders",
10123
10627
  usage: "okx bot grid orders --algoOrdType <grid|contract_grid|moon_grid> [--instId <id>] [--algoId <id>] [--history]",
10124
10628
  description: "List active or historical grid bot orders"
10125
10629
  },
10126
10630
  details: {
10631
+ toolName: "grid_get_order_details",
10127
10632
  usage: "okx bot grid details --algoOrdType <type> --algoId <id>",
10128
10633
  description: "Get details of a specific grid bot order"
10129
10634
  },
10130
10635
  "sub-orders": {
10636
+ toolName: "grid_get_sub_orders",
10131
10637
  usage: "okx bot grid sub-orders --algoOrdType <type> --algoId <id> [--live]",
10132
10638
  description: "List sub-orders of a grid bot (filled or live)"
10133
10639
  },
10134
10640
  create: {
10641
+ toolName: "grid_create_order",
10135
10642
  usage: "okx bot grid create --instId <id> --algoOrdType <grid|contract_grid> --maxPx <px> --minPx <px> --gridNum <n>\n [--runType <1|2>] [--quoteSz <n>] [--baseSz <n>]\n [--direction <long|short|neutral>] [--lever <n>] [--sz <n>] [--basePos] [--no-basePos]\n [--tpTriggerPx <px>] [--slTriggerPx <px>] [--tpRatio <n>] [--slRatio <n>] [--algoClOrdId <id>]",
10136
10643
  description: "Create a new grid bot order (contract grid opens base position by default)"
10137
10644
  },
10138
10645
  stop: {
10646
+ toolName: "grid_stop_order",
10139
10647
  usage: "okx bot grid stop --algoId <id> --algoOrdType <type> --instId <id> [--stopType <1|2|3|5|6>]",
10140
10648
  description: "Stop a running grid bot order"
10141
10649
  }
@@ -10145,22 +10653,27 @@ var HELP_TREE = {
10145
10653
  description: "DCA (Martingale) bot \u2014 spot or contract recurring buys",
10146
10654
  commands: {
10147
10655
  orders: {
10656
+ toolName: "dca_get_orders",
10148
10657
  usage: "okx bot dca orders [--algoOrdType <spot_dca|contract_dca>] [--algoId <id>] [--instId <id>] [--history]",
10149
10658
  description: "List DCA bots (spot and/or contract)"
10150
10659
  },
10151
10660
  details: {
10661
+ toolName: "dca_get_order_details",
10152
10662
  usage: "okx bot dca details --algoOrdType <spot_dca|contract_dca> --algoId <id>",
10153
10663
  description: "Get DCA bot details (spot or contract)"
10154
10664
  },
10155
10665
  "sub-orders": {
10666
+ toolName: "dca_get_sub_orders",
10156
10667
  usage: "okx bot dca sub-orders --algoOrdType <spot_dca|contract_dca> --algoId <id> [--cycleId <id>]",
10157
10668
  description: "Get DCA cycles/orders (spot or contract)"
10158
10669
  },
10159
10670
  create: {
10671
+ toolName: "dca_create_order",
10160
10672
  usage: "okx bot dca create --algoOrdType <spot_dca|contract_dca> --instId <id> --direction <long|short>\n --initOrdAmt <n> --maxSafetyOrds <n> --tpPct <n>\n [--lever <n>] [--safetyOrdAmt <n>] [--pxSteps <n>] [--pxStepsMult <n>] [--volMult <n>]\n [--slPct <n>] [--slMode <limit|market>] [--allowReinvest <true|false>]\n [--triggerStrategy <instant|price|rsi>] [--triggerPx <price>]\n [--triggerCond <cross_up|cross_down>] [--thold <n>] [--timeframe <tf>] [--timePeriod <n>]\n [--algoClOrdId <id>] [--reserveFunds <true|false>] [--tradeQuoteCcy <ccy>]\n Note: --lever required for contract_dca; safetyOrdAmt, pxSteps, pxStepsMult, volMult required when maxSafetyOrds > 0\n triggerStrategy: contract_dca supports instant|price|rsi; spot_dca supports instant|rsi",
10161
10673
  description: "Create a DCA (Martingale) bot (spot or contract)"
10162
10674
  },
10163
10675
  stop: {
10676
+ toolName: "dca_stop_order",
10164
10677
  usage: "okx bot dca stop --algoOrdType <spot_dca|contract_dca> --algoId <id> [--stopType <1|2>]\n Note: --stopType required for spot_dca (1=sell all, 2=keep tokens)",
10165
10678
  description: "Stop a DCA bot (spot or contract)"
10166
10679
  }
@@ -10168,73 +10681,254 @@ var HELP_TREE = {
10168
10681
  }
10169
10682
  }
10170
10683
  },
10684
+ // ── config ─────────────────────────────────────────────────────────────────
10171
10685
  config: {
10172
10686
  description: "Manage CLI configuration profiles",
10173
10687
  commands: {
10174
10688
  init: {
10689
+ toolName: null,
10175
10690
  usage: "okx config init [--lang zh]",
10176
10691
  description: "Initialize a new configuration profile interactively"
10177
10692
  },
10178
10693
  show: {
10694
+ toolName: null,
10179
10695
  usage: "okx config show",
10180
10696
  description: `Show current configuration (file: ${configFilePath()})`
10181
10697
  },
10182
10698
  set: {
10699
+ toolName: null,
10183
10700
  usage: "okx config set <key> <value>",
10184
10701
  description: "Set a configuration value"
10185
10702
  },
10186
10703
  "setup-clients": {
10704
+ toolName: null,
10187
10705
  usage: "okx config setup-clients",
10188
10706
  description: "Set up MCP client integrations (Cursor, Windsurf, etc.)"
10189
10707
  }
10190
10708
  }
10191
10709
  },
10710
+ // ── setup ──────────────────────────────────────────────────────────────────
10192
10711
  setup: {
10193
10712
  description: "Set up client integrations (Cursor, Windsurf, Claude, etc.)",
10194
10713
  usage: `okx setup --client <${SUPPORTED_CLIENTS.join("|")}> [--profile <name>] [--modules <list>]`
10195
10714
  },
10715
+ // ── doh ────────────────────────────────────────────────────────────────────
10716
+ doh: {
10717
+ description: "Manage DoH (DNS-over-HTTPS) resolver binary",
10718
+ commands: {
10719
+ status: {
10720
+ toolName: null,
10721
+ usage: "okx doh status [--json]",
10722
+ description: "Show DoH binary info, checksum, and CDN match status"
10723
+ },
10724
+ install: {
10725
+ toolName: null,
10726
+ usage: "okx doh install [--json]",
10727
+ description: "Download or update the DoH resolver binary"
10728
+ },
10729
+ remove: {
10730
+ toolName: null,
10731
+ usage: "okx doh remove [--force] [--json]",
10732
+ description: "Remove the DoH resolver binary (prompts for confirmation without --force)"
10733
+ }
10734
+ }
10735
+ },
10736
+ // ── diagnose ───────────────────────────────────────────────────────────────
10196
10737
  diagnose: {
10197
10738
  description: "Run network / MCP server diagnostics",
10198
10739
  usage: "okx diagnose [--cli | --mcp | --all] [--profile <name>] [--demo | --live] [--output <file>]"
10199
10740
  },
10741
+ // ── skill ──────────────────────────────────────────────────────────────────
10200
10742
  skill: {
10201
10743
  description: "OKX Skills Marketplace \u2014 search, install, and manage agent skills",
10202
10744
  commands: {
10203
10745
  search: {
10746
+ toolName: "skills_search",
10204
10747
  usage: "okx skill search [--keyword <kw>] [--categories <id>] [--page <n>] [--limit <n>]",
10205
10748
  description: "Search for skills in the marketplace"
10206
10749
  },
10207
10750
  categories: {
10751
+ toolName: "skills_get_categories",
10208
10752
  usage: "okx skill categories",
10209
10753
  description: "List available skill categories"
10210
10754
  },
10211
10755
  add: {
10756
+ // Composite: downloads + installs via npx; no single ToolSpec
10757
+ toolName: null,
10212
10758
  usage: "okx skill add <name>",
10213
10759
  description: "Download and install a skill to detected agents"
10214
10760
  },
10215
10761
  download: {
10762
+ // CLI calls downloadSkillZip directly (same capability as skills_download ToolSpec)
10763
+ toolName: "skills_download",
10216
10764
  usage: "okx skill download <name> [--dir <path>] [--format zip|skill]",
10217
10765
  description: "Download a skill package without installing"
10218
10766
  },
10219
10767
  remove: {
10768
+ toolName: null,
10220
10769
  usage: "okx skill remove <name>",
10221
10770
  description: "Remove an installed skill"
10222
10771
  },
10223
10772
  check: {
10773
+ // Uses skills_search internally; skills_search already registered above
10774
+ toolName: null,
10224
10775
  usage: "okx skill check <name>",
10225
10776
  description: "Check if an installed skill has a newer version"
10226
10777
  },
10227
10778
  list: {
10779
+ toolName: null,
10228
10780
  usage: "okx skill list",
10229
10781
  description: "List all locally installed skills"
10230
10782
  }
10231
10783
  }
10232
10784
  },
10785
+ // ── upgrade ────────────────────────────────────────────────────────────────
10233
10786
  upgrade: {
10234
10787
  description: "Upgrade okx CLI and MCP server to the latest stable version",
10235
10788
  usage: "okx upgrade [--check] [--beta] [--force] [--json]"
10236
10789
  }
10237
10790
  };
10791
+
10792
+ // src/help-generator.ts
10793
+ function buildSpecMap() {
10794
+ return new Map(allToolSpecs().map((s) => [s.name, s]));
10795
+ }
10796
+ function resolveCommandDescription(entry, specMap, fallback = "(no description)") {
10797
+ if (entry.description) return entry.description;
10798
+ if (entry.toolName != null) {
10799
+ const spec = specMap.get(entry.toolName);
10800
+ if (spec?.description) return spec.description.split(/\.\s/)[0] + ".";
10801
+ }
10802
+ return fallback;
10803
+ }
10804
+ function buildCommands(commands, specMap) {
10805
+ const result = {};
10806
+ for (const [name, entry] of Object.entries(commands)) {
10807
+ result[name] = {
10808
+ usage: entry.usage,
10809
+ description: resolveCommandDescription(entry, specMap)
10810
+ };
10811
+ }
10812
+ return result;
10813
+ }
10814
+ function buildGroupInfo(key, mod, specMap) {
10815
+ const description = mod.description ?? MODULE_DESCRIPTIONS[key] ?? key;
10816
+ return {
10817
+ description,
10818
+ usage: mod.usage,
10819
+ commands: mod.commands ? buildCommands(mod.commands, specMap) : void 0,
10820
+ subgroups: mod.subgroups ? buildSubgroups(mod.subgroups, specMap) : void 0
10821
+ };
10822
+ }
10823
+ function buildSubgroups(subgroups, specMap) {
10824
+ const result = {};
10825
+ for (const [name, entry] of Object.entries(subgroups)) {
10826
+ result[name] = buildGroupInfo(name, entry, specMap);
10827
+ }
10828
+ return result;
10829
+ }
10830
+ var _cached = null;
10831
+ function generateHelpTree() {
10832
+ if (_cached) return _cached;
10833
+ const specMap = buildSpecMap();
10834
+ const tree = {};
10835
+ for (const [key, mod] of Object.entries(CLI_REGISTRY)) {
10836
+ tree[key] = buildGroupInfo(key, mod, specMap);
10837
+ }
10838
+ _cached = tree;
10839
+ return tree;
10840
+ }
10841
+
10842
+ // src/commands/discovery.ts
10843
+ function extractParameters(toolName, specMap) {
10844
+ if (toolName == null) return [];
10845
+ const spec = specMap.get(toolName);
10846
+ if (!spec?.inputSchema) return [];
10847
+ const schema = spec.inputSchema;
10848
+ if (!schema.properties) return [];
10849
+ const required = new Set(schema.required ?? []);
10850
+ return Object.entries(schema.properties).map(([name, prop]) => ({
10851
+ name,
10852
+ type: prop.type ?? "string",
10853
+ required: required.has(name),
10854
+ description: prop.description
10855
+ }));
10856
+ }
10857
+ function collectCommands(modulePath, mod, specMap, commands) {
10858
+ for (const [cmdName, entry] of Object.entries(mod.commands ?? {})) {
10859
+ commands.push({
10860
+ path: `${modulePath} ${cmdName}`,
10861
+ toolName: entry.toolName,
10862
+ description: resolveCommandDescription(entry, specMap, ""),
10863
+ parameters: extractParameters(entry.toolName, specMap)
10864
+ });
10865
+ }
10866
+ for (const [sgName, sg] of Object.entries(mod.subgroups ?? {})) {
10867
+ collectCommands(`${modulePath} ${sgName}`, sg, specMap, commands);
10868
+ }
10869
+ }
10870
+ function getDiscoveryOutput() {
10871
+ const specs = allToolSpecs();
10872
+ const specMap = new Map(specs.map((s) => [s.name, s]));
10873
+ const moduleDescMap = new Map(specs.map((s) => [s.module, s.description]));
10874
+ const version = readCliVersion();
10875
+ const modules = [];
10876
+ for (const [moduleKey, moduleEntry] of Object.entries(CLI_REGISTRY)) {
10877
+ const commands = [];
10878
+ collectCommands(`okx ${moduleKey}`, moduleEntry, specMap, commands);
10879
+ const description = moduleEntry.description ?? MODULE_DESCRIPTIONS[moduleKey] ?? moduleDescMap.get(moduleKey) ?? moduleKey;
10880
+ modules.push({
10881
+ name: moduleKey,
10882
+ description,
10883
+ commands
10884
+ });
10885
+ }
10886
+ const totalTools = modules.reduce(
10887
+ (sum, m) => sum + m.commands.filter((c) => c.toolName !== null).length,
10888
+ 0
10889
+ );
10890
+ return { version, totalTools, modules };
10891
+ }
10892
+ function cmdListTools(json) {
10893
+ const data = getDiscoveryOutput();
10894
+ if (json) {
10895
+ output(JSON.stringify(data, null, 2) + "\n");
10896
+ return;
10897
+ }
10898
+ const lines = [
10899
+ "",
10900
+ `OKX CLI v${data.version} \u2014 ${data.totalTools} tool-backed commands across ${data.modules.length} modules`,
10901
+ "",
10902
+ "Modules:"
10903
+ ];
10904
+ for (const mod of data.modules) {
10905
+ const toolCmds = mod.commands.filter((c) => c.toolName !== null).length;
10906
+ if (toolCmds > 0) {
10907
+ lines.push(` ${mod.name.padEnd(14)}${toolCmds} commands`);
10908
+ }
10909
+ }
10910
+ lines.push("", 'Use "okx list-tools --json" for machine-readable output.', "");
10911
+ output(lines.join("\n"));
10912
+ }
10913
+
10914
+ // src/config/loader.ts
10915
+ function loadProfileConfig(opts) {
10916
+ return loadConfig({
10917
+ profile: opts.profile,
10918
+ modules: opts.modules,
10919
+ readOnly: opts.readOnly ?? false,
10920
+ demo: opts.demo,
10921
+ live: opts.live,
10922
+ site: opts.site,
10923
+ userAgent: opts.userAgent,
10924
+ sourceTag: opts.sourceTag,
10925
+ verbose: opts.verbose
10926
+ });
10927
+ }
10928
+
10929
+ // src/help.ts
10930
+ import { EOL as EOL2 } from "os";
10931
+ var HELP_TREE = generateHelpTree();
10238
10932
  function printGlobalHelp() {
10239
10933
  const lines = [
10240
10934
  "",
@@ -13356,14 +14050,14 @@ async function cmdDcdQuoteAndBuy(run, opts) {
13356
14050
  }
13357
14051
 
13358
14052
  // src/commands/skill.ts
13359
- import { tmpdir, homedir as homedir9 } from "os";
13360
- import { join as join11, dirname as dirname7 } from "path";
13361
- import { mkdirSync as mkdirSync9, rmSync, existsSync as existsSync7, copyFileSync as copyFileSync2 } from "fs";
14053
+ import { tmpdir, homedir as homedir10 } from "os";
14054
+ import { join as join12, dirname as dirname8 } from "path";
14055
+ import { mkdirSync as mkdirSync10, rmSync, existsSync as existsSync8, copyFileSync as copyFileSync2 } from "fs";
13362
14056
  import { execFileSync as execFileSync2 } from "child_process";
13363
14057
  import { randomUUID as randomUUID2 } from "crypto";
13364
14058
  function resolveNpx() {
13365
- const sibling = join11(dirname7(process.execPath), "npx");
13366
- if (existsSync7(sibling)) return sibling;
14059
+ const sibling = join12(dirname8(process.execPath), "npx");
14060
+ if (existsSync8(sibling)) return sibling;
13367
14061
  return "npx";
13368
14062
  }
13369
14063
  var THIRD_PARTY_INSTALL_NOTICE = "Note: This skill was created by a third-party developer, not by OKX. Review SKILL.md before use.";
@@ -13416,13 +14110,13 @@ async function cmdSkillCategories(run, json) {
13416
14110
  outputLine("");
13417
14111
  }
13418
14112
  async function cmdSkillAdd(name, config, json) {
13419
- const tmpBase = join11(tmpdir(), `okx-skill-${randomUUID2()}`);
13420
- mkdirSync9(tmpBase, { recursive: true });
14113
+ const tmpBase = join12(tmpdir(), `okx-skill-${randomUUID2()}`);
14114
+ mkdirSync10(tmpBase, { recursive: true });
13421
14115
  try {
13422
14116
  outputLine(`Downloading ${name}...`);
13423
14117
  const client = new OkxRestClient(config);
13424
14118
  const zipPath = await downloadSkillZip(client, name, tmpBase);
13425
- const contentDir = await extractSkillZip(zipPath, join11(tmpBase, "content"));
14119
+ const contentDir = await extractSkillZip(zipPath, join12(tmpBase, "content"));
13426
14120
  const meta = readMetaJson(contentDir);
13427
14121
  validateSkillMdExists(contentDir);
13428
14122
  outputLine("Installing to detected agents...");
@@ -13432,7 +14126,7 @@ async function cmdSkillAdd(name, config, json) {
13432
14126
  timeout: 6e4
13433
14127
  });
13434
14128
  } catch (e) {
13435
- const savedZip = join11(process.cwd(), `${name}.zip`);
14129
+ const savedZip = join12(process.cwd(), `${name}.zip`);
13436
14130
  try {
13437
14131
  copyFileSync2(zipPath, savedZip);
13438
14132
  } catch {
@@ -13471,7 +14165,7 @@ function cmdSkillRemove(name, json) {
13471
14165
  timeout: 6e4
13472
14166
  });
13473
14167
  } catch {
13474
- const agentsPath = join11(homedir9(), ".agents", "skills", name);
14168
+ const agentsPath = join12(homedir10(), ".agents", "skills", name);
13475
14169
  try {
13476
14170
  rmSync(agentsPath, { recursive: true, force: true });
13477
14171
  } catch {
@@ -13544,10 +14238,171 @@ function printSkillInstallResult(meta, json) {
13544
14238
  }
13545
14239
  }
13546
14240
 
14241
+ // src/commands/doh.ts
14242
+ import readline from "readline";
14243
+ function resolveChecksumMatch(local, cdnChecksum, cdnError) {
14244
+ if (!local.exists) return "not-installed";
14245
+ if (cdnError || !cdnChecksum) return "unavailable";
14246
+ if (cdnChecksum.sha256 === local.sha256) return "match";
14247
+ return "mismatch";
14248
+ }
14249
+ function checksumMatchLabel(match) {
14250
+ if (match === "match") return "\u2713 match";
14251
+ if (match === "mismatch") return "\u2717 mismatch (update available)";
14252
+ return "CDN unreachable";
14253
+ }
14254
+ function formatStatusText(local, checksumMatch, cdnChecksum, runtimeMode) {
14255
+ outputLine("");
14256
+ outputLine(" DoH Resolver Status");
14257
+ outputLine(" " + "\u2500".repeat(40));
14258
+ outputLine(` Binary path : ${local.binaryPath}`);
14259
+ outputLine(` Installed : ${local.exists ? "yes" : "no"}`);
14260
+ outputLine(` Platform : ${local.platform ?? "(unsupported)"}`);
14261
+ if (local.exists) {
14262
+ outputLine(` File size : ${formatBytes(local.fileSize)}`);
14263
+ outputLine(` SHA-256 : ${local.sha256 ?? "(unknown)"}`);
14264
+ outputLine(` CDN check : ${checksumMatchLabel(checksumMatch)}`);
14265
+ if (cdnChecksum) {
14266
+ outputLine(` CDN source : ${cdnChecksum.source}`);
14267
+ }
14268
+ }
14269
+ outputLine(` Runtime mode: ${runtimeMode}`);
14270
+ outputLine("");
14271
+ }
14272
+ async function cmdDohStatus(json, binaryPath) {
14273
+ const local = getDohStatus(binaryPath);
14274
+ let runtimeMode = "no cache";
14275
+ try {
14276
+ const cacheEntry = readCache("www.okx.com");
14277
+ if (cacheEntry) {
14278
+ runtimeMode = cacheEntry.mode;
14279
+ }
14280
+ } catch {
14281
+ }
14282
+ let cdnChecksum = null;
14283
+ let cdnError = null;
14284
+ if (local.exists) {
14285
+ try {
14286
+ cdnChecksum = await fetchCdnChecksum(void 0, 5e3);
14287
+ } catch (err) {
14288
+ cdnError = err instanceof Error ? err.message : String(err);
14289
+ }
14290
+ }
14291
+ const checksumMatch = resolveChecksumMatch(local, cdnChecksum, cdnError);
14292
+ if (json) {
14293
+ outputLine(
14294
+ JSON.stringify({
14295
+ binaryPath: local.binaryPath,
14296
+ exists: local.exists,
14297
+ platform: local.platform,
14298
+ fileSize: local.fileSize ?? null,
14299
+ sha256: local.sha256 ?? null,
14300
+ cdnMatch: checksumMatch,
14301
+ cdnSha256: cdnChecksum?.sha256 ?? null,
14302
+ cdnSource: cdnChecksum?.source ?? null,
14303
+ runtimeMode
14304
+ })
14305
+ );
14306
+ return;
14307
+ }
14308
+ formatStatusText(local, checksumMatch, cdnChecksum, runtimeMode);
14309
+ }
14310
+ async function cmdDohInstall(json, binaryPath) {
14311
+ const messages2 = [];
14312
+ const onProgress = (msg) => {
14313
+ if (!json) {
14314
+ outputLine(` ${msg}`);
14315
+ }
14316
+ messages2.push(msg);
14317
+ };
14318
+ if (!json) {
14319
+ outputLine("");
14320
+ outputLine(" Installing DoH resolver...");
14321
+ }
14322
+ const result = await installDohBinary(binaryPath, void 0, onProgress);
14323
+ if (json) {
14324
+ outputLine(JSON.stringify({ status: result.status, source: result.source ?? null, error: result.error ?? null, messages: messages2 }));
14325
+ if (result.status === "failed") {
14326
+ process.exitCode = 1;
14327
+ }
14328
+ return;
14329
+ }
14330
+ if (result.status === "installed") {
14331
+ outputLine(` \u2713 DoH resolver installed successfully (${result.source ?? ""})`);
14332
+ } else if (result.status === "up-to-date") {
14333
+ outputLine(" \u2713 DoH resolver is already up to date");
14334
+ } else {
14335
+ errorLine(` \u2717 Installation failed: ${result.error ?? "unknown error"}`);
14336
+ errorLine(" Hint: check network connectivity or try again later");
14337
+ process.exitCode = 1;
14338
+ }
14339
+ outputLine("");
14340
+ }
14341
+ async function cmdDohRemove(force, json, binaryPath) {
14342
+ const local = getDohStatus(binaryPath);
14343
+ if (!local.exists) {
14344
+ if (json) {
14345
+ outputLine(JSON.stringify({ status: "not-installed" }));
14346
+ } else {
14347
+ outputLine(" DoH resolver is not installed.");
14348
+ }
14349
+ return;
14350
+ }
14351
+ if (!force) {
14352
+ if (!process.stdin.isTTY) {
14353
+ errorLine(" Error: stdin is not a TTY. Use --force to skip confirmation.");
14354
+ process.exitCode = 1;
14355
+ return;
14356
+ }
14357
+ const confirmed = await askConfirmation(
14358
+ ` Remove DoH resolver at ${local.binaryPath}? [y/N] `
14359
+ );
14360
+ if (!confirmed) {
14361
+ outputLine(" Cancelled.");
14362
+ return;
14363
+ }
14364
+ }
14365
+ const result = removeDohBinary(binaryPath);
14366
+ if (json) {
14367
+ outputLine(JSON.stringify({ status: result.status, path: local.binaryPath }));
14368
+ return;
14369
+ }
14370
+ if (result.status === "removed") {
14371
+ outputLine(` \u2713 Removed: ${local.binaryPath}`);
14372
+ } else {
14373
+ outputLine(" DoH resolver is not installed.");
14374
+ }
14375
+ }
14376
+ function formatBytes(bytes) {
14377
+ if (bytes < 1024) return `${bytes} B`;
14378
+ if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
14379
+ return `${(bytes / (1024 * 1024)).toFixed(2)} MB`;
14380
+ }
14381
+ function askConfirmation(prompt2) {
14382
+ return new Promise((resolve3) => {
14383
+ const rl = readline.createInterface({
14384
+ input: process.stdin,
14385
+ output: process.stdout
14386
+ });
14387
+ rl.question(prompt2, (answer) => {
14388
+ rl.close();
14389
+ resolve3(answer.trim().toLowerCase() === "y");
14390
+ });
14391
+ });
14392
+ }
14393
+
13547
14394
  // src/index.ts
13548
14395
  var _require3 = createRequire3(import.meta.url);
13549
14396
  var CLI_VERSION2 = _require3("../package.json").version;
13550
- var GIT_HASH2 = true ? "8ae72a0" : "dev";
14397
+ var GIT_HASH2 = true ? "63a5613" : "dev";
14398
+ function handleDohCommand(action, json, force, binaryPath) {
14399
+ if (action === "status") return cmdDohStatus(json, binaryPath);
14400
+ if (action === "install") return cmdDohInstall(json, binaryPath);
14401
+ if (action === "remove") return cmdDohRemove(force, json, binaryPath);
14402
+ errorLine(`Unknown doh command: ${action}`);
14403
+ errorLine("Usage: okx doh <status|install|remove>");
14404
+ process.exitCode = 1;
14405
+ }
13551
14406
  function handleConfigCommand(action, rest, json, lang, force) {
13552
14407
  if (action === "init") return cmdConfigInit(lang === "zh" ? "zh" : "en");
13553
14408
  if (action === "show") return cmdConfigShow(json);
@@ -14399,6 +15254,24 @@ async function runDiagnose(v) {
14399
15254
  }
14400
15255
  return cmdDiagnose(config, v.profile ?? "default", { mcp: v.mcp, cli: v.cli, all: v.all, output: v.output });
14401
15256
  }
15257
+ function printVersion() {
15258
+ outputLine(`${CLI_VERSION2} (${GIT_HASH2})`);
15259
+ const dohStatus = getDohStatus(void 0, { skipHash: true });
15260
+ if (dohStatus.exists) {
15261
+ outputLine(`DoH resolver: installed (${dohStatus.platform ?? "unknown"})`);
15262
+ } else {
15263
+ outputLine("DoH resolver: not installed");
15264
+ }
15265
+ }
15266
+ function routeManagementCommand(module, action, rest, json, v) {
15267
+ if (module === "config") return handleConfigCommand(action, rest, json, v.lang, v.force);
15268
+ if (module === "setup") return handleSetupCommand(v);
15269
+ if (module === "upgrade") return cmdUpgrade(CLI_VERSION2, { beta: v.beta, check: v.check, force: v.force }, json);
15270
+ if (module === "doh") return handleDohCommand(action, json, v.force ?? false);
15271
+ if (module === "diagnose") return runDiagnose(v);
15272
+ if (module === "list-tools") return cmdListTools(json);
15273
+ return void 0;
15274
+ }
14402
15275
  async function main() {
14403
15276
  setOutput({
14404
15277
  out: (m) => process.stdout.write(m),
@@ -14407,7 +15280,7 @@ async function main() {
14407
15280
  checkForUpdates("@okx_ai/okx-trade-cli", CLI_VERSION2);
14408
15281
  const { values, positionals } = parseCli(process.argv.slice(2));
14409
15282
  if (values.version) {
14410
- outputLine(`${CLI_VERSION2} (${GIT_HASH2})`);
15283
+ printVersion();
14411
15284
  return;
14412
15285
  }
14413
15286
  if (values.help || positionals.length === 0) {
@@ -14417,10 +15290,8 @@ async function main() {
14417
15290
  const [module, action, ...rest] = positionals;
14418
15291
  const v = values;
14419
15292
  const json = v.json ?? false;
14420
- if (module === "config") return handleConfigCommand(action, rest, json, v.lang, v.force);
14421
- if (module === "setup") return handleSetupCommand(v);
14422
- if (module === "upgrade") return cmdUpgrade(CLI_VERSION2, { beta: v.beta, check: v.check, force: v.force }, json);
14423
- if (module === "diagnose") return runDiagnose(v);
15293
+ const mgmt = routeManagementCommand(module, action, rest, json, v);
15294
+ if (mgmt) return mgmt;
14424
15295
  const config = loadProfileConfig({ profile: v.profile, demo: v.demo, live: v.live, verbose: v.verbose, userAgent: `okx-trade-cli/${CLI_VERSION2}`, sourceTag: "CLI" });
14425
15296
  setEnvContext({ demo: config.demo, profile: v.profile ?? "default" });
14426
15297
  setJsonEnvEnabled(v.env ?? false);
@@ -14458,6 +15329,7 @@ export {
14458
15329
  handleBotDcaCommand,
14459
15330
  handleBotGridCommand,
14460
15331
  handleConfigCommand,
15332
+ handleDohCommand,
14461
15333
  handleEarnCommand,
14462
15334
  handleFuturesAlgoCommand,
14463
15335
  handleFuturesCommand,