@okx_ai/okx-trade-cli 1.3.8-beta.3 → 1.3.8-beta.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1003,6 +1003,7 @@ function writeCache(hostname, entry, cachePath = getDefaultCachePath()) {
1003
1003
  }
1004
1004
  }
1005
1005
  var FAILED_NODE_TTL_MS = 60 * 60 * 1e3;
1006
+ var MAX_PROXY_CACHE_AGE_MS = 60 * 60 * 1e3;
1006
1007
  function classifyAndCache(node, hostname, failedNodes, cachePath) {
1007
1008
  if (!node) {
1008
1009
  return { mode: null, node: null };
@@ -1036,6 +1037,10 @@ function resolvePilot(hostname, cachePath) {
1036
1037
  return { mode: "direct", node: null };
1037
1038
  }
1038
1039
  if (entry.mode === "proxy" && entry.node) {
1040
+ const effectiveTtlMs = Math.min(entry.node.ttl > 0 ? entry.node.ttl * 1e3 : MAX_PROXY_CACHE_AGE_MS, MAX_PROXY_CACHE_AGE_MS);
1041
+ if (Date.now() - entry.updatedAt > effectiveTtlMs) {
1042
+ return { mode: null, node: null };
1043
+ }
1039
1044
  return { mode: "proxy", node: entry.node };
1040
1045
  }
1041
1046
  }
@@ -1555,6 +1560,14 @@ var OKX_CODE_BEHAVIORS = {
1555
1560
  "51022": { retry: false, suggestion: "Instrument not available for trading." },
1556
1561
  "51027": { retry: false, suggestion: "Contract has expired." }
1557
1562
  };
1563
+ function hasOkxJsonCode(text) {
1564
+ try {
1565
+ const parsed = JSON.parse(text);
1566
+ return typeof parsed.code === "string" && parsed.code.length > 0;
1567
+ } catch {
1568
+ return false;
1569
+ }
1570
+ }
1558
1571
  function isDefined(value) {
1559
1572
  return value !== void 0 && value !== null;
1560
1573
  }
@@ -2032,6 +2045,12 @@ var OkxRestClient = class _OkxRestClient {
2032
2045
  const rawText = await response.text();
2033
2046
  const elapsed = Date.now() - t0;
2034
2047
  const traceId = extractTraceId(response.headers);
2048
+ if (this.pilot.isProxyActive && !response.ok && !hasOkxJsonCode(rawText) && !this.pilot.hasRetried) {
2049
+ const shouldRetry = await this.pilot.handleNetworkFailure();
2050
+ if (shouldRetry && (reqConfig.method === "GET" || reqConfig.retryOnNetworkError)) {
2051
+ return this.request(reqConfig);
2052
+ }
2053
+ }
2035
2054
  this.pilot.cacheDirectIfNeeded();
2036
2055
  return this.processResponse(rawText, response, elapsed, traceId, reqConfig, requestPath);
2037
2056
  }
@@ -2733,6 +2752,203 @@ var MODULE_DESCRIPTIONS = {
2733
2752
  upgrade: "Upgrade okx CLI and MCP server to the latest stable version",
2734
2753
  skill: SKILLS_MARKETPLACE_DESC
2735
2754
  };
2755
+ var AGGREGATE_ENDPOINT = "/api/v5/aigc/forward/balance-aggregate";
2756
+ var TRADING_ENDPOINT = "/api/v5/account/balance";
2757
+ var FUNDING_ENDPOINT = "/api/v5/asset/balances";
2758
+ var VALUATION_ENDPOINT = "/api/v5/asset/asset-valuation";
2759
+ function parseParams(rawArgs) {
2760
+ const args = asRecord(rawArgs);
2761
+ const accounts = readString(args, "accounts") ?? "trading,funding";
2762
+ return {
2763
+ ccy: readString(args, "ccy"),
2764
+ accounts,
2765
+ requestedAccounts: accounts.split(",").map((s) => s.trim().toLowerCase()).filter(Boolean),
2766
+ showValuation: readBoolean(args, "showValuation") ?? true,
2767
+ valuationCcy: readString(args, "valuationCcy") ?? "USDT",
2768
+ preferParallel: readBoolean(args, "preferParallel") ?? false
2769
+ };
2770
+ }
2771
+ function normalizeSubError(error) {
2772
+ if (!error || typeof error !== "object") {
2773
+ return void 0;
2774
+ }
2775
+ const e = error;
2776
+ return {
2777
+ code: e["code"] === void 0 || e["code"] === null ? "UNKNOWN" : String(e["code"]),
2778
+ msg: typeof e["msg"] === "string" ? e["msg"] : String(e["msg"] ?? "")
2779
+ };
2780
+ }
2781
+ function toIsoTimestamp(value, fallbackMs) {
2782
+ if (typeof value === "number" && Number.isFinite(value)) {
2783
+ return new Date(value).toISOString();
2784
+ }
2785
+ if (typeof value === "string" && value.trim() !== "") {
2786
+ return value;
2787
+ }
2788
+ return new Date(fallbackMs).toISOString();
2789
+ }
2790
+ function normalizeAggregate(agg, params, startTime) {
2791
+ const result = {};
2792
+ const trading = agg["trading"];
2793
+ if (trading) {
2794
+ result.trading = compactObject({
2795
+ available: trading["available"] ?? false,
2796
+ totalEq: trading["totalEq"],
2797
+ adjEq: trading["adjEq"],
2798
+ details: Array.isArray(trading["details"]) ? trading["details"] : [],
2799
+ error: normalizeSubError(trading["error"])
2800
+ });
2801
+ }
2802
+ const funding = agg["funding"];
2803
+ if (funding) {
2804
+ result.funding = compactObject({
2805
+ available: funding["available"] ?? false,
2806
+ details: Array.isArray(funding["details"]) ? funding["details"] : [],
2807
+ error: normalizeSubError(funding["error"])
2808
+ });
2809
+ }
2810
+ const valuation = agg["valuation"];
2811
+ if (valuation) {
2812
+ result.valuation = compactObject({
2813
+ available: valuation["available"] ?? false,
2814
+ valuationCcy: valuation["valuationCcy"] ?? params.valuationCcy,
2815
+ totalBal: valuation["totalBal"] ?? "0",
2816
+ details: Array.isArray(valuation["details"]) ? valuation["details"] : [],
2817
+ error: normalizeSubError(valuation["error"])
2818
+ });
2819
+ }
2820
+ const meta = agg["meta"] ?? {};
2821
+ result.meta = compactObject({
2822
+ requestedAt: toIsoTimestamp(meta["requestedAt"], startTime),
2823
+ elapsedMs: typeof meta["elapsedMs"] === "number" ? meta["elapsedMs"] : Date.now() - startTime,
2824
+ partialFailure: Boolean(meta["partialFailure"]),
2825
+ site: typeof meta["site"] === "string" ? meta["site"] : void 0,
2826
+ source: "aggregate"
2827
+ });
2828
+ return result;
2829
+ }
2830
+ async function queryAggregate(context, params, startTime) {
2831
+ const resp = await context.client.privateGet(
2832
+ AGGREGATE_ENDPOINT,
2833
+ compactObject({
2834
+ ccy: params.ccy,
2835
+ accounts: params.accounts,
2836
+ showValuation: params.showValuation,
2837
+ valuationCcy: params.valuationCcy
2838
+ }),
2839
+ privateRateLimit("account_get_balance_all", 5)
2840
+ );
2841
+ const data = resp.data;
2842
+ const agg = Array.isArray(data) ? data[0] : void 0;
2843
+ if (!agg || agg["trading"] === void 0 && agg["funding"] === void 0) {
2844
+ throw new OkxApiError("Aggregate endpoint returned no usable data", {
2845
+ code: "AGG_EMPTY",
2846
+ endpoint: `GET ${AGGREGATE_ENDPOINT}`
2847
+ });
2848
+ }
2849
+ return normalizeAggregate(agg, params, startTime);
2850
+ }
2851
+ var REQUESTED_KEYS = /* @__PURE__ */ new Set(["trading", "funding"]);
2852
+ function buildBalanceTasks(context, params) {
2853
+ const { ccy, requestedAccounts, showValuation, valuationCcy } = params;
2854
+ const tasks = [];
2855
+ if (requestedAccounts.includes("trading")) {
2856
+ tasks.push({
2857
+ key: "trading",
2858
+ run: () => context.client.privateGet(TRADING_ENDPOINT, compactObject({ ccy }), privateRateLimit("account_get_balance", 10)).then((resp) => resp.data)
2859
+ });
2860
+ }
2861
+ if (requestedAccounts.includes("funding")) {
2862
+ tasks.push({
2863
+ key: "funding",
2864
+ run: () => context.client.privateGet(FUNDING_ENDPOINT, compactObject({ ccy }), privateRateLimit("account_get_asset_balance", 6)).then((resp) => resp.data)
2865
+ });
2866
+ }
2867
+ if (showValuation) {
2868
+ tasks.push({
2869
+ key: "valuation",
2870
+ run: () => context.client.privateGet(VALUATION_ENDPOINT, { ccy: valuationCcy }, privateRateLimit("account_get_asset_valuation", 1)).then((resp) => resp.data)
2871
+ });
2872
+ }
2873
+ return tasks;
2874
+ }
2875
+ async function settleBalanceTasks(tasks) {
2876
+ return Promise.all(
2877
+ tasks.map(async (task) => {
2878
+ try {
2879
+ return { key: task.key, data: await task.run() };
2880
+ } catch (error) {
2881
+ return { key: task.key, error };
2882
+ }
2883
+ })
2884
+ );
2885
+ }
2886
+ function buildSuccessSection(key, data, valuationCcy) {
2887
+ const rows = Array.isArray(data) ? data : [];
2888
+ const first = rows[0];
2889
+ if (key === "funding") {
2890
+ return { available: true, details: rows };
2891
+ }
2892
+ if (key === "valuation") {
2893
+ return { available: true, valuationCcy, totalBal: first?.["totalBal"] ?? "0", details: rows };
2894
+ }
2895
+ return {
2896
+ available: true,
2897
+ totalEq: first?.["totalEq"] ?? "0",
2898
+ adjEq: first?.["adjEq"] ?? "0",
2899
+ details: first && Array.isArray(first["details"]) ? first["details"] : []
2900
+ };
2901
+ }
2902
+ function buildErrorSection(error) {
2903
+ return {
2904
+ available: false,
2905
+ error: {
2906
+ code: error instanceof OkxApiError ? error.code ?? "UNKNOWN" : "UNKNOWN",
2907
+ msg: error.message
2908
+ }
2909
+ };
2910
+ }
2911
+ async function queryParallel(context, params, startTime) {
2912
+ const tasks = buildBalanceTasks(context, params);
2913
+ const outcomes = await settleBalanceTasks(tasks);
2914
+ const authFailure = outcomes.find((o) => o.error instanceof AuthenticationError);
2915
+ if (authFailure?.error) {
2916
+ throw authFailure.error;
2917
+ }
2918
+ const result = {};
2919
+ for (const outcome of outcomes) {
2920
+ result[outcome.key] = outcome.error ? buildErrorSection(outcome.error) : buildSuccessSection(outcome.key, outcome.data, params.valuationCcy);
2921
+ }
2922
+ const requestedCount = tasks.filter((t) => REQUESTED_KEYS.has(t.key)).length;
2923
+ const requestedFailures = outcomes.filter((o) => o.error && REQUESTED_KEYS.has(o.key)).length;
2924
+ if (requestedCount > 0 && requestedFailures >= requestedCount) {
2925
+ throw new OkxApiError("Both balance queries failed", { code: "-30001" });
2926
+ }
2927
+ const site = typeof context.config?.site === "string" ? context.config.site : void 0;
2928
+ result.meta = compactObject({
2929
+ requestedAt: new Date(startTime).toISOString(),
2930
+ elapsedMs: Date.now() - startTime,
2931
+ partialFailure: requestedFailures > 0,
2932
+ site,
2933
+ source: "fallback"
2934
+ });
2935
+ return result;
2936
+ }
2937
+ async function buildBalanceAll(rawArgs, context) {
2938
+ const params = parseParams(rawArgs);
2939
+ const startTime = Date.now();
2940
+ if (params.preferParallel) {
2941
+ return queryParallel(context, params, startTime);
2942
+ }
2943
+ try {
2944
+ return await queryAggregate(context, params, startTime);
2945
+ } catch (error) {
2946
+ if (error instanceof AuthenticationError) {
2947
+ throw error;
2948
+ }
2949
+ return queryParallel(context, params, startTime);
2950
+ }
2951
+ }
2736
2952
  function registerAccountTools() {
2737
2953
  return [
2738
2954
  {
@@ -3312,6 +3528,39 @@ function registerAccountTools() {
3312
3528
  );
3313
3529
  return normalizeResponse(response);
3314
3530
  }
3531
+ },
3532
+ {
3533
+ name: "account_get_balance_all",
3534
+ title: "Get Aggregate Balance Snapshot",
3535
+ module: "account",
3536
+ description: "One-shot snapshot of all OKX assets: trading-account equity + funding-account balances + optional cross-account valuation. Use when the user asks for total assets / net worth / \u603B\u8D44\u4EA7 / all balances. Prefer this over calling account_get_balance + account_get_asset_balance separately. Returns {trading, funding, valuation, meta} with per-section error so partial failures don't block the rest.",
3537
+ isWrite: false,
3538
+ inputSchema: {
3539
+ type: "object",
3540
+ properties: {
3541
+ ccy: {
3542
+ type: "string",
3543
+ description: "Filter by currency, comma-separated (e.g. BTC,ETH). Omit for all. Applied to trading + funding queries only."
3544
+ },
3545
+ accounts: {
3546
+ type: "string",
3547
+ description: "Comma-separated accounts to query: 'trading','funding'. Default 'trading,funding'."
3548
+ },
3549
+ showValuation: {
3550
+ type: "boolean",
3551
+ description: "Include cross-account asset valuation. Default true."
3552
+ },
3553
+ valuationCcy: {
3554
+ type: "string",
3555
+ description: "Currency for valuation (e.g. USDT, BTC). Default USDT. Only effective when showValuation=true."
3556
+ },
3557
+ preferParallel: {
3558
+ type: "boolean",
3559
+ description: "Skip the server-side aggregate endpoint and query trading/funding/valuation directly in parallel. Default false. Use when you need the per-account valuation breakdown or a non-USD valuation currency."
3560
+ }
3561
+ }
3562
+ },
3563
+ handler: (rawArgs, context) => buildBalanceAll(rawArgs, context)
3315
3564
  }
3316
3565
  ];
3317
3566
  }
