@okx_ai/okx-trade-cli 1.3.0-beta.2 → 1.3.0-beta.4

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
@@ -4,25 +4,37 @@
4
4
  import { createRequire as createRequire3 } from "module";
5
5
 
6
6
  // ../core/dist/index.js
7
- import { ProxyAgent } from "undici";
7
+ import { Agent, ProxyAgent } from "undici";
8
+ import { execFile } from "child_process";
9
+ import { homedir } from "os";
10
+ import { join } from "path";
11
+ import {
12
+ readFileSync,
13
+ writeFileSync,
14
+ mkdirSync,
15
+ unlinkSync,
16
+ renameSync
17
+ } from "fs";
18
+ import { homedir as homedir2 } from "os";
19
+ import { join as join2, dirname } from "path";
8
20
  import { createHmac } from "crypto";
9
21
  import fs from "fs";
10
22
  import path from "path";
11
23
  import os from "os";
12
- import { writeFileSync, renameSync, unlinkSync, mkdirSync } from "fs";
13
- import { join, resolve, basename, sep } from "path";
24
+ import { writeFileSync as writeFileSync2, renameSync as renameSync2, unlinkSync as unlinkSync2, mkdirSync as mkdirSync2 } from "fs";
25
+ import { join as join3, resolve, basename, sep } from "path";
14
26
  import { randomUUID } from "crypto";
15
27
  import yauzl from "yauzl";
16
- import { createWriteStream, mkdirSync as mkdirSync2 } from "fs";
17
- import { resolve as resolve2, dirname } from "path";
18
- import { readFileSync, existsSync } from "fs";
19
- import { join as join2 } from "path";
20
- import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, mkdirSync as mkdirSync3, existsSync as existsSync2 } from "fs";
21
- import { join as join3, dirname as dirname2 } from "path";
22
- import { homedir } from "os";
23
- import { readFileSync as readFileSync3, writeFileSync as writeFileSync3, mkdirSync as mkdirSync4, existsSync as existsSync3 } from "fs";
24
- import { join as join4, dirname as dirname3 } from "path";
25
- import { homedir as homedir2 } from "os";
28
+ import { createWriteStream, mkdirSync as mkdirSync3 } from "fs";
29
+ import { resolve as resolve2, dirname as dirname2 } from "path";
30
+ import { readFileSync as readFileSync2, existsSync } from "fs";
31
+ import { join as join4 } from "path";
32
+ import { readFileSync as readFileSync3, writeFileSync as writeFileSync3, mkdirSync as mkdirSync4, existsSync as existsSync2 } from "fs";
33
+ import { join as join5, dirname as dirname3 } from "path";
34
+ import { homedir as homedir3 } from "os";
35
+ import { readFileSync as readFileSync4, writeFileSync as writeFileSync4, mkdirSync as mkdirSync5, existsSync as existsSync3 } from "fs";
36
+ import { join as join6, dirname as dirname4 } from "path";
37
+ import { homedir as homedir4 } from "os";
26
38
 
27
39
  // ../../node_modules/.pnpm/smol-toml@1.6.0/node_modules/smol-toml/dist/error.js
