@okx_ai/okx-trade-cli 1.3.5 → 1.3.6-beta.2
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 +836 -144
- package/dist/index.js.map +1 -1
- package/package.json +10 -10
- package/LICENSE +0 -21
- package/scripts/postinstall-download.js +0 -152
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";
|
|
@@ -33,16 +34,22 @@ import os from "os";
|
|
|
33
34
|
import { writeFileSync as writeFileSync2, renameSync as renameSync2, unlinkSync as unlinkSync2, mkdirSync as mkdirSync2 } from "fs";
|
|
34
35
|
import { join as join4, resolve, basename, sep } from "path";
|
|
35
36
|
import { randomUUID } from "crypto";
|
|
37
|
+
import { createHash } from "crypto";
|
|
38
|
+
import { readFileSync as readFileSync2, readdirSync, statSync } from "fs";
|
|
39
|
+
import { join as join5 } from "path";
|
|
40
|
+
import { createHash as createHash2, createPublicKey, verify as cryptoVerify } from "crypto";
|
|
41
|
+
import { readFileSync as readFileSync3 } from "fs";
|
|
42
|
+
import { join as join6, resolve as resolve2, sep as sep2 } from "path";
|
|
36
43
|
import yauzl from "yauzl";
|
|
37
44
|
import { createWriteStream, mkdirSync as mkdirSync3 } from "fs";
|
|
38
|
-
import { resolve as
|
|
39
|
-
import { readFileSync as
|
|
40
|
-
import { join as
|
|
41
|
-
import { readFileSync as
|
|
42
|
-
import { join as
|
|
45
|
+
import { resolve as resolve3, dirname as dirname2 } from "path";
|
|
46
|
+
import { readFileSync as readFileSync4, existsSync } from "fs";
|
|
47
|
+
import { join as join7 } from "path";
|
|
48
|
+
import { readFileSync as readFileSync5, writeFileSync as writeFileSync3, mkdirSync as mkdirSync4, existsSync as existsSync2 } from "fs";
|
|
49
|
+
import { join as join8, dirname as dirname3 } from "path";
|
|
43
50
|
import { homedir as homedir4 } from "os";
|
|
44
|
-
import { readFileSync as
|
|
45
|
-
import { join as
|
|
51
|
+
import { readFileSync as readFileSync6, writeFileSync as writeFileSync4, mkdirSync as mkdirSync5, existsSync as existsSync3 } from "fs";
|
|
52
|
+
import { join as join9, dirname as dirname4 } from "path";
|
|
46
53
|
import { homedir as homedir5 } from "os";
|
|
47
54
|
|
|
48
55
|
// ../../node_modules/.pnpm/smol-toml@1.6.0/node_modules/smol-toml/dist/error.js
|
|
@@ -120,7 +127,7 @@ function skipVoid(str, ptr, banNewLines, banComments) {
|
|
|
120
127
|
ptr++;
|
|
121
128
|
return banComments || c !== "#" ? ptr : skipVoid(str, skipComment(str, ptr), banNewLines);
|
|
122
129
|
}
|
|
123
|
-
function skipUntil(str, ptr,
|
|
130
|
+
function skipUntil(str, ptr, sep3, end, banNewLines = false) {
|
|
124
131
|
if (!end) {
|
|
125
132
|
ptr = indexOfNewline(str, ptr);
|
|
126
133
|
return ptr < 0 ? str.length : ptr;
|
|
@@ -129,7 +136,7 @@ function skipUntil(str, ptr, sep2, end, banNewLines = false) {
|
|
|
129
136
|
let c = str[i];
|
|
130
137
|
if (c === "#") {
|
|
131
138
|
i = indexOfNewline(str, i);
|
|
132
|
-
} else if (c ===
|
|
139
|
+
} else if (c === sep3) {
|
|
133
140
|
return i + 1;
|
|
134
141
|
} else if (c === end || banNewLines && (c === "\n" || c === "\r" && str[i + 1] === "\n")) {
|
|
135
142
|
return i;
|
|
@@ -872,8 +879,8 @@ function stringify(obj, { maxDepth = 1e3, numbersAsFloat = false } = {}) {
|
|
|
872
879
|
}
|
|
873
880
|
|
|
874
881
|
// ../core/dist/index.js
|
|
875
|
-
import { readFileSync as
|
|
876
|
-
import { join as
|
|
882
|
+
import { readFileSync as readFileSync7, writeFileSync as writeFileSync5, mkdirSync as mkdirSync6, existsSync as existsSync4 } from "fs";
|
|
883
|
+
import { join as join10 } from "path";
|
|
877
884
|
import { homedir as homedir6 } from "os";
|
|
878
885
|
import fs2 from "fs";
|
|
879
886
|
import path2 from "path";
|
|
@@ -890,24 +897,37 @@ import {
|
|
|
890
897
|
renameSync as renameSync4
|
|
891
898
|
} from "fs";
|
|
892
899
|
import { homedir as homedir9, platform as platform2 } from "os";
|
|
893
|
-
import { join as
|
|
900
|
+
import { join as join13, dirname as dirname7 } from "path";
|
|
894
901
|
import { createWriteStream as createWriteStream2, unlinkSync as unlinkSync3 } from "fs";
|
|
895
902
|
import { get as httpsGet } from "https";
|
|
896
903
|
import { get as httpGet } from "http";
|
|
897
904
|
import {
|
|
898
|
-
readFileSync as
|
|
905
|
+
readFileSync as readFileSync9,
|
|
899
906
|
mkdirSync as mkdirSync8,
|
|
900
907
|
chmodSync,
|
|
901
908
|
existsSync as existsSync6,
|
|
902
909
|
unlinkSync as unlinkSync4,
|
|
903
910
|
renameSync as renameSync3
|
|
904
911
|
} from "fs";
|
|
905
|
-
import { createHash } from "crypto";
|
|
912
|
+
import { createHash as createHash3 } from "crypto";
|
|
906
913
|
import { homedir as homedir8, platform, arch } from "os";
|
|
907
|
-
import { join as
|
|
908
|
-
import { readFileSync as
|
|
909
|
-
import { join as
|
|
914
|
+
import { join as join12, dirname as dirname6 } from "path";
|
|
915
|
+
import { readFileSync as readFileSync10, writeFileSync as writeFileSync7, mkdirSync as mkdirSync10, unlinkSync as unlinkSync6, existsSync as existsSync8 } from "fs";
|
|
916
|
+
import { join as join14 } from "path";
|
|
910
917
|
import { homedir as homedir10 } from "os";
|
|
918
|
+
function hasProxyEnv(env = process.env) {
|
|
919
|
+
return Boolean(
|
|
920
|
+
env.HTTPS_PROXY || env.https_proxy || env.HTTP_PROXY || env.http_proxy
|
|
921
|
+
);
|
|
922
|
+
}
|
|
923
|
+
function installEnvProxyDispatcher(deps = {}) {
|
|
924
|
+
const env = deps.env ?? process.env;
|
|
925
|
+
if (!hasProxyEnv(env)) return false;
|
|
926
|
+
const register = deps.register ?? (() => setGlobalDispatcher(new EnvHttpProxyAgent()));
|
|
927
|
+
register();
|
|
928
|
+
return true;
|
|
929
|
+
}
|
|
930
|
+
installEnvProxyDispatcher();
|
|
911
931
|
var EXEC_TIMEOUT_MS = 3e4;
|
|
912
932
|
var ALLOWED_DOMAIN_RE = /^[\w.-]+\.okx\.com$/;
|
|
913
933
|
var PILOT_BIN_DIR = join(homedir(), ".okx", "bin");
|
|
@@ -930,25 +950,25 @@ function execPilotBinary(domain, exclude = [], userAgent) {
|
|
|
930
950
|
if (userAgent) {
|
|
931
951
|
args.push("--user-agent", userAgent);
|
|
932
952
|
}
|
|
933
|
-
return new Promise((
|
|
953
|
+
return new Promise((resolve4) => {
|
|
934
954
|
execFile(
|
|
935
955
|
binPath,
|
|
936
956
|
args,
|
|
937
957
|
{ timeout: EXEC_TIMEOUT_MS, encoding: "utf-8" },
|
|
938
958
|
(error, stdout) => {
|
|
939
959
|
if (error) {
|
|
940
|
-
|
|
960
|
+
resolve4(null);
|
|
941
961
|
return;
|
|
942
962
|
}
|
|
943
963
|
try {
|
|
944
964
|
const result = JSON.parse(stdout);
|
|
945
965
|
if (result.code === 0 && result.data) {
|
|
946
|
-
|
|
966
|
+
resolve4(result.data);
|
|
947
967
|
} else {
|
|
948
|
-
|
|
968
|
+
resolve4(null);
|
|
949
969
|
}
|
|
950
970
|
} catch {
|
|
951
|
-
|
|
971
|
+
resolve4(null);
|
|
952
972
|
}
|
|
953
973
|
}
|
|
954
974
|
);
|
|
@@ -1282,7 +1302,7 @@ var EXIT_CODES = {
|
|
|
1282
1302
|
NOT_LOGGED_IN: 2,
|
|
1283
1303
|
REFRESH_FAILED: 3
|
|
1284
1304
|
};
|
|
1285
|
-
function finalizeToken(code, token,
|
|
1305
|
+
function finalizeToken(code, token, resolve4, reject) {
|
|
1286
1306
|
if (code === EXIT_CODES.SUCCESS) {
|
|
1287
1307
|
if (!token) {
|
|
1288
1308
|
reject(new AuthenticationError(
|
|
@@ -1291,7 +1311,7 @@ function finalizeToken(code, token, resolve3, reject) {
|
|
|
1291
1311
|
));
|
|
1292
1312
|
return;
|
|
1293
1313
|
}
|
|
1294
|
-
|
|
1314
|
+
resolve4(token);
|
|
1295
1315
|
return;
|
|
1296
1316
|
}
|
|
1297
1317
|
if (code === EXIT_CODES.NOT_LOGGED_IN) {
|
|
@@ -1328,7 +1348,7 @@ function defaultWindowsPipeName() {
|
|
|
1328
1348
|
return WIN_PIPE_PREFIX + randomBytes(32).toString("hex");
|
|
1329
1349
|
}
|
|
1330
1350
|
function execAuthTokenWindows(binPath, makePipeName = defaultWindowsPipeName) {
|
|
1331
|
-
return new Promise((
|
|
1351
|
+
return new Promise((resolve4, reject) => {
|
|
1332
1352
|
const pipeName = makePipeName();
|
|
1333
1353
|
const server = createServer();
|
|
1334
1354
|
const chunks = [];
|
|
@@ -1348,7 +1368,7 @@ function execAuthTokenWindows(binPath, makePipeName = defaultWindowsPipeName) {
|
|
|
1348
1368
|
const tryFinalize = () => {
|
|
1349
1369
|
if (!pipeClosed || exitCode === void 0) return;
|
|
1350
1370
|
const token = Buffer.concat(chunks).toString("utf-8").trim();
|
|
1351
|
-
settle(() => finalizeToken(exitCode, token,
|
|
1371
|
+
settle(() => finalizeToken(exitCode, token, resolve4, reject));
|
|
1352
1372
|
};
|
|
1353
1373
|
server.on("connection", (socket) => {
|
|
1354
1374
|
connectionMade = true;
|
|
@@ -1395,7 +1415,7 @@ function execAuthToken() {
|
|
|
1395
1415
|
return process.platform === "win32" ? execAuthTokenWindows(binPath) : execAuthTokenUnix(binPath);
|
|
1396
1416
|
}
|
|
1397
1417
|
function execAuthTokenUnix(binPath) {
|
|
1398
|
-
return new Promise((
|
|
1418
|
+
return new Promise((resolve4, reject) => {
|
|
1399
1419
|
const child = spawn2(binPath, ["token"], {
|
|
1400
1420
|
stdio: ["ignore", "ignore", "inherit", "pipe"]
|
|
1401
1421
|
// stdin stdout stderr fd3 (pipe)
|
|
@@ -1408,35 +1428,35 @@ function execAuthTokenUnix(binPath) {
|
|
|
1408
1428
|
});
|
|
1409
1429
|
child.on("close", (code) => {
|
|
1410
1430
|
const token = Buffer.concat(chunks).toString("utf-8").trim();
|
|
1411
|
-
finalizeToken(code, token,
|
|
1431
|
+
finalizeToken(code, token, resolve4, reject);
|
|
1412
1432
|
});
|
|
1413
1433
|
});
|
|
1414
1434
|
}
|
|
1415
1435
|
function execAuthStatus() {
|
|
1416
1436
|
const binPath = getAuthBinaryPath();
|
|
1417
|
-
return new Promise((
|
|
1437
|
+
return new Promise((resolve4) => {
|
|
1418
1438
|
execFile2(
|
|
1419
1439
|
binPath,
|
|
1420
1440
|
["status", "--json"],
|
|
1421
1441
|
{ timeout: EXEC_TIMEOUT_MS2, encoding: "utf-8" },
|
|
1422
1442
|
(error, stdout) => {
|
|
1423
1443
|
if (error) {
|
|
1424
|
-
|
|
1444
|
+
resolve4(null);
|
|
1425
1445
|
return;
|
|
1426
1446
|
}
|
|
1427
1447
|
try {
|
|
1428
1448
|
const result = JSON.parse(stdout);
|
|
1429
|
-
|
|
1449
|
+
resolve4(result);
|
|
1430
1450
|
} catch {
|
|
1431
|
-
|
|
1451
|
+
resolve4(null);
|
|
1432
1452
|
}
|
|
1433
1453
|
}
|
|
1434
1454
|
);
|
|
1435
1455
|
});
|
|
1436
1456
|
}
|
|
1437
1457
|
function sleep(ms) {
|
|
1438
|
-
return new Promise((
|
|
1439
|
-
setTimeout(
|
|
1458
|
+
return new Promise((resolve4) => {
|
|
1459
|
+
setTimeout(resolve4, ms);
|
|
1440
1460
|
});
|
|
1441
1461
|
}
|
|
1442
1462
|
var RateLimiter = class {
|
|
@@ -2506,6 +2526,7 @@ function registerIndicatorTools() {
|
|
|
2506
2526
|
return [
|
|
2507
2527
|
{
|
|
2508
2528
|
name: "market_get_indicator",
|
|
2529
|
+
title: "Get Technical Indicator",
|
|
2509
2530
|
module: "market",
|
|
2510
2531
|
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
2532
|
isWrite: false,
|
|
@@ -2579,6 +2600,7 @@ function registerIndicatorTools() {
|
|
|
2579
2600
|
},
|
|
2580
2601
|
{
|
|
2581
2602
|
name: "market_list_indicators",
|
|
2603
|
+
title: "List Technical Indicators",
|
|
2582
2604
|
module: "market",
|
|
2583
2605
|
description: "List all supported technical indicator names and descriptions. Call this before market_get_indicator to discover valid indicator names. No credentials required.",
|
|
2584
2606
|
isWrite: false,
|
|
@@ -2665,6 +2687,7 @@ function registerAccountTools() {
|
|
|
2665
2687
|
return [
|
|
2666
2688
|
{
|
|
2667
2689
|
name: "account_get_balance",
|
|
2690
|
+
title: "Get Trading Account Balance",
|
|
2668
2691
|
module: "account",
|
|
2669
2692
|
description: "Get account balance for trading account. Returns balances for all currencies or a specific one.",
|
|
2670
2693
|
isWrite: false,
|
|
@@ -2689,9 +2712,11 @@ function registerAccountTools() {
|
|
|
2689
2712
|
},
|
|
2690
2713
|
{
|
|
2691
2714
|
name: "account_transfer",
|
|
2715
|
+
title: "Transfer Between Accounts",
|
|
2692
2716
|
module: "account",
|
|
2693
2717
|
description: "Transfer funds between accounts (trading, funding, etc.). [CAUTION] Moves real funds.",
|
|
2694
2718
|
isWrite: true,
|
|
2719
|
+
destructiveHint: false,
|
|
2695
2720
|
inputSchema: {
|
|
2696
2721
|
type: "object",
|
|
2697
2722
|
properties: {
|
|
@@ -2746,6 +2771,7 @@ function registerAccountTools() {
|
|
|
2746
2771
|
},
|
|
2747
2772
|
{
|
|
2748
2773
|
name: "account_get_max_size",
|
|
2774
|
+
title: "Get Max Order Size",
|
|
2749
2775
|
module: "account",
|
|
2750
2776
|
description: "Get max buy/sell order size for a SWAP/FUTURES instrument given current balance and leverage. Useful before placing orders.",
|
|
2751
2777
|
isWrite: false,
|
|
@@ -2793,6 +2819,7 @@ function registerAccountTools() {
|
|
|
2793
2819
|
},
|
|
2794
2820
|
{
|
|
2795
2821
|
name: "account_get_asset_balance",
|
|
2822
|
+
title: "Get Funding Account Balance",
|
|
2796
2823
|
module: "account",
|
|
2797
2824
|
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
2825
|
isWrite: false,
|
|
@@ -2856,6 +2883,7 @@ function registerAccountTools() {
|
|
|
2856
2883
|
},
|
|
2857
2884
|
{
|
|
2858
2885
|
name: "account_get_bills",
|
|
2886
|
+
title: "Get Account Bills",
|
|
2859
2887
|
module: "account",
|
|
2860
2888
|
description: "Get account ledger: fees paid, funding charges, realized PnL, transfers, etc. Default 20 records (last 7 days), max 100.",
|
|
2861
2889
|
isWrite: false,
|
|
@@ -2922,6 +2950,7 @@ function registerAccountTools() {
|
|
|
2922
2950
|
},
|
|
2923
2951
|
{
|
|
2924
2952
|
name: "account_get_positions_history",
|
|
2953
|
+
title: "Get Closed Positions History",
|
|
2925
2954
|
module: "account",
|
|
2926
2955
|
description: "Get closed position history for SWAP or FUTURES. Default 20 records, max 100.",
|
|
2927
2956
|
isWrite: false,
|
|
@@ -2983,6 +3012,7 @@ function registerAccountTools() {
|
|
|
2983
3012
|
},
|
|
2984
3013
|
{
|
|
2985
3014
|
name: "account_get_trade_fee",
|
|
3015
|
+
title: "Get Trade Fee Tier",
|
|
2986
3016
|
module: "account",
|
|
2987
3017
|
description: "Get maker/taker fee rates for the account. Useful to understand your fee tier before trading.",
|
|
2988
3018
|
isWrite: false,
|
|
@@ -3015,6 +3045,7 @@ function registerAccountTools() {
|
|
|
3015
3045
|
},
|
|
3016
3046
|
{
|
|
3017
3047
|
name: "account_get_config",
|
|
3048
|
+
title: "Get Account Configuration",
|
|
3018
3049
|
module: "account",
|
|
3019
3050
|
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
3051
|
isWrite: false,
|
|
@@ -3033,6 +3064,7 @@ function registerAccountTools() {
|
|
|
3033
3064
|
},
|
|
3034
3065
|
{
|
|
3035
3066
|
name: "account_get_max_withdrawal",
|
|
3067
|
+
title: "Get Max Withdrawable Amount",
|
|
3036
3068
|
module: "account",
|
|
3037
3069
|
description: "Get maximum withdrawable amount for a currency from the trading account. Useful before initiating a transfer or withdrawal.",
|
|
3038
3070
|
isWrite: false,
|
|
@@ -3057,6 +3089,7 @@ function registerAccountTools() {
|
|
|
3057
3089
|
},
|
|
3058
3090
|
{
|
|
3059
3091
|
name: "account_get_max_avail_size",
|
|
3092
|
+
title: "Get Max Available Position Size",
|
|
3060
3093
|
module: "account",
|
|
3061
3094
|
description: "Get maximum available size for opening or reducing a position. Different from account_get_max_size which calculates new order size.",
|
|
3062
3095
|
isWrite: false,
|
|
@@ -3101,6 +3134,7 @@ function registerAccountTools() {
|
|
|
3101
3134
|
},
|
|
3102
3135
|
{
|
|
3103
3136
|
name: "account_get_positions",
|
|
3137
|
+
title: "Get Current Positions",
|
|
3104
3138
|
module: "account",
|
|
3105
3139
|
description: "Get current open positions across all instrument types (MARGIN, SWAP, FUTURES, OPTION, EVENTS). Use swap_get_positions for SWAP/FUTURES-only queries.",
|
|
3106
3140
|
isWrite: false,
|
|
@@ -3136,6 +3170,7 @@ function registerAccountTools() {
|
|
|
3136
3170
|
},
|
|
3137
3171
|
{
|
|
3138
3172
|
name: "account_get_bills_archive",
|
|
3173
|
+
title: "Get Archived Account Bills",
|
|
3139
3174
|
module: "account",
|
|
3140
3175
|
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
3176
|
isWrite: false,
|
|
@@ -3202,9 +3237,11 @@ function registerAccountTools() {
|
|
|
3202
3237
|
},
|
|
3203
3238
|
{
|
|
3204
3239
|
name: "account_set_position_mode",
|
|
3240
|
+
title: "Set Position Mode",
|
|
3205
3241
|
module: "account",
|
|
3206
3242
|
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
3243
|
isWrite: true,
|
|
3244
|
+
idempotentHint: true,
|
|
3208
3245
|
inputSchema: {
|
|
3209
3246
|
type: "object",
|
|
3210
3247
|
properties: {
|
|
@@ -3344,6 +3381,8 @@ function registerAlgoTradeTools() {
|
|
|
3344
3381
|
return [
|
|
3345
3382
|
{
|
|
3346
3383
|
name: "swap_place_algo_order",
|
|
3384
|
+
title: "Perpetual Futures Place Algo Order",
|
|
3385
|
+
destructiveHint: false,
|
|
3347
3386
|
module: "swap",
|
|
3348
3387
|
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
3388
|
isWrite: true,
|
|
@@ -3492,6 +3531,8 @@ function registerAlgoTradeTools() {
|
|
|
3492
3531
|
},
|
|
3493
3532
|
{
|
|
3494
3533
|
name: "swap_place_move_stop_order",
|
|
3534
|
+
title: "Perpetual Futures Place Move-Stop Order",
|
|
3535
|
+
destructiveHint: false,
|
|
3495
3536
|
module: "swap",
|
|
3496
3537
|
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
3538
|
isWrite: true,
|
|
@@ -3569,6 +3610,8 @@ function registerAlgoTradeTools() {
|
|
|
3569
3610
|
},
|
|
3570
3611
|
{
|
|
3571
3612
|
name: "swap_cancel_algo_orders",
|
|
3613
|
+
title: "Perpetual Futures Cancel Algo Orders",
|
|
3614
|
+
idempotentHint: true,
|
|
3572
3615
|
module: "swap",
|
|
3573
3616
|
description: "Cancel one or more pending SWAP/FUTURES algo orders (TP/SL). Accepts a list of {algoId, instId} objects.",
|
|
3574
3617
|
isWrite: true,
|
|
@@ -3612,6 +3655,7 @@ function registerAlgoTradeTools() {
|
|
|
3612
3655
|
},
|
|
3613
3656
|
{
|
|
3614
3657
|
name: "swap_get_algo_orders",
|
|
3658
|
+
title: "Perpetual Futures Get Algo Orders",
|
|
3615
3659
|
module: "swap",
|
|
3616
3660
|
description: "Query pending or completed SWAP/FUTURES algo orders (TP/SL, OCO, trailing stop).",
|
|
3617
3661
|
isWrite: false,
|
|
@@ -3704,6 +3748,8 @@ function registerFuturesAlgoTools() {
|
|
|
3704
3748
|
return [
|
|
3705
3749
|
{
|
|
3706
3750
|
name: "futures_place_algo_order",
|
|
3751
|
+
title: "Futures Place Algo Order",
|
|
3752
|
+
destructiveHint: false,
|
|
3707
3753
|
module: "futures",
|
|
3708
3754
|
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
3755
|
isWrite: true,
|
|
@@ -3852,6 +3898,8 @@ function registerFuturesAlgoTools() {
|
|
|
3852
3898
|
},
|
|
3853
3899
|
{
|
|
3854
3900
|
name: "futures_place_move_stop_order",
|
|
3901
|
+
title: "Futures Place Move-Stop Order",
|
|
3902
|
+
destructiveHint: false,
|
|
3855
3903
|
module: "futures",
|
|
3856
3904
|
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
3905
|
isWrite: true,
|
|
@@ -3929,6 +3977,8 @@ function registerFuturesAlgoTools() {
|
|
|
3929
3977
|
},
|
|
3930
3978
|
{
|
|
3931
3979
|
name: "futures_amend_algo_order",
|
|
3980
|
+
title: "Futures Amend Algo Order",
|
|
3981
|
+
idempotentHint: true,
|
|
3932
3982
|
module: "futures",
|
|
3933
3983
|
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
3984
|
isWrite: true,
|
|
@@ -3965,6 +4015,8 @@ function registerFuturesAlgoTools() {
|
|
|
3965
4015
|
},
|
|
3966
4016
|
{
|
|
3967
4017
|
name: "futures_cancel_algo_orders",
|
|
4018
|
+
title: "Futures Cancel Algo Orders",
|
|
4019
|
+
idempotentHint: true,
|
|
3968
4020
|
module: "futures",
|
|
3969
4021
|
description: "Cancel one or more pending FUTURES delivery algo orders (TP/SL). Accepts a list of {algoId, instId} objects.",
|
|
3970
4022
|
isWrite: true,
|
|
@@ -4002,6 +4054,7 @@ function registerFuturesAlgoTools() {
|
|
|
4002
4054
|
},
|
|
4003
4055
|
{
|
|
4004
4056
|
name: "futures_get_algo_orders",
|
|
4057
|
+
title: "Futures Get Algo Orders",
|
|
4005
4058
|
module: "futures",
|
|
4006
4059
|
description: "Query pending or completed FUTURES delivery algo orders (TP/SL, OCO, trailing stop).",
|
|
4007
4060
|
isWrite: false,
|
|
@@ -4123,6 +4176,7 @@ function registerAuditTools() {
|
|
|
4123
4176
|
{
|
|
4124
4177
|
name: "trade_get_history",
|
|
4125
4178
|
module: "account",
|
|
4179
|
+
title: "Get Tool-Call Audit Log",
|
|
4126
4180
|
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
4181
|
isWrite: false,
|
|
4128
4182
|
inputSchema: {
|
|
@@ -4228,6 +4282,223 @@ async function downloadSkillZip(client, name, targetDir, format = "zip") {
|
|
|
4228
4282
|
const filePath = safeWriteFile(targetDir, fileName, result.data);
|
|
4229
4283
|
return filePath;
|
|
4230
4284
|
}
|
|
4285
|
+
function listFilesRecursive(dir, base = "") {
|
|
4286
|
+
const results = [];
|
|
4287
|
+
for (const entry of readdirSync(dir)) {
|
|
4288
|
+
const fullPath = join5(dir, entry);
|
|
4289
|
+
const relPath = base ? `${base}/${entry}` : entry;
|
|
4290
|
+
if (statSync(fullPath).isDirectory()) {
|
|
4291
|
+
results.push(...listFilesRecursive(fullPath, relPath));
|
|
4292
|
+
} else {
|
|
4293
|
+
results.push(relPath);
|
|
4294
|
+
}
|
|
4295
|
+
}
|
|
4296
|
+
return results;
|
|
4297
|
+
}
|
|
4298
|
+
function computeFileHashes(contentDir) {
|
|
4299
|
+
const hashes = {};
|
|
4300
|
+
for (const relPath of listFilesRecursive(contentDir)) {
|
|
4301
|
+
if (relPath === "_meta.json") continue;
|
|
4302
|
+
const bytes = readFileSync2(join5(contentDir, relPath));
|
|
4303
|
+
hashes[relPath] = "sha256:" + createHash("sha256").update(bytes).digest("hex");
|
|
4304
|
+
}
|
|
4305
|
+
return hashes;
|
|
4306
|
+
}
|
|
4307
|
+
async function getPublicKey(client, keyId) {
|
|
4308
|
+
try {
|
|
4309
|
+
const query = {};
|
|
4310
|
+
if (keyId !== void 0) query.keyId = keyId;
|
|
4311
|
+
const result = await client.privateGet(
|
|
4312
|
+
"/api/v5/skill/signing-key",
|
|
4313
|
+
query
|
|
4314
|
+
);
|
|
4315
|
+
if (!result.data || result.data.status !== "active") return null;
|
|
4316
|
+
return result.data.publicKey;
|
|
4317
|
+
} catch (e) {
|
|
4318
|
+
if (e instanceof ConfigError) throw e;
|
|
4319
|
+
return null;
|
|
4320
|
+
}
|
|
4321
|
+
}
|
|
4322
|
+
var ED25519_SPKI_HEADER = Buffer.from("302a300506032b6570032100", "hex");
|
|
4323
|
+
function parseSSHPublicKey(base64) {
|
|
4324
|
+
try {
|
|
4325
|
+
const buf = Buffer.from(base64, "base64");
|
|
4326
|
+
let offset = 0;
|
|
4327
|
+
if (buf.length < 4) return null;
|
|
4328
|
+
const algLen = buf.readUInt32BE(offset);
|
|
4329
|
+
offset += 4;
|
|
4330
|
+
if (offset + algLen + 4 > buf.length) return null;
|
|
4331
|
+
if (buf.subarray(offset, offset + algLen).toString("ascii") !== "ssh-ed25519") return null;
|
|
4332
|
+
offset += algLen;
|
|
4333
|
+
const keyLen = buf.readUInt32BE(offset);
|
|
4334
|
+
offset += 4;
|
|
4335
|
+
if (keyLen !== 32 || offset + keyLen > buf.length) return null;
|
|
4336
|
+
return buf.subarray(offset, offset + keyLen);
|
|
4337
|
+
} catch {
|
|
4338
|
+
return null;
|
|
4339
|
+
}
|
|
4340
|
+
}
|
|
4341
|
+
async function verifySkillSignature(contentDir, signing, opts) {
|
|
4342
|
+
if (!signing) {
|
|
4343
|
+
const fallback = await tryServerFallback(contentDir, opts);
|
|
4344
|
+
if (fallback?.status === "verified_by_server") return fallback;
|
|
4345
|
+
return {
|
|
4346
|
+
status: "failed",
|
|
4347
|
+
error: fallback?.error ?? "Skill is not signed",
|
|
4348
|
+
...fallback?.mismatched?.length && { mismatched: fallback.mismatched }
|
|
4349
|
+
};
|
|
4350
|
+
}
|
|
4351
|
+
const publicKeyBase64 = opts?.fetchPublicKey ? await opts.fetchPublicKey(signing.public_key_id) ?? "" : "";
|
|
4352
|
+
if (!publicKeyBase64) {
|
|
4353
|
+
return localFail(
|
|
4354
|
+
contentDir,
|
|
4355
|
+
opts,
|
|
4356
|
+
`Unknown signing key: ${signing.public_key_id}. Please update CLI.`,
|
|
4357
|
+
"Unknown signing key \u2014 consider updating CLI"
|
|
4358
|
+
);
|
|
4359
|
+
}
|
|
4360
|
+
const rawKey = parseSSHPublicKey(publicKeyBase64);
|
|
4361
|
+
if (!rawKey) {
|
|
4362
|
+
return localFail(
|
|
4363
|
+
contentDir,
|
|
4364
|
+
opts,
|
|
4365
|
+
`Malformed public key for key ID: ${signing.public_key_id}`,
|
|
4366
|
+
"Malformed public key from server \u2014 consider updating CLI",
|
|
4367
|
+
signing.public_key_id
|
|
4368
|
+
);
|
|
4369
|
+
}
|
|
4370
|
+
if (!ed25519Verify(signing, rawKey)) {
|
|
4371
|
+
return localFail(
|
|
4372
|
+
contentDir,
|
|
4373
|
+
opts,
|
|
4374
|
+
"Invalid signature",
|
|
4375
|
+
"Signature mismatch \u2014 key may be outdated or file was modified",
|
|
4376
|
+
signing.public_key_id
|
|
4377
|
+
);
|
|
4378
|
+
}
|
|
4379
|
+
const bindingError = checkNameVersionBinding(signing, opts);
|
|
4380
|
+
if (bindingError) {
|
|
4381
|
+
return { status: "failed", error: bindingError, publicKeyId: signing.public_key_id };
|
|
4382
|
+
}
|
|
4383
|
+
const integrityFailure = await checkFileIntegrity(signing, contentDir, opts);
|
|
4384
|
+
if (integrityFailure) return integrityFailure;
|
|
4385
|
+
const signedFiles = new Set(Object.keys(signing.files));
|
|
4386
|
+
const extraFiles = listFilesRecursive(contentDir).filter(
|
|
4387
|
+
(f) => f !== "_meta.json" && !signedFiles.has(f)
|
|
4388
|
+
);
|
|
4389
|
+
return { status: "verified", publicKeyId: signing.public_key_id, filesChecked: signedFiles.size, extraFiles };
|
|
4390
|
+
}
|
|
4391
|
+
async function localFail(contentDir, opts, localError, serverHint, keyId) {
|
|
4392
|
+
const fallback = await tryServerFallback(contentDir, opts);
|
|
4393
|
+
if (fallback?.status === "verified_by_server") {
|
|
4394
|
+
return { ...fallback, error: serverHint };
|
|
4395
|
+
}
|
|
4396
|
+
return {
|
|
4397
|
+
status: "failed",
|
|
4398
|
+
error: localError,
|
|
4399
|
+
...keyId && { publicKeyId: keyId },
|
|
4400
|
+
...fallback?.mismatched?.length && { mismatched: fallback.mismatched }
|
|
4401
|
+
};
|
|
4402
|
+
}
|
|
4403
|
+
function ed25519Verify(signing, rawKey) {
|
|
4404
|
+
const payloadObj = { files: signing.files, public_key_id: signing.public_key_id };
|
|
4405
|
+
if (signing.name !== void 0) payloadObj.name = signing.name;
|
|
4406
|
+
if (signing.version !== void 0) payloadObj.version = signing.version;
|
|
4407
|
+
const message = Buffer.from(canonicalize(payloadObj), "utf-8");
|
|
4408
|
+
const sig = Buffer.from(signing.signature, "base64");
|
|
4409
|
+
const spkiKey = Buffer.concat([ED25519_SPKI_HEADER, rawKey]);
|
|
4410
|
+
const keyObj = createPublicKey({ key: spkiKey, format: "der", type: "spki" });
|
|
4411
|
+
return cryptoVerify(null, message, keyObj, sig);
|
|
4412
|
+
}
|
|
4413
|
+
function checkNameVersionBinding(signing, opts) {
|
|
4414
|
+
if (signing.name !== void 0 && opts?.skillName !== void 0 && signing.name !== opts.skillName) {
|
|
4415
|
+
return `Skill name mismatch: signature binds "${signing.name}" but installing as "${opts.skillName}"`;
|
|
4416
|
+
}
|
|
4417
|
+
if (signing.version !== void 0 && opts?.skillVersion !== void 0 && signing.version !== opts.skillVersion) {
|
|
4418
|
+
return `Skill version mismatch: signature binds "${signing.version}" but package declares "${opts.skillVersion}"`;
|
|
4419
|
+
}
|
|
4420
|
+
return null;
|
|
4421
|
+
}
|
|
4422
|
+
async function checkFileIntegrity(signing, contentDir, opts) {
|
|
4423
|
+
const resolvedContentDir = resolve2(contentDir);
|
|
4424
|
+
for (const [filename, expectedHash] of Object.entries(signing.files)) {
|
|
4425
|
+
const resolvedPath = resolve2(join6(contentDir, filename));
|
|
4426
|
+
if (!resolvedPath.startsWith(resolvedContentDir + sep2)) {
|
|
4427
|
+
return { status: "failed", error: `Path traversal detected in signing manifest: ${filename}`, publicKeyId: signing.public_key_id };
|
|
4428
|
+
}
|
|
4429
|
+
let bytes;
|
|
4430
|
+
try {
|
|
4431
|
+
bytes = readFileSync3(resolvedPath);
|
|
4432
|
+
} catch {
|
|
4433
|
+
return { status: "failed", error: `File missing: ${filename}`, publicKeyId: signing.public_key_id };
|
|
4434
|
+
}
|
|
4435
|
+
const actual = "sha256:" + createHash2("sha256").update(bytes).digest("hex");
|
|
4436
|
+
if (actual !== expectedHash) {
|
|
4437
|
+
return localFail(
|
|
4438
|
+
contentDir,
|
|
4439
|
+
opts,
|
|
4440
|
+
`File integrity check failed: ${filename}`,
|
|
4441
|
+
"_meta.json may be corrupted",
|
|
4442
|
+
signing.public_key_id
|
|
4443
|
+
);
|
|
4444
|
+
}
|
|
4445
|
+
}
|
|
4446
|
+
return null;
|
|
4447
|
+
}
|
|
4448
|
+
async function tryServerFallback(contentDir, opts) {
|
|
4449
|
+
if (!opts?.serverSideVerify || !opts.skillName) return null;
|
|
4450
|
+
const fileHashes = computeFileHashes(contentDir);
|
|
4451
|
+
const result = await opts.serverSideVerify(
|
|
4452
|
+
opts.skillName,
|
|
4453
|
+
opts.skillVersion,
|
|
4454
|
+
fileHashes
|
|
4455
|
+
);
|
|
4456
|
+
if (!result) return null;
|
|
4457
|
+
if (result.verified) {
|
|
4458
|
+
return {
|
|
4459
|
+
status: "verified_by_server",
|
|
4460
|
+
filesChecked: Object.keys(fileHashes).length,
|
|
4461
|
+
serverVersion: result.version
|
|
4462
|
+
};
|
|
4463
|
+
}
|
|
4464
|
+
return {
|
|
4465
|
+
status: "failed",
|
|
4466
|
+
filesChecked: Object.keys(fileHashes).length,
|
|
4467
|
+
mismatched: result.mismatched,
|
|
4468
|
+
...result.message && { error: result.message }
|
|
4469
|
+
};
|
|
4470
|
+
}
|
|
4471
|
+
function canonicalize(obj) {
|
|
4472
|
+
return JSON.stringify(deepSortKeys(obj));
|
|
4473
|
+
}
|
|
4474
|
+
function compareKeys(a, b) {
|
|
4475
|
+
if (a < b) return -1;
|
|
4476
|
+
if (a > b) return 1;
|
|
4477
|
+
return 0;
|
|
4478
|
+
}
|
|
4479
|
+
function deepSortKeys(val) {
|
|
4480
|
+
if (val === null || typeof val !== "object") return val;
|
|
4481
|
+
if (Array.isArray(val)) return val.map(deepSortKeys);
|
|
4482
|
+
const sorted = {};
|
|
4483
|
+
for (const key of Object.keys(val).sort(compareKeys)) {
|
|
4484
|
+
sorted[key] = deepSortKeys(val[key]);
|
|
4485
|
+
}
|
|
4486
|
+
return sorted;
|
|
4487
|
+
}
|
|
4488
|
+
async function serverSideVerify(client, skillName, version, files) {
|
|
4489
|
+
try {
|
|
4490
|
+
const body = { skillName, files };
|
|
4491
|
+
if (version !== void 0) body.version = version;
|
|
4492
|
+
const result = await client.privatePost(
|
|
4493
|
+
"/api/v5/skill/verify",
|
|
4494
|
+
body
|
|
4495
|
+
);
|
|
4496
|
+
return result.data;
|
|
4497
|
+
} catch (e) {
|
|
4498
|
+
if (e instanceof ConfigError) throw e;
|
|
4499
|
+
return null;
|
|
4500
|
+
}
|
|
4501
|
+
}
|
|
4231
4502
|
var DEFAULT_MAX_TOTAL_BYTES = 100 * 1024 * 1024;
|
|
4232
4503
|
var DEFAULT_MAX_FILES = 1e3;
|
|
4233
4504
|
var DEFAULT_MAX_COMPRESSION_RATIO = 100;
|
|
@@ -4266,7 +4537,7 @@ async function extractSkillZip(zipPath, targetDir, limits) {
|
|
|
4266
4537
|
const maxTotalBytes = limits?.maxTotalBytes ?? DEFAULT_MAX_TOTAL_BYTES;
|
|
4267
4538
|
const maxFiles = limits?.maxFiles ?? DEFAULT_MAX_FILES;
|
|
4268
4539
|
const maxCompressionRatio = limits?.maxCompressionRatio ?? DEFAULT_MAX_COMPRESSION_RATIO;
|
|
4269
|
-
const resolvedTarget =
|
|
4540
|
+
const resolvedTarget = resolve3(targetDir);
|
|
4270
4541
|
mkdirSync3(resolvedTarget, { recursive: true });
|
|
4271
4542
|
return new Promise((resolvePromise, reject) => {
|
|
4272
4543
|
yauzl.open(zipPath, { lazyEntries: true }, (err, zipfile) => {
|
|
@@ -4306,11 +4577,11 @@ async function extractSkillZip(zipPath, targetDir, limits) {
|
|
|
4306
4577
|
});
|
|
4307
4578
|
}
|
|
4308
4579
|
function readMetaJson(contentDir) {
|
|
4309
|
-
const metaPath =
|
|
4580
|
+
const metaPath = join7(contentDir, "_meta.json");
|
|
4310
4581
|
if (!existsSync(metaPath)) {
|
|
4311
4582
|
throw new Error(`_meta.json not found in ${contentDir}. Invalid skill package.`);
|
|
4312
4583
|
}
|
|
4313
|
-
const raw =
|
|
4584
|
+
const raw = readFileSync4(metaPath, "utf-8");
|
|
4314
4585
|
let parsed;
|
|
4315
4586
|
try {
|
|
4316
4587
|
parsed = JSON.parse(raw);
|
|
@@ -4328,22 +4599,49 @@ function readMetaJson(contentDir) {
|
|
|
4328
4599
|
name: String(meta.name),
|
|
4329
4600
|
version: String(meta.version),
|
|
4330
4601
|
title: typeof meta.title === "string" ? meta.title : "",
|
|
4331
|
-
description: typeof meta.description === "string" ? meta.description : ""
|
|
4602
|
+
description: typeof meta.description === "string" ? meta.description : "",
|
|
4603
|
+
signing: parseSigningBlock(meta.signing)
|
|
4604
|
+
};
|
|
4605
|
+
}
|
|
4606
|
+
function tryReadMetaJson(contentDir) {
|
|
4607
|
+
try {
|
|
4608
|
+
return readMetaJson(contentDir);
|
|
4609
|
+
} catch {
|
|
4610
|
+
return null;
|
|
4611
|
+
}
|
|
4612
|
+
}
|
|
4613
|
+
function parseSigningBlock(raw) {
|
|
4614
|
+
if (!raw || typeof raw !== "object") return void 0;
|
|
4615
|
+
const s = raw;
|
|
4616
|
+
if (typeof s.signature !== "string" || typeof s.public_key_id !== "string" || !s.files) {
|
|
4617
|
+
return void 0;
|
|
4618
|
+
}
|
|
4619
|
+
const rawFiles = s.files;
|
|
4620
|
+
const files = {};
|
|
4621
|
+
for (const [key, value] of Object.entries(rawFiles)) {
|
|
4622
|
+
if (typeof value === "string") files[key] = value;
|
|
4623
|
+
}
|
|
4624
|
+
return {
|
|
4625
|
+
signature: s.signature,
|
|
4626
|
+
public_key_id: s.public_key_id,
|
|
4627
|
+
files,
|
|
4628
|
+
...typeof s.name === "string" && { name: s.name },
|
|
4629
|
+
...typeof s.version === "string" && { version: s.version }
|
|
4332
4630
|
};
|
|
4333
4631
|
}
|
|
4334
4632
|
function validateSkillMdExists(contentDir) {
|
|
4335
|
-
const skillMdPath =
|
|
4633
|
+
const skillMdPath = join7(contentDir, "SKILL.md");
|
|
4336
4634
|
if (!existsSync(skillMdPath)) {
|
|
4337
4635
|
throw new Error(`SKILL.md not found in ${contentDir}. Invalid skill package.`);
|
|
4338
4636
|
}
|
|
4339
4637
|
}
|
|
4340
|
-
var DEFAULT_REGISTRY_PATH =
|
|
4638
|
+
var DEFAULT_REGISTRY_PATH = join8(homedir4(), ".okx", "skills", "registry.json");
|
|
4341
4639
|
function readRegistry(registryPath = DEFAULT_REGISTRY_PATH) {
|
|
4342
4640
|
if (!existsSync2(registryPath)) {
|
|
4343
4641
|
return { version: 1, skills: {} };
|
|
4344
4642
|
}
|
|
4345
4643
|
try {
|
|
4346
|
-
const raw =
|
|
4644
|
+
const raw = readFileSync5(registryPath, "utf-8");
|
|
4347
4645
|
return JSON.parse(raw);
|
|
4348
4646
|
} catch {
|
|
4349
4647
|
return { version: 1, skills: {} };
|
|
@@ -4353,7 +4651,7 @@ function writeRegistry(registry, registryPath = DEFAULT_REGISTRY_PATH) {
|
|
|
4353
4651
|
mkdirSync4(dirname3(registryPath), { recursive: true });
|
|
4354
4652
|
writeFileSync3(registryPath, JSON.stringify(registry, null, 2) + "\n", "utf-8");
|
|
4355
4653
|
}
|
|
4356
|
-
function upsertSkillRecord(meta, registryPath = DEFAULT_REGISTRY_PATH) {
|
|
4654
|
+
function upsertSkillRecord(meta, registryPath = DEFAULT_REGISTRY_PATH, verification) {
|
|
4357
4655
|
const registry = readRegistry(registryPath);
|
|
4358
4656
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
4359
4657
|
const existing = registry.skills[meta.name];
|
|
@@ -4363,7 +4661,8 @@ function upsertSkillRecord(meta, registryPath = DEFAULT_REGISTRY_PATH) {
|
|
|
4363
4661
|
description: meta.description,
|
|
4364
4662
|
installedAt: existing?.installedAt ?? now,
|
|
4365
4663
|
updatedAt: now,
|
|
4366
|
-
source: "marketplace"
|
|
4664
|
+
source: "marketplace",
|
|
4665
|
+
...verification !== void 0 && { verification }
|
|
4367
4666
|
};
|
|
4368
4667
|
writeRegistry(registry, registryPath);
|
|
4369
4668
|
}
|
|
@@ -4383,6 +4682,7 @@ function registerSkillsTools() {
|
|
|
4383
4682
|
{
|
|
4384
4683
|
name: "skills_get_categories",
|
|
4385
4684
|
module: "skills",
|
|
4685
|
+
title: "Skills Marketplace List Categories",
|
|
4386
4686
|
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
4687
|
inputSchema: {
|
|
4388
4688
|
type: "object",
|
|
@@ -4395,6 +4695,7 @@ function registerSkillsTools() {
|
|
|
4395
4695
|
{
|
|
4396
4696
|
name: "skills_search",
|
|
4397
4697
|
module: "skills",
|
|
4698
|
+
title: "Skills Marketplace Search",
|
|
4398
4699
|
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
4700
|
inputSchema: {
|
|
4400
4701
|
type: "object",
|
|
@@ -4424,6 +4725,7 @@ function registerSkillsTools() {
|
|
|
4424
4725
|
{
|
|
4425
4726
|
name: "skills_download",
|
|
4426
4727
|
module: "skills",
|
|
4728
|
+
title: "Skills Marketplace Download",
|
|
4427
4729
|
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
4730
|
inputSchema: {
|
|
4429
4731
|
type: "object",
|
|
@@ -4446,6 +4748,8 @@ function registerSkillsTools() {
|
|
|
4446
4748
|
additionalProperties: false
|
|
4447
4749
|
},
|
|
4448
4750
|
isWrite: true,
|
|
4751
|
+
destructiveHint: false,
|
|
4752
|
+
idempotentHint: true,
|
|
4449
4753
|
handler: handleDownload
|
|
4450
4754
|
}
|
|
4451
4755
|
];
|
|
@@ -4509,6 +4813,7 @@ function registerGridTools() {
|
|
|
4509
4813
|
return [
|
|
4510
4814
|
{
|
|
4511
4815
|
name: "grid_get_orders",
|
|
4816
|
+
title: "Grid Bot List Orders",
|
|
4512
4817
|
module: "bot.grid",
|
|
4513
4818
|
description: "List grid bots. status='active' for running; 'history' for stopped.",
|
|
4514
4819
|
isWrite: false,
|
|
@@ -4555,6 +4860,7 @@ function registerGridTools() {
|
|
|
4555
4860
|
},
|
|
4556
4861
|
{
|
|
4557
4862
|
name: "grid_get_order_details",
|
|
4863
|
+
title: "Grid Bot Get Detail",
|
|
4558
4864
|
module: "bot.grid",
|
|
4559
4865
|
description: "Get grid bot detail by algo ID. Returns config, status, PnL, and position.",
|
|
4560
4866
|
isWrite: false,
|
|
@@ -4585,6 +4891,7 @@ function registerGridTools() {
|
|
|
4585
4891
|
},
|
|
4586
4892
|
{
|
|
4587
4893
|
name: "grid_get_sub_orders",
|
|
4894
|
+
title: "Grid Bot Get Sub-Orders",
|
|
4588
4895
|
module: "bot.grid",
|
|
4589
4896
|
description: "Query sub-orders (grid trades) of a bot. type='filled' for executed; 'live' for pending.",
|
|
4590
4897
|
isWrite: false,
|
|
@@ -4629,9 +4936,11 @@ function registerGridTools() {
|
|
|
4629
4936
|
},
|
|
4630
4937
|
{
|
|
4631
4938
|
name: "grid_create_order",
|
|
4939
|
+
title: "Grid Bot Create",
|
|
4632
4940
|
module: "bot.grid",
|
|
4633
4941
|
description: "Create grid bot (spot, USDT-margined, or coin-margined contract). [CAUTION] Locks funds. Spot: quoteSz|baseSz. Contract: direction+lever+sz.",
|
|
4634
4942
|
isWrite: true,
|
|
4943
|
+
destructiveHint: false,
|
|
4635
4944
|
inputSchema: {
|
|
4636
4945
|
type: "object",
|
|
4637
4946
|
properties: {
|
|
@@ -4704,7 +5013,9 @@ function registerGridTools() {
|
|
|
4704
5013
|
},
|
|
4705
5014
|
{
|
|
4706
5015
|
name: "grid_amend_order",
|
|
5016
|
+
title: "Grid Bot Amend",
|
|
4707
5017
|
module: "bot.grid",
|
|
5018
|
+
idempotentHint: true,
|
|
4708
5019
|
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
5020
|
isWrite: true,
|
|
4710
5021
|
inputSchema: {
|
|
@@ -4827,7 +5138,9 @@ function registerGridTools() {
|
|
|
4827
5138
|
},
|
|
4828
5139
|
{
|
|
4829
5140
|
name: "grid_stop_order",
|
|
5141
|
+
title: "Grid Bot Stop",
|
|
4830
5142
|
module: "bot.grid",
|
|
5143
|
+
idempotentHint: true,
|
|
4831
5144
|
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
5145
|
isWrite: true,
|
|
4833
5146
|
inputSchema: {
|
|
@@ -4927,9 +5240,11 @@ function registerDcaTools() {
|
|
|
4927
5240
|
return [
|
|
4928
5241
|
{
|
|
4929
5242
|
name: "dca_create_order",
|
|
5243
|
+
title: "Martingale Bot Create",
|
|
4930
5244
|
module: "bot.dca",
|
|
4931
5245
|
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
5246
|
isWrite: true,
|
|
5247
|
+
destructiveHint: false,
|
|
4933
5248
|
inputSchema: {
|
|
4934
5249
|
type: "object",
|
|
4935
5250
|
properties: {
|
|
@@ -5005,7 +5320,9 @@ function registerDcaTools() {
|
|
|
5005
5320
|
},
|
|
5006
5321
|
{
|
|
5007
5322
|
name: "dca_stop_order",
|
|
5323
|
+
title: "Martingale Bot Stop",
|
|
5008
5324
|
module: "bot.dca",
|
|
5325
|
+
idempotentHint: true,
|
|
5009
5326
|
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
5327
|
isWrite: true,
|
|
5011
5328
|
inputSchema: {
|
|
@@ -5038,6 +5355,7 @@ function registerDcaTools() {
|
|
|
5038
5355
|
},
|
|
5039
5356
|
{
|
|
5040
5357
|
name: "dca_get_orders",
|
|
5358
|
+
title: "Martingale Bot List Orders",
|
|
5041
5359
|
module: "bot.dca",
|
|
5042
5360
|
description: "List DCA bots. Default: active (running). Use status=history for stopped.",
|
|
5043
5361
|
isWrite: false,
|
|
@@ -5076,6 +5394,7 @@ function registerDcaTools() {
|
|
|
5076
5394
|
},
|
|
5077
5395
|
{
|
|
5078
5396
|
name: "dca_get_order_details",
|
|
5397
|
+
title: "Martingale Bot Get Detail",
|
|
5079
5398
|
module: "bot.dca",
|
|
5080
5399
|
description: "Get DCA bot position details (avgPx, upl, liqPx, etc).",
|
|
5081
5400
|
isWrite: false,
|
|
@@ -5101,6 +5420,7 @@ function registerDcaTools() {
|
|
|
5101
5420
|
},
|
|
5102
5421
|
{
|
|
5103
5422
|
name: "dca_get_sub_orders",
|
|
5423
|
+
title: "Martingale Bot Get Sub-Orders",
|
|
5104
5424
|
module: "bot.dca",
|
|
5105
5425
|
description: "Get DCA cycles or orders in a cycle. Omit cycleId=cycle list; with cycleId=orders.",
|
|
5106
5426
|
isWrite: false,
|
|
@@ -5160,8 +5480,9 @@ function registerEarnTools() {
|
|
|
5160
5480
|
return [
|
|
5161
5481
|
{
|
|
5162
5482
|
name: "earn_get_savings_balance",
|
|
5483
|
+
title: "Get Simple Earn Balance",
|
|
5163
5484
|
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
|
|
5485
|
+
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
5486
|
isWrite: false,
|
|
5166
5487
|
inputSchema: {
|
|
5167
5488
|
type: "object",
|
|
@@ -5184,6 +5505,7 @@ function registerEarnTools() {
|
|
|
5184
5505
|
},
|
|
5185
5506
|
{
|
|
5186
5507
|
name: "earn_get_fixed_order_list",
|
|
5508
|
+
title: "Get Fixed-Term Earn Orders",
|
|
5187
5509
|
module: "earn.savings",
|
|
5188
5510
|
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
5511
|
isWrite: false,
|
|
@@ -5221,9 +5543,11 @@ function registerEarnTools() {
|
|
|
5221
5543
|
},
|
|
5222
5544
|
{
|
|
5223
5545
|
name: "earn_savings_purchase",
|
|
5546
|
+
title: "Subscribe Simple Earn",
|
|
5224
5547
|
module: "earn.savings",
|
|
5225
5548
|
description: "Purchase Simple Earn (savings/flexible earn). [CAUTION] Moves real funds into earn product.",
|
|
5226
5549
|
isWrite: true,
|
|
5550
|
+
destructiveHint: false,
|
|
5227
5551
|
inputSchema: {
|
|
5228
5552
|
type: "object",
|
|
5229
5553
|
properties: {
|
|
@@ -5259,9 +5583,11 @@ function registerEarnTools() {
|
|
|
5259
5583
|
},
|
|
5260
5584
|
{
|
|
5261
5585
|
name: "earn_savings_redeem",
|
|
5586
|
+
title: "Redeem Simple Earn",
|
|
5262
5587
|
module: "earn.savings",
|
|
5263
5588
|
description: "Redeem Simple Earn (savings/flexible earn). [CAUTION] Withdraws funds from earn product.",
|
|
5264
5589
|
isWrite: true,
|
|
5590
|
+
destructiveHint: false,
|
|
5265
5591
|
inputSchema: {
|
|
5266
5592
|
type: "object",
|
|
5267
5593
|
properties: {
|
|
@@ -5292,9 +5618,11 @@ function registerEarnTools() {
|
|
|
5292
5618
|
},
|
|
5293
5619
|
{
|
|
5294
5620
|
name: "earn_set_lending_rate",
|
|
5621
|
+
title: "Set Lending Rate",
|
|
5295
5622
|
module: "earn.savings",
|
|
5296
5623
|
description: "Set lending rate for Simple Earn. [CAUTION] Changes your lending rate preference.",
|
|
5297
5624
|
isWrite: true,
|
|
5625
|
+
idempotentHint: true,
|
|
5298
5626
|
inputSchema: {
|
|
5299
5627
|
type: "object",
|
|
5300
5628
|
properties: {
|
|
@@ -5324,6 +5652,7 @@ function registerEarnTools() {
|
|
|
5324
5652
|
},
|
|
5325
5653
|
{
|
|
5326
5654
|
name: "earn_get_lending_history",
|
|
5655
|
+
title: "Get Lending History",
|
|
5327
5656
|
module: "earn.savings",
|
|
5328
5657
|
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
5658
|
isWrite: false,
|
|
@@ -5365,9 +5694,11 @@ function registerEarnTools() {
|
|
|
5365
5694
|
},
|
|
5366
5695
|
{
|
|
5367
5696
|
name: "earn_fixed_purchase",
|
|
5697
|
+
title: "Subscribe Fixed-Term Earn",
|
|
5368
5698
|
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
|
|
5699
|
+
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
5700
|
isWrite: true,
|
|
5701
|
+
destructiveHint: false,
|
|
5371
5702
|
inputSchema: {
|
|
5372
5703
|
type: "object",
|
|
5373
5704
|
properties: {
|
|
@@ -5439,9 +5770,11 @@ function registerEarnTools() {
|
|
|
5439
5770
|
},
|
|
5440
5771
|
{
|
|
5441
5772
|
name: "earn_fixed_redeem",
|
|
5773
|
+
title: "Redeem Fixed-Term Earn",
|
|
5442
5774
|
module: "earn.savings",
|
|
5443
5775
|
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
5776
|
isWrite: true,
|
|
5777
|
+
destructiveHint: false,
|
|
5445
5778
|
inputSchema: {
|
|
5446
5779
|
type: "object",
|
|
5447
5780
|
properties: {
|
|
@@ -5467,8 +5800,9 @@ function registerEarnTools() {
|
|
|
5467
5800
|
},
|
|
5468
5801
|
{
|
|
5469
5802
|
name: "earn_get_lending_rate_history",
|
|
5803
|
+
title: "Get Lending Rates & Offers",
|
|
5470
5804
|
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
|
|
5805
|
+
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
5806
|
isWrite: false,
|
|
5473
5807
|
inputSchema: {
|
|
5474
5808
|
type: "object",
|
|
@@ -5527,6 +5861,37 @@ function registerEarnTools() {
|
|
|
5527
5861
|
fixedOffers
|
|
5528
5862
|
};
|
|
5529
5863
|
}
|
|
5864
|
+
},
|
|
5865
|
+
{
|
|
5866
|
+
name: "earn_get_fixed_earn_products",
|
|
5867
|
+
title: "Get Fixed-Term Earn Products",
|
|
5868
|
+
module: "earn.savings",
|
|
5869
|
+
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.",
|
|
5870
|
+
isWrite: false,
|
|
5871
|
+
inputSchema: {
|
|
5872
|
+
type: "object",
|
|
5873
|
+
properties: {
|
|
5874
|
+
ccy: {
|
|
5875
|
+
type: "string",
|
|
5876
|
+
description: "e.g. USDT. Omit for all currencies."
|
|
5877
|
+
}
|
|
5878
|
+
}
|
|
5879
|
+
},
|
|
5880
|
+
handler: async (rawArgs, context) => {
|
|
5881
|
+
const args = asRecord(rawArgs);
|
|
5882
|
+
const response = await context.client.privateGet(
|
|
5883
|
+
"/api/v5/finance/simple-earn-fixed/offers",
|
|
5884
|
+
compactObject({ ccy: readString(args, "ccy") }),
|
|
5885
|
+
privateRateLimit("earn_get_fixed_earn_products", 2)
|
|
5886
|
+
);
|
|
5887
|
+
const result = normalizeResponse(response);
|
|
5888
|
+
const allOffers = Array.isArray(result["data"]) ? result["data"] : [];
|
|
5889
|
+
result["data"] = allOffers.map(({ borrowingOrderQuota: _, ...rest }) => ({
|
|
5890
|
+
...rest,
|
|
5891
|
+
soldOut: rest["lendQuota"] === "0"
|
|
5892
|
+
}));
|
|
5893
|
+
return result;
|
|
5894
|
+
}
|
|
5530
5895
|
}
|
|
5531
5896
|
];
|
|
5532
5897
|
}
|
|
@@ -5537,6 +5902,7 @@ function registerOnchainEarnTools() {
|
|
|
5537
5902
|
// -------------------------------------------------------------------------
|
|
5538
5903
|
{
|
|
5539
5904
|
name: "onchain_earn_get_offers",
|
|
5905
|
+
title: "On-chain Earn List Offers",
|
|
5540
5906
|
module: "earn.onchain",
|
|
5541
5907
|
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
5908
|
isWrite: false,
|
|
@@ -5576,9 +5942,11 @@ function registerOnchainEarnTools() {
|
|
|
5576
5942
|
// -------------------------------------------------------------------------
|
|
5577
5943
|
{
|
|
5578
5944
|
name: "onchain_earn_purchase",
|
|
5945
|
+
title: "On-chain Earn Subscribe",
|
|
5579
5946
|
module: "earn.onchain",
|
|
5580
5947
|
description: "Invest in a staking/DeFi product. [CAUTION] Moves real funds.",
|
|
5581
5948
|
isWrite: true,
|
|
5949
|
+
destructiveHint: false,
|
|
5582
5950
|
inputSchema: {
|
|
5583
5951
|
type: "object",
|
|
5584
5952
|
properties: {
|
|
@@ -5629,9 +5997,11 @@ function registerOnchainEarnTools() {
|
|
|
5629
5997
|
// -------------------------------------------------------------------------
|
|
5630
5998
|
{
|
|
5631
5999
|
name: "onchain_earn_redeem",
|
|
6000
|
+
title: "On-chain Earn Redeem",
|
|
5632
6001
|
module: "earn.onchain",
|
|
5633
6002
|
description: "Redeem a staking/DeFi investment. [CAUTION] Some products have lock periods, early redemption may incur penalties.",
|
|
5634
6003
|
isWrite: true,
|
|
6004
|
+
destructiveHint: false,
|
|
5635
6005
|
inputSchema: {
|
|
5636
6006
|
type: "object",
|
|
5637
6007
|
properties: {
|
|
@@ -5669,9 +6039,11 @@ function registerOnchainEarnTools() {
|
|
|
5669
6039
|
// -------------------------------------------------------------------------
|
|
5670
6040
|
{
|
|
5671
6041
|
name: "onchain_earn_cancel",
|
|
6042
|
+
title: "On-chain Earn Cancel Order",
|
|
5672
6043
|
module: "earn.onchain",
|
|
5673
6044
|
description: "Cancel a pending staking/DeFi purchase order. [CAUTION]",
|
|
5674
6045
|
isWrite: true,
|
|
6046
|
+
idempotentHint: true,
|
|
5675
6047
|
inputSchema: {
|
|
5676
6048
|
type: "object",
|
|
5677
6049
|
properties: {
|
|
@@ -5704,6 +6076,7 @@ function registerOnchainEarnTools() {
|
|
|
5704
6076
|
// -------------------------------------------------------------------------
|
|
5705
6077
|
{
|
|
5706
6078
|
name: "onchain_earn_get_active_orders",
|
|
6079
|
+
title: "On-chain Earn Active Orders",
|
|
5707
6080
|
module: "earn.onchain",
|
|
5708
6081
|
description: "List current active staking/DeFi investments.",
|
|
5709
6082
|
isWrite: false,
|
|
@@ -5748,6 +6121,7 @@ function registerOnchainEarnTools() {
|
|
|
5748
6121
|
// -------------------------------------------------------------------------
|
|
5749
6122
|
{
|
|
5750
6123
|
name: "onchain_earn_get_order_history",
|
|
6124
|
+
title: "On-chain Earn Order History",
|
|
5751
6125
|
module: "earn.onchain",
|
|
5752
6126
|
description: "List past staking/DeFi orders including redeemed ones.",
|
|
5753
6127
|
isWrite: false,
|
|
@@ -5846,6 +6220,7 @@ function registerDcdTools() {
|
|
|
5846
6220
|
return [
|
|
5847
6221
|
{
|
|
5848
6222
|
name: "dcd_get_currency_pairs",
|
|
6223
|
+
title: "Dual Investment List Currency Pairs",
|
|
5849
6224
|
module: "earn.dcd",
|
|
5850
6225
|
description: "Get available DCD currency pairs.",
|
|
5851
6226
|
isWrite: false,
|
|
@@ -5863,6 +6238,7 @@ function registerDcdTools() {
|
|
|
5863
6238
|
},
|
|
5864
6239
|
{
|
|
5865
6240
|
name: "dcd_get_products",
|
|
6241
|
+
title: "Dual Investment List Products",
|
|
5866
6242
|
module: "earn.dcd",
|
|
5867
6243
|
description: "Get DCD products with yield and quota info. Yields in response are decimal fractions, not percentages.",
|
|
5868
6244
|
isWrite: false,
|
|
@@ -5893,6 +6269,7 @@ function registerDcdTools() {
|
|
|
5893
6269
|
},
|
|
5894
6270
|
{
|
|
5895
6271
|
name: "dcd_get_order_state",
|
|
6272
|
+
title: "Dual Investment Get Order State",
|
|
5896
6273
|
module: "earn.dcd",
|
|
5897
6274
|
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
6275
|
isWrite: false,
|
|
@@ -5917,6 +6294,7 @@ function registerDcdTools() {
|
|
|
5917
6294
|
},
|
|
5918
6295
|
{
|
|
5919
6296
|
name: "dcd_get_orders",
|
|
6297
|
+
title: "Dual Investment Get Order History",
|
|
5920
6298
|
module: "earn.dcd",
|
|
5921
6299
|
description: "Get DCD order history. Yields in response are decimal fractions, not percentages.",
|
|
5922
6300
|
isWrite: false,
|
|
@@ -5961,9 +6339,11 @@ function registerDcdTools() {
|
|
|
5961
6339
|
},
|
|
5962
6340
|
{
|
|
5963
6341
|
name: "dcd_subscribe",
|
|
6342
|
+
title: "Dual Investment Subscribe",
|
|
5964
6343
|
module: "earn.dcd",
|
|
5965
6344
|
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
6345
|
isWrite: true,
|
|
6346
|
+
destructiveHint: false,
|
|
5967
6347
|
inputSchema: {
|
|
5968
6348
|
type: "object",
|
|
5969
6349
|
properties: {
|
|
@@ -6046,9 +6426,11 @@ function registerDcdTools() {
|
|
|
6046
6426
|
},
|
|
6047
6427
|
{
|
|
6048
6428
|
name: "dcd_redeem",
|
|
6429
|
+
title: "Dual Investment Redeem",
|
|
6049
6430
|
module: "earn.dcd",
|
|
6050
6431
|
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
6432
|
isWrite: true,
|
|
6433
|
+
destructiveHint: false,
|
|
6052
6434
|
inputSchema: {
|
|
6053
6435
|
type: "object",
|
|
6054
6436
|
properties: {
|
|
@@ -6123,9 +6505,11 @@ function registerAutoEarnTools() {
|
|
|
6123
6505
|
return [
|
|
6124
6506
|
{
|
|
6125
6507
|
name: "earn_auto_set",
|
|
6508
|
+
title: "Set Auto-Earn Configuration",
|
|
6126
6509
|
module: "earn.autoearn",
|
|
6127
6510
|
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
6511
|
isWrite: true,
|
|
6512
|
+
idempotentHint: true,
|
|
6129
6513
|
inputSchema: {
|
|
6130
6514
|
type: "object",
|
|
6131
6515
|
properties: {
|
|
@@ -6175,6 +6559,7 @@ function registerFlashEarnTools() {
|
|
|
6175
6559
|
return [
|
|
6176
6560
|
{
|
|
6177
6561
|
name: "earn_get_flash_earn_projects",
|
|
6562
|
+
title: "Flash Earn List Projects",
|
|
6178
6563
|
module: "earn.flash",
|
|
6179
6564
|
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
6565
|
isWrite: false,
|
|
@@ -6597,6 +6982,7 @@ function registerEventContractTools() {
|
|
|
6597
6982
|
// -----------------------------------------------------------------------
|
|
6598
6983
|
{
|
|
6599
6984
|
name: "event_browse",
|
|
6985
|
+
title: "Event Contracts Browse Active",
|
|
6600
6986
|
module: "event",
|
|
6601
6987
|
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
6988
|
isWrite: false,
|
|
@@ -6637,6 +7023,7 @@ function registerEventContractTools() {
|
|
|
6637
7023
|
},
|
|
6638
7024
|
{
|
|
6639
7025
|
name: "event_get_series",
|
|
7026
|
+
title: "Event Contracts List Series",
|
|
6640
7027
|
module: "event",
|
|
6641
7028
|
description: "List event contract series. Returns all available series with settlement type and underlying. Use event_browse to see currently active contracts.",
|
|
6642
7029
|
isWrite: false,
|
|
@@ -6661,6 +7048,7 @@ function registerEventContractTools() {
|
|
|
6661
7048
|
},
|
|
6662
7049
|
{
|
|
6663
7050
|
name: "event_get_events",
|
|
7051
|
+
title: "Event Contracts List Events",
|
|
6664
7052
|
module: "event",
|
|
6665
7053
|
description: "List expiry periods within a series. state: preopen|live|settling|expired. expTime is pre-formatted UTC+8.",
|
|
6666
7054
|
isWrite: false,
|
|
@@ -6716,6 +7104,7 @@ function registerEventContractTools() {
|
|
|
6716
7104
|
},
|
|
6717
7105
|
{
|
|
6718
7106
|
name: "event_get_markets",
|
|
7107
|
+
title: "Event Contracts List Markets",
|
|
6719
7108
|
module: "event",
|
|
6720
7109
|
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
7110
|
isWrite: false,
|
|
@@ -6797,6 +7186,7 @@ function registerEventContractTools() {
|
|
|
6797
7186
|
},
|
|
6798
7187
|
{
|
|
6799
7188
|
name: "event_get_orders",
|
|
7189
|
+
title: "Event Contracts Get Orders",
|
|
6800
7190
|
module: "event",
|
|
6801
7191
|
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
7192
|
isWrite: false,
|
|
@@ -6855,6 +7245,7 @@ function registerEventContractTools() {
|
|
|
6855
7245
|
},
|
|
6856
7246
|
{
|
|
6857
7247
|
name: "event_get_fills",
|
|
7248
|
+
title: "Event Contracts Get Fills",
|
|
6858
7249
|
module: "event",
|
|
6859
7250
|
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
7251
|
isWrite: false,
|
|
@@ -6902,6 +7293,8 @@ function registerEventContractTools() {
|
|
|
6902
7293
|
// -----------------------------------------------------------------------
|
|
6903
7294
|
{
|
|
6904
7295
|
name: "event_place_order",
|
|
7296
|
+
title: "Event Contracts Place Order",
|
|
7297
|
+
destructiveHint: false,
|
|
6905
7298
|
module: "event",
|
|
6906
7299
|
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
7300
|
- outcome: UP/YES (bet price goes up/condition met) or DOWN/NO (bet price goes down/condition not met)
|
|
@@ -6977,6 +7370,8 @@ function registerEventContractTools() {
|
|
|
6977
7370
|
},
|
|
6978
7371
|
{
|
|
6979
7372
|
name: "event_amend_order",
|
|
7373
|
+
title: "Event Contracts Amend Order",
|
|
7374
|
+
idempotentHint: true,
|
|
6980
7375
|
module: "event",
|
|
6981
7376
|
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
7377
|
isWrite: true,
|
|
@@ -7008,6 +7403,8 @@ function registerEventContractTools() {
|
|
|
7008
7403
|
},
|
|
7009
7404
|
{
|
|
7010
7405
|
name: "event_cancel_order",
|
|
7406
|
+
title: "Event Contracts Cancel Order",
|
|
7407
|
+
idempotentHint: true,
|
|
7011
7408
|
module: "event",
|
|
7012
7409
|
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
7410
|
isWrite: true,
|
|
@@ -7416,6 +7813,7 @@ function registerSmartmoneyTools() {
|
|
|
7416
7813
|
/* ---------- T1. Top traders (leaderboard rank) ---------- */
|
|
7417
7814
|
{
|
|
7418
7815
|
name: "smartmoney_get_traders_by_filter",
|
|
7816
|
+
title: "Smart Money Leaderboard",
|
|
7419
7817
|
module: "smartmoney",
|
|
7420
7818
|
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
7819
|
isWrite: false,
|
|
@@ -7482,6 +7880,7 @@ function registerSmartmoneyTools() {
|
|
|
7482
7880
|
/* ---------- T2. Trader performance (by authorIds) ---------- */
|
|
7483
7881
|
{
|
|
7484
7882
|
name: "smartmoney_get_performance_by_trader",
|
|
7883
|
+
title: "Smart Money Trader Performance",
|
|
7485
7884
|
module: "smartmoney",
|
|
7486
7885
|
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
7886
|
isWrite: false,
|
|
@@ -7552,6 +7951,7 @@ function registerSmartmoneyTools() {
|
|
|
7552
7951
|
/* ---------- T3. Trader current positions ---------- */
|
|
7553
7952
|
{
|
|
7554
7953
|
name: "smartmoney_get_trader_positions",
|
|
7954
|
+
title: "Smart Money Trader Current Positions",
|
|
7555
7955
|
module: "smartmoney",
|
|
7556
7956
|
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
7957
|
isWrite: false,
|
|
@@ -7633,6 +8033,7 @@ function registerSmartmoneyTools() {
|
|
|
7633
8033
|
/* ---------- T4. Trader closed-position history ---------- */
|
|
7634
8034
|
{
|
|
7635
8035
|
name: "smartmoney_get_trader_positions_history",
|
|
8036
|
+
title: "Smart Money Trader Position History",
|
|
7636
8037
|
module: "smartmoney",
|
|
7637
8038
|
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
8039
|
isWrite: false,
|
|
@@ -7770,6 +8171,7 @@ function registerSmartmoneyTools() {
|
|
|
7770
8171
|
/* ---------- T5. Trader order history ---------- */
|
|
7771
8172
|
{
|
|
7772
8173
|
name: "smartmoney_get_trader_orders_history",
|
|
8174
|
+
title: "Smart Money Trader Order History",
|
|
7773
8175
|
module: "smartmoney",
|
|
7774
8176
|
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
8177
|
isWrite: false,
|
|
@@ -7888,6 +8290,7 @@ function registerSmartmoneyTools() {
|
|
|
7888
8290
|
/* ---------- T6. Search top traders by nickname keyword ---------- */
|
|
7889
8291
|
{
|
|
7890
8292
|
name: "smartmoney_search_trader",
|
|
8293
|
+
title: "Search Smart Money Top Traders",
|
|
7891
8294
|
module: "smartmoney",
|
|
7892
8295
|
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
8296
|
isWrite: false,
|
|
@@ -7938,6 +8341,7 @@ function registerSmartmoneyTools() {
|
|
|
7938
8341
|
/* ---------- S1. Signal overview by filter (multi-asset, tier-filtered pool) ---------- */
|
|
7939
8342
|
{
|
|
7940
8343
|
name: "smartmoney_get_signal_overview_by_filter",
|
|
8344
|
+
title: "Smart Money Consensus Signals by Filter",
|
|
7941
8345
|
module: "smartmoney",
|
|
7942
8346
|
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
8347
|
isWrite: false,
|
|
@@ -7998,6 +8402,7 @@ function registerSmartmoneyTools() {
|
|
|
7998
8402
|
/* ---------- S2. Signal overview by trader (multi-asset, authorIds-restricted) ---------- */
|
|
7999
8403
|
{
|
|
8000
8404
|
name: "smartmoney_get_signal_overview_by_trader",
|
|
8405
|
+
title: "Smart Money Consensus Signals by Trader",
|
|
8001
8406
|
module: "smartmoney",
|
|
8002
8407
|
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
8408
|
isWrite: false,
|
|
@@ -8080,6 +8485,7 @@ function registerSmartmoneyTools() {
|
|
|
8080
8485
|
/* ---------- S3. Signal trend by filter (single-asset, tier-filtered pool, asOfTime anchor) ---------- */
|
|
8081
8486
|
{
|
|
8082
8487
|
name: "smartmoney_get_signal_trend_by_filter",
|
|
8488
|
+
title: "Smart Money Signal Trend by Filter",
|
|
8083
8489
|
module: "smartmoney",
|
|
8084
8490
|
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
8491
|
isWrite: false,
|
|
@@ -8152,6 +8558,7 @@ function registerSmartmoneyTools() {
|
|
|
8152
8558
|
/* ---------- S4. Signal trend by trader (single-asset, authorIds-restricted) ---------- */
|
|
8153
8559
|
{
|
|
8154
8560
|
name: "smartmoney_get_signal_trend_by_trader",
|
|
8561
|
+
title: "Smart Money Signal Trend by Trader",
|
|
8155
8562
|
module: "smartmoney",
|
|
8156
8563
|
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
8564
|
isWrite: false,
|
|
@@ -8246,7 +8653,7 @@ function registerSmartmoneyTools() {
|
|
|
8246
8653
|
return tools;
|
|
8247
8654
|
}
|
|
8248
8655
|
function buildContractTradeTools(cfg) {
|
|
8249
|
-
const { prefix, module, label, instTypes, instIdExample } = cfg;
|
|
8656
|
+
const { prefix, module, label, instTypes, instIdExample, titleLabel } = cfg;
|
|
8250
8657
|
const [defaultType, otherType] = instTypes;
|
|
8251
8658
|
const instTypeDesc = `${defaultType} (default) or ${otherType}`;
|
|
8252
8659
|
const n = (suffix) => `${prefix}_${suffix}`;
|
|
@@ -8254,6 +8661,8 @@ function buildContractTradeTools(cfg) {
|
|
|
8254
8661
|
// ── place_order ──────────────────────────────────────────────────────────
|
|
8255
8662
|
{
|
|
8256
8663
|
name: n("place_order"),
|
|
8664
|
+
title: `${titleLabel} Place Order`,
|
|
8665
|
+
destructiveHint: false,
|
|
8257
8666
|
module,
|
|
8258
8667
|
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
8668
|
isWrite: true,
|
|
@@ -8350,6 +8759,8 @@ function buildContractTradeTools(cfg) {
|
|
|
8350
8759
|
// ── cancel_order ─────────────────────────────────────────────────────────
|
|
8351
8760
|
{
|
|
8352
8761
|
name: n("cancel_order"),
|
|
8762
|
+
title: `${titleLabel} Cancel Order`,
|
|
8763
|
+
idempotentHint: true,
|
|
8353
8764
|
module,
|
|
8354
8765
|
description: `Cancel an unfilled ${label} order.`,
|
|
8355
8766
|
isWrite: true,
|
|
@@ -8379,6 +8790,7 @@ function buildContractTradeTools(cfg) {
|
|
|
8379
8790
|
// ── get_order ─────────────────────────────────────────────────────────────
|
|
8380
8791
|
{
|
|
8381
8792
|
name: n("get_order"),
|
|
8793
|
+
title: `${titleLabel} Get Order`,
|
|
8382
8794
|
module,
|
|
8383
8795
|
description: `Get details of a single ${label} order by ordId or clOrdId.`,
|
|
8384
8796
|
isWrite: false,
|
|
@@ -8408,6 +8820,7 @@ function buildContractTradeTools(cfg) {
|
|
|
8408
8820
|
// ── get_orders ───────────────────────────────────────────────────────────
|
|
8409
8821
|
{
|
|
8410
8822
|
name: n("get_orders"),
|
|
8823
|
+
title: `${titleLabel} Get Orders`,
|
|
8411
8824
|
module,
|
|
8412
8825
|
description: `Query ${label} open orders, history (last 7 days), or archive (up to 3 months).`,
|
|
8413
8826
|
isWrite: false,
|
|
@@ -8466,6 +8879,7 @@ function buildContractTradeTools(cfg) {
|
|
|
8466
8879
|
// ── get_positions ────────────────────────────────────────────────────────
|
|
8467
8880
|
{
|
|
8468
8881
|
name: n("get_positions"),
|
|
8882
|
+
title: `${titleLabel} Get Positions`,
|
|
8469
8883
|
module,
|
|
8470
8884
|
description: `Get current ${label} positions.`,
|
|
8471
8885
|
isWrite: false,
|
|
@@ -8500,6 +8914,7 @@ function buildContractTradeTools(cfg) {
|
|
|
8500
8914
|
// ── get_fills ────────────────────────────────────────────────────────────
|
|
8501
8915
|
{
|
|
8502
8916
|
name: n("get_fills"),
|
|
8917
|
+
title: `${titleLabel} Get Fills`,
|
|
8503
8918
|
module,
|
|
8504
8919
|
description: `Get ${label} fill details. archive=false (default): last 3 days; archive=true: up to 3 months.`,
|
|
8505
8920
|
isWrite: false,
|
|
@@ -8550,6 +8965,8 @@ function buildContractTradeTools(cfg) {
|
|
|
8550
8965
|
// ── close_position ───────────────────────────────────────────────────────
|
|
8551
8966
|
{
|
|
8552
8967
|
name: n("close_position"),
|
|
8968
|
+
title: `${titleLabel} Close Position`,
|
|
8969
|
+
idempotentHint: true,
|
|
8553
8970
|
module,
|
|
8554
8971
|
description: `[CAUTION] Close entire ${label} position at market.`,
|
|
8555
8972
|
isWrite: true,
|
|
@@ -8592,6 +9009,8 @@ function buildContractTradeTools(cfg) {
|
|
|
8592
9009
|
// ── set_leverage ─────────────────────────────────────────────────────────
|
|
8593
9010
|
{
|
|
8594
9011
|
name: n("set_leverage"),
|
|
9012
|
+
title: `${titleLabel} Set Leverage`,
|
|
9013
|
+
idempotentHint: true,
|
|
8595
9014
|
module,
|
|
8596
9015
|
description: `Set leverage for a ${label} instrument or position. [CAUTION] Changes risk parameters.
|
|
8597
9016
|
Scenarios (SWAP/FUTURES only):
|
|
@@ -8654,6 +9073,7 @@ Not supported: PORTFOLIO MARGIN accounts cannot adjust cross leverage for SWAP/F
|
|
|
8654
9073
|
// ── get_leverage ─────────────────────────────────────────────────────────
|
|
8655
9074
|
{
|
|
8656
9075
|
name: n("get_leverage"),
|
|
9076
|
+
title: `${titleLabel} Get Leverage`,
|
|
8657
9077
|
module,
|
|
8658
9078
|
description: `Get current leverage for a ${label} instrument.`,
|
|
8659
9079
|
isWrite: false,
|
|
@@ -8681,6 +9101,8 @@ Not supported: PORTFOLIO MARGIN accounts cannot adjust cross leverage for SWAP/F
|
|
|
8681
9101
|
// ── batch_amend ──────────────────────────────────────────────────────────
|
|
8682
9102
|
{
|
|
8683
9103
|
name: n("batch_amend"),
|
|
9104
|
+
title: `${titleLabel} Batch Amend Orders`,
|
|
9105
|
+
idempotentHint: true,
|
|
8684
9106
|
module,
|
|
8685
9107
|
description: `[CAUTION] Batch amend up to 20 unfilled ${label} orders.`,
|
|
8686
9108
|
isWrite: true,
|
|
@@ -8712,6 +9134,8 @@ Not supported: PORTFOLIO MARGIN accounts cannot adjust cross leverage for SWAP/F
|
|
|
8712
9134
|
// ── batch_cancel ─────────────────────────────────────────────────────────
|
|
8713
9135
|
{
|
|
8714
9136
|
name: n("batch_cancel"),
|
|
9137
|
+
title: `${titleLabel} Batch Cancel Orders`,
|
|
9138
|
+
idempotentHint: true,
|
|
8715
9139
|
module,
|
|
8716
9140
|
description: `[CAUTION] Batch cancel up to 20 ${label} orders.`,
|
|
8717
9141
|
isWrite: true,
|
|
@@ -8747,6 +9171,7 @@ function registerFuturesTools() {
|
|
|
8747
9171
|
prefix: "futures",
|
|
8748
9172
|
module: "futures",
|
|
8749
9173
|
label: "FUTURES delivery",
|
|
9174
|
+
titleLabel: "Futures",
|
|
8750
9175
|
instTypes: ["FUTURES", "SWAP"],
|
|
8751
9176
|
instIdExample: "e.g. BTC-USDT-240329"
|
|
8752
9177
|
});
|
|
@@ -8756,6 +9181,8 @@ function registerFuturesTools() {
|
|
|
8756
9181
|
// Unique to futures: amend a regular (non-algo) unfilled order.
|
|
8757
9182
|
{
|
|
8758
9183
|
name: "futures_amend_order",
|
|
9184
|
+
title: "Futures Amend Order",
|
|
9185
|
+
idempotentHint: true,
|
|
8759
9186
|
module: "futures",
|
|
8760
9187
|
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
9188
|
isWrite: true,
|
|
@@ -8790,6 +9217,8 @@ function registerFuturesTools() {
|
|
|
8790
9217
|
// Unique to futures: batch place only (no cancel/amend action dispatch).
|
|
8791
9218
|
{
|
|
8792
9219
|
name: "futures_batch_orders",
|
|
9220
|
+
title: "Futures Batch Place Orders",
|
|
9221
|
+
destructiveHint: false,
|
|
8793
9222
|
module: "futures",
|
|
8794
9223
|
description: "[CAUTION] Batch place up to 20 FUTURES delivery orders.",
|
|
8795
9224
|
isWrite: true,
|
|
@@ -8849,6 +9278,7 @@ function registerMarketTools() {
|
|
|
8849
9278
|
return [
|
|
8850
9279
|
{
|
|
8851
9280
|
name: "market_get_ticker",
|
|
9281
|
+
title: "Get Ticker",
|
|
8852
9282
|
module: "market",
|
|
8853
9283
|
description: "Get ticker data for a single instrument.",
|
|
8854
9284
|
isWrite: false,
|
|
@@ -8876,6 +9306,7 @@ function registerMarketTools() {
|
|
|
8876
9306
|
},
|
|
8877
9307
|
{
|
|
8878
9308
|
name: "market_get_tickers",
|
|
9309
|
+
title: "Get All Tickers",
|
|
8879
9310
|
module: "market",
|
|
8880
9311
|
description: "Get ticker data for all instruments of a given type.",
|
|
8881
9312
|
isWrite: false,
|
|
@@ -8915,6 +9346,7 @@ function registerMarketTools() {
|
|
|
8915
9346
|
},
|
|
8916
9347
|
{
|
|
8917
9348
|
name: "market_get_orderbook",
|
|
9349
|
+
title: "Get Order Book",
|
|
8918
9350
|
module: "market",
|
|
8919
9351
|
description: "Get the order book (bids/asks) for an instrument.",
|
|
8920
9352
|
isWrite: false,
|
|
@@ -8949,6 +9381,7 @@ function registerMarketTools() {
|
|
|
8949
9381
|
},
|
|
8950
9382
|
{
|
|
8951
9383
|
name: "market_get_candles",
|
|
9384
|
+
title: "Get Candlesticks",
|
|
8952
9385
|
module: "market",
|
|
8953
9386
|
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
9387
|
isWrite: false,
|
|
@@ -9005,6 +9438,7 @@ function registerMarketTools() {
|
|
|
9005
9438
|
},
|
|
9006
9439
|
{
|
|
9007
9440
|
name: "market_get_instruments",
|
|
9441
|
+
title: "List Instruments",
|
|
9008
9442
|
module: "market",
|
|
9009
9443
|
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
9444
|
isWrite: false,
|
|
@@ -9049,6 +9483,7 @@ function registerMarketTools() {
|
|
|
9049
9483
|
},
|
|
9050
9484
|
{
|
|
9051
9485
|
name: "market_get_funding_rate",
|
|
9486
|
+
title: "Get Funding Rate",
|
|
9052
9487
|
module: "market",
|
|
9053
9488
|
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
9489
|
isWrite: false,
|
|
@@ -9110,6 +9545,7 @@ function registerMarketTools() {
|
|
|
9110
9545
|
},
|
|
9111
9546
|
{
|
|
9112
9547
|
name: "market_get_mark_price",
|
|
9548
|
+
title: "Get Mark Price",
|
|
9113
9549
|
module: "market",
|
|
9114
9550
|
description: "Get mark price for SWAP, FUTURES, or MARGIN instruments. Used for liquidation calculations and unrealized PnL.",
|
|
9115
9551
|
isWrite: false,
|
|
@@ -9153,6 +9589,7 @@ function registerMarketTools() {
|
|
|
9153
9589
|
},
|
|
9154
9590
|
{
|
|
9155
9591
|
name: "market_get_trades",
|
|
9592
|
+
title: "Get Recent Trades",
|
|
9156
9593
|
module: "market",
|
|
9157
9594
|
description: "Get recent trades for an instrument. Default 20 records, max 500.",
|
|
9158
9595
|
isWrite: false,
|
|
@@ -9187,6 +9624,7 @@ function registerMarketTools() {
|
|
|
9187
9624
|
},
|
|
9188
9625
|
{
|
|
9189
9626
|
name: "market_get_index_ticker",
|
|
9627
|
+
title: "Get Index Ticker",
|
|
9190
9628
|
module: "market",
|
|
9191
9629
|
description: "Get index ticker data (e.g. BTC-USD, ETH-USD index prices). Independent of any single exchange.",
|
|
9192
9630
|
isWrite: false,
|
|
@@ -9220,6 +9658,7 @@ function registerMarketTools() {
|
|
|
9220
9658
|
},
|
|
9221
9659
|
{
|
|
9222
9660
|
name: "market_get_index_candles",
|
|
9661
|
+
title: "Get Index Candlesticks",
|
|
9223
9662
|
module: "market",
|
|
9224
9663
|
description: "Get candlestick data for an index (e.g. BTC-USD index). history=false: recent up to 1440 bars; history=true: older data.",
|
|
9225
9664
|
isWrite: false,
|
|
@@ -9276,6 +9715,7 @@ function registerMarketTools() {
|
|
|
9276
9715
|
},
|
|
9277
9716
|
{
|
|
9278
9717
|
name: "market_get_price_limit",
|
|
9718
|
+
title: "Get Price Limit",
|
|
9279
9719
|
module: "market",
|
|
9280
9720
|
description: "Get the current price limit (upper and lower bands) for a SWAP or FUTURES instrument. Orders outside these limits will be rejected.",
|
|
9281
9721
|
isWrite: false,
|
|
@@ -9303,6 +9743,7 @@ function registerMarketTools() {
|
|
|
9303
9743
|
},
|
|
9304
9744
|
{
|
|
9305
9745
|
name: "market_get_open_interest",
|
|
9746
|
+
title: "Get Open Interest",
|
|
9306
9747
|
module: "market",
|
|
9307
9748
|
description: "Get open interest for SWAP, FUTURES, or OPTION instruments. Useful for gauging market sentiment and positioning.",
|
|
9308
9749
|
isWrite: false,
|
|
@@ -9346,6 +9787,7 @@ function registerMarketTools() {
|
|
|
9346
9787
|
},
|
|
9347
9788
|
{
|
|
9348
9789
|
name: "market_get_stock_tokens",
|
|
9790
|
+
title: "List Stock Tokens",
|
|
9349
9791
|
module: "market",
|
|
9350
9792
|
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
9793
|
isWrite: false,
|
|
@@ -9382,6 +9824,7 @@ function registerMarketTools() {
|
|
|
9382
9824
|
},
|
|
9383
9825
|
{
|
|
9384
9826
|
name: "market_get_instruments_by_category",
|
|
9827
|
+
title: "List Instruments by Category",
|
|
9385
9828
|
module: "market",
|
|
9386
9829
|
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
9830
|
isWrite: false,
|
|
@@ -9432,6 +9875,7 @@ function registerMarketFilterTools() {
|
|
|
9432
9875
|
// ─────────────────────────────────────────────────────────────────────────
|
|
9433
9876
|
{
|
|
9434
9877
|
name: "market_filter",
|
|
9878
|
+
title: "Screen Instruments",
|
|
9435
9879
|
module: "market",
|
|
9436
9880
|
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
9881
|
isWrite: false,
|
|
@@ -9569,6 +10013,7 @@ function registerMarketFilterTools() {
|
|
|
9569
10013
|
// ─────────────────────────────────────────────────────────────────────────
|
|
9570
10014
|
{
|
|
9571
10015
|
name: "market_get_oi_history",
|
|
10016
|
+
title: "Get Open Interest History",
|
|
9572
10017
|
module: "market",
|
|
9573
10018
|
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
10019
|
isWrite: false,
|
|
@@ -9616,6 +10061,7 @@ function registerMarketFilterTools() {
|
|
|
9616
10061
|
// ─────────────────────────────────────────────────────────────────────────
|
|
9617
10062
|
{
|
|
9618
10063
|
name: "market_filter_oi_change",
|
|
10064
|
+
title: "Find Open Interest Change Instruments",
|
|
9619
10065
|
module: "market",
|
|
9620
10066
|
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
10067
|
isWrite: false,
|
|
@@ -9688,6 +10134,7 @@ function registerMarketFilterTools() {
|
|
|
9688
10134
|
// ─────────────────────────────────────────────────────────────────────────
|
|
9689
10135
|
{
|
|
9690
10136
|
name: "market_get_pair_spread",
|
|
10137
|
+
title: "Get Pair Spread",
|
|
9691
10138
|
module: "market",
|
|
9692
10139
|
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
10140
|
isWrite: false,
|
|
@@ -9980,6 +10427,7 @@ function registerNewsTools() {
|
|
|
9980
10427
|
// -----------------------------------------------------------------------
|
|
9981
10428
|
{
|
|
9982
10429
|
name: "news_get_latest",
|
|
10430
|
+
title: "Get Latest Crypto News",
|
|
9983
10431
|
module: "news",
|
|
9984
10432
|
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
10433
|
isWrite: false,
|
|
@@ -10025,6 +10473,7 @@ function registerNewsTools() {
|
|
|
10025
10473
|
},
|
|
10026
10474
|
{
|
|
10027
10475
|
name: "news_get_by_coin",
|
|
10476
|
+
title: "Get News by Coin",
|
|
10028
10477
|
module: "news",
|
|
10029
10478
|
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
10479
|
isWrite: false,
|
|
@@ -10068,6 +10517,7 @@ function registerNewsTools() {
|
|
|
10068
10517
|
},
|
|
10069
10518
|
{
|
|
10070
10519
|
name: "news_search",
|
|
10520
|
+
title: "Search News",
|
|
10071
10521
|
module: "news",
|
|
10072
10522
|
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
10523
|
isWrite: false,
|
|
@@ -10125,6 +10575,7 @@ function registerNewsTools() {
|
|
|
10125
10575
|
},
|
|
10126
10576
|
{
|
|
10127
10577
|
name: "news_get_detail",
|
|
10578
|
+
title: "Get News Article",
|
|
10128
10579
|
module: "news",
|
|
10129
10580
|
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
10581
|
isWrite: false,
|
|
@@ -10156,6 +10607,7 @@ function registerNewsTools() {
|
|
|
10156
10607
|
},
|
|
10157
10608
|
{
|
|
10158
10609
|
name: "news_get_domains",
|
|
10610
|
+
title: "List News Sources",
|
|
10159
10611
|
module: "news",
|
|
10160
10612
|
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
10613
|
isWrite: false,
|
|
@@ -10178,6 +10630,7 @@ function registerNewsTools() {
|
|
|
10178
10630
|
// -----------------------------------------------------------------------
|
|
10179
10631
|
{
|
|
10180
10632
|
name: "news_get_coin_sentiment",
|
|
10633
|
+
title: "Get Coin Sentiment",
|
|
10181
10634
|
module: "news",
|
|
10182
10635
|
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
10636
|
isWrite: false,
|
|
@@ -10219,6 +10672,7 @@ function registerNewsTools() {
|
|
|
10219
10672
|
},
|
|
10220
10673
|
{
|
|
10221
10674
|
name: "news_get_sentiment_ranking",
|
|
10675
|
+
title: "Get Sentiment Ranking",
|
|
10222
10676
|
module: "news",
|
|
10223
10677
|
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
10678
|
isWrite: false,
|
|
@@ -10258,6 +10712,7 @@ function registerNewsTools() {
|
|
|
10258
10712
|
// -----------------------------------------------------------------------
|
|
10259
10713
|
{
|
|
10260
10714
|
name: "news_list_calendar_regions",
|
|
10715
|
+
title: "List Calendar Regions",
|
|
10261
10716
|
module: "news",
|
|
10262
10717
|
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
10718
|
isWrite: false,
|
|
@@ -10266,6 +10721,7 @@ function registerNewsTools() {
|
|
|
10266
10721
|
},
|
|
10267
10722
|
{
|
|
10268
10723
|
name: "news_get_economic_calendar",
|
|
10724
|
+
title: "Get Economic Calendar",
|
|
10269
10725
|
module: "news",
|
|
10270
10726
|
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
10727
|
isWrite: false,
|
|
@@ -10326,6 +10782,8 @@ function registerOptionAlgoTools() {
|
|
|
10326
10782
|
return [
|
|
10327
10783
|
{
|
|
10328
10784
|
name: "option_place_algo_order",
|
|
10785
|
+
title: "Option Place Algo Order",
|
|
10786
|
+
destructiveHint: false,
|
|
10329
10787
|
module: "option",
|
|
10330
10788
|
description: "Place OPTION TP/SL algo order (conditional/oco). [CAUTION] Executes real trades. conditional=single TP/SL; oco=TP+SL pair. -1=market.",
|
|
10331
10789
|
isWrite: true,
|
|
@@ -10432,6 +10890,8 @@ function registerOptionAlgoTools() {
|
|
|
10432
10890
|
},
|
|
10433
10891
|
{
|
|
10434
10892
|
name: "option_amend_algo_order",
|
|
10893
|
+
title: "Option Amend Algo Order",
|
|
10894
|
+
idempotentHint: true,
|
|
10435
10895
|
module: "option",
|
|
10436
10896
|
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
10897
|
isWrite: true,
|
|
@@ -10468,6 +10928,8 @@ function registerOptionAlgoTools() {
|
|
|
10468
10928
|
},
|
|
10469
10929
|
{
|
|
10470
10930
|
name: "option_cancel_algo_orders",
|
|
10931
|
+
title: "Option Cancel Algo Orders",
|
|
10932
|
+
idempotentHint: true,
|
|
10471
10933
|
module: "option",
|
|
10472
10934
|
description: "Cancel OPTION algo orders (TP/SL). Each item: {algoId, instId}.",
|
|
10473
10935
|
isWrite: true,
|
|
@@ -10511,6 +10973,7 @@ function registerOptionAlgoTools() {
|
|
|
10511
10973
|
},
|
|
10512
10974
|
{
|
|
10513
10975
|
name: "option_get_algo_orders",
|
|
10976
|
+
title: "Option Get Algo Orders",
|
|
10514
10977
|
module: "option",
|
|
10515
10978
|
description: "Query pending or completed OPTION algo orders (TP/SL, OCO).",
|
|
10516
10979
|
isWrite: false,
|
|
@@ -10595,6 +11058,8 @@ function registerOptionTools() {
|
|
|
10595
11058
|
return [
|
|
10596
11059
|
{
|
|
10597
11060
|
name: "option_place_order",
|
|
11061
|
+
title: "Option Place Order",
|
|
11062
|
+
destructiveHint: false,
|
|
10598
11063
|
module: "option",
|
|
10599
11064
|
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
11065
|
isWrite: true,
|
|
@@ -10698,6 +11163,8 @@ function registerOptionTools() {
|
|
|
10698
11163
|
},
|
|
10699
11164
|
{
|
|
10700
11165
|
name: "option_cancel_order",
|
|
11166
|
+
title: "Option Cancel Order",
|
|
11167
|
+
idempotentHint: true,
|
|
10701
11168
|
module: "option",
|
|
10702
11169
|
description: "Cancel an unfilled OPTION order. Provide ordId or clOrdId.",
|
|
10703
11170
|
isWrite: true,
|
|
@@ -10726,6 +11193,8 @@ function registerOptionTools() {
|
|
|
10726
11193
|
},
|
|
10727
11194
|
{
|
|
10728
11195
|
name: "option_batch_cancel",
|
|
11196
|
+
title: "Option Batch Cancel Orders",
|
|
11197
|
+
idempotentHint: true,
|
|
10729
11198
|
module: "option",
|
|
10730
11199
|
description: "[CAUTION] Batch cancel up to 20 OPTION orders.",
|
|
10731
11200
|
isWrite: true,
|
|
@@ -10756,6 +11225,8 @@ function registerOptionTools() {
|
|
|
10756
11225
|
},
|
|
10757
11226
|
{
|
|
10758
11227
|
name: "option_amend_order",
|
|
11228
|
+
title: "Option Amend Order",
|
|
11229
|
+
idempotentHint: true,
|
|
10759
11230
|
module: "option",
|
|
10760
11231
|
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
11232
|
isWrite: true,
|
|
@@ -10788,6 +11259,7 @@ function registerOptionTools() {
|
|
|
10788
11259
|
},
|
|
10789
11260
|
{
|
|
10790
11261
|
name: "option_get_order",
|
|
11262
|
+
title: "Option Get Order",
|
|
10791
11263
|
module: "option",
|
|
10792
11264
|
description: "Get details of a single OPTION order by ordId or clOrdId.",
|
|
10793
11265
|
isWrite: false,
|
|
@@ -10816,6 +11288,7 @@ function registerOptionTools() {
|
|
|
10816
11288
|
},
|
|
10817
11289
|
{
|
|
10818
11290
|
name: "option_get_orders",
|
|
11291
|
+
title: "Option Get Orders",
|
|
10819
11292
|
module: "option",
|
|
10820
11293
|
description: "List OPTION orders. status: live=pending (default), history=7d, archive=3mo.",
|
|
10821
11294
|
isWrite: false,
|
|
@@ -10870,6 +11343,7 @@ function registerOptionTools() {
|
|
|
10870
11343
|
},
|
|
10871
11344
|
{
|
|
10872
11345
|
name: "option_get_positions",
|
|
11346
|
+
title: "Option Get Positions with Greeks",
|
|
10873
11347
|
module: "option",
|
|
10874
11348
|
description: "Get current OPTION positions including Greeks (delta, gamma, theta, vega).",
|
|
10875
11349
|
isWrite: false,
|
|
@@ -10896,6 +11370,7 @@ function registerOptionTools() {
|
|
|
10896
11370
|
},
|
|
10897
11371
|
{
|
|
10898
11372
|
name: "option_get_fills",
|
|
11373
|
+
title: "Option Get Fills",
|
|
10899
11374
|
module: "option",
|
|
10900
11375
|
description: "Get OPTION fill history. archive=false: last 3 days (default); archive=true: up to 3 months.",
|
|
10901
11376
|
isWrite: false,
|
|
@@ -10938,6 +11413,7 @@ function registerOptionTools() {
|
|
|
10938
11413
|
},
|
|
10939
11414
|
{
|
|
10940
11415
|
name: "option_get_instruments",
|
|
11416
|
+
title: "Option List Instruments (Chain)",
|
|
10941
11417
|
module: "option",
|
|
10942
11418
|
description: "List available OPTION contracts for a given underlying (option chain). Use to find valid instIds before placing orders.",
|
|
10943
11419
|
isWrite: false,
|
|
@@ -10971,6 +11447,7 @@ function registerOptionTools() {
|
|
|
10971
11447
|
},
|
|
10972
11448
|
{
|
|
10973
11449
|
name: "option_get_greeks",
|
|
11450
|
+
title: "Option Get Greeks",
|
|
10974
11451
|
module: "option",
|
|
10975
11452
|
description: "Get implied volatility and Greeks (delta, gamma, theta, vega) for OPTION contracts by underlying.",
|
|
10976
11453
|
isWrite: false,
|
|
@@ -11007,6 +11484,8 @@ function registerSpotTradeTools() {
|
|
|
11007
11484
|
return [
|
|
11008
11485
|
{
|
|
11009
11486
|
name: "spot_place_order",
|
|
11487
|
+
title: "Spot Place Order",
|
|
11488
|
+
destructiveHint: false,
|
|
11010
11489
|
module: "spot",
|
|
11011
11490
|
description: "Place a spot order. Attach TP/SL via tpTriggerPx/slTriggerPx. [CAUTION] Executes real trades.",
|
|
11012
11491
|
isWrite: true,
|
|
@@ -11101,6 +11580,8 @@ function registerSpotTradeTools() {
|
|
|
11101
11580
|
},
|
|
11102
11581
|
{
|
|
11103
11582
|
name: "spot_cancel_order",
|
|
11583
|
+
title: "Spot Cancel Order",
|
|
11584
|
+
idempotentHint: true,
|
|
11104
11585
|
module: "spot",
|
|
11105
11586
|
description: "Cancel an unfilled spot order.",
|
|
11106
11587
|
isWrite: true,
|
|
@@ -11136,6 +11617,8 @@ function registerSpotTradeTools() {
|
|
|
11136
11617
|
},
|
|
11137
11618
|
{
|
|
11138
11619
|
name: "spot_amend_order",
|
|
11620
|
+
title: "Spot Amend Order",
|
|
11621
|
+
idempotentHint: true,
|
|
11139
11622
|
module: "spot",
|
|
11140
11623
|
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
11624
|
isWrite: true,
|
|
@@ -11186,6 +11669,7 @@ function registerSpotTradeTools() {
|
|
|
11186
11669
|
},
|
|
11187
11670
|
{
|
|
11188
11671
|
name: "spot_get_orders",
|
|
11672
|
+
title: "Spot Get Orders",
|
|
11189
11673
|
module: "spot",
|
|
11190
11674
|
description: "Query spot orders. status: open(active)|history(7d)|archive(3mo).",
|
|
11191
11675
|
isWrite: false,
|
|
@@ -11254,6 +11738,8 @@ function registerSpotTradeTools() {
|
|
|
11254
11738
|
},
|
|
11255
11739
|
{
|
|
11256
11740
|
name: "spot_place_algo_order",
|
|
11741
|
+
title: "Spot Place Algo Order",
|
|
11742
|
+
destructiveHint: false,
|
|
11257
11743
|
module: "spot",
|
|
11258
11744
|
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
11745
|
isWrite: true,
|
|
@@ -11369,6 +11855,8 @@ function registerSpotTradeTools() {
|
|
|
11369
11855
|
},
|
|
11370
11856
|
{
|
|
11371
11857
|
name: "spot_amend_algo_order",
|
|
11858
|
+
title: "Spot Amend Algo Order",
|
|
11859
|
+
idempotentHint: true,
|
|
11372
11860
|
module: "spot",
|
|
11373
11861
|
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
11862
|
isWrite: true,
|
|
@@ -11405,6 +11893,8 @@ function registerSpotTradeTools() {
|
|
|
11405
11893
|
},
|
|
11406
11894
|
{
|
|
11407
11895
|
name: "spot_cancel_algo_order",
|
|
11896
|
+
title: "Spot Cancel Algo Order",
|
|
11897
|
+
idempotentHint: true,
|
|
11408
11898
|
module: "spot",
|
|
11409
11899
|
description: "Cancel a spot algo order (TP/SL).",
|
|
11410
11900
|
isWrite: true,
|
|
@@ -11438,6 +11928,7 @@ function registerSpotTradeTools() {
|
|
|
11438
11928
|
},
|
|
11439
11929
|
{
|
|
11440
11930
|
name: "spot_get_algo_orders",
|
|
11931
|
+
title: "Spot Get Algo Orders",
|
|
11441
11932
|
module: "spot",
|
|
11442
11933
|
description: "Query spot algo orders (TP/SL) - pending or history.",
|
|
11443
11934
|
isWrite: false,
|
|
@@ -11515,6 +12006,7 @@ function registerSpotTradeTools() {
|
|
|
11515
12006
|
},
|
|
11516
12007
|
{
|
|
11517
12008
|
name: "spot_get_fills",
|
|
12009
|
+
title: "Spot Get Fills",
|
|
11518
12010
|
module: "spot",
|
|
11519
12011
|
description: "Get spot transaction fills. archive=false(3d, default)|true(up to 3mo).",
|
|
11520
12012
|
isWrite: false,
|
|
@@ -11578,6 +12070,7 @@ function registerSpotTradeTools() {
|
|
|
11578
12070
|
},
|
|
11579
12071
|
{
|
|
11580
12072
|
name: "spot_batch_orders",
|
|
12073
|
+
title: "Spot Batch Orders",
|
|
11581
12074
|
module: "spot",
|
|
11582
12075
|
description: "[CAUTION] Batch place/cancel/amend up to 20 spot orders. action: place|cancel|amend.",
|
|
11583
12076
|
isWrite: true,
|
|
@@ -11636,6 +12129,7 @@ function registerSpotTradeTools() {
|
|
|
11636
12129
|
},
|
|
11637
12130
|
{
|
|
11638
12131
|
name: "spot_get_order",
|
|
12132
|
+
title: "Spot Get Order",
|
|
11639
12133
|
module: "spot",
|
|
11640
12134
|
description: "Get details of a single spot order.",
|
|
11641
12135
|
isWrite: false,
|
|
@@ -11673,6 +12167,8 @@ function registerSpotTradeTools() {
|
|
|
11673
12167
|
},
|
|
11674
12168
|
{
|
|
11675
12169
|
name: "spot_batch_amend",
|
|
12170
|
+
title: "Spot Batch Amend Orders",
|
|
12171
|
+
idempotentHint: true,
|
|
11676
12172
|
module: "spot",
|
|
11677
12173
|
description: "[CAUTION] Batch amend up to 20 unfilled spot orders.",
|
|
11678
12174
|
isWrite: true,
|
|
@@ -11703,6 +12199,8 @@ function registerSpotTradeTools() {
|
|
|
11703
12199
|
},
|
|
11704
12200
|
{
|
|
11705
12201
|
name: "spot_batch_cancel",
|
|
12202
|
+
title: "Spot Batch Cancel Orders",
|
|
12203
|
+
idempotentHint: true,
|
|
11706
12204
|
module: "spot",
|
|
11707
12205
|
description: "[CAUTION] Batch cancel up to 20 spot orders.",
|
|
11708
12206
|
isWrite: true,
|
|
@@ -11740,6 +12238,8 @@ function registerSpotTradeTools() {
|
|
|
11740
12238
|
// Not applicable: posSide (spot has no long/short hedge).
|
|
11741
12239
|
{
|
|
11742
12240
|
name: "spot_set_leverage",
|
|
12241
|
+
title: "Spot Set Leverage",
|
|
12242
|
+
idempotentHint: true,
|
|
11743
12243
|
module: "spot",
|
|
11744
12244
|
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
12245
|
isWrite: true,
|
|
@@ -11814,6 +12314,7 @@ function registerSwapTradeTools() {
|
|
|
11814
12314
|
prefix: "swap",
|
|
11815
12315
|
module: "swap",
|
|
11816
12316
|
label: "SWAP/FUTURES",
|
|
12317
|
+
titleLabel: "Perpetual Futures",
|
|
11817
12318
|
instTypes: ["SWAP", "FUTURES"],
|
|
11818
12319
|
instIdExample: "e.g. BTC-USDT-SWAP"
|
|
11819
12320
|
});
|
|
@@ -11823,6 +12324,8 @@ function registerSwapTradeTools() {
|
|
|
11823
12324
|
// Unique to swap: amend a pending TP/SL algo order attached to a position.
|
|
11824
12325
|
{
|
|
11825
12326
|
name: "swap_amend_algo_order",
|
|
12327
|
+
title: "Perpetual Futures Amend Algo Order",
|
|
12328
|
+
idempotentHint: true,
|
|
11826
12329
|
module: "swap",
|
|
11827
12330
|
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
12331
|
isWrite: true,
|
|
@@ -11861,6 +12364,7 @@ function registerSwapTradeTools() {
|
|
|
11861
12364
|
// Unique to swap: 3-in-1 batch tool (place / cancel / amend via action param).
|
|
11862
12365
|
{
|
|
11863
12366
|
name: "swap_batch_orders",
|
|
12367
|
+
title: "Perpetual Futures Batch Orders",
|
|
11864
12368
|
module: "swap",
|
|
11865
12369
|
description: "[CAUTION] Batch place/cancel/amend SWAP/FUTURES orders (max 20). action=place|cancel|amend.",
|
|
11866
12370
|
isWrite: true,
|
|
@@ -11954,12 +12458,12 @@ function createToolRunner(client, config) {
|
|
|
11954
12458
|
};
|
|
11955
12459
|
}
|
|
11956
12460
|
function configFilePath() {
|
|
11957
|
-
return
|
|
12461
|
+
return join9(homedir5(), ".okx", "config.toml");
|
|
11958
12462
|
}
|
|
11959
12463
|
function readFullConfig() {
|
|
11960
12464
|
const path42 = configFilePath();
|
|
11961
12465
|
if (!existsSync3(path42)) return { profiles: {} };
|
|
11962
|
-
const raw =
|
|
12466
|
+
const raw = readFileSync6(path42, "utf-8");
|
|
11963
12467
|
try {
|
|
11964
12468
|
return parse(raw);
|
|
11965
12469
|
} catch (err) {
|
|
@@ -12109,7 +12613,7 @@ async function loadConfig(cli) {
|
|
|
12109
12613
|
verbose: cli.verbose ?? false
|
|
12110
12614
|
};
|
|
12111
12615
|
}
|
|
12112
|
-
var CACHE_FILE =
|
|
12616
|
+
var CACHE_FILE = join10(homedir6(), ".okx", "update-check.json");
|
|
12113
12617
|
var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
12114
12618
|
var NEGATIVE_CHECK_INTERVAL_MS = 60 * 60 * 1e3;
|
|
12115
12619
|
var DEFAULT_REGISTRY = "https://registry.npmjs.org/";
|
|
@@ -12117,7 +12621,7 @@ var FETCH_TIMEOUT_MS = 3e3;
|
|
|
12117
12621
|
function readCache2() {
|
|
12118
12622
|
try {
|
|
12119
12623
|
if (existsSync4(CACHE_FILE)) {
|
|
12120
|
-
return JSON.parse(
|
|
12624
|
+
return JSON.parse(readFileSync7(CACHE_FILE, "utf-8"));
|
|
12121
12625
|
}
|
|
12122
12626
|
} catch {
|
|
12123
12627
|
}
|
|
@@ -12125,7 +12629,7 @@ function readCache2() {
|
|
|
12125
12629
|
}
|
|
12126
12630
|
function writeCache2(cache) {
|
|
12127
12631
|
try {
|
|
12128
|
-
mkdirSync6(
|
|
12632
|
+
mkdirSync6(join10(homedir6(), ".okx"), { recursive: true });
|
|
12129
12633
|
writeFileSync5(CACHE_FILE, JSON.stringify(cache, null, 2), "utf-8");
|
|
12130
12634
|
} catch {
|
|
12131
12635
|
}
|
|
@@ -12155,13 +12659,13 @@ function buildNpmrcCandidates() {
|
|
|
12155
12659
|
let dir = process.cwd();
|
|
12156
12660
|
const root = dir.startsWith("/") ? "/" : dir.slice(0, 3);
|
|
12157
12661
|
while (true) {
|
|
12158
|
-
add(
|
|
12662
|
+
add(join10(dir, ".npmrc"));
|
|
12159
12663
|
if (dir === root) break;
|
|
12160
|
-
const parent =
|
|
12664
|
+
const parent = join10(dir, "..");
|
|
12161
12665
|
if (parent === dir) break;
|
|
12162
12666
|
dir = parent;
|
|
12163
12667
|
}
|
|
12164
|
-
add(
|
|
12668
|
+
add(join10(homedir6(), ".npmrc"));
|
|
12165
12669
|
if (process.platform !== "win32") {
|
|
12166
12670
|
add("/etc/npmrc");
|
|
12167
12671
|
}
|
|
@@ -12170,7 +12674,7 @@ function buildNpmrcCandidates() {
|
|
|
12170
12674
|
function readNpmrcRegistry(filePath) {
|
|
12171
12675
|
try {
|
|
12172
12676
|
if (!existsSync4(filePath)) return null;
|
|
12173
|
-
const lines =
|
|
12677
|
+
const lines = readFileSync7(filePath, "utf-8").split(/\r?\n/);
|
|
12174
12678
|
for (const line of lines) {
|
|
12175
12679
|
const trimmed = line.trim();
|
|
12176
12680
|
if (trimmed.startsWith("#") || !trimmed.includes("=")) continue;
|
|
@@ -12497,6 +13001,14 @@ function runSetup(options) {
|
|
|
12497
13001
|
`);
|
|
12498
13002
|
}
|
|
12499
13003
|
}
|
|
13004
|
+
var HttpStatusError = class extends Error {
|
|
13005
|
+
statusCode;
|
|
13006
|
+
constructor(statusCode) {
|
|
13007
|
+
super(`HTTP ${statusCode}`);
|
|
13008
|
+
this.name = "HttpStatusError";
|
|
13009
|
+
this.statusCode = statusCode;
|
|
13010
|
+
}
|
|
13011
|
+
};
|
|
12500
13012
|
function isRedirect(statusCode) {
|
|
12501
13013
|
return statusCode !== void 0 && statusCode >= 300 && statusCode < 400;
|
|
12502
13014
|
}
|
|
@@ -12511,7 +13023,7 @@ function validateRedirect(res, requestUrl, redirectCount, maxRedirects) {
|
|
|
12511
13023
|
return location;
|
|
12512
13024
|
}
|
|
12513
13025
|
function fetchResponse(url, timeoutMs) {
|
|
12514
|
-
return new Promise((
|
|
13026
|
+
return new Promise((resolve4, reject) => {
|
|
12515
13027
|
let redirects = 0;
|
|
12516
13028
|
const maxRedirects = 5;
|
|
12517
13029
|
function doRequest(requestUrl) {
|
|
@@ -12529,10 +13041,14 @@ function fetchResponse(url, timeoutMs) {
|
|
|
12529
13041
|
return;
|
|
12530
13042
|
}
|
|
12531
13043
|
if (res.statusCode !== 200) {
|
|
12532
|
-
|
|
13044
|
+
if (res.statusCode === void 0) {
|
|
13045
|
+
reject(new Error("HTTP unknown"));
|
|
13046
|
+
} else {
|
|
13047
|
+
reject(new HttpStatusError(res.statusCode));
|
|
13048
|
+
}
|
|
12533
13049
|
return;
|
|
12534
13050
|
}
|
|
12535
|
-
|
|
13051
|
+
resolve4(res);
|
|
12536
13052
|
});
|
|
12537
13053
|
req.on("error", reject);
|
|
12538
13054
|
req.on("timeout", () => {
|
|
@@ -12545,10 +13061,10 @@ function fetchResponse(url, timeoutMs) {
|
|
|
12545
13061
|
}
|
|
12546
13062
|
function download(url, destPath, timeoutMs) {
|
|
12547
13063
|
return fetchResponse(url, timeoutMs).then(
|
|
12548
|
-
(res) => new Promise((
|
|
13064
|
+
(res) => new Promise((resolve4, reject) => {
|
|
12549
13065
|
const file = createWriteStream2(destPath);
|
|
12550
13066
|
res.pipe(file);
|
|
12551
|
-
file.on("finish", () => file.close(() =>
|
|
13067
|
+
file.on("finish", () => file.close(() => resolve4()));
|
|
12552
13068
|
file.on("error", (err) => {
|
|
12553
13069
|
try {
|
|
12554
13070
|
unlinkSync3(destPath);
|
|
@@ -12561,10 +13077,10 @@ function download(url, destPath, timeoutMs) {
|
|
|
12561
13077
|
}
|
|
12562
13078
|
function downloadText(url, timeoutMs) {
|
|
12563
13079
|
return fetchResponse(url, timeoutMs).then(
|
|
12564
|
-
(res) => new Promise((
|
|
13080
|
+
(res) => new Promise((resolve4, reject) => {
|
|
12565
13081
|
const chunks = [];
|
|
12566
13082
|
res.on("data", (chunk) => chunks.push(chunk));
|
|
12567
|
-
res.on("end", () =>
|
|
13083
|
+
res.on("end", () => resolve4(Buffer.concat(chunks).toString("utf8")));
|
|
12568
13084
|
res.on("error", reject);
|
|
12569
13085
|
})
|
|
12570
13086
|
);
|
|
@@ -12593,10 +13109,10 @@ function getBinaryName() {
|
|
|
12593
13109
|
return platform() === "win32" ? "okx-pilot.exe" : "okx-pilot";
|
|
12594
13110
|
}
|
|
12595
13111
|
function hashFile(filePath) {
|
|
12596
|
-
const buf =
|
|
13112
|
+
const buf = readFileSync9(filePath);
|
|
12597
13113
|
return {
|
|
12598
13114
|
size: buf.byteLength,
|
|
12599
|
-
sha256:
|
|
13115
|
+
sha256: createHash3("sha256").update(buf).digest("hex")
|
|
12600
13116
|
};
|
|
12601
13117
|
}
|
|
12602
13118
|
function getPilotStatus(binaryPath, opts) {
|
|
@@ -12711,7 +13227,7 @@ async function installPilotBinary(destPath, sources = CDN_SOURCES, onProgress) {
|
|
|
12711
13227
|
if (earlyResult) return earlyResult;
|
|
12712
13228
|
const platformDir = getPlatformDir();
|
|
12713
13229
|
const binaryName = getBinaryName();
|
|
12714
|
-
const resolvedDest = destPath ??
|
|
13230
|
+
const resolvedDest = destPath ?? join12(homedir8(), ".okx", "bin", binaryName);
|
|
12715
13231
|
const tmpPath = resolvedDest + ".tmp";
|
|
12716
13232
|
mkdirSync8(dirname6(resolvedDest), { recursive: true });
|
|
12717
13233
|
const localHash = existsSync6(resolvedDest) ? hashFile(resolvedDest) : null;
|
|
@@ -12763,9 +13279,41 @@ function removePilotBinary(binaryPath) {
|
|
|
12763
13279
|
}
|
|
12764
13280
|
}
|
|
12765
13281
|
var AUTH_CDN_PATH_PREFIX = "/upgradeapp/tools/oauth";
|
|
13282
|
+
var LINUX_ARM64_FALLBACK_DIR = "linux-x64";
|
|
12766
13283
|
function getAuthBinaryName() {
|
|
12767
13284
|
return platform2() === "win32" ? "okx-auth.exe" : "okx-auth";
|
|
12768
13285
|
}
|
|
13286
|
+
async function _resolveAuthPlatformFromNative(native, sources, timeoutMs) {
|
|
13287
|
+
if (native !== "linux-arm64") {
|
|
13288
|
+
return native;
|
|
13289
|
+
}
|
|
13290
|
+
const checksumPath = `${AUTH_CDN_PATH_PREFIX}/linux-arm64/checksum.json`;
|
|
13291
|
+
let got404 = false;
|
|
13292
|
+
for (const { host, protocol } of sources) {
|
|
13293
|
+
const url = `${protocol}://${host}${checksumPath}`;
|
|
13294
|
+
try {
|
|
13295
|
+
await downloadText(url, timeoutMs);
|
|
13296
|
+
return "linux-arm64";
|
|
13297
|
+
} catch (err) {
|
|
13298
|
+
if (err instanceof HttpStatusError && err.statusCode === 404) {
|
|
13299
|
+
got404 = true;
|
|
13300
|
+
break;
|
|
13301
|
+
}
|
|
13302
|
+
}
|
|
13303
|
+
}
|
|
13304
|
+
if (got404) {
|
|
13305
|
+
console.warn(
|
|
13306
|
+
"[okx-auth] Native linux-arm64 binary not yet on CDN. Falling back to linux-x64 binary \u2014 x86_64 emulation (binfmt_misc/Rosetta) required."
|
|
13307
|
+
);
|
|
13308
|
+
return LINUX_ARM64_FALLBACK_DIR;
|
|
13309
|
+
}
|
|
13310
|
+
return "linux-arm64";
|
|
13311
|
+
}
|
|
13312
|
+
async function resolveAuthPlatformDir(sources = CDN_SOURCES, timeoutMs = DOWNLOAD_TIMEOUT_MS) {
|
|
13313
|
+
const native = getPlatformDir();
|
|
13314
|
+
if (!native) return null;
|
|
13315
|
+
return _resolveAuthPlatformFromNative(native, sources, timeoutMs);
|
|
13316
|
+
}
|
|
12769
13317
|
function getAuthStatus(binaryPath, opts) {
|
|
12770
13318
|
const resolvedPath = binaryPath ?? getAuthBinaryPath();
|
|
12771
13319
|
const platformDir = getPlatformDir();
|
|
@@ -12779,7 +13327,7 @@ function getAuthStatus(binaryPath, opts) {
|
|
|
12779
13327
|
return { binaryPath: resolvedPath, exists: true, platform: platformDir, fileSize: size, sha256 };
|
|
12780
13328
|
}
|
|
12781
13329
|
async function fetchAuthCdnChecksum(sources = CDN_SOURCES, timeoutMs = DOWNLOAD_TIMEOUT_MS) {
|
|
12782
|
-
const platformDir =
|
|
13330
|
+
const platformDir = await resolveAuthPlatformDir(sources, timeoutMs);
|
|
12783
13331
|
if (!platformDir) return null;
|
|
12784
13332
|
const checksumPath = `${AUTH_CDN_PATH_PREFIX}/${platformDir}/checksum.json`;
|
|
12785
13333
|
for (const { host, protocol } of sources) {
|
|
@@ -12848,12 +13396,51 @@ function installPreChecks2(destPath, sources) {
|
|
|
12848
13396
|
function isLocalUpToDate2(localHash, checksum) {
|
|
12849
13397
|
return localHash !== null && localHash.size === checksum.size && localHash.sha256 === checksum.sha256;
|
|
12850
13398
|
}
|
|
13399
|
+
async function tryInstallFromOneSource(ctx) {
|
|
13400
|
+
const { host, protocol, platformDir, checksumPath, binaryPath, tmpPath, resolvedDest, localHash, onProgress } = ctx;
|
|
13401
|
+
try {
|
|
13402
|
+
const checksum = await fetchAndValidateChecksum2(
|
|
13403
|
+
host,
|
|
13404
|
+
protocol,
|
|
13405
|
+
checksumPath,
|
|
13406
|
+
platformDir,
|
|
13407
|
+
DOWNLOAD_TIMEOUT_MS,
|
|
13408
|
+
onProgress
|
|
13409
|
+
);
|
|
13410
|
+
if (isLocalUpToDate2(localHash, checksum)) {
|
|
13411
|
+
onProgress?.("Already up to date (checksum match)");
|
|
13412
|
+
return { kind: "done", result: { status: "up-to-date", source: host } };
|
|
13413
|
+
}
|
|
13414
|
+
await downloadAndVerify2(host, protocol, binaryPath, tmpPath, checksum, DOWNLOAD_TIMEOUT_MS, onProgress);
|
|
13415
|
+
atomicReplace2(tmpPath, resolvedDest);
|
|
13416
|
+
onProgress?.(`Downloaded and verified from ${host}`);
|
|
13417
|
+
return { kind: "done", result: { status: "installed", source: host } };
|
|
13418
|
+
} catch (err) {
|
|
13419
|
+
try {
|
|
13420
|
+
unlinkSync5(tmpPath);
|
|
13421
|
+
} catch {
|
|
13422
|
+
}
|
|
13423
|
+
const errObj = err instanceof Error ? err : new Error(String(err));
|
|
13424
|
+
onProgress?.(`${host} failed: ${errObj.message}`);
|
|
13425
|
+
return { kind: "next", err: errObj };
|
|
13426
|
+
}
|
|
13427
|
+
}
|
|
13428
|
+
function buildInstallFailureResult(errors) {
|
|
13429
|
+
const formatted = errors.map(({ host, err }) => `${host}: ${err.message}`).join("\n");
|
|
13430
|
+
const allHttpErrors = errors.length > 0 && errors.every(({ err }) => err instanceof HttpStatusError);
|
|
13431
|
+
const prefix = allHttpErrors ? "okx-auth binary not available on CDN for this platform" : "All CDN sources failed";
|
|
13432
|
+
return { status: "failed", error: `${prefix}:
|
|
13433
|
+
${formatted}` };
|
|
13434
|
+
}
|
|
12851
13435
|
async function installAuthBinary(destPath, sources = CDN_SOURCES, onProgress) {
|
|
12852
13436
|
const earlyResult = installPreChecks2(destPath, sources);
|
|
12853
13437
|
if (earlyResult) return earlyResult;
|
|
12854
|
-
const platformDir =
|
|
13438
|
+
const platformDir = await resolveAuthPlatformDir(sources);
|
|
13439
|
+
if (!platformDir) {
|
|
13440
|
+
return { status: "failed", error: "Unsupported platform" };
|
|
13441
|
+
}
|
|
12855
13442
|
const binaryName = getAuthBinaryName();
|
|
12856
|
-
const resolvedDest = destPath ??
|
|
13443
|
+
const resolvedDest = destPath ?? join13(homedir9(), ".okx", "bin", binaryName);
|
|
12857
13444
|
const tmpPath = resolvedDest + ".tmp";
|
|
12858
13445
|
mkdirSync9(dirname7(resolvedDest), { recursive: true });
|
|
12859
13446
|
const localHash = existsSync7(resolvedDest) ? hashFile(resolvedDest) : null;
|
|
@@ -12861,35 +13448,21 @@ async function installAuthBinary(destPath, sources = CDN_SOURCES, onProgress) {
|
|
|
12861
13448
|
const binaryPath = `${AUTH_CDN_PATH_PREFIX}/${platformDir}/${binaryName}`;
|
|
12862
13449
|
const errors = [];
|
|
12863
13450
|
for (const { host, protocol } of sources) {
|
|
12864
|
-
|
|
12865
|
-
|
|
12866
|
-
|
|
12867
|
-
|
|
12868
|
-
|
|
12869
|
-
|
|
12870
|
-
|
|
12871
|
-
|
|
12872
|
-
|
|
12873
|
-
|
|
12874
|
-
|
|
12875
|
-
|
|
12876
|
-
|
|
12877
|
-
await downloadAndVerify2(host, protocol, binaryPath, tmpPath, checksum, DOWNLOAD_TIMEOUT_MS, onProgress);
|
|
12878
|
-
atomicReplace2(tmpPath, resolvedDest);
|
|
12879
|
-
onProgress?.(`Downloaded and verified from ${host}`);
|
|
12880
|
-
return { status: "installed", source: host };
|
|
12881
|
-
} catch (err) {
|
|
12882
|
-
try {
|
|
12883
|
-
unlinkSync5(tmpPath);
|
|
12884
|
-
} catch {
|
|
12885
|
-
}
|
|
12886
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
12887
|
-
errors.push(`${host}: ${msg}`);
|
|
12888
|
-
onProgress?.(`${host} failed: ${msg}`);
|
|
12889
|
-
}
|
|
13451
|
+
const attempt = await tryInstallFromOneSource({
|
|
13452
|
+
host,
|
|
13453
|
+
protocol,
|
|
13454
|
+
platformDir,
|
|
13455
|
+
checksumPath,
|
|
13456
|
+
binaryPath,
|
|
13457
|
+
tmpPath,
|
|
13458
|
+
resolvedDest,
|
|
13459
|
+
localHash,
|
|
13460
|
+
onProgress
|
|
13461
|
+
});
|
|
13462
|
+
if (attempt.kind === "done") return attempt.result;
|
|
13463
|
+
errors.push({ host, err: attempt.err });
|
|
12890
13464
|
}
|
|
12891
|
-
return
|
|
12892
|
-
${errors.join("\n")}` };
|
|
13465
|
+
return buildInstallFailureResult(errors);
|
|
12893
13466
|
}
|
|
12894
13467
|
function removeAuthBinary(binaryPath) {
|
|
12895
13468
|
const resolvedPath = binaryPath ?? getAuthBinaryPath();
|
|
@@ -12904,12 +13477,12 @@ function removeAuthBinary(binaryPath) {
|
|
|
12904
13477
|
throw new Error(`Failed to remove ${resolvedPath}: ${msg}`);
|
|
12905
13478
|
}
|
|
12906
13479
|
}
|
|
12907
|
-
var CACHE_PATH =
|
|
13480
|
+
var CACHE_PATH = join14(homedir10(), ".okx", "auth-binary-check.json");
|
|
12908
13481
|
var CHECK_INTERVAL_MS2 = 2 * 60 * 60 * 1e3;
|
|
12909
13482
|
function readCache3() {
|
|
12910
13483
|
try {
|
|
12911
13484
|
if (!existsSync8(CACHE_PATH)) return null;
|
|
12912
|
-
const data = JSON.parse(
|
|
13485
|
+
const data = JSON.parse(readFileSync10(CACHE_PATH, "utf-8"));
|
|
12913
13486
|
if (typeof data.cdnSha256 !== "string" || typeof data.checkedAt !== "number") return null;
|
|
12914
13487
|
return { cdnSha256: data.cdnSha256, checkedAt: data.checkedAt };
|
|
12915
13488
|
} catch {
|
|
@@ -12918,7 +13491,7 @@ function readCache3() {
|
|
|
12918
13491
|
}
|
|
12919
13492
|
function writeCache3(cdnSha256) {
|
|
12920
13493
|
try {
|
|
12921
|
-
mkdirSync10(
|
|
13494
|
+
mkdirSync10(join14(homedir10(), ".okx"), { recursive: true });
|
|
12922
13495
|
writeFileSync7(CACHE_PATH, JSON.stringify({ cdnSha256, checkedAt: Date.now() }, null, 2), "utf-8");
|
|
12923
13496
|
} catch {
|
|
12924
13497
|
}
|
|
@@ -13053,7 +13626,7 @@ function markFailedIfSCodeError(data) {
|
|
|
13053
13626
|
// src/commands/auth.ts
|
|
13054
13627
|
function runOkxAuth(args) {
|
|
13055
13628
|
const binPath = getAuthBinaryPath();
|
|
13056
|
-
return new Promise((
|
|
13629
|
+
return new Promise((resolve4, reject) => {
|
|
13057
13630
|
const child = spawn3(binPath, args, {
|
|
13058
13631
|
stdio: "inherit"
|
|
13059
13632
|
});
|
|
@@ -13061,13 +13634,13 @@ function runOkxAuth(args) {
|
|
|
13061
13634
|
reject(new Error(`Failed to spawn okx-auth: ${err.message}`));
|
|
13062
13635
|
});
|
|
13063
13636
|
child.on("close", (code) => {
|
|
13064
|
-
|
|
13637
|
+
resolve4(code ?? 1);
|
|
13065
13638
|
});
|
|
13066
13639
|
});
|
|
13067
13640
|
}
|
|
13068
13641
|
function runOkxAuthCapture(args) {
|
|
13069
13642
|
const binPath = getAuthBinaryPath();
|
|
13070
|
-
return new Promise((
|
|
13643
|
+
return new Promise((resolve4, reject) => {
|
|
13071
13644
|
const child = spawn3(binPath, args, {
|
|
13072
13645
|
stdio: ["inherit", "pipe", "inherit"]
|
|
13073
13646
|
});
|
|
@@ -13077,7 +13650,7 @@ function runOkxAuthCapture(args) {
|
|
|
13077
13650
|
reject(new Error(`Failed to spawn okx-auth: ${err.message}`));
|
|
13078
13651
|
});
|
|
13079
13652
|
child.on("close", (code) => {
|
|
13080
|
-
|
|
13653
|
+
resolve4({
|
|
13081
13654
|
code: code ?? 1,
|
|
13082
13655
|
stdout: Buffer.concat(chunks).toString("utf-8")
|
|
13083
13656
|
});
|
|
@@ -13282,14 +13855,14 @@ async function confirmRemoval(force, binaryPath) {
|
|
|
13282
13855
|
return true;
|
|
13283
13856
|
}
|
|
13284
13857
|
function askConfirmation(prompt2) {
|
|
13285
|
-
return new Promise((
|
|
13858
|
+
return new Promise((resolve4) => {
|
|
13286
13859
|
const rl = readline.createInterface({
|
|
13287
13860
|
input: process.stdin,
|
|
13288
13861
|
output: process.stdout
|
|
13289
13862
|
});
|
|
13290
13863
|
rl.question(prompt2, (answer) => {
|
|
13291
13864
|
rl.close();
|
|
13292
|
-
|
|
13865
|
+
resolve4(answer.trim().toLowerCase() === "y");
|
|
13293
13866
|
});
|
|
13294
13867
|
});
|
|
13295
13868
|
}
|
|
@@ -13346,26 +13919,26 @@ var Report = class {
|
|
|
13346
13919
|
this.lines.push({ key, value });
|
|
13347
13920
|
}
|
|
13348
13921
|
print() {
|
|
13349
|
-
const
|
|
13922
|
+
const sep3 = "\u2500".repeat(52);
|
|
13350
13923
|
outputLine("");
|
|
13351
|
-
outputLine(` \u2500\u2500 Diagnostic Report (copy & share) ${
|
|
13924
|
+
outputLine(` \u2500\u2500 Diagnostic Report (copy & share) ${sep3.slice(35)}`);
|
|
13352
13925
|
for (const { key, value } of this.lines) {
|
|
13353
13926
|
outputLine(` ${key.padEnd(14)} ${value}`);
|
|
13354
13927
|
}
|
|
13355
|
-
outputLine(` ${
|
|
13928
|
+
outputLine(` ${sep3}`);
|
|
13356
13929
|
outputLine("");
|
|
13357
13930
|
}
|
|
13358
13931
|
/** Write report to a file path, returns true on success. */
|
|
13359
13932
|
writeToFile(filePath) {
|
|
13360
13933
|
try {
|
|
13361
|
-
const
|
|
13934
|
+
const sep3 = "\u2500".repeat(52);
|
|
13362
13935
|
const lines = [
|
|
13363
|
-
`\u2500\u2500 Diagnostic Report (copy & share) ${
|
|
13936
|
+
`\u2500\u2500 Diagnostic Report (copy & share) ${sep3.slice(35)}`
|
|
13364
13937
|
];
|
|
13365
13938
|
for (const { key, value } of this.lines) {
|
|
13366
13939
|
lines.push(`${key.padEnd(14)} ${value}`);
|
|
13367
13940
|
}
|
|
13368
|
-
lines.push(
|
|
13941
|
+
lines.push(sep3, "");
|
|
13369
13942
|
fs4.writeFileSync(filePath, lines.join("\n"), "utf8");
|
|
13370
13943
|
return true;
|
|
13371
13944
|
} catch (_e) {
|
|
@@ -13741,13 +14314,13 @@ async function checkStdioHandshake(entryPath, report) {
|
|
|
13741
14314
|
clientInfo: { name: "okx-diagnose", version: "1.0" }
|
|
13742
14315
|
}
|
|
13743
14316
|
});
|
|
13744
|
-
return new Promise((
|
|
14317
|
+
return new Promise((resolve4) => {
|
|
13745
14318
|
let settled = false;
|
|
13746
14319
|
const settle = (passed) => {
|
|
13747
14320
|
if (settled) return;
|
|
13748
14321
|
settled = true;
|
|
13749
14322
|
clearTimeout(timer);
|
|
13750
|
-
|
|
14323
|
+
resolve4(passed);
|
|
13751
14324
|
};
|
|
13752
14325
|
const child = spawn4(process.execPath, [entryPath], {
|
|
13753
14326
|
stdio: ["pipe", "pipe", "pipe"],
|
|
@@ -13868,7 +14441,7 @@ async function cmdDiagnoseMcp(options = {}) {
|
|
|
13868
14441
|
|
|
13869
14442
|
// src/commands/diagnose.ts
|
|
13870
14443
|
var CLI_VERSION = readCliVersion();
|
|
13871
|
-
var GIT_HASH = true ? "
|
|
14444
|
+
var GIT_HASH = true ? "e5abc21f" : "dev";
|
|
13872
14445
|
function maskKey2(key) {
|
|
13873
14446
|
if (!key) return "(not set)";
|
|
13874
14447
|
if (key.length <= 8) return "****";
|
|
@@ -13885,7 +14458,7 @@ async function checkDns(hostname) {
|
|
|
13885
14458
|
}
|
|
13886
14459
|
async function checkSocket(createFn, successEvent, timeoutMs) {
|
|
13887
14460
|
const t0 = Date.now();
|
|
13888
|
-
return new Promise((
|
|
14461
|
+
return new Promise((resolve4) => {
|
|
13889
14462
|
const socket = createFn();
|
|
13890
14463
|
const cleanup = () => {
|
|
13891
14464
|
socket.removeAllListeners();
|
|
@@ -13893,15 +14466,15 @@ async function checkSocket(createFn, successEvent, timeoutMs) {
|
|
|
13893
14466
|
};
|
|
13894
14467
|
socket.once(successEvent, () => {
|
|
13895
14468
|
cleanup();
|
|
13896
|
-
|
|
14469
|
+
resolve4({ ok: true, ms: Date.now() - t0 });
|
|
13897
14470
|
});
|
|
13898
14471
|
socket.once("timeout", () => {
|
|
13899
14472
|
cleanup();
|
|
13900
|
-
|
|
14473
|
+
resolve4({ ok: false, ms: Date.now() - t0, error: `timed out after ${timeoutMs}ms` });
|
|
13901
14474
|
});
|
|
13902
14475
|
socket.once("error", (err) => {
|
|
13903
14476
|
cleanup();
|
|
13904
|
-
|
|
14477
|
+
resolve4({ ok: false, ms: Date.now() - t0, error: err.message });
|
|
13905
14478
|
});
|
|
13906
14479
|
});
|
|
13907
14480
|
}
|
|
@@ -14229,23 +14802,23 @@ function suggestSubcommand(action, knownActions, knownPaths = []) {
|
|
|
14229
14802
|
|
|
14230
14803
|
// src/commands/upgrade.ts
|
|
14231
14804
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
14232
|
-
import { readFileSync as
|
|
14233
|
-
import { dirname as dirname8, join as
|
|
14805
|
+
import { readFileSync as readFileSync11, writeFileSync as writeFileSync8, mkdirSync as mkdirSync11 } from "fs";
|
|
14806
|
+
import { dirname as dirname8, join as join15 } from "path";
|
|
14234
14807
|
import { homedir as homedir11 } from "os";
|
|
14235
14808
|
var PACKAGES = ["@okx_ai/okx-trade-mcp", "@okx_ai/okx-trade-cli"];
|
|
14236
|
-
var CACHE_FILE2 =
|
|
14809
|
+
var CACHE_FILE2 = join15(homedir11(), ".okx", "last_check");
|
|
14237
14810
|
var THROTTLE_MS = 12 * 60 * 60 * 1e3;
|
|
14238
|
-
var NPM_BIN =
|
|
14811
|
+
var NPM_BIN = join15(dirname8(process.execPath), process.platform === "win32" ? "npm.cmd" : "npm");
|
|
14239
14812
|
function readLastCheck() {
|
|
14240
14813
|
try {
|
|
14241
|
-
return parseInt(
|
|
14814
|
+
return parseInt(readFileSync11(CACHE_FILE2, "utf-8").trim(), 10) || 0;
|
|
14242
14815
|
} catch {
|
|
14243
14816
|
return 0;
|
|
14244
14817
|
}
|
|
14245
14818
|
}
|
|
14246
14819
|
function writeLastCheck() {
|
|
14247
14820
|
try {
|
|
14248
|
-
mkdirSync11(
|
|
14821
|
+
mkdirSync11(join15(homedir11(), ".okx"), { recursive: true });
|
|
14249
14822
|
writeFileSync8(CACHE_FILE2, String(Math.floor(Date.now() / 1e3)), "utf-8");
|
|
14250
14823
|
} catch {
|
|
14251
14824
|
}
|
|
@@ -14932,6 +15505,11 @@ var CLI_REGISTRY = {
|
|
|
14932
15505
|
usage: "okx earn savings rate-history [--ccy <ccy>] [--limit <n>]",
|
|
14933
15506
|
description: "Query Simple Earn lending rates and fixed-term offers (requires auth)"
|
|
14934
15507
|
},
|
|
15508
|
+
"fixed-products": {
|
|
15509
|
+
toolName: "earn_get_fixed_earn_products",
|
|
15510
|
+
usage: "okx earn savings fixed-products [--ccy <ccy>]",
|
|
15511
|
+
description: "List available fixed-term earn products with APR, term, and remaining quota"
|
|
15512
|
+
},
|
|
14935
15513
|
"fixed-orders": {
|
|
14936
15514
|
toolName: "earn_get_fixed_order_list",
|
|
14937
15515
|
usage: "okx earn savings fixed-orders [--ccy <ccy>] [--state <pending|earning|expired|settled|cancelled>]",
|
|
@@ -14944,7 +15522,7 @@ var CLI_REGISTRY = {
|
|
|
14944
15522
|
},
|
|
14945
15523
|
"fixed-redeem": {
|
|
14946
15524
|
toolName: "earn_fixed_redeem",
|
|
14947
|
-
usage: "okx earn savings fixed-redeem <reqId>",
|
|
15525
|
+
usage: "okx earn savings fixed-redeem --reqId <reqId>",
|
|
14948
15526
|
description: "Redeem a fixed-term earn order (full amount)"
|
|
14949
15527
|
}
|
|
14950
15528
|
}
|
|
@@ -18247,7 +18825,7 @@ Config saved to ${p}
|
|
|
18247
18825
|
}
|
|
18248
18826
|
};
|
|
18249
18827
|
function prompt(rl, question) {
|
|
18250
|
-
return new Promise((
|
|
18828
|
+
return new Promise((resolve4) => rl.question(question, resolve4));
|
|
18251
18829
|
}
|
|
18252
18830
|
function cmdConfigShow(json) {
|
|
18253
18831
|
const config = readFullConfig();
|
|
@@ -18659,13 +19237,24 @@ async function cmdEarnLendingRateHistory(run, opts) {
|
|
|
18659
19237
|
printTable(fixedOffers.map((r) => ({
|
|
18660
19238
|
ccy: r["ccy"],
|
|
18661
19239
|
term: r["term"],
|
|
18662
|
-
|
|
19240
|
+
apr: r["apr"],
|
|
18663
19241
|
minLend: r["minLend"],
|
|
18664
19242
|
remainingQuota: r["lendQuota"],
|
|
18665
19243
|
soldOut: r["soldOut"] ? "Yes" : "No"
|
|
18666
19244
|
})));
|
|
18667
19245
|
}
|
|
18668
19246
|
}
|
|
19247
|
+
async function cmdEarnFixedProducts(run, opts) {
|
|
19248
|
+
const data = extractData(await run("earn_get_fixed_earn_products", { ccy: opts.ccy }));
|
|
19249
|
+
printDataList(data, opts.json, "No fixed earn products available", (r) => ({
|
|
19250
|
+
ccy: r["ccy"],
|
|
19251
|
+
term: r["term"],
|
|
19252
|
+
apr: r["apr"],
|
|
19253
|
+
minLend: r["minLend"],
|
|
19254
|
+
remainingQuota: r["lendQuota"],
|
|
19255
|
+
soldOut: r["soldOut"] ? "Yes" : "No"
|
|
19256
|
+
}));
|
|
19257
|
+
}
|
|
18669
19258
|
function extractFixedOffers(result) {
|
|
18670
19259
|
if (result && typeof result === "object") {
|
|
18671
19260
|
const offers = result["fixedOffers"];
|
|
@@ -19659,18 +20248,21 @@ async function cmdDcdQuoteAndBuy(run, opts) {
|
|
|
19659
20248
|
|
|
19660
20249
|
// src/commands/skill.ts
|
|
19661
20250
|
import { tmpdir, homedir as homedir13 } from "os";
|
|
19662
|
-
import { join as
|
|
20251
|
+
import { join as join17, dirname as dirname9 } from "path";
|
|
19663
20252
|
import { mkdirSync as mkdirSync12, rmSync, existsSync as existsSync10, copyFileSync as copyFileSync2 } from "fs";
|
|
19664
20253
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
19665
20254
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
19666
20255
|
function resolveNpx() {
|
|
19667
|
-
const sibling =
|
|
20256
|
+
const sibling = join17(dirname9(process.execPath), "npx");
|
|
19668
20257
|
if (existsSync10(sibling)) return sibling;
|
|
19669
20258
|
return "npx";
|
|
19670
20259
|
}
|
|
19671
20260
|
function npxEnv() {
|
|
19672
20261
|
return { ...process.env, NO_COLOR: "1", FORCE_COLOR: "0" };
|
|
19673
20262
|
}
|
|
20263
|
+
function getSkillContentDir(name) {
|
|
20264
|
+
return join17(homedir13(), ".agents", "skills", name);
|
|
20265
|
+
}
|
|
19674
20266
|
var THIRD_PARTY_INSTALL_NOTICE = "Note: This skill was created by a third-party developer, not by OKX. Review SKILL.md before use.";
|
|
19675
20267
|
async function cmdSkillSearch(run, opts) {
|
|
19676
20268
|
const args = {};
|
|
@@ -19720,16 +20312,56 @@ async function cmdSkillCategories(run, json) {
|
|
|
19720
20312
|
}
|
|
19721
20313
|
outputLine("");
|
|
19722
20314
|
}
|
|
19723
|
-
async function
|
|
19724
|
-
|
|
20315
|
+
async function wrapVerify(fn) {
|
|
20316
|
+
try {
|
|
20317
|
+
return await fn();
|
|
20318
|
+
} catch (e) {
|
|
20319
|
+
if (e instanceof ConfigError) {
|
|
20320
|
+
throw new Error(
|
|
20321
|
+
`Signature verification requires authentication \u2014 run \`okx auth login\` first, or use --force to bypass.`
|
|
20322
|
+
);
|
|
20323
|
+
}
|
|
20324
|
+
throw e;
|
|
20325
|
+
}
|
|
20326
|
+
}
|
|
20327
|
+
async function cmdSkillAdd(name, config, json, force = false, exec = execFileSync2, _deps) {
|
|
20328
|
+
const _download = _deps?.download ?? downloadSkillZip;
|
|
20329
|
+
const _extract = _deps?.extract ?? extractSkillZip;
|
|
20330
|
+
const tmpBase = join17(tmpdir(), `okx-skill-${randomUUID2()}`);
|
|
19725
20331
|
mkdirSync12(tmpBase, { recursive: true });
|
|
19726
20332
|
try {
|
|
19727
20333
|
outputLine(`Downloading ${name}...`);
|
|
19728
20334
|
const client = new OkxRestClient(config);
|
|
19729
|
-
const zipPath = await
|
|
19730
|
-
const contentDir = await
|
|
20335
|
+
const zipPath = await _download(client, name, tmpBase);
|
|
20336
|
+
const contentDir = await _extract(zipPath, join17(tmpBase, "content"));
|
|
19731
20337
|
const meta = readMetaJson(contentDir);
|
|
19732
20338
|
validateSkillMdExists(contentDir);
|
|
20339
|
+
outputLine("Verifying signature...");
|
|
20340
|
+
const verifyResult = await wrapVerify(
|
|
20341
|
+
() => verifySkillSignature(contentDir, meta.signing, {
|
|
20342
|
+
fetchPublicKey: (keyId) => getPublicKey(client, keyId),
|
|
20343
|
+
serverSideVerify: (sName, version, files) => serverSideVerify(client, sName, version, files),
|
|
20344
|
+
skillName: meta.name,
|
|
20345
|
+
skillVersion: meta.version
|
|
20346
|
+
})
|
|
20347
|
+
);
|
|
20348
|
+
if (verifyResult.status === "failed") {
|
|
20349
|
+
if (!force) {
|
|
20350
|
+
throw new Error(`Signature verification failed: ${verifyResult.error ?? "unknown error"}. Use --force to install anyway.`);
|
|
20351
|
+
}
|
|
20352
|
+
process.stderr.write(`WARNING: Signature verification failed \u2014 ${verifyResult.error ?? "unknown"}. Installing anyway (--force).
|
|
20353
|
+
`);
|
|
20354
|
+
} else if (verifyResult.status === "verified_by_server") {
|
|
20355
|
+
if (!json) {
|
|
20356
|
+
outputLine(` Verified by server (v${verifyResult.serverVersion ?? "?"})`);
|
|
20357
|
+
if (verifyResult.error) outputLine(` Note: ${verifyResult.error}`);
|
|
20358
|
+
}
|
|
20359
|
+
} else if (!json) {
|
|
20360
|
+
outputLine(` Signature verified (key: ${verifyResult.publicKeyId}, files: ${verifyResult.filesChecked})`);
|
|
20361
|
+
if (verifyResult.extraFiles?.length) {
|
|
20362
|
+
outputLine(` Note: ${verifyResult.extraFiles.length} extra unsigned file(s) present`);
|
|
20363
|
+
}
|
|
20364
|
+
}
|
|
19733
20365
|
outputLine("Installing to detected agents...");
|
|
19734
20366
|
try {
|
|
19735
20367
|
exec(resolveNpx(), ["skills", "add", contentDir, "-y", "-g"], {
|
|
@@ -19738,7 +20370,7 @@ async function cmdSkillAdd(name, config, json, exec = execFileSync2) {
|
|
|
19738
20370
|
env: npxEnv()
|
|
19739
20371
|
});
|
|
19740
20372
|
} catch (e) {
|
|
19741
|
-
const savedZip =
|
|
20373
|
+
const savedZip = join17(process.cwd(), `${name}.zip`);
|
|
19742
20374
|
try {
|
|
19743
20375
|
copyFileSync2(zipPath, savedZip);
|
|
19744
20376
|
} catch {
|
|
@@ -19747,7 +20379,8 @@ async function cmdSkillAdd(name, config, json, exec = execFileSync2) {
|
|
|
19747
20379
|
errorLine(`You can manually install from: ${savedZip}`);
|
|
19748
20380
|
throw e;
|
|
19749
20381
|
}
|
|
19750
|
-
|
|
20382
|
+
const registryStatus = verifyResult.status === "failed" && force ? "bypassed" : verifyResult.status;
|
|
20383
|
+
upsertSkillRecord(meta, void 0, registryStatus);
|
|
19751
20384
|
printSkillInstallResult(meta, json);
|
|
19752
20385
|
} finally {
|
|
19753
20386
|
rmSync(tmpBase, { recursive: true, force: true });
|
|
@@ -19778,7 +20411,7 @@ function cmdSkillRemove(name, json, exec = execFileSync2) {
|
|
|
19778
20411
|
env: npxEnv()
|
|
19779
20412
|
});
|
|
19780
20413
|
} catch {
|
|
19781
|
-
const agentsPath =
|
|
20414
|
+
const agentsPath = getSkillContentDir(name);
|
|
19782
20415
|
try {
|
|
19783
20416
|
rmSync(agentsPath, { recursive: true, force: true });
|
|
19784
20417
|
} catch {
|
|
@@ -19842,6 +20475,59 @@ function cmdSkillList(json) {
|
|
|
19842
20475
|
outputLine("");
|
|
19843
20476
|
outputLine(`${skills.length} skills installed.`);
|
|
19844
20477
|
}
|
|
20478
|
+
async function cmdSkillVerify(name, config, json) {
|
|
20479
|
+
const record = getSkillRecord(name);
|
|
20480
|
+
if (!record) {
|
|
20481
|
+
errorLine(`Skill "${name}" is not installed.`);
|
|
20482
|
+
process.exitCode = 1;
|
|
20483
|
+
return;
|
|
20484
|
+
}
|
|
20485
|
+
const contentDir = getSkillContentDir(name);
|
|
20486
|
+
if (!existsSync10(contentDir)) {
|
|
20487
|
+
errorLine(`Skill content directory not found: ${contentDir}`);
|
|
20488
|
+
errorLine(`Try reinstalling with: okx skill add ${name}`);
|
|
20489
|
+
process.exitCode = 1;
|
|
20490
|
+
return;
|
|
20491
|
+
}
|
|
20492
|
+
const meta = tryReadMetaJson(contentDir);
|
|
20493
|
+
const client = new OkxRestClient(config);
|
|
20494
|
+
let result;
|
|
20495
|
+
try {
|
|
20496
|
+
result = await wrapVerify(
|
|
20497
|
+
() => verifySkillSignature(contentDir, meta?.signing, {
|
|
20498
|
+
fetchPublicKey: (keyId) => getPublicKey(client, keyId),
|
|
20499
|
+
serverSideVerify: (sName, version, files) => serverSideVerify(client, sName, version, files),
|
|
20500
|
+
skillName: name,
|
|
20501
|
+
skillVersion: meta?.version
|
|
20502
|
+
})
|
|
20503
|
+
);
|
|
20504
|
+
} catch (e) {
|
|
20505
|
+
errorLine(e instanceof Error ? e.message : String(e));
|
|
20506
|
+
process.exitCode = 1;
|
|
20507
|
+
return;
|
|
20508
|
+
}
|
|
20509
|
+
if (meta) {
|
|
20510
|
+
upsertSkillRecord(meta, void 0, result.status);
|
|
20511
|
+
}
|
|
20512
|
+
if (result.status === "failed") {
|
|
20513
|
+
process.exitCode = 1;
|
|
20514
|
+
}
|
|
20515
|
+
if (json) {
|
|
20516
|
+
outputLine(JSON.stringify(result, null, 2));
|
|
20517
|
+
return;
|
|
20518
|
+
}
|
|
20519
|
+
if (result.status === "verified") {
|
|
20520
|
+
outputLine(`\u2713 ${name}: signature verified (key: ${result.publicKeyId}, files: ${result.filesChecked})`);
|
|
20521
|
+
if (result.extraFiles?.length) {
|
|
20522
|
+
outputLine(` Note: ${result.extraFiles.length} extra unsigned file(s) present`);
|
|
20523
|
+
}
|
|
20524
|
+
} else if (result.status === "verified_by_server") {
|
|
20525
|
+
outputLine(`\u2713 ${name}: verified by server (v${result.serverVersion ?? "?"})`);
|
|
20526
|
+
if (result.error) outputLine(` Note: ${result.error}`);
|
|
20527
|
+
} else if (result.status === "failed") {
|
|
20528
|
+
errorLine(`\u2717 ${name}: verification failed \u2014 ${result.error ?? "unknown"}`);
|
|
20529
|
+
}
|
|
20530
|
+
}
|
|
19845
20531
|
function printSkillInstallResult(meta, json) {
|
|
19846
20532
|
if (json) {
|
|
19847
20533
|
outputLine(JSON.stringify({ name: meta.name, version: meta.version, status: "installed" }, null, 2));
|
|
@@ -19992,14 +20678,14 @@ function formatBytes2(bytes) {
|
|
|
19992
20678
|
return `${(bytes / (1024 * 1024)).toFixed(2)} MB`;
|
|
19993
20679
|
}
|
|
19994
20680
|
function askConfirmation2(prompt2) {
|
|
19995
|
-
return new Promise((
|
|
20681
|
+
return new Promise((resolve4) => {
|
|
19996
20682
|
const rl = readline2.createInterface({
|
|
19997
20683
|
input: process.stdin,
|
|
19998
20684
|
output: process.stdout
|
|
19999
20685
|
});
|
|
20000
20686
|
rl.question(prompt2, (answer) => {
|
|
20001
20687
|
rl.close();
|
|
20002
|
-
|
|
20688
|
+
resolve4(answer.trim().toLowerCase() === "y");
|
|
20003
20689
|
});
|
|
20004
20690
|
});
|
|
20005
20691
|
}
|
|
@@ -20462,7 +21148,7 @@ async function cmdEventCancel(run, opts) {
|
|
|
20462
21148
|
// src/index.ts
|
|
20463
21149
|
var _require3 = createRequire3(import.meta.url);
|
|
20464
21150
|
var CLI_VERSION2 = _require3("../package.json").version;
|
|
20465
|
-
var GIT_HASH2 = true ? "
|
|
21151
|
+
var GIT_HASH2 = true ? "e5abc21f" : "dev";
|
|
20466
21152
|
function handlePilotCommand(action, json, force, binaryPath) {
|
|
20467
21153
|
if (action === "status") return cmdPilotStatus(json, binaryPath);
|
|
20468
21154
|
if (action === "install") return cmdPilotInstall(json, binaryPath);
|
|
@@ -21656,6 +22342,7 @@ function handleEarnSavingsCommand(run, action, rest, v, json) {
|
|
|
21656
22342
|
if (action === "set-rate") return cmdEarnSetLendingRate(run, { ccy: v.ccy, rate: v.rate, json });
|
|
21657
22343
|
if (action === "lending-history") return cmdEarnLendingHistory(run, { ccy: v.ccy, limit, json });
|
|
21658
22344
|
if (action === "rate-history") return cmdEarnLendingRateHistory(run, { ccy: v.ccy, limit, json });
|
|
22345
|
+
if (action === "fixed-products") return cmdEarnFixedProducts(run, { ccy: v.ccy, json });
|
|
21659
22346
|
if (action === "fixed-orders") return cmdEarnFixedOrderList(run, { ccy: v.ccy, state: v.state, json });
|
|
21660
22347
|
if (action === "fixed-purchase") return cmdEarnFixedPurchase(run, { ccy: v.ccy, amt: v.amt, term: v.term, confirm: v.confirm ?? false, json });
|
|
21661
22348
|
if (action === "fixed-redeem") return cmdEarnFixedRedeem(run, { reqId: v.reqId, json });
|
|
@@ -21773,9 +22460,13 @@ function requireSkillName(rest, usage) {
|
|
|
21773
22460
|
}
|
|
21774
22461
|
return name;
|
|
21775
22462
|
}
|
|
21776
|
-
function handleSkillAdd(rest, config, json) {
|
|
22463
|
+
function handleSkillAdd(rest, v, config, json) {
|
|
21777
22464
|
const n = requireSkillName(rest, "Usage: okx skill add <name>");
|
|
21778
|
-
if (n) return cmdSkillAdd(n, config, json);
|
|
22465
|
+
if (n) return cmdSkillAdd(n, config, json, v.force ?? false);
|
|
22466
|
+
}
|
|
22467
|
+
function handleSkillVerify(rest, config, json) {
|
|
22468
|
+
const n = requireSkillName(rest, "Usage: okx skill verify <name>");
|
|
22469
|
+
if (n) return cmdSkillVerify(n, config, json);
|
|
21779
22470
|
}
|
|
21780
22471
|
function handleSkillDownload(rest, v, config, json) {
|
|
21781
22472
|
const n = requireSkillName(rest, "Usage: okx skill download <name> [--dir <path>] [--format zip|skill]");
|
|
@@ -21794,12 +22485,13 @@ function handleSkillCommand(run, action, rest, v, json, config) {
|
|
|
21794
22485
|
if (action === "search") return cmdSkillSearch(run, { keyword: rest[0] ?? v.keyword, categories: v.categories, page: v.page, limit: v.limit, json });
|
|
21795
22486
|
if (action === "categories") return cmdSkillCategories(run, json);
|
|
21796
22487
|
if (action === "list") return cmdSkillList(json);
|
|
21797
|
-
if (action === "add") return handleSkillAdd(rest, config, json);
|
|
22488
|
+
if (action === "add") return handleSkillAdd(rest, v, config, json);
|
|
21798
22489
|
if (action === "download") return handleSkillDownload(rest, v, config, json);
|
|
21799
22490
|
if (action === "remove") return handleSkillRemove(rest, json);
|
|
21800
22491
|
if (action === "check") return handleSkillCheck(run, rest, json);
|
|
22492
|
+
if (action === "verify") return handleSkillVerify(rest, config, json);
|
|
21801
22493
|
errorLine(`Unknown skill command: ${action}`);
|
|
21802
|
-
errorLine("Valid: search, categories, add, download, remove, check, list");
|
|
22494
|
+
errorLine("Valid: search, categories, add, download, remove, check, list, verify");
|
|
21803
22495
|
process.exitCode = 1;
|
|
21804
22496
|
}
|
|
21805
22497
|
function handleEventCommand(run, action, rest, v, json) {
|