@okx_ai/okx-trade-cli 1.3.5-beta.1 → 1.3.6-beta.1

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,6 +4,7 @@
4
4
  import { createRequire as createRequire3 } from "module";
5
5
 
6
6
  // ../core/dist/index.js
7
+ import { EnvHttpProxyAgent, setGlobalDispatcher } from "undici";
7
8
  import { ProxyAgent } from "undici";
8
9
  import { isIP } from "net";
9
10
  import { lookup as dnsLookup } from "dns";
@@ -908,6 +909,7 @@ import { join as join10, dirname as dirname6 } from "path";
908
909
  import { readFileSync as readFileSync8, writeFileSync as writeFileSync7, mkdirSync as mkdirSync10, unlinkSync as unlinkSync6, existsSync as existsSync8 } from "fs";
909
910
  import { join as join12 } from "path";
910
911
  import { homedir as homedir10 } from "os";
912
+ setGlobalDispatcher(new EnvHttpProxyAgent());
911
913
  var EXEC_TIMEOUT_MS = 3e4;
912
914
  var ALLOWED_DOMAIN_RE = /^[\w.-]+\.okx\.com$/;
913
915
  var PILOT_BIN_DIR = join(homedir(), ".okx", "bin");
@@ -2506,6 +2508,7 @@ function registerIndicatorTools() {
2506
2508
  return [
2507
2509
  {
2508
2510
  name: "market_get_indicator",
2511
+ title: "Get Technical Indicator",
2509
2512
  module: "market",
2510
2513
  description: "Get technical indicator values for an instrument. Common indicators: ma, ema, rsi, macd, bb (Bollinger), kdj, supertrend, ahr999. Call market_list_indicators first to see all valid names. No credentials required.",
2511
2514
  isWrite: false,
@@ -2579,6 +2582,7 @@ function registerIndicatorTools() {
2579
2582
  },
2580
2583
  {
2581
2584
  name: "market_list_indicators",
2585
+ title: "List Technical Indicators",
2582
2586
  module: "market",
2583
2587
  description: "List all supported technical indicator names and descriptions. Call this before market_get_indicator to discover valid indicator names. No credentials required.",
2584
2588
  isWrite: false,
@@ -2665,6 +2669,7 @@ function registerAccountTools() {
2665
2669
  return [
2666
2670
  {
2667
2671
  name: "account_get_balance",
2672
+ title: "Get Trading Account Balance",
2668
2673
  module: "account",
2669
2674
  description: "Get account balance for trading account. Returns balances for all currencies or a specific one.",
2670
2675
  isWrite: false,
@@ -2689,9 +2694,11 @@ function registerAccountTools() {
2689
2694
  },
2690
2695
  {
2691
2696
  name: "account_transfer",
2697
+ title: "Transfer Between Accounts",
2692
2698
  module: "account",
2693
2699
  description: "Transfer funds between accounts (trading, funding, etc.). [CAUTION] Moves real funds.",
2694
2700
  isWrite: true,
2701
+ destructiveHint: false,
2695
2702
  inputSchema: {
2696
2703
  type: "object",
2697
2704
  properties: {
@@ -2746,6 +2753,7 @@ function registerAccountTools() {
2746
2753
  },
2747
2754
  {
2748
2755
  name: "account_get_max_size",
2756
+ title: "Get Max Order Size",
2749
2757
  module: "account",
2750
2758
  description: "Get max buy/sell order size for a SWAP/FUTURES instrument given current balance and leverage. Useful before placing orders.",
2751
2759
  isWrite: false,
@@ -2793,6 +2801,7 @@ function registerAccountTools() {
2793
2801
  },
2794
2802
  {
2795
2803
  name: "account_get_asset_balance",
2804
+ title: "Get Funding Account Balance",
2796
2805
  module: "account",
2797
2806
  description: "Get funding account balance (asset account). Different from account_get_balance which queries the trading account. Optionally includes total asset valuation across all account types (trading, funding, earn, etc.).",
2798
2807
  isWrite: false,
@@ -2856,6 +2865,7 @@ function registerAccountTools() {
2856
2865
  },
2857
2866
  {
2858
2867
  name: "account_get_bills",
2868
+ title: "Get Account Bills",
2859
2869
  module: "account",
2860
2870
  description: "Get account ledger: fees paid, funding charges, realized PnL, transfers, etc. Default 20 records (last 7 days), max 100.",
2861
2871
  isWrite: false,
@@ -2922,6 +2932,7 @@ function registerAccountTools() {
2922
2932
  },
2923
2933
  {
2924
2934
  name: "account_get_positions_history",
2935
+ title: "Get Closed Positions History",
2925
2936
  module: "account",
2926
2937
  description: "Get closed position history for SWAP or FUTURES. Default 20 records, max 100.",
2927
2938
  isWrite: false,
@@ -2983,6 +2994,7 @@ function registerAccountTools() {
2983
2994
  },
2984
2995
  {
2985
2996
  name: "account_get_trade_fee",
2997
+ title: "Get Trade Fee Tier",
2986
2998
  module: "account",
2987
2999
  description: "Get maker/taker fee rates for the account. Useful to understand your fee tier before trading.",
2988
3000
  isWrite: false,
@@ -3015,6 +3027,7 @@ function registerAccountTools() {
3015
3027
  },
3016
3028
  {
3017
3029
  name: "account_get_config",
3030
+ title: "Get Account Configuration",
3018
3031
  module: "account",
3019
3032
  description: "Get account configuration: position mode (net vs hedge), account level, auto-loan settings, etc. Note: `settleCcy` is the current settlement currency for USDS-margined contracts. `settleCcyList` is the list of available settlement currencies to choose from. These fields only apply to USDS-margined contracts and can be ignored for standard USDT/coin-margined trading.",
3020
3033
  isWrite: false,
@@ -3033,6 +3046,7 @@ function registerAccountTools() {
3033
3046
  },
3034
3047
  {
3035
3048
  name: "account_get_max_withdrawal",
3049
+ title: "Get Max Withdrawable Amount",
3036
3050
  module: "account",
3037
3051
  description: "Get maximum withdrawable amount for a currency from the trading account. Useful before initiating a transfer or withdrawal.",
3038
3052
  isWrite: false,
@@ -3057,6 +3071,7 @@ function registerAccountTools() {
3057
3071
  },
3058
3072
  {
3059
3073
  name: "account_get_max_avail_size",
3074
+ title: "Get Max Available Position Size",
3060
3075
  module: "account",
3061
3076
  description: "Get maximum available size for opening or reducing a position. Different from account_get_max_size which calculates new order size.",
3062
3077
  isWrite: false,
@@ -3101,6 +3116,7 @@ function registerAccountTools() {
3101
3116
  },
3102
3117
  {
3103
3118
  name: "account_get_positions",
3119
+ title: "Get Current Positions",
3104
3120
  module: "account",
3105
3121
  description: "Get current open positions across all instrument types (MARGIN, SWAP, FUTURES, OPTION, EVENTS). Use swap_get_positions for SWAP/FUTURES-only queries.",
3106
3122
  isWrite: false,
@@ -3136,6 +3152,7 @@ function registerAccountTools() {
3136
3152
  },
3137
3153
  {
3138
3154
  name: "account_get_bills_archive",
3155
+ title: "Get Archived Account Bills",
3139
3156
  module: "account",
3140
3157
  description: "Get archived account ledger (bills older than 7 days, up to 3 months). Use account_get_bills for recent 7-day records. Default 20 records, max 100.",
3141
3158
  isWrite: false,
@@ -3202,9 +3219,11 @@ function registerAccountTools() {
3202
3219
  },
3203
3220
  {
3204
3221
  name: "account_set_position_mode",
3222
+ title: "Set Position Mode",
3205
3223
  module: "account",
3206
3224
  description: "Switch between net position mode and long/short hedge mode. net: one position per instrument (default). long_short_mode: separate long and short positions. [CAUTION] Requires no open positions or pending orders.",
3207
3225
  isWrite: true,
3226
+ idempotentHint: true,
3208
3227
  inputSchema: {
3209
3228
  type: "object",
3210
3229
  properties: {
@@ -3344,6 +3363,8 @@ function registerAlgoTradeTools() {
3344
3363
  return [
3345
3364
  {
3346
3365
  name: "swap_place_algo_order",
3366
+ title: "Perpetual Futures Place Algo Order",
3367
+ destructiveHint: false,
3347
3368
  module: "swap",
3348
3369
  description: "Place a SWAP/FUTURES algo order. [CAUTION] Executes real trades. conditional: single TP, single SL, or both on one order. oco: TP+SL simultaneously - first trigger cancels the other. move_order_stop: trailing stop (callbackRatio or callbackSpread). trigger: pending order activated when triggerPx is hit (provide triggerPx + orderPx). chase: smart-follow best bid/ask. iceberg: split large order into child orders at intervals. twap: time-weighted average price order splitting.",
3349
3370
  isWrite: true,
@@ -3492,6 +3513,8 @@ function registerAlgoTradeTools() {
3492
3513
  },
3493
3514
  {
3494
3515
  name: "swap_place_move_stop_order",
3516
+ title: "Perpetual Futures Place Move-Stop Order",
3517
+ destructiveHint: false,
3495
3518
  module: "swap",
3496
3519
  description: "[DEPRECATED] Use swap_place_algo_order with ordType='move_order_stop' instead. Place a SWAP/FUTURES trailing stop order. [CAUTION] Executes real trades. Specify callbackRatio (e.g. '0.01'=1%) or callbackSpread (fixed price distance), not both. Optionally set activePx so tracking starts once market reaches that price.",
3497
3520
  isWrite: true,
@@ -3569,6 +3592,8 @@ function registerAlgoTradeTools() {
3569
3592
  },
3570
3593
  {
3571
3594
  name: "swap_cancel_algo_orders",
3595
+ title: "Perpetual Futures Cancel Algo Orders",
3596
+ idempotentHint: true,
3572
3597
  module: "swap",
3573
3598
  description: "Cancel one or more pending SWAP/FUTURES algo orders (TP/SL). Accepts a list of {algoId, instId} objects.",
3574
3599
  isWrite: true,
@@ -3612,6 +3637,7 @@ function registerAlgoTradeTools() {
3612
3637
  },
3613
3638
  {
3614
3639
  name: "swap_get_algo_orders",
3640
+ title: "Perpetual Futures Get Algo Orders",
3615
3641
  module: "swap",
3616
3642
  description: "Query pending or completed SWAP/FUTURES algo orders (TP/SL, OCO, trailing stop).",
3617
3643
  isWrite: false,
@@ -3704,6 +3730,8 @@ function registerFuturesAlgoTools() {
3704
3730
  return [
3705
3731
  {
3706
3732
  name: "futures_place_algo_order",
3733
+ title: "Futures Place Algo Order",
3734
+ destructiveHint: false,
3707
3735
  module: "futures",
3708
3736
  description: "Place a FUTURES delivery algo order. [CAUTION] Executes real trades. conditional: single TP, single SL, or both on one order. oco: TP+SL simultaneously - first trigger cancels the other. move_order_stop: trailing stop (callbackRatio or callbackSpread). trigger: pending order activated when triggerPx is hit (provide triggerPx + orderPx). chase: smart-follow best bid/ask. iceberg: split large order into child orders at intervals. twap: time-weighted average price order splitting.",
3709
3737
  isWrite: true,
@@ -3852,6 +3880,8 @@ function registerFuturesAlgoTools() {
3852
3880
  },
3853
3881
  {
3854
3882
  name: "futures_place_move_stop_order",
3883
+ title: "Futures Place Move-Stop Order",
3884
+ destructiveHint: false,
3855
3885
  module: "futures",
3856
3886
  description: "[DEPRECATED] Use futures_place_algo_order with ordType='move_order_stop' instead. Place a FUTURES delivery trailing stop order. [CAUTION] Executes real trades.",
3857
3887
  isWrite: true,
@@ -3929,6 +3959,8 @@ function registerFuturesAlgoTools() {
3929
3959
  },
3930
3960
  {
3931
3961
  name: "futures_amend_algo_order",
3962
+ title: "Futures Amend Algo Order",
3963
+ idempotentHint: true,
3932
3964
  module: "futures",
3933
3965
  description: "Amend a pending FUTURES delivery algo order (modify TP/SL prices or size). Also covers TP/SL orders attached when placing the main order - look up algoId via futures_get_algo_orders first.",
3934
3966
  isWrite: true,
@@ -3965,6 +3997,8 @@ function registerFuturesAlgoTools() {
3965
3997
  },
3966
3998
  {
3967
3999
  name: "futures_cancel_algo_orders",
4000
+ title: "Futures Cancel Algo Orders",
4001
+ idempotentHint: true,
3968
4002
  module: "futures",
3969
4003
  description: "Cancel one or more pending FUTURES delivery algo orders (TP/SL). Accepts a list of {algoId, instId} objects.",
3970
4004
  isWrite: true,
@@ -4002,6 +4036,7 @@ function registerFuturesAlgoTools() {
4002
4036
  },
4003
4037
  {
4004
4038
  name: "futures_get_algo_orders",
4039
+ title: "Futures Get Algo Orders",
4005
4040
  module: "futures",
4006
4041
  description: "Query pending or completed FUTURES delivery algo orders (TP/SL, OCO, trailing stop).",
4007
4042
  isWrite: false,
@@ -4123,6 +4158,7 @@ function registerAuditTools() {
4123
4158
  {
4124
4159
  name: "trade_get_history",
4125
4160
  module: "account",
4161
+ title: "Get Tool-Call Audit Log",
4126
4162
  description: "Query local audit log of tool calls made through this MCP server. Returns recent operations with timestamps, duration, params, and results. Use to review what trades or queries were executed in this session or past sessions.",
4127
4163
  isWrite: false,
4128
4164
  inputSchema: {
@@ -4383,6 +4419,7 @@ function registerSkillsTools() {
4383
4419
  {
4384
4420
  name: "skills_get_categories",
4385
4421
  module: "skills",
4422
+ title: "Skills Marketplace List Categories",
4386
4423
  description: "List all available skill categories in OKX Skills Marketplace. Use the returned categoryId as input to skills_search for category filtering. Do NOT use for searching or downloading skills - use skills_search or skills_download.",
4387
4424
  inputSchema: {
4388
4425
  type: "object",
@@ -4395,6 +4432,7 @@ function registerSkillsTools() {
4395
4432
  {
4396
4433
  name: "skills_search",
4397
4434
  module: "skills",
4435
+ title: "Skills Marketplace Search",
4398
4436
  description: "Search for skills in OKX Skills Marketplace by keyword or category. To get valid category IDs, call skills_get_categories first. Returns skill names for use with skills_download. Do NOT use for downloading - use skills_download.",
4399
4437
  inputSchema: {
4400
4438
  type: "object",
@@ -4424,6 +4462,7 @@ function registerSkillsTools() {
4424
4462
  {
4425
4463
  name: "skills_download",
4426
4464
  module: "skills",
4465
+ title: "Skills Marketplace Download",
4427
4466
  description: "Download a skill package from OKX Skills Marketplace to a local directory. Always call skills_search first to confirm the skill name exists. Downloads the latest approved version. NOTE: Downloads third-party developer content - does NOT install to agents. For full installation use CLI: okx skill add <name>. Use when the user wants to inspect or manually install a skill package.",
4428
4467
  inputSchema: {
4429
4468
  type: "object",
@@ -4446,6 +4485,8 @@ function registerSkillsTools() {
4446
4485
  additionalProperties: false
4447
4486
  },
4448
4487
  isWrite: true,
4488
+ destructiveHint: false,
4489
+ idempotentHint: true,
4449
4490
  handler: handleDownload
4450
4491
  }
4451
4492
  ];
@@ -4509,6 +4550,7 @@ function registerGridTools() {
4509
4550
  return [
4510
4551
  {
4511
4552
  name: "grid_get_orders",
4553
+ title: "Grid Bot List Orders",
4512
4554
  module: "bot.grid",
4513
4555
  description: "List grid bots. status='active' for running; 'history' for stopped.",
4514
4556
  isWrite: false,
@@ -4555,6 +4597,7 @@ function registerGridTools() {
4555
4597
  },
4556
4598
  {
4557
4599
  name: "grid_get_order_details",
4600
+ title: "Grid Bot Get Detail",
4558
4601
  module: "bot.grid",
4559
4602
  description: "Get grid bot detail by algo ID. Returns config, status, PnL, and position.",
4560
4603
  isWrite: false,
@@ -4585,6 +4628,7 @@ function registerGridTools() {
4585
4628
  },
4586
4629
  {
4587
4630
  name: "grid_get_sub_orders",
4631
+ title: "Grid Bot Get Sub-Orders",
4588
4632
  module: "bot.grid",
4589
4633
  description: "Query sub-orders (grid trades) of a bot. type='filled' for executed; 'live' for pending.",
4590
4634
  isWrite: false,
@@ -4629,9 +4673,11 @@ function registerGridTools() {
4629
4673
  },
4630
4674
  {
4631
4675
  name: "grid_create_order",
4676
+ title: "Grid Bot Create",
4632
4677
  module: "bot.grid",
4633
4678
  description: "Create grid bot (spot, USDT-margined, or coin-margined contract). [CAUTION] Locks funds. Spot: quoteSz|baseSz. Contract: direction+lever+sz.",
4634
4679
  isWrite: true,
4680
+ destructiveHint: false,
4635
4681
  inputSchema: {
4636
4682
  type: "object",
4637
4683
  properties: {
@@ -4704,7 +4750,9 @@ function registerGridTools() {
4704
4750
  },
4705
4751
  {
4706
4752
  name: "grid_amend_order",
4753
+ title: "Grid Bot Amend",
4707
4754
  module: "bot.grid",
4755
+ idempotentHint: true,
4708
4756
  description: "Amend a running grid bot. [CAUTION] Modifies a running bot. Use grid_list_orders to confirm the bot is running and obtain the algoId before calling.\nSupports two modes, which can be combined in a single call:\n\u2022 Price-range mode (maxPx+minPx+gridNum): change upper/lower price boundary and grid count. Contract grid: if new range requires more margin, pass topUpAmt; omit to auto-use the minimum required. Spot grid: topUpAmt is not supported.\n\u2022 TP/SL mode (instId + any of tpTriggerPx/slTriggerPx/tpRatio/slRatio): update take-profit and/or stop-loss. Pass '-1' to explicitly clear an existing TP or SL. tpTriggerPx/slTriggerPx are absolute prices; tpRatio/slRatio are profit ratios (e.g. '0.1' = 10%).\nWhen both sets of params are provided, both APIs are called sequentially.\nDo NOT use to create a new grid bot - use grid_create_order instead. Do NOT use to stop a grid bot - use grid_stop_order instead.",
4709
4757
  isWrite: true,
4710
4758
  inputSchema: {
@@ -4827,7 +4875,9 @@ function registerGridTools() {
4827
4875
  },
4828
4876
  {
4829
4877
  name: "grid_stop_order",
4878
+ title: "Grid Bot Stop",
4830
4879
  module: "bot.grid",
4880
+ idempotentHint: true,
4831
4881
  description: "[CAUTION] Stop a grid bot or close its remaining open position \u2014 real trades, irreversible. Workflow: (1) If the user has not specified which bot to stop, call grid_get_orders first and ask the user to confirm which bot before proceeding. (2) Call grid_get_order_details to check the current 'state' field. (3) If state='running' \u2192 call this tool: stopType='1' (default, clean exit) \u2014 spot grid sells all base assets back to quote; contract grid market-closes all positions. stopType='2' (keep assets) \u2014 spot grid keeps base assets as-is; contract grid cancels grid orders but leaves the position open. (4) If state='no_close_position' \u2192 call this tool with stopType='1' to close the remaining open position.",
4832
4882
  isWrite: true,
4833
4883
  inputSchema: {
@@ -4927,9 +4977,11 @@ function registerDcaTools() {
4927
4977
  return [
4928
4978
  {
4929
4979
  name: "dca_create_order",
4980
+ title: "Martingale Bot Create",
4930
4981
  module: "bot.dca",
4931
4982
  description: "Create a DCA (Martingale) bot. [CAUTION] Real trades. contract_dca requires lever; spot_dca must be long. If maxSafetyOrds>0: need safetyOrdAmt, pxSteps.",
4932
4983
  isWrite: true,
4984
+ destructiveHint: false,
4933
4985
  inputSchema: {
4934
4986
  type: "object",
4935
4987
  properties: {
@@ -5005,7 +5057,9 @@ function registerDcaTools() {
5005
5057
  },
5006
5058
  {
5007
5059
  name: "dca_stop_order",
5060
+ title: "Martingale Bot Stop",
5008
5061
  module: "bot.dca",
5062
+ idempotentHint: true,
5009
5063
  description: "[CAUTION] Stop a DCA bot or close its remaining open position \u2014 real trades, irreversible. Workflow: (1) If the user has not specified which bot to stop, call dca_get_orders first and ask the user to confirm which bot before proceeding. (2) Call dca_get_order_details to check the current 'state' field. (3) If state='running' \u2192 call this tool. (4) If state='no_close_position' \u2192 call this tool with stopType='1' to close the remaining open position. spot_dca requires stopType: 1=sell all tokens, 2=keep tokens.",
5010
5064
  isWrite: true,
5011
5065
  inputSchema: {
@@ -5038,6 +5092,7 @@ function registerDcaTools() {
5038
5092
  },
5039
5093
  {
5040
5094
  name: "dca_get_orders",
5095
+ title: "Martingale Bot List Orders",
5041
5096
  module: "bot.dca",
5042
5097
  description: "List DCA bots. Default: active (running). Use status=history for stopped.",
5043
5098
  isWrite: false,
@@ -5076,6 +5131,7 @@ function registerDcaTools() {
5076
5131
  },
5077
5132
  {
5078
5133
  name: "dca_get_order_details",
5134
+ title: "Martingale Bot Get Detail",
5079
5135
  module: "bot.dca",
5080
5136
  description: "Get DCA bot position details (avgPx, upl, liqPx, etc).",
5081
5137
  isWrite: false,
@@ -5101,6 +5157,7 @@ function registerDcaTools() {
5101
5157
  },
5102
5158
  {
5103
5159
  name: "dca_get_sub_orders",
5160
+ title: "Martingale Bot Get Sub-Orders",
5104
5161
  module: "bot.dca",
5105
5162
  description: "Get DCA cycles or orders in a cycle. Omit cycleId=cycle list; with cycleId=orders.",
5106
5163
  isWrite: false,
@@ -5160,8 +5217,9 @@ function registerEarnTools() {
5160
5217
  return [
5161
5218
  {
5162
5219
  name: "earn_get_savings_balance",
5220
+ title: "Get Simple Earn Balance",
5163
5221
  module: "earn.savings",
5164
- description: "Get Simple Earn (savings/flexible earn) balance. Returns current holdings for all currencies or a specific one. To show market rates alongside balance (\u5E02\u573A\u5747\u5229\u7387), call earn_get_lending_rate_history. earn_get_lending_rate_history also returns fixed-term (\u5B9A\u671F) product offers, so one call gives a complete view of both flexible and fixed options. Do NOT use for fixed-term (\u5B9A\u671F) order queries - use earn_get_fixed_order_list instead.",
5222
+ description: "Get Simple Earn (savings/flexible earn) balance. Returns current holdings for all currencies or a specific one. To show market rates alongside balance, call earn_get_lending_rate_history. To browse available fixed-term products with quota info, use earn_get_fixed_earn_products. Do NOT use for fixed-term order queries \u2014 use earn_get_fixed_order_list instead.",
5165
5223
  isWrite: false,
5166
5224
  inputSchema: {
5167
5225
  type: "object",
@@ -5184,6 +5242,7 @@ function registerEarnTools() {
5184
5242
  },
5185
5243
  {
5186
5244
  name: "earn_get_fixed_order_list",
5245
+ title: "Get Fixed-Term Earn Orders",
5187
5246
  module: "earn.savings",
5188
5247
  description: "Get Simple Earn Fixed (\u5B9A\u671F\u8D5A\u5E01) lending order list. Returns orders sorted by creation time descending. Use this to check status of fixed-term lending orders (pending/earning/expired/settled/cancelled). Do NOT use for flexible earn balance - use earn_get_savings_balance instead. If the result is empty, do NOT display any fixed-term section in the output.",
5189
5248
  isWrite: false,
@@ -5221,9 +5280,11 @@ function registerEarnTools() {
5221
5280
  },
5222
5281
  {
5223
5282
  name: "earn_savings_purchase",
5283
+ title: "Subscribe Simple Earn",
5224
5284
  module: "earn.savings",
5225
5285
  description: "Purchase Simple Earn (savings/flexible earn). [CAUTION] Moves real funds into earn product.",
5226
5286
  isWrite: true,
5287
+ destructiveHint: false,
5227
5288
  inputSchema: {
5228
5289
  type: "object",
5229
5290
  properties: {
@@ -5259,9 +5320,11 @@ function registerEarnTools() {
5259
5320
  },
5260
5321
  {
5261
5322
  name: "earn_savings_redeem",
5323
+ title: "Redeem Simple Earn",
5262
5324
  module: "earn.savings",
5263
5325
  description: "Redeem Simple Earn (savings/flexible earn). [CAUTION] Withdraws funds from earn product.",
5264
5326
  isWrite: true,
5327
+ destructiveHint: false,
5265
5328
  inputSchema: {
5266
5329
  type: "object",
5267
5330
  properties: {
@@ -5292,9 +5355,11 @@ function registerEarnTools() {
5292
5355
  },
5293
5356
  {
5294
5357
  name: "earn_set_lending_rate",
5358
+ title: "Set Lending Rate",
5295
5359
  module: "earn.savings",
5296
5360
  description: "Set lending rate for Simple Earn. [CAUTION] Changes your lending rate preference.",
5297
5361
  isWrite: true,
5362
+ idempotentHint: true,
5298
5363
  inputSchema: {
5299
5364
  type: "object",
5300
5365
  properties: {
@@ -5324,6 +5389,7 @@ function registerEarnTools() {
5324
5389
  },
5325
5390
  {
5326
5391
  name: "earn_get_lending_history",
5392
+ title: "Get Lending History",
5327
5393
  module: "earn.savings",
5328
5394
  description: "Get personal lending records for Simple Earn (your own lending history). NOT for market rate queries. Returns your lending records with amount, rate, and earnings data.",
5329
5395
  isWrite: false,
@@ -5365,9 +5431,11 @@ function registerEarnTools() {
5365
5431
  },
5366
5432
  {
5367
5433
  name: "earn_fixed_purchase",
5434
+ title: "Subscribe Fixed-Term Earn",
5368
5435
  module: "earn.savings",
5369
- description: "Purchase Simple Earn Fixed (\u5B9A\u671F) product, two-step flow. First call (confirm omitted or false): returns purchase preview with product details and risk warning. Preview offer fields: lendQuota = remaining quota (\u5269\u4F59\u989D\u5EA6), soldOut = whether product is sold out (lendQuota is 0). YOU MUST display the 'warning' field from the preview response to the user VERBATIM before asking for confirmation - do NOT omit or summarize it. Second call (confirm=true): executes the purchase. Only proceed after the user explicitly confirms. IMPORTANT: Orders in 'pending' (\u5339\u914D\u4E2D) state can still be cancelled via earn_fixed_redeem; once the status changes to 'earning' (\u8D5A\u5E01\u4E2D), funds are LOCKED until maturity - no early redemption allowed.",
5436
+ description: "Purchase Simple Earn Fixed (\u5B9A\u671F) product, two-step flow. First call (confirm omitted or false): returns purchase preview with product details and risk warning. Preview offer fields: lendQuota = remaining quota, soldOut = whether product is sold out (lendQuota is 0). YOU MUST display the 'warning' field from the preview response to the user VERBATIM before asking for confirmation \u2014 do NOT omit or summarize it. Second call (confirm=true): executes the purchase. Only proceed after the user explicitly confirms. IMPORTANT: Orders in 'pending' (\u5339\u914D\u4E2D) state can still be cancelled via earn_fixed_redeem; once the status changes to 'earning' (\u8D5A\u5E01\u4E2D), funds are LOCKED until maturity - no early redemption allowed.",
5370
5437
  isWrite: true,
5438
+ destructiveHint: false,
5371
5439
  inputSchema: {
5372
5440
  type: "object",
5373
5441
  properties: {
@@ -5439,9 +5507,11 @@ function registerEarnTools() {
5439
5507
  },
5440
5508
  {
5441
5509
  name: "earn_fixed_redeem",
5510
+ title: "Redeem Fixed-Term Earn",
5442
5511
  module: "earn.savings",
5443
5512
  description: "Redeem Simple Earn Fixed (\u5B9A\u671F\u8D5A\u5E01) order. [CAUTION] Redeems a fixed-term lending order. Always redeems the full order amount. Only orders in 'pending' (\u5339\u914D\u4E2D) state can be redeemed - orders in 'earning' state are locked until maturity and cannot be redeemed early. Do NOT use for flexible earn redemption - use earn_savings_redeem instead.",
5444
5513
  isWrite: true,
5514
+ destructiveHint: false,
5445
5515
  inputSchema: {
5446
5516
  type: "object",
5447
5517
  properties: {
@@ -5467,8 +5537,9 @@ function registerEarnTools() {
5467
5537
  },
5468
5538
  {
5469
5539
  name: "earn_get_lending_rate_history",
5540
+ title: "Get Lending Rates & Offers",
5470
5541
  module: "earn.savings",
5471
- description: "Query Simple Earn lending rates and fixed-term offers. Use this tool when the user asks about Simple Earn products, current or historical lending rates, or when displaying savings balance with market rate context (\u5E02\u573A\u5747\u5229\u7387). Returns lending rate history (lendingRate field, newest-first) AND available fixed-term (\u5B9A\u671F) offers with APR, term, min amount, and quota - one call gives a complete view of both flexible and fixed options. In fixedOffers: lendQuota = remaining quota (\u5269\u4F59\u989D\u5EA6), soldOut = whether product is sold out (lendQuota is 0). To get current flexible APY: use limit=1 and read lendingRate.",
5542
+ description: "Query Simple Earn lending rates and fixed-term offers. Use this tool when the user asks about Simple Earn products, current or historical lending rates, or when displaying savings balance with market rate context (\u5E02\u573A\u5747\u5229\u7387). Returns lending rate history (lendingRate field, newest-first) AND available fixed-term (\u5B9A\u671F) offers with APR, term, min amount, and quota \u2014 one call gives a complete view of both flexible and fixed options. In fixedOffers: lendQuota = remaining quota, soldOut = whether product is sold out (lendQuota is 0). For dedicated fixed-term product queries, use earn_get_fixed_earn_products. To get current flexible APY: use limit=1 and read lendingRate.",
5472
5543
  isWrite: false,
5473
5544
  inputSchema: {
5474
5545
  type: "object",
@@ -5527,6 +5598,37 @@ function registerEarnTools() {
5527
5598
  fixedOffers
5528
5599
  };
5529
5600
  }
5601
+ },
5602
+ {
5603
+ name: "earn_get_fixed_earn_products",
5604
+ title: "Get Fixed-Term Earn Products",
5605
+ module: "earn.savings",
5606
+ description: "Query available Simple Earn Fixed-term products. Returns fixed-term offers with APR, term, min investment, and remaining quota. Use to check available products and quota before purchasing. Do NOT use for querying your own orders \u2014 use earn_get_fixed_order_list instead. Do NOT use just for current flexible APY -- use earn_get_lending_rate_history with limit=1 instead.",
5607
+ isWrite: false,
5608
+ inputSchema: {
5609
+ type: "object",
5610
+ properties: {
5611
+ ccy: {
5612
+ type: "string",
5613
+ description: "e.g. USDT. Omit for all currencies."
5614
+ }
5615
+ }
5616
+ },
5617
+ handler: async (rawArgs, context) => {
5618
+ const args = asRecord(rawArgs);
5619
+ const response = await context.client.privateGet(
5620
+ "/api/v5/finance/simple-earn-fixed/offers",
5621
+ compactObject({ ccy: readString(args, "ccy") }),
5622
+ privateRateLimit("earn_get_fixed_earn_products", 2)
5623
+ );
5624
+ const result = normalizeResponse(response);
5625
+ const allOffers = Array.isArray(result["data"]) ? result["data"] : [];
5626
+ result["data"] = allOffers.map(({ borrowingOrderQuota: _, ...rest }) => ({
5627
+ ...rest,
5628
+ soldOut: rest["lendQuota"] === "0"
5629
+ }));
5630
+ return result;
5631
+ }
5530
5632
  }
5531
5633
  ];
5532
5634
  }
@@ -5537,6 +5639,7 @@ function registerOnchainEarnTools() {
5537
5639
  // -------------------------------------------------------------------------
5538
5640
  {
5539
5641
  name: "onchain_earn_get_offers",
5642
+ title: "On-chain Earn List Offers",
5540
5643
  module: "earn.onchain",
5541
5644
  description: "List staking/DeFi products with APY, terms, and limits. Always show protocol name (protocol field) and earnings currency (earningData[].ccy) when presenting results.",
5542
5645
  isWrite: false,
@@ -5576,9 +5679,11 @@ function registerOnchainEarnTools() {
5576
5679
  // -------------------------------------------------------------------------
5577
5680
  {
5578
5681
  name: "onchain_earn_purchase",
5682
+ title: "On-chain Earn Subscribe",
5579
5683
  module: "earn.onchain",
5580
5684
  description: "Invest in a staking/DeFi product. [CAUTION] Moves real funds.",
5581
5685
  isWrite: true,
5686
+ destructiveHint: false,
5582
5687
  inputSchema: {
5583
5688
  type: "object",
5584
5689
  properties: {
@@ -5629,9 +5734,11 @@ function registerOnchainEarnTools() {
5629
5734
  // -------------------------------------------------------------------------
5630
5735
  {
5631
5736
  name: "onchain_earn_redeem",
5737
+ title: "On-chain Earn Redeem",
5632
5738
  module: "earn.onchain",
5633
5739
  description: "Redeem a staking/DeFi investment. [CAUTION] Some products have lock periods, early redemption may incur penalties.",
5634
5740
  isWrite: true,
5741
+ destructiveHint: false,
5635
5742
  inputSchema: {
5636
5743
  type: "object",
5637
5744
  properties: {
@@ -5669,9 +5776,11 @@ function registerOnchainEarnTools() {
5669
5776
  // -------------------------------------------------------------------------
5670
5777
  {
5671
5778
  name: "onchain_earn_cancel",
5779
+ title: "On-chain Earn Cancel Order",
5672
5780
  module: "earn.onchain",
5673
5781
  description: "Cancel a pending staking/DeFi purchase order. [CAUTION]",
5674
5782
  isWrite: true,
5783
+ idempotentHint: true,
5675
5784
  inputSchema: {
5676
5785
  type: "object",
5677
5786
  properties: {
@@ -5704,6 +5813,7 @@ function registerOnchainEarnTools() {
5704
5813
  // -------------------------------------------------------------------------
5705
5814
  {
5706
5815
  name: "onchain_earn_get_active_orders",
5816
+ title: "On-chain Earn Active Orders",
5707
5817
  module: "earn.onchain",
5708
5818
  description: "List current active staking/DeFi investments.",
5709
5819
  isWrite: false,
@@ -5748,6 +5858,7 @@ function registerOnchainEarnTools() {
5748
5858
  // -------------------------------------------------------------------------
5749
5859
  {
5750
5860
  name: "onchain_earn_get_order_history",
5861
+ title: "On-chain Earn Order History",
5751
5862
  module: "earn.onchain",
5752
5863
  description: "List past staking/DeFi orders including redeemed ones.",
5753
5864
  isWrite: false,
@@ -5846,6 +5957,7 @@ function registerDcdTools() {
5846
5957
  return [
5847
5958
  {
5848
5959
  name: "dcd_get_currency_pairs",
5960
+ title: "Dual Investment List Currency Pairs",
5849
5961
  module: "earn.dcd",
5850
5962
  description: "Get available DCD currency pairs.",
5851
5963
  isWrite: false,
@@ -5863,6 +5975,7 @@ function registerDcdTools() {
5863
5975
  },
5864
5976
  {
5865
5977
  name: "dcd_get_products",
5978
+ title: "Dual Investment List Products",
5866
5979
  module: "earn.dcd",
5867
5980
  description: "Get DCD products with yield and quota info. Yields in response are decimal fractions, not percentages.",
5868
5981
  isWrite: false,
@@ -5893,6 +6006,7 @@ function registerDcdTools() {
5893
6006
  },
5894
6007
  {
5895
6008
  name: "dcd_get_order_state",
6009
+ title: "Dual Investment Get Order State",
5896
6010
  module: "earn.dcd",
5897
6011
  description: "Check DCD order state after subscription (returns ordId + state only). For full order details (productId, strike, yield, settlement info), use dcd_get_orders instead.",
5898
6012
  isWrite: false,
@@ -5917,6 +6031,7 @@ function registerDcdTools() {
5917
6031
  },
5918
6032
  {
5919
6033
  name: "dcd_get_orders",
6034
+ title: "Dual Investment Get Order History",
5920
6035
  module: "earn.dcd",
5921
6036
  description: "Get DCD order history. Yields in response are decimal fractions, not percentages.",
5922
6037
  isWrite: false,
@@ -5961,9 +6076,11 @@ function registerDcdTools() {
5961
6076
  },
5962
6077
  {
5963
6078
  name: "dcd_subscribe",
6079
+ title: "Dual Investment Subscribe",
5964
6080
  module: "earn.dcd",
5965
6081
  description: "Subscribe to a DCD product: get quote and execute atomically. Confirm product, amount, and currency with user before calling. Optional minAnnualizedYield rejects the order if quote yield falls below threshold. Returns order result with quote snapshot (minAnnualizedYield is in percent; response yields are decimal fractions).",
5966
6082
  isWrite: true,
6083
+ destructiveHint: false,
5967
6084
  inputSchema: {
5968
6085
  type: "object",
5969
6086
  properties: {
@@ -6046,9 +6163,11 @@ function registerDcdTools() {
6046
6163
  },
6047
6164
  {
6048
6165
  name: "dcd_redeem",
6166
+ title: "Dual Investment Redeem",
6049
6167
  module: "earn.dcd",
6050
6168
  description: "Early redemption of a DCD order, two-step flow. First call (no quoteId): returns redemption quote for user confirmation. Second call (with quoteId): executes redemption. If the quote expired, auto-refreshes and executes; response includes autoRefreshedQuote: true.",
6051
6169
  isWrite: true,
6170
+ destructiveHint: false,
6052
6171
  inputSchema: {
6053
6172
  type: "object",
6054
6173
  properties: {
@@ -6123,9 +6242,11 @@ function registerAutoEarnTools() {
6123
6242
  return [
6124
6243
  {
6125
6244
  name: "earn_auto_set",
6245
+ title: "Set Auto-Earn Configuration",
6126
6246
  module: "earn.autoearn",
6127
6247
  description: "Enable or disable auto-earn for a currency. earnType='0' for auto-lend+stake (most currencies); earnType='1' for USDG earn (USDG, BUIDL). Use account_get_balance first: if autoLendStatus or autoStakingStatus != 'unsupported', use earnType='0'; for USDG/BUIDL use earnType='1'. [CAUTION] Cannot disable within 24h of enabling.",
6128
6248
  isWrite: true,
6249
+ idempotentHint: true,
6129
6250
  inputSchema: {
6130
6251
  type: "object",
6131
6252
  properties: {
@@ -6175,6 +6296,7 @@ function registerFlashEarnTools() {
6175
6296
  return [
6176
6297
  {
6177
6298
  name: "earn_get_flash_earn_projects",
6299
+ title: "Flash Earn List Projects",
6178
6300
  module: "earn.flash",
6179
6301
  description: "Get Flash Earn projects. Use this to browse upcoming or in-progress Flash Earn opportunities. Do NOT use for purchase or redeem actions - Flash Earn is query-only in this module.",
6180
6302
  isWrite: false,
@@ -6597,6 +6719,7 @@ function registerEventContractTools() {
6597
6719
  // -----------------------------------------------------------------------
6598
6720
  {
6599
6721
  name: "event_browse",
6722
+ title: "Event Contracts Browse Active",
6600
6723
  module: "event",
6601
6724
  description: "Browse currently active (in-progress) event contracts. Call when user asks what event contracts are available to trade. Returns only in-progress contracts (floorStrike set). If a live quote field px is present, it is the event contract price (0.01-0.99), not the underlying asset price; it reflects the market-implied probability when actively trading. Grouped by settlement type and underlying. Do NOT use for querying contracts within a specific series - use event_get_markets with seriesId instead.",
6602
6725
  isWrite: false,
@@ -6637,6 +6760,7 @@ function registerEventContractTools() {
6637
6760
  },
6638
6761
  {
6639
6762
  name: "event_get_series",
6763
+ title: "Event Contracts List Series",
6640
6764
  module: "event",
6641
6765
  description: "List event contract series. Returns all available series with settlement type and underlying. Use event_browse to see currently active contracts.",
6642
6766
  isWrite: false,
@@ -6661,6 +6785,7 @@ function registerEventContractTools() {
6661
6785
  },
6662
6786
  {
6663
6787
  name: "event_get_events",
6788
+ title: "Event Contracts List Events",
6664
6789
  module: "event",
6665
6790
  description: "List expiry periods within a series. state: preopen|live|settling|expired. expTime is pre-formatted UTC+8.",
6666
6791
  isWrite: false,
@@ -6716,6 +6841,7 @@ function registerEventContractTools() {
6716
6841
  },
6717
6842
  {
6718
6843
  name: "event_get_markets",
6844
+ title: "Event Contracts List Markets",
6719
6845
  module: "event",
6720
6846
  description: "List tradeable contracts within a series. state=live for active contracts, state=expired for settlement results. floorStrike=strike price; px (when present) is the event contract price (0.01-0.99), not the underlying asset price - reflects the market-implied probability when actively trading; outcome pre-translated (pending/YES/NO/UP/DOWN); timestamps UTC+8. Do NOT use for discovering what series are available across all underlyings - use event_browse instead.",
6721
6847
  isWrite: false,
@@ -6797,6 +6923,7 @@ function registerEventContractTools() {
6797
6923
  },
6798
6924
  {
6799
6925
  name: "event_get_orders",
6926
+ title: "Event Contracts Get Orders",
6800
6927
  module: "event",
6801
6928
  description: "Query event contract orders (open, 7d history, or 3-month archive). outcome pre-translated (YES/NO/UP/DOWN). Do NOT use for trade executions - use event_get_fills for fill records and settlement outcomes.",
6802
6929
  isWrite: false,
@@ -6855,6 +6982,7 @@ function registerEventContractTools() {
6855
6982
  },
6856
6983
  {
6857
6984
  name: "event_get_fills",
6985
+ title: "Event Contracts Get Fills",
6858
6986
  module: "event",
6859
6987
  description: "Get event contract fill history (trade executions and settlement payouts). archive=true for up to 3mo, false (default) for last 3d. outcome pre-translated (YES/NO/UP/DOWN). Each record includes a 'type' field: 'fill' (opening trade) or 'settlement' (expiry payout with settlementResult win/loss and pnl). Do NOT use for order status - use event_get_orders instead.",
6860
6988
  isWrite: false,
@@ -6902,6 +7030,8 @@ function registerEventContractTools() {
6902
7030
  // -----------------------------------------------------------------------
6903
7031
  {
6904
7032
  name: "event_place_order",
7033
+ title: "Event Contracts Place Order",
7034
+ destructiveHint: false,
6905
7035
  module: "event",
6906
7036
  description: `Place an event contract order. [CAUTION] Places a real order. Before placing, call event_get_markets(seriesId, state=live) to obtain the instId of the target contract.
6907
7037
  - outcome: UP/YES (bet price goes up/condition met) or DOWN/NO (bet price goes down/condition not met)
@@ -6977,6 +7107,8 @@ function registerEventContractTools() {
6977
7107
  },
6978
7108
  {
6979
7109
  name: "event_amend_order",
7110
+ title: "Event Contracts Amend Order",
7111
+ idempotentHint: true,
6980
7112
  module: "event",
6981
7113
  description: "Amend a pending event contract order (change price or size). [CAUTION] Modifies a real order. Before amending, call event_get_orders(status=open) to obtain the ordId and confirm the order is still pending. Only limit/post_only orders can be amended.",
6982
7114
  isWrite: true,
@@ -7008,6 +7140,8 @@ function registerEventContractTools() {
7008
7140
  },
7009
7141
  {
7010
7142
  name: "event_cancel_order",
7143
+ title: "Event Contracts Cancel Order",
7144
+ idempotentHint: true,
7011
7145
  module: "event",
7012
7146
  description: "Cancel a pending event contract order. [CAUTION] Cancels a real order. Before cancelling, call event_get_orders(status=open) to obtain the ordId and confirm the order is still pending. instId must be the full event contract instrument ID (e.g. BTC-ABOVE-DAILY-260224-1600-69700), NOT a spot trading pair.",
7013
7147
  isWrite: true,
@@ -7416,6 +7550,7 @@ function registerSmartmoneyTools() {
7416
7550
  /* ---------- T1. Top traders (leaderboard rank) ---------- */
7417
7551
  {
7418
7552
  name: "smartmoney_get_traders_by_filter",
7553
+ title: "Smart Money Leaderboard",
7419
7554
  module: "smartmoney",
7420
7555
  description: "Leaderboard ranking of OKX smart-money traders, filtered by pool conditions and ranked by `sortBy`. Use when: discovering top performers by criteria (PnL / win-rate / drawdown / AUM). See also: `smartmoney_get_performance_by_trader` (lookup by ID), `smartmoney_search_trader` (lookup by nickname). Note: `updateTime` is 12-digit `yyyyMMddHHmm` UTC+8, different from signal tools' 10-digit UTC `asOfTime`/`dataVersion` - do not cross-pass.",
7421
7556
  isWrite: false,
@@ -7482,6 +7617,7 @@ function registerSmartmoneyTools() {
7482
7617
  /* ---------- T2. Trader performance (by authorIds) ---------- */
7483
7618
  {
7484
7619
  name: "smartmoney_get_performance_by_trader",
7620
+ title: "Smart Money Trader Performance",
7485
7621
  module: "smartmoney",
7486
7622
  description: "PnL / win-rate / drawdown profile for one or more traders looked up by `authorIds`. Use when: caller already has trader IDs and needs their performance metrics. See also: `smartmoney_search_trader` (resolve nickname -> authorId), `smartmoney_get_traders_by_filter` (criteria-based discovery). Note: response `updateTime` is 12-digit `yyyyMMddHHmm` UTC+8 - do not pass to signal-side tools' `asOfTime` (10-digit UTC).",
7487
7623
  isWrite: false,
@@ -7552,6 +7688,7 @@ function registerSmartmoneyTools() {
7552
7688
  /* ---------- T3. Trader current positions ---------- */
7553
7689
  {
7554
7690
  name: "smartmoney_get_trader_positions",
7691
+ title: "Smart Money Trader Current Positions",
7555
7692
  module: "smartmoney",
7556
7693
  description: "Currently-open positions held by a single trader (direction, size, leverage, entry, conviction). Use when: inspecting what a top trader is holding RIGHT NOW. See also: `smartmoney_get_trader_positions_history` (closed positions), `smartmoney_search_trader` (nickname -> authorId), `smartmoney_get_traders_by_filter` (discover trader).",
7557
7694
  isWrite: false,
@@ -7633,6 +7770,7 @@ function registerSmartmoneyTools() {
7633
7770
  /* ---------- T4. Trader closed-position history ---------- */
7634
7771
  {
7635
7772
  name: "smartmoney_get_trader_positions_history",
7773
+ title: "Smart Money Trader Position History",
7636
7774
  module: "smartmoney",
7637
7775
  description: "Closed-position history of a single trader, paginated by `posId` cursor. Use when: studying realized PnL pattern, holding duration, win/loss streaks, or how positions ended (closed vs liquidated). See also: `smartmoney_get_trader_positions` (currently-open), `smartmoney_search_trader` (nickname -> authorId), `smartmoney_get_traders_by_filter` (discover trader).",
7638
7776
  isWrite: false,
@@ -7770,6 +7908,7 @@ function registerSmartmoneyTools() {
7770
7908
  /* ---------- T5. Trader order history ---------- */
7771
7909
  {
7772
7910
  name: "smartmoney_get_trader_orders_history",
7911
+ title: "Smart Money Trader Order History",
7773
7912
  module: "smartmoney",
7774
7913
  description: "Recent orders/fills placed by a single trader (direction, size, price, leverage), paginated by `ordId` cursor. Aligned with the cross-module `*_get_orders` family. Use when: tracking a top trader's latest trade activity. See also: `smartmoney_search_trader` (nickname -> authorId), `smartmoney_get_traders_by_filter` (discover trader).",
7775
7914
  isWrite: false,
@@ -7888,6 +8027,7 @@ function registerSmartmoneyTools() {
7888
8027
  /* ---------- T6. Search top traders by nickname keyword ---------- */
7889
8028
  {
7890
8029
  name: "smartmoney_search_trader",
8030
+ title: "Search Smart Money Top Traders",
7891
8031
  module: "smartmoney",
7892
8032
  description: "Search Top Traders by nickname keyword, ranked by OKX-platform follower count DESC. Returns up to 10 matches; intersects KOL full-text recall with the Top Trader set. Use when: resolving a nickname or partial name to `authorId`(s) before calling other `smartmoney_get_trader_*` tools. See also: `smartmoney_get_traders_by_filter` (discover top performers by criteria), `smartmoney_get_performance_by_trader` (lookup by known authorId).",
7893
8033
  isWrite: false,
@@ -7938,6 +8078,7 @@ function registerSmartmoneyTools() {
7938
8078
  /* ---------- S1. Signal overview by filter (multi-asset, tier-filtered pool) ---------- */
7939
8079
  {
7940
8080
  name: "smartmoney_get_signal_overview_by_filter",
8081
+ title: "Smart Money Consensus Signals by Filter",
7941
8082
  module: "smartmoney",
7942
8083
  description: "Multi-asset smart-money consensus signals (long/short ratio, weighted entry, capital flow, deltas vs 1h/24h/7d), aggregated over a tier-filtered trader pool (PnL / win-rate / drawdown / AUM). Pick instruments via `topInstruments` OR `instCcyList` - exactly one. Snapshot time auto-resolved to current hour. **Linear (USDT/USDS-margined) contracts only - coin-margined (`-USD-SWAP` / `-USD-DELIVERY`) positions are excluded by upstream and silently omitted from the aggregation.** Use when: latest cross-asset consensus from a criteria-defined pool. See also: `smartmoney_get_signal_overview_by_trader` (restrict pool to specific traders), `smartmoney_get_signal_trend_by_filter` (time-series instead of latest snapshot).",
7943
8084
  isWrite: false,
@@ -7998,6 +8139,7 @@ function registerSmartmoneyTools() {
7998
8139
  /* ---------- S2. Signal overview by trader (multi-asset, authorIds-restricted) ---------- */
7999
8140
  {
8000
8141
  name: "smartmoney_get_signal_overview_by_trader",
8142
+ title: "Smart Money Consensus Signals by Trader",
8001
8143
  module: "smartmoney",
8002
8144
  description: "Multi-asset smart-money signals aggregated over a hand-picked set of traders (`authorIds`). Pick instruments via `topInstruments` OR `instCcyList`. Capability tier filters (pnlTier / winRateTier / etc.) not exposed - backend uses defaults for direct-lookup scenarios. **Linear (USDT/USDS-margined) contracts only - a trader's coin-margined (`-USD-SWAP` / `-USD-DELIVERY`) positions are silently excluded from the aggregation, even when those positions are large.** Use `smartmoney_get_trader_positions` if the full position book is needed. Use when: caller already knows which traders to follow and wants their cross-asset consensus at the latest hour. See also: `smartmoney_get_signal_overview_by_filter` (criteria-defined pool), `smartmoney_get_signal_trend_by_trader` (time-series), `smartmoney_get_traders_by_filter` / `smartmoney_search_trader` (discover authorIds).",
8003
8145
  isWrite: false,
@@ -8080,6 +8222,7 @@ function registerSmartmoneyTools() {
8080
8222
  /* ---------- S3. Signal trend by filter (single-asset, tier-filtered pool, asOfTime anchor) ---------- */
8081
8223
  {
8082
8224
  name: "smartmoney_get_signal_trend_by_filter",
8225
+ title: "Smart Money Signal Trend by Filter",
8083
8226
  module: "smartmoney",
8084
8227
  description: "Time-series of single-asset smart-money signal across hourly/daily buckets, aggregated over a tier-filtered trader pool. Returns the latest `limit` buckets ending at `asOfTime` (defaults to current UTC hour). **Linear (USDT/USDS-margined) contracts only - coin-margined (`-USD-SWAP` / `-USD-DELIVERY`) positions are excluded by upstream and silently omitted.** Use when: tracking how long/short conviction and capital evolve over time (smart money adding exposure or retreating). See also: `smartmoney_get_signal_overview_by_filter` (latest snapshot only), `smartmoney_get_signal_trend_by_trader` (restrict to specific traders). Note: `asOfTime` is 10-digit `yyyyMMddHH` UTC, different from leaderboard tools' 12-digit UTC+8 `updateTime` - do not cross-pass.",
8085
8228
  isWrite: false,
@@ -8152,6 +8295,7 @@ function registerSmartmoneyTools() {
8152
8295
  /* ---------- S4. Signal trend by trader (single-asset, authorIds-restricted) ---------- */
8153
8296
  {
8154
8297
  name: "smartmoney_get_signal_trend_by_trader",
8298
+ title: "Smart Money Signal Trend by Trader",
8155
8299
  module: "smartmoney",
8156
8300
  description: "Time-series of single-asset smart-money signal aggregated over a hand-picked set of traders (`authorIds`). Returns the latest `limit` buckets ending at `asOfTime` (defaults to current UTC hour). Capability tier filters (pnlTier / winRateTier / etc.) not exposed - backend uses defaults for direct-lookup scenarios. **Linear (USDT/USDS-margined) contracts only - a trader's coin-margined (`-USD-SWAP` / `-USD-DELIVERY`) positions on the requested base ccy are silently excluded from each bucket.** Use `smartmoney_get_trader_positions` to inspect the full position book. Use when: tracking how a specific group of traders has evolved their long/short consensus over time on one coin. See also: `smartmoney_get_signal_trend_by_filter` (criteria-defined pool), `smartmoney_get_signal_overview_by_trader` (latest snapshot only), `smartmoney_get_traders_by_filter` / `smartmoney_search_trader` (discover authorIds). Note: `asOfTime` is 10-digit `yyyyMMddHH` UTC, different from leaderboard tools' 12-digit UTC+8 `updateTime` - do not cross-pass.",
8157
8301
  isWrite: false,
@@ -8246,7 +8390,7 @@ function registerSmartmoneyTools() {
8246
8390
  return tools;
8247
8391
  }
8248
8392
  function buildContractTradeTools(cfg) {
8249
- const { prefix, module, label, instTypes, instIdExample } = cfg;
8393
+ const { prefix, module, label, instTypes, instIdExample, titleLabel } = cfg;
8250
8394
  const [defaultType, otherType] = instTypes;
8251
8395
  const instTypeDesc = `${defaultType} (default) or ${otherType}`;
8252
8396
  const n = (suffix) => `${prefix}_${suffix}`;
@@ -8254,6 +8398,8 @@ function buildContractTradeTools(cfg) {
8254
8398
  // ── place_order ──────────────────────────────────────────────────────────
8255
8399
  {
8256
8400
  name: n("place_order"),
8401
+ title: `${titleLabel} Place Order`,
8402
+ destructiveHint: false,
8257
8403
  module,
8258
8404
  description: `Place ${label} order. Attach TP/SL via tpTriggerPx/slTriggerPx. Before placing, use market_get_instruments to get ctVal (contract face value) - do NOT assume contract sizes. [CAUTION] Executes real trades.`,
8259
8405
  isWrite: true,
@@ -8350,6 +8496,8 @@ function buildContractTradeTools(cfg) {
8350
8496
  // ── cancel_order ─────────────────────────────────────────────────────────
8351
8497
  {
8352
8498
  name: n("cancel_order"),
8499
+ title: `${titleLabel} Cancel Order`,
8500
+ idempotentHint: true,
8353
8501
  module,
8354
8502
  description: `Cancel an unfilled ${label} order.`,
8355
8503
  isWrite: true,
@@ -8379,6 +8527,7 @@ function buildContractTradeTools(cfg) {
8379
8527
  // ── get_order ─────────────────────────────────────────────────────────────
8380
8528
  {
8381
8529
  name: n("get_order"),
8530
+ title: `${titleLabel} Get Order`,
8382
8531
  module,
8383
8532
  description: `Get details of a single ${label} order by ordId or clOrdId.`,
8384
8533
  isWrite: false,
@@ -8408,6 +8557,7 @@ function buildContractTradeTools(cfg) {
8408
8557
  // ── get_orders ───────────────────────────────────────────────────────────
8409
8558
  {
8410
8559
  name: n("get_orders"),
8560
+ title: `${titleLabel} Get Orders`,
8411
8561
  module,
8412
8562
  description: `Query ${label} open orders, history (last 7 days), or archive (up to 3 months).`,
8413
8563
  isWrite: false,
@@ -8466,6 +8616,7 @@ function buildContractTradeTools(cfg) {
8466
8616
  // ── get_positions ────────────────────────────────────────────────────────
8467
8617
  {
8468
8618
  name: n("get_positions"),
8619
+ title: `${titleLabel} Get Positions`,
8469
8620
  module,
8470
8621
  description: `Get current ${label} positions.`,
8471
8622
  isWrite: false,
@@ -8500,6 +8651,7 @@ function buildContractTradeTools(cfg) {
8500
8651
  // ── get_fills ────────────────────────────────────────────────────────────
8501
8652
  {
8502
8653
  name: n("get_fills"),
8654
+ title: `${titleLabel} Get Fills`,
8503
8655
  module,
8504
8656
  description: `Get ${label} fill details. archive=false (default): last 3 days; archive=true: up to 3 months.`,
8505
8657
  isWrite: false,
@@ -8550,6 +8702,8 @@ function buildContractTradeTools(cfg) {
8550
8702
  // ── close_position ───────────────────────────────────────────────────────
8551
8703
  {
8552
8704
  name: n("close_position"),
8705
+ title: `${titleLabel} Close Position`,
8706
+ idempotentHint: true,
8553
8707
  module,
8554
8708
  description: `[CAUTION] Close entire ${label} position at market.`,
8555
8709
  isWrite: true,
@@ -8592,6 +8746,8 @@ function buildContractTradeTools(cfg) {
8592
8746
  // ── set_leverage ─────────────────────────────────────────────────────────
8593
8747
  {
8594
8748
  name: n("set_leverage"),
8749
+ title: `${titleLabel} Set Leverage`,
8750
+ idempotentHint: true,
8595
8751
  module,
8596
8752
  description: `Set leverage for a ${label} instrument or position. [CAUTION] Changes risk parameters.
8597
8753
  Scenarios (SWAP/FUTURES only):
@@ -8654,6 +8810,7 @@ Not supported: PORTFOLIO MARGIN accounts cannot adjust cross leverage for SWAP/F
8654
8810
  // ── get_leverage ─────────────────────────────────────────────────────────
8655
8811
  {
8656
8812
  name: n("get_leverage"),
8813
+ title: `${titleLabel} Get Leverage`,
8657
8814
  module,
8658
8815
  description: `Get current leverage for a ${label} instrument.`,
8659
8816
  isWrite: false,
@@ -8681,6 +8838,8 @@ Not supported: PORTFOLIO MARGIN accounts cannot adjust cross leverage for SWAP/F
8681
8838
  // ── batch_amend ──────────────────────────────────────────────────────────
8682
8839
  {
8683
8840
  name: n("batch_amend"),
8841
+ title: `${titleLabel} Batch Amend Orders`,
8842
+ idempotentHint: true,
8684
8843
  module,
8685
8844
  description: `[CAUTION] Batch amend up to 20 unfilled ${label} orders.`,
8686
8845
  isWrite: true,
@@ -8712,6 +8871,8 @@ Not supported: PORTFOLIO MARGIN accounts cannot adjust cross leverage for SWAP/F
8712
8871
  // ── batch_cancel ─────────────────────────────────────────────────────────
8713
8872
  {
8714
8873
  name: n("batch_cancel"),
8874
+ title: `${titleLabel} Batch Cancel Orders`,
8875
+ idempotentHint: true,
8715
8876
  module,
8716
8877
  description: `[CAUTION] Batch cancel up to 20 ${label} orders.`,
8717
8878
  isWrite: true,
@@ -8747,6 +8908,7 @@ function registerFuturesTools() {
8747
8908
  prefix: "futures",
8748
8909
  module: "futures",
8749
8910
  label: "FUTURES delivery",
8911
+ titleLabel: "Futures",
8750
8912
  instTypes: ["FUTURES", "SWAP"],
8751
8913
  instIdExample: "e.g. BTC-USDT-240329"
8752
8914
  });
@@ -8756,6 +8918,8 @@ function registerFuturesTools() {
8756
8918
  // Unique to futures: amend a regular (non-algo) unfilled order.
8757
8919
  {
8758
8920
  name: "futures_amend_order",
8921
+ title: "Futures Amend Order",
8922
+ idempotentHint: true,
8759
8923
  module: "futures",
8760
8924
  description: "Amend an unfilled FUTURES delivery order (modify price and/or size). To modify attached TP/SL, use futures_amend_algo_order with the algoId from futures_get_algo_orders.",
8761
8925
  isWrite: true,
@@ -8790,6 +8954,8 @@ function registerFuturesTools() {
8790
8954
  // Unique to futures: batch place only (no cancel/amend action dispatch).
8791
8955
  {
8792
8956
  name: "futures_batch_orders",
8957
+ title: "Futures Batch Place Orders",
8958
+ destructiveHint: false,
8793
8959
  module: "futures",
8794
8960
  description: "[CAUTION] Batch place up to 20 FUTURES delivery orders.",
8795
8961
  isWrite: true,
@@ -8849,6 +9015,7 @@ function registerMarketTools() {
8849
9015
  return [
8850
9016
  {
8851
9017
  name: "market_get_ticker",
9018
+ title: "Get Ticker",
8852
9019
  module: "market",
8853
9020
  description: "Get ticker data for a single instrument.",
8854
9021
  isWrite: false,
@@ -8876,6 +9043,7 @@ function registerMarketTools() {
8876
9043
  },
8877
9044
  {
8878
9045
  name: "market_get_tickers",
9046
+ title: "Get All Tickers",
8879
9047
  module: "market",
8880
9048
  description: "Get ticker data for all instruments of a given type.",
8881
9049
  isWrite: false,
@@ -8915,6 +9083,7 @@ function registerMarketTools() {
8915
9083
  },
8916
9084
  {
8917
9085
  name: "market_get_orderbook",
9086
+ title: "Get Order Book",
8918
9087
  module: "market",
8919
9088
  description: "Get the order book (bids/asks) for an instrument.",
8920
9089
  isWrite: false,
@@ -8949,6 +9118,7 @@ function registerMarketTools() {
8949
9118
  },
8950
9119
  {
8951
9120
  name: "market_get_candles",
9121
+ title: "Get Candlesticks",
8952
9122
  module: "market",
8953
9123
  description: "Get candlestick (OHLCV) data for an instrument. Automatically retrieves historical data (back to 2021) when requesting older time ranges. Use the `after` parameter to paginate back in time (the old `history` parameter has been removed). IMPORTANT: Before fetching with `after`/`before`, estimate the number of candles: time_range_ms / bar_interval_ms. If the estimate exceeds ~500 candles, inform the user of the estimated count and ask for confirmation before proceeding.",
8954
9124
  isWrite: false,
@@ -9005,6 +9175,7 @@ function registerMarketTools() {
9005
9175
  },
9006
9176
  {
9007
9177
  name: "market_get_instruments",
9178
+ title: "List Instruments",
9008
9179
  module: "market",
9009
9180
  description: "Get tradable instruments for a given type. Returns contract specs: min order size, lot size, tick size, contract value, settlement currency, listing/expiry time. Essential before placing orders.",
9010
9181
  isWrite: false,
@@ -9049,6 +9220,7 @@ function registerMarketTools() {
9049
9220
  },
9050
9221
  {
9051
9222
  name: "market_get_funding_rate",
9223
+ title: "Get Funding Rate",
9052
9224
  module: "market",
9053
9225
  description: "Get funding rate for a perpetual SWAP instrument. IMPORTANT: instId must end with -SWAP (e.g. BTC-USDT-SWAP). Spot IDs like BTC-USDT are NOT valid. history=false (default): current rate + next estimated rate; history=true: historical rates.",
9054
9226
  isWrite: false,
@@ -9110,6 +9282,7 @@ function registerMarketTools() {
9110
9282
  },
9111
9283
  {
9112
9284
  name: "market_get_mark_price",
9285
+ title: "Get Mark Price",
9113
9286
  module: "market",
9114
9287
  description: "Get mark price for SWAP, FUTURES, or MARGIN instruments. Used for liquidation calculations and unrealized PnL.",
9115
9288
  isWrite: false,
@@ -9153,6 +9326,7 @@ function registerMarketTools() {
9153
9326
  },
9154
9327
  {
9155
9328
  name: "market_get_trades",
9329
+ title: "Get Recent Trades",
9156
9330
  module: "market",
9157
9331
  description: "Get recent trades for an instrument. Default 20 records, max 500.",
9158
9332
  isWrite: false,
@@ -9187,6 +9361,7 @@ function registerMarketTools() {
9187
9361
  },
9188
9362
  {
9189
9363
  name: "market_get_index_ticker",
9364
+ title: "Get Index Ticker",
9190
9365
  module: "market",
9191
9366
  description: "Get index ticker data (e.g. BTC-USD, ETH-USD index prices). Independent of any single exchange.",
9192
9367
  isWrite: false,
@@ -9220,6 +9395,7 @@ function registerMarketTools() {
9220
9395
  },
9221
9396
  {
9222
9397
  name: "market_get_index_candles",
9398
+ title: "Get Index Candlesticks",
9223
9399
  module: "market",
9224
9400
  description: "Get candlestick data for an index (e.g. BTC-USD index). history=false: recent up to 1440 bars; history=true: older data.",
9225
9401
  isWrite: false,
@@ -9276,6 +9452,7 @@ function registerMarketTools() {
9276
9452
  },
9277
9453
  {
9278
9454
  name: "market_get_price_limit",
9455
+ title: "Get Price Limit",
9279
9456
  module: "market",
9280
9457
  description: "Get the current price limit (upper and lower bands) for a SWAP or FUTURES instrument. Orders outside these limits will be rejected.",
9281
9458
  isWrite: false,
@@ -9303,6 +9480,7 @@ function registerMarketTools() {
9303
9480
  },
9304
9481
  {
9305
9482
  name: "market_get_open_interest",
9483
+ title: "Get Open Interest",
9306
9484
  module: "market",
9307
9485
  description: "Get open interest for SWAP, FUTURES, or OPTION instruments. Useful for gauging market sentiment and positioning.",
9308
9486
  isWrite: false,
@@ -9346,6 +9524,7 @@ function registerMarketTools() {
9346
9524
  },
9347
9525
  {
9348
9526
  name: "market_get_stock_tokens",
9527
+ title: "List Stock Tokens",
9349
9528
  module: "market",
9350
9529
  description: '[Deprecated: use market_get_instruments_by_category with instCategory="3" instead] Get all stock token instruments (instCategory=3). Stock tokens track real-world stock prices on OKX (e.g. AAPL-USDT-SWAP).',
9351
9530
  isWrite: false,
@@ -9382,6 +9561,7 @@ function registerMarketTools() {
9382
9561
  },
9383
9562
  {
9384
9563
  name: "market_get_instruments_by_category",
9564
+ title: "List Instruments by Category",
9385
9565
  module: "market",
9386
9566
  description: "Discover tradeable instruments by asset category. Stock tokens (instCategory=3, e.g. AAPL-USDT-SWAP, TSLA-USDT-SWAP), Metals (4, e.g. XAUUSDT-USDT-SWAP for gold), Commodities (5, e.g. OIL-USDT-SWAP for crude oil), Forex (6, e.g. EURUSDT-USDT-SWAP for EUR/USD), Bonds (7, e.g. US30Y-USDT-SWAP for crude oil). Use this to find instIds before querying prices or placing orders. Filters client-side by instCategory.",
9387
9567
  isWrite: false,
@@ -9432,6 +9612,7 @@ function registerMarketFilterTools() {
9432
9612
  // ─────────────────────────────────────────────────────────────────────────
9433
9613
  {
9434
9614
  name: "market_filter",
9615
+ title: "Screen Instruments",
9435
9616
  module: "market",
9436
9617
  description: "Screen / rank instruments across SPOT, SWAP, or FUTURES by multi-dimensional criteria: price range, 24h change %, market cap, 24h volume (USD), funding rate (SWAP), open interest (USD), listing time. Returns ranked rows with full ticker snapshot. Use to find top movers, high-OI contracts, newly listed tokens, etc. No credentials required. Do NOT use to get OI change rankings across contracts - use market_filter_oi_change instead. Do NOT use to get OI time series for a single instrument - use market_get_oi_history instead.",
9437
9618
  isWrite: false,
@@ -9569,6 +9750,7 @@ function registerMarketFilterTools() {
9569
9750
  // ─────────────────────────────────────────────────────────────────────────
9570
9751
  {
9571
9752
  name: "market_get_oi_history",
9753
+ title: "Get Open Interest History",
9572
9754
  module: "market",
9573
9755
  description: "Get open interest (OI) history time series for a single SWAP or FUTURES instrument. Returns per-bar OI in contracts, base currency and USD, plus bar-over-bar delta and delta %. Useful for tracking how OI evolves around price moves. No credentials required. Do NOT use to compare OI changes across multiple contracts - use market_filter_oi_change instead. Do NOT use to screen instruments by current OI level - use market_filter instead.",
9574
9756
  isWrite: false,
@@ -9616,6 +9798,7 @@ function registerMarketFilterTools() {
9616
9798
  // ─────────────────────────────────────────────────────────────────────────
9617
9799
  {
9618
9800
  name: "market_filter_oi_change",
9801
+ title: "Find Open Interest Change Instruments",
9619
9802
  module: "market",
9620
9803
  description: "Find SWAP or FUTURES instruments with significant open interest changes over a given bar window. Returns ranked rows with current OI (USD), previous OI (USD), OI delta (USD and %), price change %, 24h volume and funding rate. Ideal for spotting unusual accumulation/distribution or confirming trend momentum. No credentials required. Do NOT use to get OI time series for a single instrument - use market_get_oi_history instead. Do NOT use to screen by current OI absolute level or other non-OI metrics - use market_filter instead.",
9621
9804
  isWrite: false,
@@ -9688,6 +9871,7 @@ function registerMarketFilterTools() {
9688
9871
  // ─────────────────────────────────────────────────────────────────────────
9689
9872
  {
9690
9873
  name: "market_get_pair_spread",
9874
+ title: "Get Pair Spread",
9691
9875
  module: "market",
9692
9876
  description: "Compute spread statistics between two instruments over a lookback window. Returns absolute and ratio spread (mean / stdDev / median / min / max) plus an optional realtime spread snapshot. Use to size pairs trades, detect mean-reversion setups, or compare cross-listed contracts. Results are cached ~60s per (pair, bar, window) tuple. Read-only, no credentials required.\nDo NOT use to fetch raw candles (use market_get_candles) or single-instrument OI/funding (use market_get_oi_history / market_filter_oi_change).",
9693
9877
  isWrite: false,
@@ -9980,6 +10164,7 @@ function registerNewsTools() {
9980
10164
  // -----------------------------------------------------------------------
9981
10165
  {
9982
10166
  name: "news_get_latest",
10167
+ title: "Get Latest Crypto News",
9983
10168
  module: "news",
9984
10169
  description: "Get crypto news sorted by time. For broad browsing ('what happened recently', 'latest news', 'any big news today'), pass importance='low' to include both high and low importance. Server default (when importance omitted) returns only high-importance news. For coin-specific news, use news_get_by_coin instead.",
9985
10170
  isWrite: false,
@@ -10025,6 +10210,7 @@ function registerNewsTools() {
10025
10210
  },
10026
10211
  {
10027
10212
  name: "news_get_by_coin",
10213
+ title: "Get News by Coin",
10028
10214
  module: "news",
10029
10215
  description: "Get news for specific coins or tokens. Use when user mentions a coin: 'BTC news', 'any SOL updates'. Supports multiple coins (comma-separated). For general browsing without a coin filter, use news_get_latest.",
10030
10216
  isWrite: false,
@@ -10068,6 +10254,7 @@ function registerNewsTools() {
10068
10254
  },
10069
10255
  {
10070
10256
  name: "news_search",
10257
+ title: "Search News",
10071
10258
  module: "news",
10072
10259
  description: "Search crypto news by keyword with optional filters. Use when user provides specific search terms: 'SEC ETF news', 'stablecoin regulation'. Keyword is optional - pass sentiment alone to browse by sentiment direction. For coin-only queries prefer news_get_by_coin.",
10073
10260
  isWrite: false,
@@ -10125,6 +10312,7 @@ function registerNewsTools() {
10125
10312
  },
10126
10313
  {
10127
10314
  name: "news_get_detail",
10315
+ title: "Get News Article",
10128
10316
  module: "news",
10129
10317
  description: "Get full article content by news ID (returns title + summary + full original text). Use when user says 'show full article', 'read more', or provides a specific news ID from a previous result.",
10130
10318
  isWrite: false,
@@ -10156,6 +10344,7 @@ function registerNewsTools() {
10156
10344
  },
10157
10345
  {
10158
10346
  name: "news_get_domains",
10347
+ title: "List News Sources",
10159
10348
  module: "news",
10160
10349
  description: "List available news source domains (e.g. coindesk, cointelegraph). Use when user asks what news sources are available or which platforms are covered.",
10161
10350
  isWrite: false,
@@ -10178,6 +10367,7 @@ function registerNewsTools() {
10178
10367
  // -----------------------------------------------------------------------
10179
10368
  {
10180
10369
  name: "news_get_coin_sentiment",
10370
+ title: "Get Coin Sentiment",
10181
10371
  module: "news",
10182
10372
  description: "Get sentiment snapshot or time-series trend for coins. Returns bullish/bearish ratios and mention counts. Pass trendPoints for trend data (1h->24 points, 4h->6, 24h->7). Use when user asks about coin sentiment, sentiment trend, or how bullish/bearish a coin is. For ranking all coins by sentiment, use news_get_sentiment_ranking instead.",
10183
10373
  isWrite: false,
@@ -10219,6 +10409,7 @@ function registerNewsTools() {
10219
10409
  },
10220
10410
  {
10221
10411
  name: "news_get_sentiment_ranking",
10412
+ title: "Get Sentiment Ranking",
10222
10413
  module: "news",
10223
10414
  description: "Get coin ranking by social hotness or sentiment direction. Use when user asks which coins are trending, most bullish/bearish coins. Sort by hot (mention count), bullish, or bearish. For sentiment data on a specific coin, use news_get_coin_sentiment instead.",
10224
10415
  isWrite: false,
@@ -10258,6 +10449,7 @@ function registerNewsTools() {
10258
10449
  // -----------------------------------------------------------------------
10259
10450
  {
10260
10451
  name: "news_list_calendar_regions",
10452
+ title: "List Calendar Regions",
10261
10453
  module: "news",
10262
10454
  description: "List all valid region values for the economic calendar. Returns a string array of snake_case region codes. Call this when economic-calendar returns empty results to verify the region value, or to help the user pick a valid region. Do NOT use to list news source platforms - use news_get_domains instead.",
10263
10455
  isWrite: false,
@@ -10266,6 +10458,7 @@ function registerNewsTools() {
10266
10458
  },
10267
10459
  {
10268
10460
  name: "news_get_economic_calendar",
10461
+ title: "Get Economic Calendar",
10269
10462
  module: "news",
10270
10463
  description: "Get macro-economic calendar data (GDP, CPI, NFP, interest rate decisions, PMI, etc.). Returns scheduled and released economic events with forecast, previous, and actual values. Use when user asks about economic calendar, macro data, or specific indicators like NFP/CPI/GDP/FOMC. Do NOT use for news articles or sentiment - use news_get_latest or news_search instead.",
10271
10464
  isWrite: false,
@@ -10326,6 +10519,8 @@ function registerOptionAlgoTools() {
10326
10519
  return [
10327
10520
  {
10328
10521
  name: "option_place_algo_order",
10522
+ title: "Option Place Algo Order",
10523
+ destructiveHint: false,
10329
10524
  module: "option",
10330
10525
  description: "Place OPTION TP/SL algo order (conditional/oco). [CAUTION] Executes real trades. conditional=single TP/SL; oco=TP+SL pair. -1=market.",
10331
10526
  isWrite: true,
@@ -10432,6 +10627,8 @@ function registerOptionAlgoTools() {
10432
10627
  },
10433
10628
  {
10434
10629
  name: "option_amend_algo_order",
10630
+ title: "Option Amend Algo Order",
10631
+ idempotentHint: true,
10435
10632
  module: "option",
10436
10633
  description: "Amend a pending OPTION algo order (modify TP/SL prices or size). Also covers TP/SL orders attached when placing the main order - look up algoId via option_get_algo_orders first.",
10437
10634
  isWrite: true,
@@ -10468,6 +10665,8 @@ function registerOptionAlgoTools() {
10468
10665
  },
10469
10666
  {
10470
10667
  name: "option_cancel_algo_orders",
10668
+ title: "Option Cancel Algo Orders",
10669
+ idempotentHint: true,
10471
10670
  module: "option",
10472
10671
  description: "Cancel OPTION algo orders (TP/SL). Each item: {algoId, instId}.",
10473
10672
  isWrite: true,
@@ -10511,6 +10710,7 @@ function registerOptionAlgoTools() {
10511
10710
  },
10512
10711
  {
10513
10712
  name: "option_get_algo_orders",
10713
+ title: "Option Get Algo Orders",
10514
10714
  module: "option",
10515
10715
  description: "Query pending or completed OPTION algo orders (TP/SL, OCO).",
10516
10716
  isWrite: false,
@@ -10595,6 +10795,8 @@ function registerOptionTools() {
10595
10795
  return [
10596
10796
  {
10597
10797
  name: "option_place_order",
10798
+ title: "Option Place Order",
10799
+ destructiveHint: false,
10598
10800
  module: "option",
10599
10801
  description: "Place OPTION order. instId: {uly}-{expiry}-{strike}-C/P, e.g. BTC-USD-241227-50000-C. Before placing, use market_get_instruments to get ctVal (contract face value) - do NOT assume contract sizes. [CAUTION] Executes real trades.",
10600
10802
  isWrite: true,
@@ -10698,6 +10900,8 @@ function registerOptionTools() {
10698
10900
  },
10699
10901
  {
10700
10902
  name: "option_cancel_order",
10903
+ title: "Option Cancel Order",
10904
+ idempotentHint: true,
10701
10905
  module: "option",
10702
10906
  description: "Cancel an unfilled OPTION order. Provide ordId or clOrdId.",
10703
10907
  isWrite: true,
@@ -10726,6 +10930,8 @@ function registerOptionTools() {
10726
10930
  },
10727
10931
  {
10728
10932
  name: "option_batch_cancel",
10933
+ title: "Option Batch Cancel Orders",
10934
+ idempotentHint: true,
10729
10935
  module: "option",
10730
10936
  description: "[CAUTION] Batch cancel up to 20 OPTION orders.",
10731
10937
  isWrite: true,
@@ -10756,6 +10962,8 @@ function registerOptionTools() {
10756
10962
  },
10757
10963
  {
10758
10964
  name: "option_amend_order",
10965
+ title: "Option Amend Order",
10966
+ idempotentHint: true,
10759
10967
  module: "option",
10760
10968
  description: "Amend an unfilled OPTION order (price and/or size). Provide ordId or clOrdId. To modify attached TP/SL, use option_amend_algo_order with the algoId from option_get_algo_orders.",
10761
10969
  isWrite: true,
@@ -10788,6 +10996,7 @@ function registerOptionTools() {
10788
10996
  },
10789
10997
  {
10790
10998
  name: "option_get_order",
10999
+ title: "Option Get Order",
10791
11000
  module: "option",
10792
11001
  description: "Get details of a single OPTION order by ordId or clOrdId.",
10793
11002
  isWrite: false,
@@ -10816,6 +11025,7 @@ function registerOptionTools() {
10816
11025
  },
10817
11026
  {
10818
11027
  name: "option_get_orders",
11028
+ title: "Option Get Orders",
10819
11029
  module: "option",
10820
11030
  description: "List OPTION orders. status: live=pending (default), history=7d, archive=3mo.",
10821
11031
  isWrite: false,
@@ -10870,6 +11080,7 @@ function registerOptionTools() {
10870
11080
  },
10871
11081
  {
10872
11082
  name: "option_get_positions",
11083
+ title: "Option Get Positions with Greeks",
10873
11084
  module: "option",
10874
11085
  description: "Get current OPTION positions including Greeks (delta, gamma, theta, vega).",
10875
11086
  isWrite: false,
@@ -10896,6 +11107,7 @@ function registerOptionTools() {
10896
11107
  },
10897
11108
  {
10898
11109
  name: "option_get_fills",
11110
+ title: "Option Get Fills",
10899
11111
  module: "option",
10900
11112
  description: "Get OPTION fill history. archive=false: last 3 days (default); archive=true: up to 3 months.",
10901
11113
  isWrite: false,
@@ -10938,6 +11150,7 @@ function registerOptionTools() {
10938
11150
  },
10939
11151
  {
10940
11152
  name: "option_get_instruments",
11153
+ title: "Option List Instruments (Chain)",
10941
11154
  module: "option",
10942
11155
  description: "List available OPTION contracts for a given underlying (option chain). Use to find valid instIds before placing orders.",
10943
11156
  isWrite: false,
@@ -10971,6 +11184,7 @@ function registerOptionTools() {
10971
11184
  },
10972
11185
  {
10973
11186
  name: "option_get_greeks",
11187
+ title: "Option Get Greeks",
10974
11188
  module: "option",
10975
11189
  description: "Get implied volatility and Greeks (delta, gamma, theta, vega) for OPTION contracts by underlying.",
10976
11190
  isWrite: false,
@@ -11007,6 +11221,8 @@ function registerSpotTradeTools() {
11007
11221
  return [
11008
11222
  {
11009
11223
  name: "spot_place_order",
11224
+ title: "Spot Place Order",
11225
+ destructiveHint: false,
11010
11226
  module: "spot",
11011
11227
  description: "Place a spot order. Attach TP/SL via tpTriggerPx/slTriggerPx. [CAUTION] Executes real trades.",
11012
11228
  isWrite: true,
@@ -11101,6 +11317,8 @@ function registerSpotTradeTools() {
11101
11317
  },
11102
11318
  {
11103
11319
  name: "spot_cancel_order",
11320
+ title: "Spot Cancel Order",
11321
+ idempotentHint: true,
11104
11322
  module: "spot",
11105
11323
  description: "Cancel an unfilled spot order.",
11106
11324
  isWrite: true,
@@ -11136,6 +11354,8 @@ function registerSpotTradeTools() {
11136
11354
  },
11137
11355
  {
11138
11356
  name: "spot_amend_order",
11357
+ title: "Spot Amend Order",
11358
+ idempotentHint: true,
11139
11359
  module: "spot",
11140
11360
  description: "Amend an unfilled spot order (modify price or size). To modify attached TP/SL, use the corresponding algo amend tool: spot_amend_algo_order (spot), swap_amend_algo_order (swap), futures_amend_algo_order (futures), option_amend_algo_order (option). Use spot_get_algo_orders to find the algoId for spot orders.",
11141
11361
  isWrite: true,
@@ -11186,6 +11406,7 @@ function registerSpotTradeTools() {
11186
11406
  },
11187
11407
  {
11188
11408
  name: "spot_get_orders",
11409
+ title: "Spot Get Orders",
11189
11410
  module: "spot",
11190
11411
  description: "Query spot orders. status: open(active)|history(7d)|archive(3mo).",
11191
11412
  isWrite: false,
@@ -11254,6 +11475,8 @@ function registerSpotTradeTools() {
11254
11475
  },
11255
11476
  {
11256
11477
  name: "spot_place_algo_order",
11478
+ title: "Spot Place Algo Order",
11479
+ destructiveHint: false,
11257
11480
  module: "spot",
11258
11481
  description: "Place a spot algo order. [CAUTION] Executes real trades. conditional: single TP/SL. oco: TP+SL pair. move_order_stop: trailing stop. trigger: pending order at triggerPx. chase: follow best bid/ask. iceberg: split large order into child orders. twap: time-weighted split.",
11259
11482
  isWrite: true,
@@ -11369,6 +11592,8 @@ function registerSpotTradeTools() {
11369
11592
  },
11370
11593
  {
11371
11594
  name: "spot_amend_algo_order",
11595
+ title: "Spot Amend Algo Order",
11596
+ idempotentHint: true,
11372
11597
  module: "spot",
11373
11598
  description: "Amend a pending spot algo order (modify TP/SL prices or size). Also covers TP/SL orders attached when placing the main order - look up algoId via spot_get_algo_orders first.",
11374
11599
  isWrite: true,
@@ -11405,6 +11630,8 @@ function registerSpotTradeTools() {
11405
11630
  },
11406
11631
  {
11407
11632
  name: "spot_cancel_algo_order",
11633
+ title: "Spot Cancel Algo Order",
11634
+ idempotentHint: true,
11408
11635
  module: "spot",
11409
11636
  description: "Cancel a spot algo order (TP/SL).",
11410
11637
  isWrite: true,
@@ -11438,6 +11665,7 @@ function registerSpotTradeTools() {
11438
11665
  },
11439
11666
  {
11440
11667
  name: "spot_get_algo_orders",
11668
+ title: "Spot Get Algo Orders",
11441
11669
  module: "spot",
11442
11670
  description: "Query spot algo orders (TP/SL) - pending or history.",
11443
11671
  isWrite: false,
@@ -11515,6 +11743,7 @@ function registerSpotTradeTools() {
11515
11743
  },
11516
11744
  {
11517
11745
  name: "spot_get_fills",
11746
+ title: "Spot Get Fills",
11518
11747
  module: "spot",
11519
11748
  description: "Get spot transaction fills. archive=false(3d, default)|true(up to 3mo).",
11520
11749
  isWrite: false,
@@ -11578,6 +11807,7 @@ function registerSpotTradeTools() {
11578
11807
  },
11579
11808
  {
11580
11809
  name: "spot_batch_orders",
11810
+ title: "Spot Batch Orders",
11581
11811
  module: "spot",
11582
11812
  description: "[CAUTION] Batch place/cancel/amend up to 20 spot orders. action: place|cancel|amend.",
11583
11813
  isWrite: true,
@@ -11636,6 +11866,7 @@ function registerSpotTradeTools() {
11636
11866
  },
11637
11867
  {
11638
11868
  name: "spot_get_order",
11869
+ title: "Spot Get Order",
11639
11870
  module: "spot",
11640
11871
  description: "Get details of a single spot order.",
11641
11872
  isWrite: false,
@@ -11673,6 +11904,8 @@ function registerSpotTradeTools() {
11673
11904
  },
11674
11905
  {
11675
11906
  name: "spot_batch_amend",
11907
+ title: "Spot Batch Amend Orders",
11908
+ idempotentHint: true,
11676
11909
  module: "spot",
11677
11910
  description: "[CAUTION] Batch amend up to 20 unfilled spot orders.",
11678
11911
  isWrite: true,
@@ -11703,6 +11936,8 @@ function registerSpotTradeTools() {
11703
11936
  },
11704
11937
  {
11705
11938
  name: "spot_batch_cancel",
11939
+ title: "Spot Batch Cancel Orders",
11940
+ idempotentHint: true,
11706
11941
  module: "spot",
11707
11942
  description: "[CAUTION] Batch cancel up to 20 spot orders.",
11708
11943
  isWrite: true,
@@ -11740,6 +11975,8 @@ function registerSpotTradeTools() {
11740
11975
  // Not applicable: posSide (spot has no long/short hedge).
11741
11976
  {
11742
11977
  name: "spot_set_leverage",
11978
+ title: "Spot Set Leverage",
11979
+ idempotentHint: true,
11743
11980
  module: "spot",
11744
11981
  description: "Set leverage for SPOT margin trading. Provide exactly ONE of instId (pair-level) or ccy (currency-level cross, requires borrow-enabled account / multi-ccy / portfolio margin). [CAUTION] Changes risk parameters.\nScenarios:\n \u2022 instId + mgnMode=isolated -> pair-level isolated margin\n \u2022 instId + mgnMode=cross -> pair-level cross margin (contract-mode account)\n \u2022 ccy + mgnMode=cross -> currency-level cross margin (spot-with-borrow / multi-ccy / portfolio margin)\nWhen ccy is supplied, mgnMode MUST be cross. posSide is never applicable to spot margin.",
11745
11982
  isWrite: true,
@@ -11814,6 +12051,7 @@ function registerSwapTradeTools() {
11814
12051
  prefix: "swap",
11815
12052
  module: "swap",
11816
12053
  label: "SWAP/FUTURES",
12054
+ titleLabel: "Perpetual Futures",
11817
12055
  instTypes: ["SWAP", "FUTURES"],
11818
12056
  instIdExample: "e.g. BTC-USDT-SWAP"
11819
12057
  });
@@ -11823,6 +12061,8 @@ function registerSwapTradeTools() {
11823
12061
  // Unique to swap: amend a pending TP/SL algo order attached to a position.
11824
12062
  {
11825
12063
  name: "swap_amend_algo_order",
12064
+ title: "Perpetual Futures Amend Algo Order",
12065
+ idempotentHint: true,
11826
12066
  module: "swap",
11827
12067
  description: "Amend a pending SWAP/FUTURES algo order (modify TP/SL prices or size). Also covers TP/SL orders attached when placing the main order - look up algoId via swap_get_algo_orders first.",
11828
12068
  isWrite: true,
@@ -11861,6 +12101,7 @@ function registerSwapTradeTools() {
11861
12101
  // Unique to swap: 3-in-1 batch tool (place / cancel / amend via action param).
11862
12102
  {
11863
12103
  name: "swap_batch_orders",
12104
+ title: "Perpetual Futures Batch Orders",
11864
12105
  module: "swap",
11865
12106
  description: "[CAUTION] Batch place/cancel/amend SWAP/FUTURES orders (max 20). action=place|cancel|amend.",
11866
12107
  isWrite: true,
@@ -13358,9 +13599,9 @@ var Report = class {
13358
13599
  /** Write report to a file path, returns true on success. */
13359
13600
  writeToFile(filePath) {
13360
13601
  try {
13361
- const sep2 = "-".repeat(52);
13602
+ const sep2 = "\u2500".repeat(52);
13362
13603
  const lines = [
13363
- `-- Diagnostic Report (copy & share) ${sep2.slice(35)}`
13604
+ `\u2500\u2500 Diagnostic Report (copy & share) ${sep2.slice(35)}`
13364
13605
  ];
13365
13606
  for (const { key, value } of this.lines) {
13366
13607
  lines.push(`${key.padEnd(14)} ${value}`);
@@ -13374,10 +13615,10 @@ var Report = class {
13374
13615
  }
13375
13616
  };
13376
13617
  function ok(label, detail) {
13377
- outputLine(` \u2713 ${label.padEnd(14)} ${detail}`);
13618
+ outputLine(` [ok] ${label.padEnd(14)} ${detail}`);
13378
13619
  }
13379
13620
  function fail(label, detail, hints) {
13380
- outputLine(` \u2717 ${label.padEnd(14)} ${detail}`);
13621
+ outputLine(` [x] ${label.padEnd(14)} ${detail}`);
13381
13622
  for (const hint of hints) {
13382
13623
  outputLine(` \u2192 ${hint}`);
13383
13624
  }
@@ -13868,7 +14109,7 @@ async function cmdDiagnoseMcp(options = {}) {
13868
14109
 
13869
14110
  // src/commands/diagnose.ts
13870
14111
  var CLI_VERSION = readCliVersion();
13871
- var GIT_HASH = true ? "ce35abd7" : "dev";
14112
+ var GIT_HASH = true ? "f632d898" : "dev";
13872
14113
  function maskKey2(key) {
13873
14114
  if (!key) return "(not set)";
13874
14115
  if (key.length <= 8) return "****";
@@ -14932,6 +15173,11 @@ var CLI_REGISTRY = {
14932
15173
  usage: "okx earn savings rate-history [--ccy <ccy>] [--limit <n>]",
14933
15174
  description: "Query Simple Earn lending rates and fixed-term offers (requires auth)"
14934
15175
  },
15176
+ "fixed-products": {
15177
+ toolName: "earn_get_fixed_earn_products",
15178
+ usage: "okx earn savings fixed-products [--ccy <ccy>]",
15179
+ description: "List available fixed-term earn products with APR, term, and remaining quota"
15180
+ },
14935
15181
  "fixed-orders": {
14936
15182
  toolName: "earn_get_fixed_order_list",
14937
15183
  usage: "okx earn savings fixed-orders [--ccy <ccy>] [--state <pending|earning|expired|settled|cancelled>]",
@@ -14944,7 +15190,7 @@ var CLI_REGISTRY = {
14944
15190
  },
14945
15191
  "fixed-redeem": {
14946
15192
  toolName: "earn_fixed_redeem",
14947
- usage: "okx earn savings fixed-redeem <reqId>",
15193
+ usage: "okx earn savings fixed-redeem --reqId <reqId>",
14948
15194
  description: "Redeem a fixed-term earn order (full amount)"
14949
15195
  }
14950
15196
  }
@@ -16504,9 +16750,9 @@ async function cmdMarketIndicator(run, indicator, instId, opts) {
16504
16750
  const indicators = tfData?.["indicators"];
16505
16751
  const values = indicators?.[apiCode];
16506
16752
  if (!values?.length) continue;
16507
- process.stdout.write(`${instId} \xB7 ${apiCode} \xB7 ${tf}
16753
+ process.stdout.write(`${instId} | ${apiCode} | ${tf}
16508
16754
  `);
16509
- process.stdout.write("\u2500".repeat(40) + "\n");
16755
+ process.stdout.write("-".repeat(40) + "\n");
16510
16756
  if (opts.list) {
16511
16757
  const tableRows = values.map((entry) => ({
16512
16758
  ts: new Date(Number(entry["ts"])).toLocaleString(),
@@ -18659,13 +18905,24 @@ async function cmdEarnLendingRateHistory(run, opts) {
18659
18905
  printTable(fixedOffers.map((r) => ({
18660
18906
  ccy: r["ccy"],
18661
18907
  term: r["term"],
18662
- rate: r["rate"],
18908
+ apr: r["apr"],
18663
18909
  minLend: r["minLend"],
18664
18910
  remainingQuota: r["lendQuota"],
18665
18911
  soldOut: r["soldOut"] ? "Yes" : "No"
18666
18912
  })));
18667
18913
  }
18668
18914
  }
18915
+ async function cmdEarnFixedProducts(run, opts) {
18916
+ const data = extractData(await run("earn_get_fixed_earn_products", { ccy: opts.ccy }));
18917
+ printDataList(data, opts.json, "No fixed earn products available", (r) => ({
18918
+ ccy: r["ccy"],
18919
+ term: r["term"],
18920
+ apr: r["apr"],
18921
+ minLend: r["minLend"],
18922
+ remainingQuota: r["lendQuota"],
18923
+ soldOut: r["soldOut"] ? "Yes" : "No"
18924
+ }));
18925
+ }
18669
18926
  function extractFixedOffers(result) {
18670
18927
  if (result && typeof result === "object") {
18671
18928
  const offers = result["fixedOffers"];
@@ -19668,6 +19925,9 @@ function resolveNpx() {
19668
19925
  if (existsSync10(sibling)) return sibling;
19669
19926
  return "npx";
19670
19927
  }
19928
+ function npxEnv() {
19929
+ return { ...process.env, NO_COLOR: "1", FORCE_COLOR: "0" };
19930
+ }
19671
19931
  var THIRD_PARTY_INSTALL_NOTICE = "Note: This skill was created by a third-party developer, not by OKX. Review SKILL.md before use.";
19672
19932
  async function cmdSkillSearch(run, opts) {
19673
19933
  const args = {};
@@ -19717,7 +19977,7 @@ async function cmdSkillCategories(run, json) {
19717
19977
  }
19718
19978
  outputLine("");
19719
19979
  }
19720
- async function cmdSkillAdd(name, config, json) {
19980
+ async function cmdSkillAdd(name, config, json, exec = execFileSync2) {
19721
19981
  const tmpBase = join15(tmpdir(), `okx-skill-${randomUUID2()}`);
19722
19982
  mkdirSync12(tmpBase, { recursive: true });
19723
19983
  try {
@@ -19729,9 +19989,10 @@ async function cmdSkillAdd(name, config, json) {
19729
19989
  validateSkillMdExists(contentDir);
19730
19990
  outputLine("Installing to detected agents...");
19731
19991
  try {
19732
- execFileSync2(resolveNpx(), ["skills", "add", contentDir, "-y", "-g"], {
19992
+ exec(resolveNpx(), ["skills", "add", contentDir, "-y", "-g"], {
19733
19993
  stdio: "inherit",
19734
- timeout: 6e4
19994
+ timeout: 6e4,
19995
+ env: npxEnv()
19735
19996
  });
19736
19997
  } catch (e) {
19737
19998
  const savedZip = join15(process.cwd(), `${name}.zip`);
@@ -19760,7 +20021,7 @@ async function cmdSkillDownload(name, targetDir, config, json, format = "zip") {
19760
20021
  outputLine(` Path: ${filePath}`);
19761
20022
  }
19762
20023
  }
19763
- function cmdSkillRemove(name, json) {
20024
+ function cmdSkillRemove(name, json, exec = execFileSync2) {
19764
20025
  const removed = removeSkillRecord(name);
19765
20026
  if (!removed) {
19766
20027
  errorLine(`Skill "${name}" is not installed.`);
@@ -19768,9 +20029,10 @@ function cmdSkillRemove(name, json) {
19768
20029
  return;
19769
20030
  }
19770
20031
  try {
19771
- execFileSync2(resolveNpx(), ["skills", "remove", name, "-y", "-g"], {
20032
+ exec(resolveNpx(), ["skills", "remove", name, "-y", "-g"], {
19772
20033
  stdio: "inherit",
19773
- timeout: 6e4
20034
+ timeout: 6e4,
20035
+ env: npxEnv()
19774
20036
  });
19775
20037
  } catch {
19776
20038
  const agentsPath = join15(homedir13(), ".agents", "skills", name);
@@ -19782,7 +20044,7 @@ function cmdSkillRemove(name, json) {
19782
20044
  if (json) {
19783
20045
  outputLine(JSON.stringify({ name, status: "removed" }, null, 2));
19784
20046
  } else {
19785
- outputLine(`\u2713 Skill "${name}" removed`);
20047
+ outputLine(`[ok] Skill "${name}" removed`);
19786
20048
  }
19787
20049
  }
19788
20050
  async function cmdSkillCheck(run, name, json) {
@@ -20156,16 +20418,16 @@ async function cmdEventSeries(run, opts) {
20156
20418
  const updown = mainSeries.filter(isUpDownSeries);
20157
20419
  const above = mainSeries.filter((s) => !isUpDownSeries(s));
20158
20420
  if (updown.length > 0) {
20159
- process.stdout.write("\n\u2500\u2500 Up/Down \u2500\u2500\n");
20421
+ process.stdout.write("\n-- Up/Down --\n");
20160
20422
  printTable(updown.map((s) => toRow(s, isFeatured(s))));
20161
20423
  }
20162
20424
  if (above.length > 0) {
20163
- process.stdout.write("\n\u2500\u2500 Price Above \u2500\u2500\n");
20425
+ process.stdout.write("\n-- Price Above --\n");
20164
20426
  printTable(above.map((s) => toRow(s, isFeatured(s))));
20165
20427
  }
20166
20428
  if (testSeries.length > 0) {
20167
20429
  if (opts.all) {
20168
- process.stdout.write("\n\u2500\u2500 Test / Other \u2500\u2500\n");
20430
+ process.stdout.write("\n-- Test / Other --\n");
20169
20431
  printTable(testSeries.map((s) => toRow(s, false)));
20170
20432
  } else {
20171
20433
  process.stdout.write(`
@@ -20457,7 +20719,7 @@ async function cmdEventCancel(run, opts) {
20457
20719
  // src/index.ts
20458
20720
  var _require3 = createRequire3(import.meta.url);
20459
20721
  var CLI_VERSION2 = _require3("../package.json").version;
20460
- var GIT_HASH2 = true ? "ce35abd7" : "dev";
20722
+ var GIT_HASH2 = true ? "f632d898" : "dev";
20461
20723
  function handlePilotCommand(action, json, force, binaryPath) {
20462
20724
  if (action === "status") return cmdPilotStatus(json, binaryPath);
20463
20725
  if (action === "install") return cmdPilotInstall(json, binaryPath);
@@ -21651,6 +21913,7 @@ function handleEarnSavingsCommand(run, action, rest, v, json) {
21651
21913
  if (action === "set-rate") return cmdEarnSetLendingRate(run, { ccy: v.ccy, rate: v.rate, json });
21652
21914
  if (action === "lending-history") return cmdEarnLendingHistory(run, { ccy: v.ccy, limit, json });
21653
21915
  if (action === "rate-history") return cmdEarnLendingRateHistory(run, { ccy: v.ccy, limit, json });
21916
+ if (action === "fixed-products") return cmdEarnFixedProducts(run, { ccy: v.ccy, json });
21654
21917
  if (action === "fixed-orders") return cmdEarnFixedOrderList(run, { ccy: v.ccy, state: v.state, json });
21655
21918
  if (action === "fixed-purchase") return cmdEarnFixedPurchase(run, { ccy: v.ccy, amt: v.amt, term: v.term, confirm: v.confirm ?? false, json });
21656
21919
  if (action === "fixed-redeem") return cmdEarnFixedRedeem(run, { reqId: v.reqId, json });