28
40
  function getLineColFromPtr(string, ptr) {
@@ -851,9 +863,9 @@ function stringify(obj, { maxDepth = 1e3, numbersAsFloat = false } = {}) {
851
863
  }
852
864
 
853
865
  // ../core/dist/index.js
854
- import { readFileSync as readFileSync4, writeFileSync as writeFileSync4, mkdirSync as mkdirSync5, existsSync as existsSync4 } from "fs";
855
- import { join as join5 } from "path";
856
- import { homedir as homedir3 } from "os";
866
+ import { readFileSync as readFileSync5, writeFileSync as writeFileSync5, mkdirSync as mkdirSync6, existsSync as existsSync4 } from "fs";
867
+ import { join as join7 } from "path";
868
+ import { homedir as homedir5 } from "os";
857
869
  import fs2 from "fs";
858
870
  import path2 from "path";
859
871
  import os2 from "os";
@@ -861,6 +873,123 @@ import * as fs3 from "fs";
861
873
  import * as path3 from "path";
862
874
  import * as os3 from "os";
863
875
  import { execFileSync } from "child_process";
876
+ var EXEC_TIMEOUT_MS = 3e4;
877
+ var DOH_BIN_DIR = join(homedir(), ".okx", "bin");
878
+ function getDohBinaryPath() {
879
+ if (process.env.OKX_DOH_BINARY_PATH) {
880
+ return process.env.OKX_DOH_BINARY_PATH;
881
+ }
882
+ const ext = process.platform === "win32" ? ".exe" : "";
883
+ return join(DOH_BIN_DIR, `okx-doh-resolver${ext}`);
884
+ }
885
+ function execDohBinary(domain, exclude = [], userAgent) {
886
+ const binPath = getDohBinaryPath();
887
+ const args = ["--domain", domain];
888
+ if (exclude.length > 0) {
889
+ args.push("--exclude", exclude.join(","));
890
+ }
891
+ if (userAgent) {
892
+ args.push("--user-agent", userAgent);
893
+ }
894
+ return new Promise((resolve3) => {
895
+ execFile(
896
+ binPath,
897
+ args,
898
+ { timeout: EXEC_TIMEOUT_MS, encoding: "utf-8" },
899
+ (error, stdout) => {
900
+ if (error) {
901
+ resolve3(null);
902
+ return;
903
+ }
904
+ try {
905
+ const result = JSON.parse(stdout);
906
+ if (result.code === 0 && result.data) {
907
+ resolve3(result.data);
908
+ } else {
909
+ resolve3(null);
910
+ }
911
+ } catch {
912
+ resolve3(null);
913
+ }
914
+ }
915
+ );
916
+ });
917
+ }
918
+ var DOH_CACHE_PATH = join2(homedir2(), ".okx", "doh-node-cache.json");
919
+ function readCache(hostname, cachePath = DOH_CACHE_PATH) {
920
+ try {
921
+ const raw = readFileSync(cachePath, "utf-8");
922
+ const file = JSON.parse(raw);
923
+ return file[hostname] ?? null;
924
+ } catch {
925
+ return null;
926
+ }
927
+ }
928
+ function writeCache(hostname, entry, cachePath = DOH_CACHE_PATH) {
929
+ try {
930
+ const dir = dirname(cachePath);
931
+ mkdirSync(dir, { recursive: true });
932
+ let file = {};
933
+ try {
934
+ file = JSON.parse(readFileSync(cachePath, "utf-8"));
935
+ } catch {
936
+ }
937
+ file[hostname] = entry;
938
+ const tmpPath = `${cachePath}.tmp`;
939
+ writeFileSync(tmpPath, JSON.stringify(file));
940
+ renameSync(tmpPath, cachePath);
941
+ } catch {
942
+ }
943
+ }
944
+ var FAILED_NODE_TTL_MS = 60 * 60 * 1e3;
945
+ function classifyAndCache(node, hostname, failedNodes, cachePath) {
946
+ if (!node) {
947
+ return { mode: null, node: null };
948
+ }
949
+ if (node.ip === hostname || node.host === hostname) {
950
+ writeCache(hostname, {
951
+ mode: "direct",
952
+ node: null,
953
+ failedNodes,
954
+ updatedAt: Date.now()
955
+ }, cachePath);
956
+ return { mode: "direct", node: null };
957
+ }
958
+ writeCache(hostname, {
959
+ mode: "proxy",
960
+ node,
961
+ failedNodes,
962
+ updatedAt: Date.now()
963
+ }, cachePath);
964
+ return { mode: "proxy", node };
965
+ }
966
+ function getActiveFailedNodes(nodes) {
967
+ if (!nodes || nodes.length === 0) return [];
968
+ const now = Date.now();
969
+ return nodes.filter((n) => now - n.failedAt < FAILED_NODE_TTL_MS);
970
+ }
971
+ function resolveDoh(hostname, cachePath) {
972
+ const entry = readCache(hostname, cachePath);
973
+ if (entry) {
974
+ if (entry.mode === "direct") {
975
+ return { mode: "direct", node: null };
976
+ }
977
+ if (entry.mode === "proxy" && entry.node) {
978
+ return { mode: "proxy", node: entry.node };
979
+ }
980
+ }
981
+ return { mode: null, node: null };
982
+ }
983
+ async function reResolveDoh(hostname, failedIp, userAgent, cachePath) {
984
+ const entry = readCache(hostname, cachePath);
985
+ const active = getActiveFailedNodes(entry?.failedNodes);
986
+ const now = Date.now();
987
+ const alreadyFailed = failedIp && active.some((n) => n.ip === failedIp);
988
+ const failedNodes = failedIp && !alreadyFailed ? [...active, { ip: failedIp, failedAt: now }] : active;
989
+ const excludeIps = failedNodes.map((n) => n.ip);
990
+ const node = await execDohBinary(hostname, excludeIps, userAgent);
991
+ return classifyAndCache(node, hostname, failedNodes, cachePath);
992
+ }
864
993
  function getNow() {
865
994
  return (/* @__PURE__ */ new Date()).toISOString();
866
995
  }
@@ -1079,6 +1208,14 @@ var OkxRestClient = class _OkxRestClient {
1079
1208
  config;
1080
1209
  rateLimiter;
1081
1210
  dispatcher;
1211
+ // DoH proxy state (lazy-resolved on first request)
1212
+ dohResolved = false;
1213
+ dohRetried = false;
1214
+ directUnverified = false;
1215
+ // The first direct connection has not yet been verified
1216
+ dohNode = null;
1217
+ dohAgent = null;
1218
+ dohBaseUrl = null;
1082
1219
  constructor(config) {
1083
1220
  this.config = config;
1084
1221
  this.rateLimiter = new RateLimiter(3e4, config.verbose);
@@ -1086,6 +1223,98 @@ var OkxRestClient = class _OkxRestClient {
1086
1223
  this.dispatcher = new ProxyAgent(config.proxyUrl);
1087
1224
  }
1088
1225
  }
1226
+ /**
1227
+ * Lazily resolve the DoH proxy node on the first request.
1228
+ * Uses cache-first strategy via the resolver.
1229
+ */
1230
+ ensureDoh() {
1231
+ if (this.dohResolved || this.dispatcher) return;
1232
+ this.dohResolved = true;
1233
+ try {
1234
+ const { hostname, protocol } = new URL(this.config.baseUrl);
1235
+ const result = resolveDoh(hostname);
1236
+ if (!result.mode) {
1237
+ this.directUnverified = true;
1238
+ if (this.config.verbose) {
1239
+ vlog("DoH: no cache, trying direct connection first");
1240
+ }
1241
+ return;
1242
+ }
1243
+ if (result.mode === "direct") {
1244
+ if (this.config.verbose) {
1245
+ vlog("DoH: mode=direct (overseas or cached), using direct connection");
1246
+ }
1247
+ return;
1248
+ }
1249
+ if (result.node) {
1250
+ this.applyDohNode(result.node, protocol);
1251
+ }
1252
+ } catch (err) {
1253
+ if (this.config.verbose) {
1254
+ const cause = err instanceof Error ? err.message : String(err);
1255
+ vlog(`DoH resolution failed, falling back to direct: ${cause}`);
1256
+ }
1257
+ }
1258
+ }
1259
+ /** Apply a DoH node: set up the custom Agent + base URL. */
1260
+ applyDohNode(node, protocol) {
1261
+ this.dohNode = node;
1262
+ this.dohBaseUrl = `${protocol}//${node.host}`;
1263
+ this.dohAgent = new Agent({
1264
+ connect: {
1265
+ lookup: (_hostname, options, callback) => {
1266
+ if (options?.all) {
1267
+ callback(null, [{ address: node.ip, family: 4 }]);
1268
+ } else {
1269
+ callback(null, node.ip, 4);
1270
+ }
1271
+ }
1272
+ }
1273
+ });
1274
+ if (this.config.verbose) {
1275
+ vlog(`DoH proxy active: \u2192 ${node.host} (${node.ip}), ttl=${node.ttl}s`);
1276
+ }
1277
+ }
1278
+ /**
1279
+ * Handle network failure: re-resolve with --exclude and retry once.
1280
+ * Returns true if retry should proceed, false if already retried.
1281
+ */
1282
+ async handleDohNetworkFailure() {
1283
+ if (this.dohRetried) return false;
1284
+ this.dohRetried = true;
1285
+ const failedIp = this.dohNode?.ip ?? "";
1286
+ const { hostname, protocol } = new URL(this.config.baseUrl);
1287
+ this.dohNode = null;
1288
+ this.dohAgent = null;
1289
+ this.dohBaseUrl = null;
1290
+ if (!failedIp) this.directUnverified = false;
1291
+ if (this.config.verbose) {
1292
+ vlog(failedIp ? `DoH: proxy node ${failedIp} failed, re-resolving with --exclude` : "DoH: direct connection failed, calling binary for DoH resolution");
1293
+ }
1294
+ try {
1295
+ const result = await reResolveDoh(hostname, failedIp, this.dohUserAgent);
1296
+ if (result.mode === "proxy" && result.node) {
1297
+ this.applyDohNode(result.node, protocol);
1298
+ this.dohRetried = false;
1299
+ return true;
1300
+ }
1301
+ } catch {
1302
+ }
1303
+ if (this.config.verbose) {
1304
+ vlog("DoH: re-resolution failed or switched to direct, retrying with direct connection");
1305
+ }
1306
+ return true;
1307
+ }
1308
+ get activeBaseUrl() {
1309
+ return this.dohNode ? this.dohBaseUrl : this.config.baseUrl;
1310
+ }
1311
+ get activeDispatcher() {
1312
+ return this.dispatcher ?? this.dohAgent ?? void 0;
1313
+ }
1314
+ /** User-Agent for DoH proxy requests: OKX/@okx_ai/{packageName}/{version} */
1315
+ get dohUserAgent() {
1316
+ return `OKX/@okx_ai/${this.config.userAgent ?? "unknown"}`;
1317
+ }
1089
1318
  logRequest(method, url, auth) {
1090
1319
  if (!this.config.verbose) return;
1091
1320
  vlog(`\u2192 ${method} ${url}`);
@@ -1255,13 +1484,17 @@ var OkxRestClient = class _OkxRestClient {
1255
1484
  * Security: validates Content-Type and enforces maxBytes limit.
1256
1485
  */
1257
1486
  async privatePostBinary(path42, body, opts) {
1487
+ this.ensureDoh();
1258
1488
  const maxBytes = opts?.maxBytes ?? _OkxRestClient.DEFAULT_MAX_BYTES;
1259
1489
  const expectedCT = opts?.expectedContentType ?? "application/octet-stream";
1260
1490
  const bodyJson = body ? JSON.stringify(body) : "";
1261
1491
  const endpoint = `POST ${path42}`;
1262
- this.logRequest("POST", `${this.config.baseUrl}${path42}`, "private");
1492
+ this.logRequest("POST", `${this.activeBaseUrl}${path42}`, "private");
1263
1493
  const reqConfig = { method: "POST", path: path42, auth: "private" };
1264
1494
  const headers = this.buildHeaders(reqConfig, path42, bodyJson, getNow());
1495
+ if (this.dohNode) {
1496
+ headers.set("User-Agent", this.dohUserAgent);
1497
+ }
1265
1498
  const t0 = Date.now();
1266
1499
  const response = await this.fetchBinary(path42, endpoint, headers, bodyJson, t0);
1267
1500
  const elapsed = Date.now() - t0;
@@ -1295,10 +1528,10 @@ var OkxRestClient = class _OkxRestClient {
1295
1528
  method: "POST",
1296
1529
  headers,
1297
1530
  body: bodyJson || void 0,
1298
- signal: AbortSignal.timeout(this.config.timeoutMs)
1531
+ signal: AbortSignal.timeout(this.config.timeoutMs),
1532
+ dispatcher: this.activeDispatcher
1299
1533
  };
1300
- if (this.dispatcher) fetchOptions.dispatcher = this.dispatcher;
1301
- return await fetch(`${this.config.baseUrl}${path42}`, fetchOptions);
1534
+ return await fetch(`${this.activeBaseUrl}${path42}`, fetchOptions);
1302
1535
  } catch (error) {
1303
1536
  if (this.config.verbose) {
1304
1537
  vlog(`\u2717 NetworkError after ${Date.now() - t0}ms: ${error instanceof Error ? error.message : String(error)}`);
@@ -1328,10 +1561,55 @@ var OkxRestClient = class _OkxRestClient {
1328
1561
  // ---------------------------------------------------------------------------
1329
1562
  // JSON request
1330
1563
  // ---------------------------------------------------------------------------
1564
+ /**
1565
+ * Handle network error during a JSON request: refresh DoH and maybe retry.
1566
+ * Always either returns a retry result or throws NetworkError.
1567
+ */
1568
+ async handleRequestNetworkError(error, reqConfig, requestPath, t0) {
1569
+ if (!this.dohRetried) {
1570
+ if (this.config.verbose) {
1571
+ const cause = error instanceof Error ? error.message : String(error);
1572
+ vlog(`Network failure, refreshing DoH: ${cause}`);
1573
+ }
1574
+ const shouldRetry = await this.handleDohNetworkFailure();
1575
+ if (shouldRetry && reqConfig.method === "GET") {
1576
+ return this.request(reqConfig);
1577
+ }
1578
+ }
1579
+ if (this.config.verbose) {
1580
+ const elapsed = Date.now() - t0;
1581
+ const cause = error instanceof Error ? error.message : String(error);
1582
+ vlog(`\u2717 NetworkError after ${elapsed}ms: ${cause}`);
1583
+ }
1584
+ throw new NetworkError(
1585
+ `Failed to call OKX endpoint ${reqConfig.method} ${requestPath}.`,
1586
+ `${reqConfig.method} ${requestPath}`,
1587
+ error
1588
+ );
1589
+ }
1590
+ /**
1591
+ * After a successful HTTP response on direct connection, cache mode=direct.
1592
+ * (Even if the business response is an error, the network path is valid.)
1593
+ */
1594
+ cacheDirectConnectionIfNeeded() {
1595
+ if (!this.directUnverified || this.dohNode) return;
1596
+ this.directUnverified = false;
1597
+ const { hostname } = new URL(this.config.baseUrl);
1598
+ writeCache(hostname, {
1599
+ mode: "direct",
1600
+ node: null,
1601
+ failedNodes: [],
1602
+ updatedAt: Date.now()
1603
+ });
1604
+ if (this.config.verbose) {
1605
+ vlog("DoH: direct connection succeeded, cached mode=direct");
1606
+ }
1607
+ }
1331
1608
  async request(reqConfig) {
1609
+ this.ensureDoh();
1332
1610
  const queryString = buildQueryString(reqConfig.query);
1333
1611
  const requestPath = queryString.length > 0 ? `${reqConfig.path}?${queryString}` : reqConfig.path;
1334
- const url = `${this.config.baseUrl}${requestPath}`;
1612
+ const url = `${this.activeBaseUrl}${requestPath}`;
1335
1613
  const bodyJson = reqConfig.body ? JSON.stringify(reqConfig.body) : "";
1336
1614
  const timestamp = getNow();
1337
1615
  this.logRequest(reqConfig.method, url, reqConfig.auth);
@@ -1339,6 +1617,9 @@ var OkxRestClient = class _OkxRestClient {
1339
1617
  await this.rateLimiter.consume(reqConfig.rateLimit);
1340
1618
  }
1341
1619
  const headers = this.buildHeaders(reqConfig, requestPath, bodyJson, timestamp);
1620
+ if (this.dohNode) {
1621
+ headers.set("User-Agent", this.dohUserAgent);
1622
+ }
1342
1623
  const t0 = Date.now();
1343
1624
  let response;
1344
1625
  try {
@@ -1346,27 +1627,17 @@ var OkxRestClient = class _OkxRestClient {
1346
1627
  method: reqConfig.method,
1347
1628
  headers,
1348
1629
  body: reqConfig.method === "POST" ? bodyJson : void 0,
1349
- signal: AbortSignal.timeout(this.config.timeoutMs)
1630
+ signal: AbortSignal.timeout(this.config.timeoutMs),
1631
+ dispatcher: this.activeDispatcher
1350
1632
  };
1351
- if (this.dispatcher) {
1352
- fetchOptions.dispatcher = this.dispatcher;
1353
- }
1354
1633
  response = await fetch(url, fetchOptions);
1355
1634
  } catch (error) {
1356
- if (this.config.verbose) {
1357
- const elapsed2 = Date.now() - t0;
1358
- const cause = error instanceof Error ? error.message : String(error);
1359
- vlog(`\u2717 NetworkError after ${elapsed2}ms: ${cause}`);
1360
- }
1361
- throw new NetworkError(
1362
- `Failed to call OKX endpoint ${reqConfig.method} ${requestPath}.`,
1363
- `${reqConfig.method} ${requestPath}`,
1364
- error
1365
- );
1635
+ return await this.handleRequestNetworkError(error, reqConfig, requestPath, t0);
1366
1636
  }
1367
1637
  const rawText = await response.text();
1368
1638
  const elapsed = Date.now() - t0;
1369
1639
  const traceId = extractTraceId(response.headers);
1640
+ this.cacheDirectConnectionIfNeeded();
1370
1641
  return this.processResponse(rawText, response, elapsed, traceId, reqConfig, requestPath);
1371
1642
  }
1372
1643
  };
@@ -2398,9 +2669,15 @@ function computeContracts(p) {
2398
2669
  return { contractsStr, conversionNote };
2399
2670
  }
2400
2671
  async function resolveQuoteCcySz(instId, sz, tgtCcy, instType, client, tdMode) {
2401
- if (tgtCcy !== "quote_ccy" && tgtCcy !== "margin") {
2672
+ if (tgtCcy === void 0 || tgtCcy === "base_ccy") {
2402
2673
  return { sz, tgtCcy, conversionNote: void 0 };
2403
2674
  }
2675
+ if (tgtCcy !== "quote_ccy" && tgtCcy !== "margin") {
2676
+ throw new ValidationError(
2677
+ `Unknown tgtCcy value "${tgtCcy}". Valid values: base_ccy, quote_ccy, margin.`,
2678
+ `Check the --tgtCcy flag. Use base_ccy (default, sz in contracts), quote_ccy (sz in USDT notional), or margin (sz in USDT margin cost).`
2679
+ );
2680
+ }
2404
2681
  const isMarginMode = tgtCcy === "margin";
2405
2682
  if (isMarginMode && !tdMode) {
2406
2683
  throw new Error(
@@ -3239,19 +3516,19 @@ function safeWriteFile(targetDir, fileName, data) {
3239
3516
  throw new Error(`Invalid file name: "${fileName}"`);
3240
3517
  }
3241
3518
  const resolvedDir = resolve(targetDir);
3242
- const filePath = join(resolvedDir, safeName);
3519
+ const filePath = join3(resolvedDir, safeName);
3243
3520
  const resolvedPath = resolve(filePath);
3244
3521
  if (!resolvedPath.startsWith(resolvedDir + sep)) {
3245
3522
  throw new Error(`Path traversal detected: "${fileName}" resolves outside target directory`);
3246
3523
  }
3247
- mkdirSync(resolvedDir, { recursive: true });
3524
+ mkdirSync2(resolvedDir, { recursive: true });
3248
3525
  const tmpPath = `${resolvedPath}.${randomUUID()}.tmp`;
3249
3526
  try {
3250
- writeFileSync(tmpPath, data);
3251
- renameSync(tmpPath, resolvedPath);
3527
+ writeFileSync2(tmpPath, data);
3528
+ renameSync2(tmpPath, resolvedPath);
3252
3529
  } catch (err) {
3253
3530
  try {
3254
- unlinkSync(tmpPath);
3531
+ unlinkSync2(tmpPath);
3255
3532
  } catch {
3256
3533
  }
3257
3534
  throw err;
@@ -3316,7 +3593,7 @@ async function extractSkillZip(zipPath, targetDir, limits) {
3316
3593
  const maxFiles = limits?.maxFiles ?? DEFAULT_MAX_FILES;
3317
3594
  const maxCompressionRatio = limits?.maxCompressionRatio ?? DEFAULT_MAX_COMPRESSION_RATIO;
3318
3595
  const resolvedTarget = resolve2(targetDir);
3319
- mkdirSync2(resolvedTarget, { recursive: true });
3596
+ mkdirSync3(resolvedTarget, { recursive: true });
3320
3597
  return new Promise((resolvePromise, reject) => {
3321
3598
  yauzl.open(zipPath, { lazyEntries: true }, (err, zipfile) => {
3322
3599
  if (err) return reject(err);
@@ -3339,7 +3616,7 @@ async function extractSkillZip(zipPath, targetDir, limits) {
3339
3616
  zipfile.close();
3340
3617
  return reject(streamErr);
3341
3618
  }
3342
- mkdirSync2(dirname(resolvedPath), { recursive: true });
3619
+ mkdirSync3(dirname2(resolvedPath), { recursive: true });
3343
3620
  const writeStream = createWriteStream(resolvedPath);
3344
3621
  readStream.pipe(writeStream);
3345
3622
  writeStream.on("close", () => zipfile.readEntry());
@@ -3355,11 +3632,11 @@ async function extractSkillZip(zipPath, targetDir, limits) {
3355
3632
  });
3356
3633
  }
3357
3634
  function readMetaJson(contentDir) {
3358
- const metaPath = join2(contentDir, "_meta.json");
3635
+ const metaPath = join4(contentDir, "_meta.json");
3359
3636
  if (!existsSync(metaPath)) {
3360
3637
  throw new Error(`_meta.json not found in ${contentDir}. Invalid skill package.`);
3361
3638
  }
3362
- const raw = readFileSync(metaPath, "utf-8");
3639
+ const raw = readFileSync2(metaPath, "utf-8");
3363
3640
  let parsed;
3364
3641
  try {
3365
3642
  parsed = JSON.parse(raw);
@@ -3381,26 +3658,26 @@ function readMetaJson(contentDir) {
3381
3658
  };
3382
3659
  }
3383
3660
  function validateSkillMdExists(contentDir) {
3384
- const skillMdPath = join2(contentDir, "SKILL.md");
3661
+ const skillMdPath = join4(contentDir, "SKILL.md");
3385
3662
  if (!existsSync(skillMdPath)) {
3386
3663
  throw new Error(`SKILL.md not found in ${contentDir}. Invalid skill package.`);
3387
3664
  }
3388
3665
  }
3389
- var DEFAULT_REGISTRY_PATH = join3(homedir(), ".okx", "skills", "registry.json");
3666
+ var DEFAULT_REGISTRY_PATH = join5(homedir3(), ".okx", "skills", "registry.json");
3390
3667
  function readRegistry(registryPath = DEFAULT_REGISTRY_PATH) {
3391
3668
  if (!existsSync2(registryPath)) {
3392
3669
  return { version: 1, skills: {} };
3393
3670
  }
3394
3671
  try {
3395
- const raw = readFileSync2(registryPath, "utf-8");
3672
+ const raw = readFileSync3(registryPath, "utf-8");
3396
3673
  return JSON.parse(raw);
3397
3674
  } catch {
3398
3675
  return { version: 1, skills: {} };
3399
3676
  }
3400
3677
  }
3401
3678
  function writeRegistry(registry, registryPath = DEFAULT_REGISTRY_PATH) {
3402
- mkdirSync3(dirname2(registryPath), { recursive: true });
3403
- writeFileSync2(registryPath, JSON.stringify(registry, null, 2) + "\n", "utf-8");
3679
+ mkdirSync4(dirname3(registryPath), { recursive: true });
3680
+ writeFileSync3(registryPath, JSON.stringify(registry, null, 2) + "\n", "utf-8");
3404
3681
  }
3405
3682
  function upsertSkillRecord(meta, registryPath = DEFAULT_REGISTRY_PATH) {
3406
3683
  const registry = readRegistry(registryPath);
@@ -7723,12 +8000,12 @@ function createToolRunner(client, config) {
7723
8000
  };
7724
8001
  }
7725
8002
  function configFilePath() {
7726
- return join4(homedir2(), ".okx", "config.toml");
8003
+ return join6(homedir4(), ".okx", "config.toml");
7727
8004
  }
7728
8005
  function readFullConfig() {
7729
8006
  const path42 = configFilePath();
7730
8007
  if (!existsSync3(path42)) return { profiles: {} };
7731
- const raw = readFileSync3(path42, "utf-8");
8008
+ const raw = readFileSync4(path42, "utf-8");
7732
8009
  try {
7733
8010
  return parse(raw);
7734
8011
  } catch (err) {
@@ -7756,11 +8033,11 @@ var CONFIG_HEADER = `# OKX Trade Kit Configuration
7756
8033
  `;
7757
8034
  function writeFullConfig(config) {
7758
8035
  const path42 = configFilePath();
7759
- const dir = dirname3(path42);
8036
+ const dir = dirname4(path42);
7760
8037
  if (!existsSync3(dir)) {
7761
- mkdirSync4(dir, { recursive: true });
8038
+ mkdirSync5(dir, { recursive: true });
7762
8039
  }
7763
- writeFileSync3(path42, CONFIG_HEADER + stringify(config), "utf-8");
8040
+ writeFileSync4(path42, CONFIG_HEADER + stringify(config), "utf-8");
7764
8041
  }
7765
8042
  function expandShorthand(moduleId) {
7766
8043
  if (moduleId === "all") return [...MODULES];
@@ -7874,21 +8151,21 @@ function loadConfig(cli) {
7874
8151
  verbose: cli.verbose ?? false
7875
8152
  };
7876
8153
  }
7877
- var CACHE_FILE = join5(homedir3(), ".okx", "update-check.json");
8154
+ var CACHE_FILE = join7(homedir5(), ".okx", "update-check.json");
7878
8155
  var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
7879
- function readCache() {
8156
+ function readCache2() {
7880
8157
  try {
7881
8158
  if (existsSync4(CACHE_FILE)) {
7882
- return JSON.parse(readFileSync4(CACHE_FILE, "utf-8"));
8159
+ return JSON.parse(readFileSync5(CACHE_FILE, "utf-8"));
7883
8160
  }
7884
8161
  } catch {
7885
8162
  }
7886
8163
  return {};
7887
8164
  }
7888
- function writeCache(cache) {
8165
+ function writeCache2(cache) {
7889
8166
  try {
7890
- mkdirSync5(join5(homedir3(), ".okx"), { recursive: true });
7891
- writeFileSync4(CACHE_FILE, JSON.stringify(cache, null, 2), "utf-8");
8167
+ mkdirSync6(join7(homedir5(), ".okx"), { recursive: true });
8168
+ writeFileSync5(CACHE_FILE, JSON.stringify(cache, null, 2), "utf-8");
7892
8169
  } catch {
7893
8170
  }
7894
8171
  }
@@ -7935,14 +8212,14 @@ async function fetchLatestVersion(packageName) {
7935
8212
  function refreshCacheInBackground(packageName) {
7936
8213
  fetchLatestVersion(packageName).then((latest) => {
7937
8214
  if (!latest) return;
7938
- const cache = readCache();
8215
+ const cache = readCache2();
7939
8216
  cache[packageName] = { latestVersion: latest, checkedAt: Date.now() };
7940
- writeCache(cache);
8217
+ writeCache2(cache);
7941
8218
  }).catch(() => {
7942
8219
  });
7943
8220
  }
7944
8221
  function checkForUpdates(packageName, currentVersion) {
7945
- const cache = readCache();
8222
+ const cache = readCache2();
7946
8223
  const entry = cache[packageName];
7947
8224
  if (entry && isNewerVersion(currentVersion, entry.latestVersion)) {
7948
8225
  process.stderr.write(
@@ -8797,7 +9074,7 @@ async function cmdDiagnoseMcp(options = {}) {
8797
9074
 
8798
9075
  // src/commands/diagnose.ts
8799
9076
  var CLI_VERSION = readCliVersion();
8800
- var GIT_HASH = true ? "0a5911f" : "dev";
9077
+ var GIT_HASH = true ? "caa6dae" : "dev";
8801
9078
  function maskKey2(key) {
8802
9079
  if (!key) return "(not set)";
8803
9080
  if (key.length <= 8) return "****";
@@ -9094,24 +9371,24 @@ async function runCliChecks(config, profile, outputPath) {
9094
9371
 
9095
9372
  // src/commands/upgrade.ts
9096
9373
  import { spawnSync as spawnSync2 } from "child_process";
9097
- import { readFileSync as readFileSync6, writeFileSync as writeFileSync6, mkdirSync as mkdirSync7 } from "fs";
9098
- import { dirname as dirname5, join as join7 } from "path";
9099
- import { homedir as homedir5 } from "os";
9374
+ import { readFileSync as readFileSync7, writeFileSync as writeFileSync7, mkdirSync as mkdirSync8 } from "fs";
9375
+ import { dirname as dirname6, join as join9 } from "path";
9376
+ import { homedir as homedir7 } from "os";
9100
9377
  var PACKAGES = ["@okx_ai/okx-trade-mcp", "@okx_ai/okx-trade-cli"];
9101
- var CACHE_FILE2 = join7(homedir5(), ".okx", "last_check");
9378
+ var CACHE_FILE2 = join9(homedir7(), ".okx", "last_check");
9102
9379
  var THROTTLE_MS = 12 * 60 * 60 * 1e3;
9103
- var NPM_BIN = join7(dirname5(process.execPath), process.platform === "win32" ? "npm.cmd" : "npm");
9380
+ var NPM_BIN = join9(dirname6(process.execPath), process.platform === "win32" ? "npm.cmd" : "npm");
9104
9381
  function readLastCheck() {
9105
9382
  try {
9106
- return parseInt(readFileSync6(CACHE_FILE2, "utf-8").trim(), 10) || 0;
9383
+ return parseInt(readFileSync7(CACHE_FILE2, "utf-8").trim(), 10) || 0;
9107
9384
  } catch {
9108
9385
  return 0;
9109
9386
  }
9110
9387
  }
9111
9388
  function writeLastCheck() {
9112
9389
  try {
9113
- mkdirSync7(join7(homedir5(), ".okx"), { recursive: true });
9114
- writeFileSync6(CACHE_FILE2, String(Math.floor(Date.now() / 1e3)), "utf-8");
9390
+ mkdirSync8(join9(homedir7(), ".okx"), { recursive: true });
9391
+ writeFileSync7(CACHE_FILE2, String(Math.floor(Date.now() / 1e3)), "utf-8");
9115
9392
  } catch {
9116
9393
  }
9117
9394
  }
@@ -12939,13 +13216,13 @@ async function cmdDcdQuoteAndBuy(run, opts) {
12939
13216
  }
12940
13217
 
12941
13218
  // src/commands/skill.ts
12942
- import { tmpdir, homedir as homedir7 } from "os";
12943
- import { join as join9, dirname as dirname6 } from "path";
12944
- import { mkdirSync as mkdirSync8, rmSync, existsSync as existsSync7, copyFileSync as copyFileSync2 } from "fs";
13219
+ import { tmpdir, homedir as homedir9 } from "os";
13220
+ import { join as join11, dirname as dirname7 } from "path";
13221
+ import { mkdirSync as mkdirSync9, rmSync, existsSync as existsSync7, copyFileSync as copyFileSync2 } from "fs";
12945
13222
  import { execFileSync as execFileSync2 } from "child_process";
12946
13223
  import { randomUUID as randomUUID2 } from "crypto";
12947
13224
  function resolveNpx() {
12948
- const sibling = join9(dirname6(process.execPath), "npx");
13225
+ const sibling = join11(dirname7(process.execPath), "npx");
12949
13226
  if (existsSync7(sibling)) return sibling;
12950
13227
  return "npx";
12951
13228
  }
@@ -12999,13 +13276,13 @@ async function cmdSkillCategories(run, json) {
12999
13276
  outputLine("");
13000
13277
  }
13001
13278
  async function cmdSkillAdd(name, config, json) {
13002
- const tmpBase = join9(tmpdir(), `okx-skill-${randomUUID2()}`);
13003
- mkdirSync8(tmpBase, { recursive: true });
13279
+ const tmpBase = join11(tmpdir(), `okx-skill-${randomUUID2()}`);
13280
+ mkdirSync9(tmpBase, { recursive: true });
13004
13281
  try {
13005
13282
  outputLine(`Downloading ${name}...`);
13006
13283
  const client = new OkxRestClient(config);
13007
13284
  const zipPath = await downloadSkillZip(client, name, tmpBase);
13008
- const contentDir = await extractSkillZip(zipPath, join9(tmpBase, "content"));
13285
+ const contentDir = await extractSkillZip(zipPath, join11(tmpBase, "content"));
13009
13286
  const meta = readMetaJson(contentDir);
13010
13287
  validateSkillMdExists(contentDir);
13011
13288
  outputLine("Installing to detected agents...");
@@ -13015,7 +13292,7 @@ async function cmdSkillAdd(name, config, json) {
13015
13292
  timeout: 6e4
13016
13293
  });
13017
13294
  } catch (e) {
13018
- const savedZip = join9(process.cwd(), `${name}.zip`);
13295
+ const savedZip = join11(process.cwd(), `${name}.zip`);
13019
13296
  try {
13020
13297
  copyFileSync2(zipPath, savedZip);
13021
13298
  } catch {
@@ -13054,7 +13331,7 @@ function cmdSkillRemove(name, json) {
13054
13331
  timeout: 6e4
13055
13332
  });
13056
13333
  } catch {
13057
- const agentsPath = join9(homedir7(), ".agents", "skills", name);
13334
+ const agentsPath = join11(homedir9(), ".agents", "skills", name);
13058
13335
  try {
13059
13336
  rmSync(agentsPath, { recursive: true, force: true });
13060
13337
  } catch {
@@ -13130,7 +13407,7 @@ function printSkillInstallResult(meta, json) {
13130
13407
  // src/index.ts
13131
13408
  var _require3 = createRequire3(import.meta.url);
13132
13409
  var CLI_VERSION2 = _require3("../package.json").version;
13133
- var GIT_HASH2 = true ? "0a5911f" : "dev";
13410
+ var GIT_HASH2 = true ? "caa6dae" : "dev";
13134
13411
  function handleConfigCommand(action, rest, json, lang, force) {
13135
13412
  if (action === "init") return cmdConfigInit(lang === "zh" ? "zh" : "en");
13136
13413
  if (action === "show") return cmdConfigShow(json);