@@ -14491,7 +14740,7 @@ async function cmdDiagnoseMcp(options = {}) {
14491
14740
 
14492
14741
  // src/commands/diagnose.ts
14493
14742
  var CLI_VERSION = readCliVersion();
14494
- var GIT_HASH = true ? "52f6524c" : "dev";
14743
+ var GIT_HASH = true ? "884b3632" : "dev";
14495
14744
  function maskKey2(key) {
14496
14745
  if (!key) return "(not set)";
14497
14746
  if (key.length <= 8) return "****";
@@ -15101,6 +15350,11 @@ var CLI_REGISTRY = {
15101
15350
  usage: "okx account balance [<ccy>]",
15102
15351
  description: "Get trading account balance"
15103
15352
  },
15353
+ "balance-all": {
15354
+ toolName: "account_get_balance_all",
15355
+ usage: "okx account balance-all [<ccy>] [--accounts trading,funding] [--no-valuation] [--no-aggregate] [--valuationCcy <ccy>]",
15356
+ description: "One-shot snapshot: trading + funding (+ valuation) via server aggregate, auto-fallback to parallel"
15357
+ },
15104
15358
  "asset-balance": {
15105
15359
  toolName: "account_get_asset_balance",
15106
15360
  usage: "okx account asset-balance [--ccy <ccy>]",
@@ -16679,8 +16933,10 @@ var CLI_OPTIONS = {
16679
16933
  quoteCcy: { type: "string" },
16680
16934
  // account extras
16681
16935
  archive: { type: "boolean", default: false },
16682
- valuation: { type: "boolean", default: false },
16936
+ valuation: { type: "boolean" },
16937
+ aggregate: { type: "boolean" },
16683
16938
  valuationCcy: { type: "string" },
16939
+ accounts: { type: "string" },
16684
16940
  posMode: { type: "string" },
16685
16941
  ccy: { type: "string" },
16686
16942
  from: { type: "string" },
@@ -17556,6 +17812,88 @@ async function cmdAccountTransfer(run, opts) {
17556
17812
  const r = data?.[0];
17557
17813
  outputLine(`Transfer: ${r?.["transId"]} (${r?.["ccy"]} ${r?.["amt"]})`);
17558
17814
  }
17815
+ async function cmdAccountBalanceAll(run, ccy, opts) {
17816
+ const result = await run("account_get_balance_all", {
17817
+ ...ccy ? { ccy } : {},
17818
+ ...opts.accounts ? { accounts: opts.accounts } : {},
17819
+ showValuation: !opts.noValuation,
17820
+ ...opts.preferParallel ? { preferParallel: true } : {},
17821
+ ...opts.valuationCcy ? { valuationCcy: opts.valuationCcy } : {}
17822
+ });
17823
+ if (opts.json) return printJson(result);
17824
+ const meta = result.meta;
17825
+ if (meta?.partialFailure) {
17826
+ outputLine("[PARTIAL] Some balance queries failed \u2014 results may be incomplete.");
17827
+ outputLine("");
17828
+ }
17829
+ const trading = result.trading;
17830
+ if (trading) {
17831
+ if (trading.available) {
17832
+ outputLine("=== Trading Account ===");
17833
+ outputLine(`Total Equity: ${String(trading["totalEq"] ?? "0")}`);
17834
+ const details = trading.details ?? [];
17835
+ const rows = details.filter((d) => Number(d["eq"] ?? d["bal"] ?? 0) > 0).map((d) => ({
17836
+ ccy: d["ccy"],
17837
+ equity: d["eq"] ?? d["bal"],
17838
+ available: d["availEq"] ?? d["availBal"],
17839
+ frozen: d["frozenBal"]
17840
+ }));
17841
+ if (rows.length > 0) printTable(rows);
17842
+ } else {
17843
+ const error = trading.error;
17844
+ outputLine(`=== Trading Account === [ERROR: ${error?.msg ?? "unavailable"}]`);
17845
+ }
17846
+ outputLine("");
17847
+ }
17848
+ const funding = result.funding;
17849
+ if (funding) {
17850
+ if (funding.available) {
17851
+ outputLine("=== Funding Account ===");
17852
+ const details = funding.details ?? [];
17853
+ const rows = details.filter((d) => Number(d["bal"] ?? 0) > 0).map((d) => ({
17854
+ ccy: d["ccy"],
17855
+ balance: d["bal"],
17856
+ available: d["availBal"],
17857
+ frozen: d["frozenBal"]
17858
+ }));
17859
+ if (rows.length > 0) printTable(rows);
17860
+ else outputLine("(no non-zero balances)");
17861
+ } else {
17862
+ const error = funding.error;
17863
+ outputLine(`=== Funding Account === [ERROR: ${error?.msg ?? "unavailable"}]`);
17864
+ }
17865
+ outputLine("");
17866
+ }
17867
+ const valuation = result.valuation;
17868
+ if (valuation) {
17869
+ if (valuation.available) {
17870
+ outputLine(`=== Valuation (${String(valuation.valuationCcy ?? "USDT")}) ===`);
17871
+ printKv({ totalBal: valuation.totalBal });
17872
+ const details = valuation.details ?? [];
17873
+ if (details.length > 0) {
17874
+ const first = details[0];
17875
+ const breakdown = first?.["details"] ?? {};
17876
+ if (Object.keys(breakdown).length > 0) {
17877
+ printKv(breakdown);
17878
+ }
17879
+ }
17880
+ } else {
17881
+ const error = valuation.error;
17882
+ outputLine(`=== Valuation === [ERROR: ${error?.msg ?? "unavailable"}]`);
17883
+ }
17884
+ }
17885
+ const source = meta?.source;
17886
+ if (source) {
17887
+ const site = meta?.site;
17888
+ const elapsed = meta?.elapsedMs;
17889
+ let line = `[source: ${source}`;
17890
+ if (site) line += `, site: ${site}`;
17891
+ if (elapsed !== void 0) line += `, ${String(elapsed)}ms`;
17892
+ line += "]";
17893
+ outputLine("");
17894
+ outputLine(line);
17895
+ }
17896
+ }
17559
17897
  function readAuditLogs(logDir, days = 7) {
17560
17898
  const entries = [];
17561
17899
  const now = /* @__PURE__ */ new Date();
@@ -21205,7 +21543,7 @@ async function cmdEventCancel(run, opts) {
21205
21543
  // src/index.ts
21206
21544
  var _require3 = createRequire3(import.meta.url);
21207
21545
  var CLI_VERSION2 = _require3("../package.json").version;
21208
- var GIT_HASH2 = true ? "52f6524c" : "dev";
21546
+ var GIT_HASH2 = true ? "884b3632" : "dev";
21209
21547
  function handlePilotCommand(action, json, force, binaryPath) {
21210
21548
  if (action === "status") return cmdPilotStatus(json, binaryPath);
21211
21549
  if (action === "install") return cmdPilotInstall(json, binaryPath);
@@ -21392,6 +21730,7 @@ function handleAccountWriteCommand(run, action, v, json) {
21392
21730
  unknownSubcommand("account", action, [
21393
21731
  "audit",
21394
21732
  "balance",
21733
+ "balance-all",
21395
21734
  "asset-balance",
21396
21735
  "positions",
21397
21736
  "positions-history",
@@ -21410,6 +21749,14 @@ function handleAccountCommand(run, action, rest, v, json) {
21410
21749
  return cmdAccountAudit({ limit: v.limit, tool: v.tool, since: v.since, json });
21411
21750
  const limit = v.limit !== void 0 ? Number(v.limit) : void 0;
21412
21751
  if (action === "balance") return cmdAccountBalance(run, rest[0], json);
21752
+ if (action === "balance-all")
21753
+ return cmdAccountBalanceAll(run, v.ccy ?? rest[0], {
21754
+ accounts: v.accounts,
21755
+ noValuation: v.valuation === false,
21756
+ preferParallel: v.aggregate === false,
21757
+ valuationCcy: v.valuationCcy,
21758
+ json
21759
+ });
21413
21760
  if (action === "asset-balance") return cmdAccountAssetBalance(run, v.ccy, json, v.valuation, v.valuationCcy);
21414
21761
  if (action === "positions")
21415
21762
  return cmdAccountPositions(run, { instType: v.instType, instId: v.instId, json });