@okx_ai/okx-trade-cli 1.3.2-beta.5 → 1.3.2-beta.7
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 +819 -159
- package/dist/index.js.map +1 -1
- package/package.json +10 -10
- package/scripts/postinstall.js +78 -1
- package/LICENSE +0 -21
- package/scripts/postinstall-download.js +0 -152
package/dist/index.js
CHANGED
|
@@ -21,23 +21,26 @@ import {
|
|
|
21
21
|
import { homedir as homedir2 } from "os";
|
|
22
22
|
import { join as join2, dirname } from "path";
|
|
23
23
|
import { createHmac } from "crypto";
|
|
24
|
+
import { spawn, execFile as execFile2 } from "child_process";
|
|
25
|
+
import { homedir as homedir3 } from "os";
|
|
26
|
+
import { join as join3 } from "path";
|
|
24
27
|
import fs from "fs";
|
|
25
28
|
import path from "path";
|
|
26
29
|
import os from "os";
|
|
27
30
|
import { writeFileSync as writeFileSync2, renameSync as renameSync2, unlinkSync as unlinkSync2, mkdirSync as mkdirSync2 } from "fs";
|
|
28
|
-
import { join as
|
|
31
|
+
import { join as join4, resolve, basename, sep } from "path";
|
|
29
32
|
import { randomUUID } from "crypto";
|
|
30
33
|
import yauzl from "yauzl";
|
|
31
34
|
import { createWriteStream, mkdirSync as mkdirSync3 } from "fs";
|
|
32
35
|
import { resolve as resolve2, dirname as dirname2 } from "path";
|
|
33
36
|
import { readFileSync as readFileSync2, existsSync } from "fs";
|
|
34
|
-
import { join as
|
|
37
|
+
import { join as join5 } from "path";
|
|
35
38
|
import { readFileSync as readFileSync3, writeFileSync as writeFileSync3, mkdirSync as mkdirSync4, existsSync as existsSync2 } from "fs";
|
|
36
|
-
import { join as
|
|
37
|
-
import { homedir as homedir3 } from "os";
|
|
38
|
-
import { readFileSync as readFileSync4, writeFileSync as writeFileSync4, mkdirSync as mkdirSync5, existsSync as existsSync3 } from "fs";
|
|
39
|
-
import { join as join6, dirname as dirname4 } from "path";
|
|
39
|
+
import { join as join6, dirname as dirname3 } from "path";
|
|
40
40
|
import { homedir as homedir4 } from "os";
|
|
41
|
+
import { readFileSync as readFileSync4, writeFileSync as writeFileSync4, mkdirSync as mkdirSync5, existsSync as existsSync3 } from "fs";
|
|
42
|
+
import { join as join7, dirname as dirname4 } from "path";
|
|
43
|
+
import { homedir as homedir5 } from "os";
|
|
41
44
|
|
|
42
45
|
// ../../node_modules/.pnpm/smol-toml@1.6.0/node_modules/smol-toml/dist/error.js
|
|
43
46
|
function getLineColFromPtr(string, ptr) {
|
|
@@ -867,8 +870,8 @@ function stringify(obj, { maxDepth = 1e3, numbersAsFloat = false } = {}) {
|
|
|
867
870
|
|
|
868
871
|
// ../core/dist/index.js
|
|
869
872
|
import { readFileSync as readFileSync5, writeFileSync as writeFileSync5, mkdirSync as mkdirSync6, existsSync as existsSync4 } from "fs";
|
|
870
|
-
import { join as
|
|
871
|
-
import { homedir as
|
|
873
|
+
import { join as join8 } from "path";
|
|
874
|
+
import { homedir as homedir6 } from "os";
|
|
872
875
|
import fs2 from "fs";
|
|
873
876
|
import path2 from "path";
|
|
874
877
|
import os2 from "os";
|
|
@@ -876,6 +879,18 @@ import * as fs3 from "fs";
|
|
|
876
879
|
import * as path3 from "path";
|
|
877
880
|
import * as os3 from "os";
|
|
878
881
|
import { execFileSync } from "child_process";
|
|
882
|
+
import {
|
|
883
|
+
createWriteStream as createWriteStream3,
|
|
884
|
+
mkdirSync as mkdirSync9,
|
|
885
|
+
chmodSync as chmodSync2,
|
|
886
|
+
existsSync as existsSync7,
|
|
887
|
+
unlinkSync as unlinkSync4,
|
|
888
|
+
renameSync as renameSync4
|
|
889
|
+
} from "fs";
|
|
890
|
+
import { homedir as homedir9, platform as platform2 } from "os";
|
|
891
|
+
import { join as join11, dirname as dirname7 } from "path";
|
|
892
|
+
import { get as httpsGet2 } from "https";
|
|
893
|
+
import { get as httpGet2 } from "http";
|
|
879
894
|
import {
|
|
880
895
|
readFileSync as readFileSync7,
|
|
881
896
|
createWriteStream as createWriteStream2,
|
|
@@ -886,10 +901,13 @@ import {
|
|
|
886
901
|
renameSync as renameSync3
|
|
887
902
|
} from "fs";
|
|
888
903
|
import { createHash } from "crypto";
|
|
889
|
-
import { homedir as
|
|
890
|
-
import { join as
|
|
904
|
+
import { homedir as homedir8, platform, arch } from "os";
|
|
905
|
+
import { join as join10, dirname as dirname6 } from "path";
|
|
891
906
|
import { get as httpsGet } from "https";
|
|
892
907
|
import { get as httpGet } from "http";
|
|
908
|
+
import { readFileSync as readFileSync8, writeFileSync as writeFileSync7, mkdirSync as mkdirSync10, unlinkSync as unlinkSync5, existsSync as existsSync8 } from "fs";
|
|
909
|
+
import { join as join12 } from "path";
|
|
910
|
+
import { homedir as homedir10 } from "os";
|
|
893
911
|
var EXEC_TIMEOUT_MS = 3e4;
|
|
894
912
|
var ALLOWED_DOMAIN_RE = /^[\w.-]+\.okx\.com$/;
|
|
895
913
|
var PILOT_BIN_DIR = join(homedir(), ".okx", "bin");
|
|
@@ -1201,6 +1219,11 @@ var ConfigError = class extends OkxMcpError {
|
|
|
1201
1219
|
super("ConfigError", message, { suggestion });
|
|
1202
1220
|
}
|
|
1203
1221
|
};
|
|
1222
|
+
var NotLoggedInError = class extends ConfigError {
|
|
1223
|
+
constructor(suggestion = "Run `okx auth login` to authenticate.") {
|
|
1224
|
+
super("Not logged in.", suggestion);
|
|
1225
|
+
}
|
|
1226
|
+
};
|
|
1204
1227
|
var ValidationError = class extends OkxMcpError {
|
|
1205
1228
|
constructor(message, suggestion) {
|
|
1206
1229
|
super("ValidationError", message, { suggestion });
|
|
@@ -1253,6 +1276,97 @@ function toToolErrorPayload(error, fallbackEndpoint) {
|
|
|
1253
1276
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1254
1277
|
};
|
|
1255
1278
|
}
|
|
1279
|
+
var EXIT_CODES = {
|
|
1280
|
+
SUCCESS: 0,
|
|
1281
|
+
UNAUTHORIZED_CALLER: 1,
|
|
1282
|
+
NOT_LOGGED_IN: 2,
|
|
1283
|
+
REFRESH_FAILED: 3
|
|
1284
|
+
};
|
|
1285
|
+
var EXEC_TIMEOUT_MS2 = 5e3;
|
|
1286
|
+
var AUTH_BIN_DIR = join3(homedir3(), ".okx", "bin");
|
|
1287
|
+
function getAuthBinaryPath() {
|
|
1288
|
+
if (process.env.OKX_AUTH_BIN) {
|
|
1289
|
+
return process.env.OKX_AUTH_BIN;
|
|
1290
|
+
}
|
|
1291
|
+
const ext = process.platform === "win32" ? ".exe" : "";
|
|
1292
|
+
return join3(AUTH_BIN_DIR, `okx-auth${ext}`);
|
|
1293
|
+
}
|
|
1294
|
+
function execAuthToken() {
|
|
1295
|
+
const binPath = getAuthBinaryPath();
|
|
1296
|
+
return new Promise((resolve3, reject) => {
|
|
1297
|
+
const child = spawn(binPath, ["token"], {
|
|
1298
|
+
stdio: ["ignore", "ignore", "inherit", "pipe"]
|
|
1299
|
+
// stdin stdout stderr fd3 (pipe)
|
|
1300
|
+
});
|
|
1301
|
+
const chunks = [];
|
|
1302
|
+
const fd3 = child.stdio[3];
|
|
1303
|
+
fd3.on("data", (chunk) => chunks.push(chunk));
|
|
1304
|
+
child.on("error", (err) => {
|
|
1305
|
+
reject(new ConfigError(
|
|
1306
|
+
`Failed to spawn okx-auth: ${err.message}`,
|
|
1307
|
+
"Ensure the okx-auth binary exists and is executable."
|
|
1308
|
+
));
|
|
1309
|
+
});
|
|
1310
|
+
child.on("close", (code) => {
|
|
1311
|
+
if (code === EXIT_CODES.SUCCESS) {
|
|
1312
|
+
const token = Buffer.concat(chunks).toString("utf-8").trim();
|
|
1313
|
+
if (!token) {
|
|
1314
|
+
reject(new AuthenticationError(
|
|
1315
|
+
"okx-auth returned empty token.",
|
|
1316
|
+
"Run `okx auth login` to re-authenticate."
|
|
1317
|
+
));
|
|
1318
|
+
return;
|
|
1319
|
+
}
|
|
1320
|
+
resolve3(token);
|
|
1321
|
+
return;
|
|
1322
|
+
}
|
|
1323
|
+
if (code === EXIT_CODES.NOT_LOGGED_IN) {
|
|
1324
|
+
reject(new NotLoggedInError());
|
|
1325
|
+
return;
|
|
1326
|
+
}
|
|
1327
|
+
if (code === EXIT_CODES.UNAUTHORIZED_CALLER) {
|
|
1328
|
+
reject(new AuthenticationError(
|
|
1329
|
+
"okx-auth rejected the caller (unauthorized).",
|
|
1330
|
+
"Ensure you are running from a trusted OKX tool."
|
|
1331
|
+
));
|
|
1332
|
+
return;
|
|
1333
|
+
}
|
|
1334
|
+
if (code === EXIT_CODES.REFRESH_FAILED) {
|
|
1335
|
+
reject(new AuthenticationError(
|
|
1336
|
+
"Token refresh failed.",
|
|
1337
|
+
"Run `okx auth login` to re-authenticate."
|
|
1338
|
+
));
|
|
1339
|
+
return;
|
|
1340
|
+
}
|
|
1341
|
+
reject(new AuthenticationError(
|
|
1342
|
+
`okx-auth token exited with code ${code}.`,
|
|
1343
|
+
"Run `okx auth login` to re-authenticate."
|
|
1344
|
+
));
|
|
1345
|
+
});
|
|
1346
|
+
});
|
|
1347
|
+
}
|
|
1348
|
+
function execAuthStatus() {
|
|
1349
|
+
const binPath = getAuthBinaryPath();
|
|
1350
|
+
return new Promise((resolve3) => {
|
|
1351
|
+
execFile2(
|
|
1352
|
+
binPath,
|
|
1353
|
+
["status", "--json"],
|
|
1354
|
+
{ timeout: EXEC_TIMEOUT_MS2, encoding: "utf-8" },
|
|
1355
|
+
(error, stdout) => {
|
|
1356
|
+
if (error) {
|
|
1357
|
+
resolve3(null);
|
|
1358
|
+
return;
|
|
1359
|
+
}
|
|
1360
|
+
try {
|
|
1361
|
+
const result = JSON.parse(stdout);
|
|
1362
|
+
resolve3(result);
|
|
1363
|
+
} catch {
|
|
1364
|
+
resolve3(null);
|
|
1365
|
+
}
|
|
1366
|
+
}
|
|
1367
|
+
);
|
|
1368
|
+
});
|
|
1369
|
+
}
|
|
1256
1370
|
function sleep(ms) {
|
|
1257
1371
|
return new Promise((resolve3) => {
|
|
1258
1372
|
setTimeout(resolve3, ms);
|
|
@@ -1384,6 +1498,7 @@ function maskKey(key) {
|
|
|
1384
1498
|
if (key.length <= 8) return "***";
|
|
1385
1499
|
return `${key.slice(0, 3)}***${key.slice(-3)}`;
|
|
1386
1500
|
}
|
|
1501
|
+
var TOKEN_CACHE_TTL_MS = 6e4;
|
|
1387
1502
|
function vlog2(message) {
|
|
1388
1503
|
process.stderr.write(`[verbose] ${message}
|
|
1389
1504
|
`);
|
|
@@ -1392,6 +1507,8 @@ var OkxRestClient = class _OkxRestClient {
|
|
|
1392
1507
|
config;
|
|
1393
1508
|
rateLimiter;
|
|
1394
1509
|
dispatcher;
|
|
1510
|
+
cachedAccessToken;
|
|
1511
|
+
cachedAccessTokenAt = 0;
|
|
1395
1512
|
pilot;
|
|
1396
1513
|
constructor(config) {
|
|
1397
1514
|
this.config = config;
|
|
@@ -1406,6 +1523,51 @@ var OkxRestClient = class _OkxRestClient {
|
|
|
1406
1523
|
hasCustomProxy: !!config.proxyUrl
|
|
1407
1524
|
});
|
|
1408
1525
|
}
|
|
1526
|
+
/**
|
|
1527
|
+
* Resolve OAuth access token via the okx-auth binary (fd3 pipe).
|
|
1528
|
+
* Caches the token for 60 s to avoid spawning the binary on every
|
|
1529
|
+
* request. The binary handles refresh internally (300s TTL lead),
|
|
1530
|
+
* so periodic re-calls let it serve a fresh token when needed.
|
|
1531
|
+
* Returns null when not logged in.
|
|
1532
|
+
*/
|
|
1533
|
+
async resolveAccessToken() {
|
|
1534
|
+
if (this.cachedAccessToken && Date.now() - this.cachedAccessTokenAt < TOKEN_CACHE_TTL_MS) {
|
|
1535
|
+
return this.cachedAccessToken;
|
|
1536
|
+
}
|
|
1537
|
+
try {
|
|
1538
|
+
const token = await execAuthToken();
|
|
1539
|
+
this.cachedAccessToken = token;
|
|
1540
|
+
this.cachedAccessTokenAt = Date.now();
|
|
1541
|
+
return token;
|
|
1542
|
+
} catch (e) {
|
|
1543
|
+
if (e instanceof NotLoggedInError) {
|
|
1544
|
+
return null;
|
|
1545
|
+
}
|
|
1546
|
+
throw e;
|
|
1547
|
+
}
|
|
1548
|
+
}
|
|
1549
|
+
/**
|
|
1550
|
+
* Dynamic auth — determines auth method per request.
|
|
1551
|
+
*
|
|
1552
|
+
* 1. API key in config → HMAC signing (no OAuth fallback)
|
|
1553
|
+
* 2. OAuth token via okx-auth binary → Bearer token
|
|
1554
|
+
* 3. Neither → throw ConfigError
|
|
1555
|
+
*/
|
|
1556
|
+
async applyAuth(headers, method, requestPath, bodyJson, timestamp) {
|
|
1557
|
+
if (this.config.apiKey && this.config.secretKey && this.config.passphrase) {
|
|
1558
|
+
this.setAuthHeaders(headers, method, requestPath, bodyJson, timestamp);
|
|
1559
|
+
return;
|
|
1560
|
+
}
|
|
1561
|
+
const accessToken = await this.resolveAccessToken();
|
|
1562
|
+
if (accessToken) {
|
|
1563
|
+
headers.set("Authorization", `Bearer ${accessToken}`);
|
|
1564
|
+
return;
|
|
1565
|
+
}
|
|
1566
|
+
throw new ConfigError(
|
|
1567
|
+
"No credentials found.",
|
|
1568
|
+
"Run `okx auth login` to authenticate, or configure API key credentials."
|
|
1569
|
+
);
|
|
1570
|
+
}
|
|
1409
1571
|
/** The canonical base URL for this client (e.g. https://www.okx.com). */
|
|
1410
1572
|
get baseUrl() {
|
|
1411
1573
|
return this.config.baseUrl;
|
|
@@ -1464,18 +1626,6 @@ var OkxRestClient = class _OkxRestClient {
|
|
|
1464
1626
|
});
|
|
1465
1627
|
}
|
|
1466
1628
|
setAuthHeaders(headers, method, requestPath, bodyJson, timestamp) {
|
|
1467
|
-
if (!this.config.hasAuth) {
|
|
1468
|
-
throw new ConfigError(
|
|
1469
|
-
"Private endpoint requires API credentials.",
|
|
1470
|
-
"Configure OKX_API_KEY, OKX_SECRET_KEY and OKX_PASSPHRASE."
|
|
1471
|
-
);
|
|
1472
|
-
}
|
|
1473
|
-
if (!this.config.apiKey || !this.config.secretKey || !this.config.passphrase) {
|
|
1474
|
-
throw new ConfigError(
|
|
1475
|
-
"Invalid private API credentials state.",
|
|
1476
|
-
"Ensure all OKX credentials are set."
|
|
1477
|
-
);
|
|
1478
|
-
}
|
|
1479
1629
|
const payload = `${timestamp}${method.toUpperCase()}${requestPath}${bodyJson}`;
|
|
1480
1630
|
const signature = signOkxPayload(payload, this.config.secretKey);
|
|
1481
1631
|
headers.set("OK-ACCESS-KEY", this.config.apiKey);
|
|
@@ -1590,7 +1740,7 @@ var OkxRestClient = class _OkxRestClient {
|
|
|
1590
1740
|
const conn = this.pilot.getConnectionParams();
|
|
1591
1741
|
this.logRequest("POST", `${conn.baseUrl}${path42}`, "private");
|
|
1592
1742
|
const reqConfig = { method: "POST", path: path42, auth: "private" };
|
|
1593
|
-
const headers = this.buildHeaders(reqConfig, path42, bodyJson, getNow());
|
|
1743
|
+
const headers = await this.buildHeaders(reqConfig, path42, bodyJson, getNow());
|
|
1594
1744
|
if (conn.userAgent) {
|
|
1595
1745
|
headers.set("User-Agent", conn.userAgent);
|
|
1596
1746
|
}
|
|
@@ -1711,7 +1861,7 @@ var OkxRestClient = class _OkxRestClient {
|
|
|
1711
1861
|
// Header building
|
|
1712
1862
|
// ---------------------------------------------------------------------------
|
|
1713
1863
|
/** Build HTTP headers. reqConfig.extraHeaders must NOT contain auth keys (OK-ACCESS-*). */
|
|
1714
|
-
buildHeaders(reqConfig, requestPath, bodyJson, timestamp) {
|
|
1864
|
+
async buildHeaders(reqConfig, requestPath, bodyJson, timestamp) {
|
|
1715
1865
|
const headers = new Headers({
|
|
1716
1866
|
"Content-Type": "application/json",
|
|
1717
1867
|
Accept: "application/json"
|
|
@@ -1720,7 +1870,7 @@ var OkxRestClient = class _OkxRestClient {
|
|
|
1720
1870
|
headers.set("User-Agent", this.config.userAgent);
|
|
1721
1871
|
}
|
|
1722
1872
|
if (reqConfig.auth === "private") {
|
|
1723
|
-
this.
|
|
1873
|
+
await this.applyAuth(headers, reqConfig.method, requestPath, bodyJson, timestamp);
|
|
1724
1874
|
}
|
|
1725
1875
|
const useSimulated = reqConfig.simulatedTrading !== void 0 ? reqConfig.simulatedTrading : this.config.demo;
|
|
1726
1876
|
if (useSimulated) {
|
|
@@ -1774,7 +1924,7 @@ var OkxRestClient = class _OkxRestClient {
|
|
|
1774
1924
|
if (reqConfig.rateLimit) {
|
|
1775
1925
|
await this.rateLimiter.consume(reqConfig.rateLimit);
|
|
1776
1926
|
}
|
|
1777
|
-
const headers = this.buildHeaders(reqConfig, requestPath, bodyJson, timestamp);
|
|
1927
|
+
const headers = await this.buildHeaders(reqConfig, requestPath, bodyJson, timestamp);
|
|
1778
1928
|
if (conn.userAgent) {
|
|
1779
1929
|
headers.set("User-Agent", conn.userAgent);
|
|
1780
1930
|
}
|
|
@@ -2201,8 +2351,6 @@ function registerIndicatorTools() {
|
|
|
2201
2351
|
];
|
|
2202
2352
|
}
|
|
2203
2353
|
var DEFAULT_SOURCE_TAG = "MCP";
|
|
2204
|
-
var TOKEN_REGEX = /^[A-Z2-7]{12}$/;
|
|
2205
|
-
var TAG_JOIN = "";
|
|
2206
2354
|
var OKX_SITES = {
|
|
2207
2355
|
global: {
|
|
2208
2356
|
label: "Global",
|
|
@@ -2216,7 +2364,7 @@ var OKX_SITES = {
|
|
|
2216
2364
|
},
|
|
2217
2365
|
us: {
|
|
2218
2366
|
label: "US",
|
|
2219
|
-
apiBaseUrl: "https://
|
|
2367
|
+
apiBaseUrl: "https://us.okx.com",
|
|
2220
2368
|
webUrl: "https://app.okx.com"
|
|
2221
2369
|
}
|
|
2222
2370
|
};
|
|
@@ -3160,8 +3308,7 @@ function registerAlgoTradeTools() {
|
|
|
3160
3308
|
callBackSpread: readString(args, "callbackSpread"),
|
|
3161
3309
|
activePx: readString(args, "activePx"),
|
|
3162
3310
|
reduceOnly: typeof reduceOnly === "boolean" ? String(reduceOnly) : void 0,
|
|
3163
|
-
clOrdId: readString(args, "clOrdId")
|
|
3164
|
-
tag: context.config.sourceTag
|
|
3311
|
+
clOrdId: readString(args, "clOrdId")
|
|
3165
3312
|
}),
|
|
3166
3313
|
privateRateLimit("swap_place_move_stop_order", 20)
|
|
3167
3314
|
);
|
|
@@ -3506,8 +3653,7 @@ function registerFuturesAlgoTools() {
|
|
|
3506
3653
|
callBackSpread: readString(args, "callbackSpread"),
|
|
3507
3654
|
activePx: readString(args, "activePx"),
|
|
3508
3655
|
reduceOnly: typeof reduceOnly === "boolean" ? String(reduceOnly) : void 0,
|
|
3509
|
-
clOrdId: readString(args, "clOrdId")
|
|
3510
|
-
tag: context.config.sourceTag
|
|
3656
|
+
clOrdId: readString(args, "clOrdId")
|
|
3511
3657
|
}),
|
|
3512
3658
|
privateRateLimit("futures_place_move_stop_order", 20)
|
|
3513
3659
|
);
|
|
@@ -3765,7 +3911,7 @@ function safeWriteFile(targetDir, fileName, data) {
|
|
|
3765
3911
|
throw new Error(`Invalid file name: "${fileName}"`);
|
|
3766
3912
|
}
|
|
3767
3913
|
const resolvedDir = resolve(targetDir);
|
|
3768
|
-
const filePath =
|
|
3914
|
+
const filePath = join4(resolvedDir, safeName);
|
|
3769
3915
|
const resolvedPath = resolve(filePath);
|
|
3770
3916
|
if (!resolvedPath.startsWith(resolvedDir + sep)) {
|
|
3771
3917
|
throw new Error(`Path traversal detected: "${fileName}" resolves outside target directory`);
|
|
@@ -3893,7 +4039,7 @@ async function extractSkillZip(zipPath, targetDir, limits) {
|
|
|
3893
4039
|
});
|
|
3894
4040
|
}
|
|
3895
4041
|
function readMetaJson(contentDir) {
|
|
3896
|
-
const metaPath =
|
|
4042
|
+
const metaPath = join5(contentDir, "_meta.json");
|
|
3897
4043
|
if (!existsSync(metaPath)) {
|
|
3898
4044
|
throw new Error(`_meta.json not found in ${contentDir}. Invalid skill package.`);
|
|
3899
4045
|
}
|
|
@@ -3919,12 +4065,12 @@ function readMetaJson(contentDir) {
|
|
|
3919
4065
|
};
|
|
3920
4066
|
}
|
|
3921
4067
|
function validateSkillMdExists(contentDir) {
|
|
3922
|
-
const skillMdPath =
|
|
4068
|
+
const skillMdPath = join5(contentDir, "SKILL.md");
|
|
3923
4069
|
if (!existsSync(skillMdPath)) {
|
|
3924
4070
|
throw new Error(`SKILL.md not found in ${contentDir}. Invalid skill package.`);
|
|
3925
4071
|
}
|
|
3926
4072
|
}
|
|
3927
|
-
var DEFAULT_REGISTRY_PATH =
|
|
4073
|
+
var DEFAULT_REGISTRY_PATH = join6(homedir4(), ".okx", "skills", "registry.json");
|
|
3928
4074
|
function readRegistry(registryPath = DEFAULT_REGISTRY_PATH) {
|
|
3929
4075
|
if (!existsSync2(registryPath)) {
|
|
3930
4076
|
return { version: 1, skills: {} };
|
|
@@ -10339,7 +10485,7 @@ function createToolRunner(client, config) {
|
|
|
10339
10485
|
};
|
|
10340
10486
|
}
|
|
10341
10487
|
function configFilePath() {
|
|
10342
|
-
return
|
|
10488
|
+
return join7(homedir5(), ".okx", "config.toml");
|
|
10343
10489
|
}
|
|
10344
10490
|
function readFullConfig() {
|
|
10345
10491
|
const path42 = configFilePath();
|
|
@@ -10358,11 +10504,6 @@ Or re-run: okx config init`
|
|
|
10358
10504
|
);
|
|
10359
10505
|
}
|
|
10360
10506
|
}
|
|
10361
|
-
function readTomlProfile(profileName) {
|
|
10362
|
-
const config = readFullConfig();
|
|
10363
|
-
const name = profileName ?? config.default_profile ?? "default";
|
|
10364
|
-
return config.profiles?.[name] ?? {};
|
|
10365
|
-
}
|
|
10366
10507
|
var CONFIG_HEADER = `# OKX Trade Kit Configuration
|
|
10367
10508
|
# If editing manually, wrap values containing special chars in quotes:
|
|
10368
10509
|
# passphrase = 'value' (if value contains # \\ ")
|
|
@@ -10411,18 +10552,24 @@ function parseModuleList(rawModules) {
|
|
|
10411
10552
|
}
|
|
10412
10553
|
return Array.from(deduped);
|
|
10413
10554
|
}
|
|
10414
|
-
function loadCredentials(toml) {
|
|
10555
|
+
async function loadCredentials(toml) {
|
|
10415
10556
|
const apiKey = process.env.OKX_API_KEY?.trim() ?? toml.api_key;
|
|
10416
10557
|
const secretKey = process.env.OKX_SECRET_KEY?.trim() ?? toml.secret_key;
|
|
10417
10558
|
const passphrase = process.env.OKX_PASSPHRASE?.trim() ?? toml.passphrase;
|
|
10418
|
-
const
|
|
10559
|
+
const hasApiKey = Boolean(apiKey && secretKey && passphrase);
|
|
10419
10560
|
const partialAuth = Boolean(apiKey) || Boolean(secretKey) || Boolean(passphrase);
|
|
10420
|
-
if (partialAuth && !
|
|
10561
|
+
if (partialAuth && !hasApiKey) {
|
|
10421
10562
|
throw new ConfigError(
|
|
10422
10563
|
"Partial API credentials detected.",
|
|
10423
10564
|
"Set OKX_API_KEY, OKX_SECRET_KEY and OKX_PASSPHRASE together (env vars or config.toml profile)."
|
|
10424
10565
|
);
|
|
10425
10566
|
}
|
|
10567
|
+
let hasOAuth = false;
|
|
10568
|
+
if (!hasApiKey) {
|
|
10569
|
+
const status = await execAuthStatus();
|
|
10570
|
+
hasOAuth = status?.status === "logged_in";
|
|
10571
|
+
}
|
|
10572
|
+
const hasAuth = hasOAuth || hasApiKey;
|
|
10426
10573
|
return { apiKey, secretKey, passphrase, hasAuth };
|
|
10427
10574
|
}
|
|
10428
10575
|
function resolveSite(cliSite, tomlSite) {
|
|
@@ -10445,30 +10592,6 @@ function resolveBaseUrl(site, tomlBaseUrl) {
|
|
|
10445
10592
|
}
|
|
10446
10593
|
return rawBaseUrl.replace(/\/+$/, "");
|
|
10447
10594
|
}
|
|
10448
|
-
function resolveSourceTag(cli, defaultTag, opts = {}) {
|
|
10449
|
-
const readEnv = opts.readEnv !== false;
|
|
10450
|
-
const cliSkill = cli.skill?.trim();
|
|
10451
|
-
if (cliSkill) {
|
|
10452
|
-
if (isValidToken(cliSkill)) return composeTag(defaultTag, cliSkill);
|
|
10453
|
-
console.warn("--skill value format invalid (expected 12-char Base32), ignoring");
|
|
10454
|
-
}
|
|
10455
|
-
if (readEnv) {
|
|
10456
|
-
const envToken = process.env.OKX_SKILL_TOKEN?.trim();
|
|
10457
|
-
if (envToken) {
|
|
10458
|
-
if (isValidToken(envToken)) return composeTag(defaultTag, envToken);
|
|
10459
|
-
console.warn("OKX_SKILL_TOKEN format invalid, ignoring");
|
|
10460
|
-
}
|
|
10461
|
-
}
|
|
10462
|
-
const sourceTag = cli.sourceTag?.trim();
|
|
10463
|
-
if (sourceTag) return sourceTag;
|
|
10464
|
-
return defaultTag;
|
|
10465
|
-
}
|
|
10466
|
-
function composeTag(defaultTag, token) {
|
|
10467
|
-
return `${defaultTag}${TAG_JOIN}${token}`;
|
|
10468
|
-
}
|
|
10469
|
-
function isValidToken(s) {
|
|
10470
|
-
return TOKEN_REGEX.test(s);
|
|
10471
|
-
}
|
|
10472
10595
|
function resolveDemo(cli, toml) {
|
|
10473
10596
|
if (cli.demo && cli.live) {
|
|
10474
10597
|
throw new ConfigError(
|
|
@@ -10480,9 +10603,11 @@ function resolveDemo(cli, toml) {
|
|
|
10480
10603
|
if (cli.demo === true) return true;
|
|
10481
10604
|
return process.env.OKX_DEMO === "1" || process.env.OKX_DEMO === "true" || (toml.demo ?? false);
|
|
10482
10605
|
}
|
|
10483
|
-
function loadConfig(cli
|
|
10484
|
-
const
|
|
10485
|
-
const
|
|
10606
|
+
async function loadConfig(cli) {
|
|
10607
|
+
const config = readFullConfig();
|
|
10608
|
+
const profileName = cli.profile ?? config.default_profile ?? "default";
|
|
10609
|
+
const toml = config.profiles?.[profileName] ?? {};
|
|
10610
|
+
const creds = await loadCredentials(toml);
|
|
10486
10611
|
const demo = resolveDemo(cli, toml);
|
|
10487
10612
|
const site = resolveSite(cli.site, toml.site);
|
|
10488
10613
|
const baseUrl = resolveBaseUrl(site, toml.base_url);
|
|
@@ -10500,9 +10625,9 @@ function loadConfig(cli, opts = {}) {
|
|
|
10500
10625
|
"proxy_url must start with http:// or https://. SOCKS proxies are not supported."
|
|
10501
10626
|
);
|
|
10502
10627
|
}
|
|
10503
|
-
const defaultTag = opts.entryPrefix ?? DEFAULT_SOURCE_TAG;
|
|
10504
10628
|
return {
|
|
10505
10629
|
...creds,
|
|
10630
|
+
profile: profileName,
|
|
10506
10631
|
baseUrl,
|
|
10507
10632
|
timeoutMs: Math.floor(rawTimeout),
|
|
10508
10633
|
modules: parseModuleList(cli.modules),
|
|
@@ -10510,14 +10635,12 @@ function loadConfig(cli, opts = {}) {
|
|
|
10510
10635
|
demo,
|
|
10511
10636
|
site,
|
|
10512
10637
|
userAgent: cli.userAgent,
|
|
10513
|
-
|
|
10514
|
-
// MCP entry passes { readEnv: false } to honour the "MCP path not attributed" contract.
|
|
10515
|
-
sourceTag: resolveSourceTag(cli, defaultTag, { readEnv: opts.readEnv }),
|
|
10638
|
+
sourceTag: cli.sourceTag ?? DEFAULT_SOURCE_TAG,
|
|
10516
10639
|
proxyUrl: rawProxyUrl || void 0,
|
|
10517
10640
|
verbose: cli.verbose ?? false
|
|
10518
10641
|
};
|
|
10519
10642
|
}
|
|
10520
|
-
var CACHE_FILE =
|
|
10643
|
+
var CACHE_FILE = join8(homedir6(), ".okx", "update-check.json");
|
|
10521
10644
|
var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
10522
10645
|
function readCache2() {
|
|
10523
10646
|
try {
|
|
@@ -10530,7 +10653,7 @@ function readCache2() {
|
|
|
10530
10653
|
}
|
|
10531
10654
|
function writeCache2(cache) {
|
|
10532
10655
|
try {
|
|
10533
|
-
mkdirSync6(
|
|
10656
|
+
mkdirSync6(join8(homedir6(), ".okx"), { recursive: true });
|
|
10534
10657
|
writeFileSync5(CACHE_FILE, JSON.stringify(cache, null, 2), "utf-8");
|
|
10535
10658
|
} catch {
|
|
10536
10659
|
}
|
|
@@ -10700,13 +10823,13 @@ function findMsStoreClaudePath() {
|
|
|
10700
10823
|
}
|
|
10701
10824
|
function getConfigPath(client) {
|
|
10702
10825
|
const home = os3.homedir();
|
|
10703
|
-
const
|
|
10826
|
+
const platform3 = process.platform;
|
|
10704
10827
|
switch (client) {
|
|
10705
10828
|
case "claude-desktop":
|
|
10706
|
-
if (
|
|
10829
|
+
if (platform3 === "win32") {
|
|
10707
10830
|
return findMsStoreClaudePath() ?? path3.join(appData(), "Claude", CLAUDE_CONFIG_FILE);
|
|
10708
10831
|
}
|
|
10709
|
-
if (
|
|
10832
|
+
if (platform3 === "darwin") {
|
|
10710
10833
|
return path3.join(home, "Library", "Application Support", "Claude", CLAUDE_CONFIG_FILE);
|
|
10711
10834
|
}
|
|
10712
10835
|
return path3.join(process.env.XDG_CONFIG_HOME ?? path3.join(home, ".config"), "Claude", CLAUDE_CONFIG_FILE);
|
|
@@ -10818,7 +10941,7 @@ var DOWNLOAD_TIMEOUT_MS = 3e4;
|
|
|
10818
10941
|
var PLATFORM_MAP = {
|
|
10819
10942
|
"darwin-arm64": "darwin-arm64",
|
|
10820
10943
|
"darwin-x64": "darwin-x64",
|
|
10821
|
-
"linux-arm64": "linux-
|
|
10944
|
+
"linux-arm64": "linux-x64",
|
|
10822
10945
|
"linux-x64": "linux-x64",
|
|
10823
10946
|
"win32-arm64": "win32-arm64",
|
|
10824
10947
|
"win32-x64": "win32-x64"
|
|
@@ -10950,7 +11073,7 @@ async function installPilotBinary(destPath, sources = CDN_SOURCES, onProgress) {
|
|
|
10950
11073
|
if (earlyResult) return earlyResult;
|
|
10951
11074
|
const platformDir = getPlatformDir();
|
|
10952
11075
|
const binaryName = getBinaryName();
|
|
10953
|
-
const resolvedDest = destPath ??
|
|
11076
|
+
const resolvedDest = destPath ?? join10(homedir8(), ".okx", "bin", binaryName);
|
|
10954
11077
|
const tmpPath = resolvedDest + ".tmp";
|
|
10955
11078
|
mkdirSync8(dirname6(resolvedDest), { recursive: true });
|
|
10956
11079
|
const localHash = existsSync6(resolvedDest) ? hashFile(resolvedDest) : null;
|
|
@@ -11073,16 +11196,276 @@ function downloadText(url, timeoutMs) {
|
|
|
11073
11196
|
})
|
|
11074
11197
|
);
|
|
11075
11198
|
}
|
|
11199
|
+
var AUTH_CDN_PATH_PREFIX = "/upgradeapp/tools/oauth";
|
|
11200
|
+
function getAuthBinaryName() {
|
|
11201
|
+
return platform2() === "win32" ? "okx-auth.exe" : "okx-auth";
|
|
11202
|
+
}
|
|
11203
|
+
function getAuthStatus(binaryPath, opts) {
|
|
11204
|
+
const resolvedPath = binaryPath ?? getAuthBinaryPath();
|
|
11205
|
+
const platformDir = getPlatformDir();
|
|
11206
|
+
if (!existsSync7(resolvedPath)) {
|
|
11207
|
+
return { binaryPath: resolvedPath, exists: false, platform: platformDir };
|
|
11208
|
+
}
|
|
11209
|
+
if (opts?.skipHash) {
|
|
11210
|
+
return { binaryPath: resolvedPath, exists: true, platform: platformDir };
|
|
11211
|
+
}
|
|
11212
|
+
const { size, sha256 } = hashFile(resolvedPath);
|
|
11213
|
+
return { binaryPath: resolvedPath, exists: true, platform: platformDir, fileSize: size, sha256 };
|
|
11214
|
+
}
|
|
11215
|
+
async function fetchAuthCdnChecksum(sources = CDN_SOURCES, timeoutMs = DOWNLOAD_TIMEOUT_MS) {
|
|
11216
|
+
const platformDir = getPlatformDir();
|
|
11217
|
+
if (!platformDir) return null;
|
|
11218
|
+
const checksumPath = `${AUTH_CDN_PATH_PREFIX}/${platformDir}/checksum.json`;
|
|
11219
|
+
for (const { host, protocol } of sources) {
|
|
11220
|
+
try {
|
|
11221
|
+
const url = `${protocol}://${host}${checksumPath}`;
|
|
11222
|
+
const raw = await downloadText2(url, timeoutMs);
|
|
11223
|
+
const data = JSON.parse(raw);
|
|
11224
|
+
if (typeof data.sha256 !== "string" || typeof data.size !== "number" || typeof data.target !== "string") {
|
|
11225
|
+
continue;
|
|
11226
|
+
}
|
|
11227
|
+
return { sha256: data.sha256, size: data.size, target: data.target, source: host };
|
|
11228
|
+
} catch {
|
|
11229
|
+
}
|
|
11230
|
+
}
|
|
11231
|
+
return null;
|
|
11232
|
+
}
|
|
11233
|
+
async function fetchAndValidateChecksum2(host, protocol, checksumPath, platformDir, timeoutMs, onProgress) {
|
|
11234
|
+
const checksumUrl = `${protocol}://${host}${checksumPath}`;
|
|
11235
|
+
onProgress?.(`Fetching checksum from ${host}...`);
|
|
11236
|
+
const raw = await downloadText2(checksumUrl, timeoutMs);
|
|
11237
|
+
const checksum = JSON.parse(raw);
|
|
11238
|
+
if (typeof checksum.sha256 !== "string" || typeof checksum.size !== "number" || typeof checksum.target !== "string") {
|
|
11239
|
+
throw new Error("Invalid checksum.json: missing sha256, size, or target");
|
|
11240
|
+
}
|
|
11241
|
+
if (checksum.target !== platformDir) {
|
|
11242
|
+
throw new Error(`Target mismatch: expected ${platformDir}, got ${checksum.target}`);
|
|
11243
|
+
}
|
|
11244
|
+
return { sha256: checksum.sha256, size: checksum.size, target: checksum.target };
|
|
11245
|
+
}
|
|
11246
|
+
async function downloadAndVerify2(host, protocol, binaryPath, tmpPath, checksum, timeoutMs, onProgress) {
|
|
11247
|
+
const binaryUrl = `${protocol}://${host}${binaryPath}`;
|
|
11248
|
+
onProgress?.(`Downloading binary from ${host}...`);
|
|
11249
|
+
await download2(binaryUrl, tmpPath, timeoutMs);
|
|
11250
|
+
const actual = hashFile(tmpPath);
|
|
11251
|
+
if (actual.size !== checksum.size) {
|
|
11252
|
+
throw new Error(`Size mismatch: expected ${checksum.size}, got ${actual.size}`);
|
|
11253
|
+
}
|
|
11254
|
+
if (actual.sha256 !== checksum.sha256) {
|
|
11255
|
+
throw new Error(`SHA-256 mismatch: expected ${checksum.sha256}, got ${actual.sha256}`);
|
|
11256
|
+
}
|
|
11257
|
+
}
|
|
11258
|
+
function atomicReplace2(tmpPath, resolvedDest) {
|
|
11259
|
+
if (platform2() === "win32") {
|
|
11260
|
+
try {
|
|
11261
|
+
unlinkSync4(resolvedDest);
|
|
11262
|
+
} catch {
|
|
11263
|
+
}
|
|
11264
|
+
}
|
|
11265
|
+
renameSync4(tmpPath, resolvedDest);
|
|
11266
|
+
if (platform2() !== "win32") {
|
|
11267
|
+
chmodSync2(resolvedDest, 493);
|
|
11268
|
+
}
|
|
11269
|
+
}
|
|
11270
|
+
function installPreChecks2(destPath, sources) {
|
|
11271
|
+
if (!destPath && process.env.OKX_AUTH_BIN) {
|
|
11272
|
+
return { status: "up-to-date", source: "(env override)" };
|
|
11273
|
+
}
|
|
11274
|
+
if (!getPlatformDir()) {
|
|
11275
|
+
return { status: "failed", error: "Unsupported platform" };
|
|
11276
|
+
}
|
|
11277
|
+
if (sources.length === 0) {
|
|
11278
|
+
return { status: "failed", error: "No CDN sources available" };
|
|
11279
|
+
}
|
|
11280
|
+
return null;
|
|
11281
|
+
}
|
|
11282
|
+
function isLocalUpToDate2(localHash, checksum) {
|
|
11283
|
+
return localHash !== null && localHash.size === checksum.size && localHash.sha256 === checksum.sha256;
|
|
11284
|
+
}
|
|
11285
|
+
async function installAuthBinary(destPath, sources = CDN_SOURCES, onProgress) {
|
|
11286
|
+
const earlyResult = installPreChecks2(destPath, sources);
|
|
11287
|
+
if (earlyResult) return earlyResult;
|
|
11288
|
+
const platformDir = getPlatformDir();
|
|
11289
|
+
const binaryName = getAuthBinaryName();
|
|
11290
|
+
const resolvedDest = destPath ?? join11(homedir9(), ".okx", "bin", binaryName);
|
|
11291
|
+
const tmpPath = resolvedDest + ".tmp";
|
|
11292
|
+
mkdirSync9(dirname7(resolvedDest), { recursive: true });
|
|
11293
|
+
const localHash = existsSync7(resolvedDest) ? hashFile(resolvedDest) : null;
|
|
11294
|
+
const checksumPath = `${AUTH_CDN_PATH_PREFIX}/${platformDir}/checksum.json`;
|
|
11295
|
+
const binaryPath = `${AUTH_CDN_PATH_PREFIX}/${platformDir}/${binaryName}`;
|
|
11296
|
+
const errors = [];
|
|
11297
|
+
for (const { host, protocol } of sources) {
|
|
11298
|
+
try {
|
|
11299
|
+
const checksum = await fetchAndValidateChecksum2(
|
|
11300
|
+
host,
|
|
11301
|
+
protocol,
|
|
11302
|
+
checksumPath,
|
|
11303
|
+
platformDir,
|
|
11304
|
+
DOWNLOAD_TIMEOUT_MS,
|
|
11305
|
+
onProgress
|
|
11306
|
+
);
|
|
11307
|
+
if (isLocalUpToDate2(localHash, checksum)) {
|
|
11308
|
+
onProgress?.("Already up to date (checksum match)");
|
|
11309
|
+
return { status: "up-to-date", source: host };
|
|
11310
|
+
}
|
|
11311
|
+
await downloadAndVerify2(host, protocol, binaryPath, tmpPath, checksum, DOWNLOAD_TIMEOUT_MS, onProgress);
|
|
11312
|
+
atomicReplace2(tmpPath, resolvedDest);
|
|
11313
|
+
onProgress?.(`Downloaded and verified from ${host}`);
|
|
11314
|
+
return { status: "installed", source: host };
|
|
11315
|
+
} catch (err) {
|
|
11316
|
+
try {
|
|
11317
|
+
unlinkSync4(tmpPath);
|
|
11318
|
+
} catch {
|
|
11319
|
+
}
|
|
11320
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
11321
|
+
errors.push(`${host}: ${msg}`);
|
|
11322
|
+
onProgress?.(`${host} failed: ${msg}`);
|
|
11323
|
+
}
|
|
11324
|
+
}
|
|
11325
|
+
return { status: "failed", error: `All CDN sources failed:
|
|
11326
|
+
${errors.join("\n")}` };
|
|
11327
|
+
}
|
|
11328
|
+
function removeAuthBinary(binaryPath) {
|
|
11329
|
+
const resolvedPath = binaryPath ?? getAuthBinaryPath();
|
|
11330
|
+
try {
|
|
11331
|
+
unlinkSync4(resolvedPath);
|
|
11332
|
+
return { status: "removed" };
|
|
11333
|
+
} catch (err) {
|
|
11334
|
+
if (err.code === "ENOENT") {
|
|
11335
|
+
return { status: "not-found" };
|
|
11336
|
+
}
|
|
11337
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
11338
|
+
throw new Error(`Failed to remove ${resolvedPath}: ${msg}`);
|
|
11339
|
+
}
|
|
11340
|
+
}
|
|
11341
|
+
function isRedirect2(statusCode) {
|
|
11342
|
+
return statusCode !== void 0 && statusCode >= 300 && statusCode < 400;
|
|
11343
|
+
}
|
|
11344
|
+
function validateRedirect2(res, requestUrl, redirectCount, maxRedirects) {
|
|
11345
|
+
if (redirectCount > maxRedirects) {
|
|
11346
|
+
throw new Error(`Too many redirects (${maxRedirects})`);
|
|
11347
|
+
}
|
|
11348
|
+
const location = res.headers.location;
|
|
11349
|
+
if (requestUrl.startsWith("https") && !location.startsWith("https")) {
|
|
11350
|
+
throw new Error("Refused HTTPS \u2192 HTTP redirect downgrade");
|
|
11351
|
+
}
|
|
11352
|
+
return location;
|
|
11353
|
+
}
|
|
11354
|
+
function fetchResponse2(url, timeoutMs) {
|
|
11355
|
+
return new Promise((resolve3, reject) => {
|
|
11356
|
+
let redirects = 0;
|
|
11357
|
+
const maxRedirects = 5;
|
|
11358
|
+
function doRequest(requestUrl) {
|
|
11359
|
+
const reqFn = requestUrl.startsWith("https") ? httpsGet2 : httpGet2;
|
|
11360
|
+
const req = reqFn(requestUrl, { timeout: timeoutMs }, (res) => {
|
|
11361
|
+
if (isRedirect2(res.statusCode) && res.headers.location) {
|
|
11362
|
+
redirects++;
|
|
11363
|
+
try {
|
|
11364
|
+
const location = validateRedirect2(res, requestUrl, redirects, maxRedirects);
|
|
11365
|
+
res.resume();
|
|
11366
|
+
doRequest(location);
|
|
11367
|
+
} catch (err) {
|
|
11368
|
+
reject(err);
|
|
11369
|
+
}
|
|
11370
|
+
return;
|
|
11371
|
+
}
|
|
11372
|
+
if (res.statusCode !== 200) {
|
|
11373
|
+
reject(new Error(`HTTP ${res.statusCode ?? "unknown"}`));
|
|
11374
|
+
return;
|
|
11375
|
+
}
|
|
11376
|
+
resolve3(res);
|
|
11377
|
+
});
|
|
11378
|
+
req.on("error", reject);
|
|
11379
|
+
req.on("timeout", () => {
|
|
11380
|
+
req.destroy();
|
|
11381
|
+
reject(new Error("Download timed out"));
|
|
11382
|
+
});
|
|
11383
|
+
}
|
|
11384
|
+
doRequest(url);
|
|
11385
|
+
});
|
|
11386
|
+
}
|
|
11387
|
+
function download2(url, destPath, timeoutMs) {
|
|
11388
|
+
return fetchResponse2(url, timeoutMs).then(
|
|
11389
|
+
(res) => new Promise((resolve3, reject) => {
|
|
11390
|
+
const file = createWriteStream3(destPath);
|
|
11391
|
+
res.pipe(file);
|
|
11392
|
+
file.on("finish", () => file.close(() => resolve3()));
|
|
11393
|
+
file.on("error", (err) => {
|
|
11394
|
+
try {
|
|
11395
|
+
unlinkSync4(destPath);
|
|
11396
|
+
} catch {
|
|
11397
|
+
}
|
|
11398
|
+
reject(err);
|
|
11399
|
+
});
|
|
11400
|
+
})
|
|
11401
|
+
);
|
|
11402
|
+
}
|
|
11403
|
+
function downloadText2(url, timeoutMs) {
|
|
11404
|
+
return fetchResponse2(url, timeoutMs).then(
|
|
11405
|
+
(res) => new Promise((resolve3, reject) => {
|
|
11406
|
+
const chunks = [];
|
|
11407
|
+
res.on("data", (chunk) => chunks.push(chunk));
|
|
11408
|
+
res.on("end", () => resolve3(Buffer.concat(chunks).toString("utf8")));
|
|
11409
|
+
res.on("error", reject);
|
|
11410
|
+
})
|
|
11411
|
+
);
|
|
11412
|
+
}
|
|
11413
|
+
var CACHE_PATH = join12(homedir10(), ".okx", "auth-binary-check.json");
|
|
11414
|
+
var CHECK_INTERVAL_MS2 = 2 * 60 * 60 * 1e3;
|
|
11415
|
+
function readCache3() {
|
|
11416
|
+
try {
|
|
11417
|
+
if (!existsSync8(CACHE_PATH)) return null;
|
|
11418
|
+
const data = JSON.parse(readFileSync8(CACHE_PATH, "utf-8"));
|
|
11419
|
+
if (typeof data.cdnSha256 !== "string" || typeof data.checkedAt !== "number") return null;
|
|
11420
|
+
return { cdnSha256: data.cdnSha256, checkedAt: data.checkedAt };
|
|
11421
|
+
} catch {
|
|
11422
|
+
return null;
|
|
11423
|
+
}
|
|
11424
|
+
}
|
|
11425
|
+
function writeCache3(cdnSha256) {
|
|
11426
|
+
try {
|
|
11427
|
+
mkdirSync10(join12(homedir10(), ".okx"), { recursive: true });
|
|
11428
|
+
writeFileSync7(CACHE_PATH, JSON.stringify({ cdnSha256, checkedAt: Date.now() }, null, 2), "utf-8");
|
|
11429
|
+
} catch {
|
|
11430
|
+
}
|
|
11431
|
+
}
|
|
11432
|
+
async function ensureAuthBinaryLatest(onProgress) {
|
|
11433
|
+
if (process.env.OKX_AUTH_BIN) return;
|
|
11434
|
+
const cache = readCache3();
|
|
11435
|
+
if (cache && Date.now() - cache.checkedAt < CHECK_INTERVAL_MS2) return;
|
|
11436
|
+
try {
|
|
11437
|
+
const cdn = await fetchAuthCdnChecksum(void 0, 5e3);
|
|
11438
|
+
if (!cdn) return;
|
|
11439
|
+
const local = getAuthStatus();
|
|
11440
|
+
if (local.exists && local.sha256 === cdn.sha256) {
|
|
11441
|
+
writeCache3(cdn.sha256);
|
|
11442
|
+
return;
|
|
11443
|
+
}
|
|
11444
|
+
onProgress?.("Updating okx-auth binary...");
|
|
11445
|
+
const result = await installAuthBinary(void 0, void 0, onProgress);
|
|
11446
|
+
if (result.status === "installed" || result.status === "up-to-date") {
|
|
11447
|
+
const updated = getAuthStatus();
|
|
11448
|
+
if (updated.sha256) writeCache3(updated.sha256);
|
|
11449
|
+
if (result.status === "installed") {
|
|
11450
|
+
onProgress?.("\u2713 okx-auth updated successfully");
|
|
11451
|
+
}
|
|
11452
|
+
}
|
|
11453
|
+
} catch {
|
|
11454
|
+
}
|
|
11455
|
+
}
|
|
11456
|
+
function updateAuthBinaryCache(sha256) {
|
|
11457
|
+
writeCache3(sha256);
|
|
11458
|
+
}
|
|
11459
|
+
function clearAuthBinaryCache() {
|
|
11460
|
+
try {
|
|
11461
|
+
unlinkSync5(CACHE_PATH);
|
|
11462
|
+
} catch {
|
|
11463
|
+
}
|
|
11464
|
+
}
|
|
11076
11465
|
|
|
11077
|
-
// src/commands/
|
|
11078
|
-
import
|
|
11079
|
-
import
|
|
11080
|
-
import os5 from "os";
|
|
11081
|
-
import tls from "tls";
|
|
11082
|
-
|
|
11083
|
-
// src/commands/diagnose-utils.ts
|
|
11084
|
-
import fs4 from "fs";
|
|
11085
|
-
import { createRequire } from "module";
|
|
11466
|
+
// src/commands/auth.ts
|
|
11467
|
+
import { spawn as spawn2 } from "child_process";
|
|
11468
|
+
import readline from "readline";
|
|
11086
11469
|
|
|
11087
11470
|
// src/formatter.ts
|
|
11088
11471
|
import { EOL } from "os";
|
|
@@ -11173,7 +11556,286 @@ function markFailedIfSCodeError(data) {
|
|
|
11173
11556
|
}
|
|
11174
11557
|
}
|
|
11175
11558
|
|
|
11559
|
+
// src/commands/auth.ts
|
|
11560
|
+
function runOkxAuth(args) {
|
|
11561
|
+
const binPath = getAuthBinaryPath();
|
|
11562
|
+
return new Promise((resolve3, reject) => {
|
|
11563
|
+
const child = spawn2(binPath, args, {
|
|
11564
|
+
stdio: "inherit"
|
|
11565
|
+
});
|
|
11566
|
+
child.on("error", (err) => {
|
|
11567
|
+
reject(new Error(`Failed to spawn okx-auth: ${err.message}`));
|
|
11568
|
+
});
|
|
11569
|
+
child.on("close", (code) => {
|
|
11570
|
+
resolve3(code ?? 1);
|
|
11571
|
+
});
|
|
11572
|
+
});
|
|
11573
|
+
}
|
|
11574
|
+
function runOkxAuthCapture(args) {
|
|
11575
|
+
const binPath = getAuthBinaryPath();
|
|
11576
|
+
return new Promise((resolve3, reject) => {
|
|
11577
|
+
const child = spawn2(binPath, args, {
|
|
11578
|
+
stdio: ["inherit", "pipe", "inherit"]
|
|
11579
|
+
});
|
|
11580
|
+
const chunks = [];
|
|
11581
|
+
child.stdout.on("data", (chunk) => chunks.push(chunk));
|
|
11582
|
+
child.on("error", (err) => {
|
|
11583
|
+
reject(new Error(`Failed to spawn okx-auth: ${err.message}`));
|
|
11584
|
+
});
|
|
11585
|
+
child.on("close", (code) => {
|
|
11586
|
+
resolve3({
|
|
11587
|
+
code: code ?? 1,
|
|
11588
|
+
stdout: Buffer.concat(chunks).toString("utf-8")
|
|
11589
|
+
});
|
|
11590
|
+
});
|
|
11591
|
+
});
|
|
11592
|
+
}
|
|
11593
|
+
function findApiKeyProfile() {
|
|
11594
|
+
let config;
|
|
11595
|
+
try {
|
|
11596
|
+
config = readFullConfig();
|
|
11597
|
+
} catch {
|
|
11598
|
+
return null;
|
|
11599
|
+
}
|
|
11600
|
+
for (const [name, profile] of Object.entries(config.profiles ?? {})) {
|
|
11601
|
+
if (profile?.api_key) return name;
|
|
11602
|
+
}
|
|
11603
|
+
return null;
|
|
11604
|
+
}
|
|
11605
|
+
async function cmdAuthLogin(args) {
|
|
11606
|
+
const apiKeyProfile = findApiKeyProfile();
|
|
11607
|
+
if (apiKeyProfile) {
|
|
11608
|
+
if (args.manual) {
|
|
11609
|
+
outputLine(JSON.stringify({
|
|
11610
|
+
status: "skipped",
|
|
11611
|
+
reason: "api_key_configured",
|
|
11612
|
+
profile: apiKeyProfile,
|
|
11613
|
+
message: `API key already configured (profile: ${apiKeyProfile}). OAuth login skipped \u2014 API key will be used automatically.`
|
|
11614
|
+
}));
|
|
11615
|
+
} else {
|
|
11616
|
+
outputLine(`API key already configured (profile: ${apiKeyProfile}). OAuth login skipped \u2014 API key will be used automatically.`);
|
|
11617
|
+
}
|
|
11618
|
+
return;
|
|
11619
|
+
}
|
|
11620
|
+
const cliArgs = ["login"];
|
|
11621
|
+
if (args.site) cliArgs.push("--site", args.site);
|
|
11622
|
+
if (args.manual) cliArgs.push("--manual");
|
|
11623
|
+
const code = await runOkxAuth(cliArgs);
|
|
11624
|
+
if (code !== 0) {
|
|
11625
|
+
process.exitCode = code;
|
|
11626
|
+
}
|
|
11627
|
+
}
|
|
11628
|
+
async function cmdAuthLogout() {
|
|
11629
|
+
const code = await runOkxAuth(["logout"]);
|
|
11630
|
+
if (code !== 0) {
|
|
11631
|
+
process.exitCode = code;
|
|
11632
|
+
}
|
|
11633
|
+
}
|
|
11634
|
+
async function cmdAuthStatus(args) {
|
|
11635
|
+
const cliArgs = ["status"];
|
|
11636
|
+
if (args.json) cliArgs.push("--json");
|
|
11637
|
+
const result = await runOkxAuthCapture(cliArgs);
|
|
11638
|
+
if (result.stdout) {
|
|
11639
|
+
process.stdout.write(result.stdout);
|
|
11640
|
+
}
|
|
11641
|
+
if (result.code !== 0) {
|
|
11642
|
+
process.exitCode = result.code;
|
|
11643
|
+
}
|
|
11644
|
+
}
|
|
11645
|
+
function resolveChecksumMatch(local, cdnChecksum, cdnError) {
|
|
11646
|
+
if (!local.exists) return "not-installed";
|
|
11647
|
+
if (cdnError || !cdnChecksum) return "unavailable";
|
|
11648
|
+
if (cdnChecksum.sha256 === local.sha256) return "match";
|
|
11649
|
+
return "mismatch";
|
|
11650
|
+
}
|
|
11651
|
+
function checksumMatchLabel(match) {
|
|
11652
|
+
if (match === "match") return "\u2713 match";
|
|
11653
|
+
if (match === "mismatch") return "\u2717 mismatch (update available)";
|
|
11654
|
+
return "CDN unreachable";
|
|
11655
|
+
}
|
|
11656
|
+
function formatBytes(bytes) {
|
|
11657
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
11658
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
11659
|
+
return `${(bytes / (1024 * 1024)).toFixed(2)} MB`;
|
|
11660
|
+
}
|
|
11661
|
+
async function cmdAuthInstallStatus(json) {
|
|
11662
|
+
const local = getAuthStatus();
|
|
11663
|
+
let cdnChecksum = null;
|
|
11664
|
+
let cdnError = null;
|
|
11665
|
+
if (local.exists) {
|
|
11666
|
+
try {
|
|
11667
|
+
cdnChecksum = await fetchAuthCdnChecksum(void 0, 5e3);
|
|
11668
|
+
} catch (err) {
|
|
11669
|
+
cdnError = err instanceof Error ? err.message : String(err);
|
|
11670
|
+
}
|
|
11671
|
+
}
|
|
11672
|
+
const checksumMatch = resolveChecksumMatch(local, cdnChecksum, cdnError);
|
|
11673
|
+
if (json) {
|
|
11674
|
+
outputLine(
|
|
11675
|
+
JSON.stringify({
|
|
11676
|
+
binaryPath: local.binaryPath,
|
|
11677
|
+
exists: local.exists,
|
|
11678
|
+
platform: local.platform,
|
|
11679
|
+
fileSize: local.fileSize ?? null,
|
|
11680
|
+
sha256: local.sha256 ?? null,
|
|
11681
|
+
cdnMatch: checksumMatch,
|
|
11682
|
+
cdnSha256: cdnChecksum?.sha256 ?? null,
|
|
11683
|
+
cdnSource: cdnChecksum?.source ?? null
|
|
11684
|
+
})
|
|
11685
|
+
);
|
|
11686
|
+
return;
|
|
11687
|
+
}
|
|
11688
|
+
outputLine("");
|
|
11689
|
+
outputLine(" okx-auth Binary Status");
|
|
11690
|
+
outputLine(" " + "\u2500".repeat(40));
|
|
11691
|
+
outputLine(` Binary path : ${local.binaryPath}`);
|
|
11692
|
+
outputLine(` Installed : ${local.exists ? "yes" : "no"}`);
|
|
11693
|
+
outputLine(` Platform : ${local.platform ?? "(unsupported)"}`);
|
|
11694
|
+
if (local.exists) {
|
|
11695
|
+
outputLine(` File size : ${formatBytes(local.fileSize)}`);
|
|
11696
|
+
outputLine(` SHA-256 : ${local.sha256 ?? "(unknown)"}`);
|
|
11697
|
+
outputLine(` CDN check : ${checksumMatchLabel(checksumMatch)}`);
|
|
11698
|
+
if (cdnChecksum) {
|
|
11699
|
+
outputLine(` CDN source : ${cdnChecksum.source}`);
|
|
11700
|
+
}
|
|
11701
|
+
}
|
|
11702
|
+
outputLine("");
|
|
11703
|
+
}
|
|
11704
|
+
async function cmdAuthInstall(json) {
|
|
11705
|
+
const messages2 = [];
|
|
11706
|
+
const onProgress = (msg) => {
|
|
11707
|
+
if (!json) {
|
|
11708
|
+
outputLine(` ${msg}`);
|
|
11709
|
+
}
|
|
11710
|
+
messages2.push(msg);
|
|
11711
|
+
};
|
|
11712
|
+
if (!json) {
|
|
11713
|
+
outputLine("");
|
|
11714
|
+
outputLine(" Installing okx-auth...");
|
|
11715
|
+
}
|
|
11716
|
+
const result = await installAuthBinary(void 0, void 0, onProgress);
|
|
11717
|
+
if (json) {
|
|
11718
|
+
outputLine(JSON.stringify({ status: result.status, source: result.source ?? null, error: result.error ?? null, messages: messages2 }));
|
|
11719
|
+
if (result.status === "failed") {
|
|
11720
|
+
process.exitCode = 1;
|
|
11721
|
+
}
|
|
11722
|
+
return;
|
|
11723
|
+
}
|
|
11724
|
+
if (result.status === "installed" || result.status === "up-to-date") {
|
|
11725
|
+
const local = getAuthStatus();
|
|
11726
|
+
if (local.sha256) updateAuthBinaryCache(local.sha256);
|
|
11727
|
+
}
|
|
11728
|
+
if (result.status === "installed") {
|
|
11729
|
+
outputLine(` \u2713 okx-auth installed successfully (${result.source ?? ""})`);
|
|
11730
|
+
} else if (result.status === "up-to-date") {
|
|
11731
|
+
outputLine(" \u2713 okx-auth is already up to date");
|
|
11732
|
+
} else {
|
|
11733
|
+
errorLine(` \u2717 Installation failed: ${result.error ?? "unknown error"}`);
|
|
11734
|
+
errorLine(" Hint: check network connectivity or try again later");
|
|
11735
|
+
process.exitCode = 1;
|
|
11736
|
+
}
|
|
11737
|
+
outputLine("");
|
|
11738
|
+
}
|
|
11739
|
+
async function cmdAuthRemove(force, json) {
|
|
11740
|
+
const local = getAuthStatus(void 0, { skipHash: true });
|
|
11741
|
+
if (!local.exists) {
|
|
11742
|
+
if (json) {
|
|
11743
|
+
outputLine(JSON.stringify({ status: "not-installed" }));
|
|
11744
|
+
} else {
|
|
11745
|
+
outputLine(" okx-auth is not installed.");
|
|
11746
|
+
}
|
|
11747
|
+
return;
|
|
11748
|
+
}
|
|
11749
|
+
if (!await confirmRemoval(force, local.binaryPath)) return;
|
|
11750
|
+
let result;
|
|
11751
|
+
try {
|
|
11752
|
+
result = removeAuthBinary();
|
|
11753
|
+
} catch (err) {
|
|
11754
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
11755
|
+
if (json) {
|
|
11756
|
+
outputLine(JSON.stringify({ status: "failed", error: msg }));
|
|
11757
|
+
} else {
|
|
11758
|
+
errorLine(` \u2717 Failed to remove: ${msg}`);
|
|
11759
|
+
}
|
|
11760
|
+
process.exitCode = 1;
|
|
11761
|
+
return;
|
|
11762
|
+
}
|
|
11763
|
+
if (json) {
|
|
11764
|
+
outputLine(JSON.stringify({ status: result.status, path: local.binaryPath }));
|
|
11765
|
+
return;
|
|
11766
|
+
}
|
|
11767
|
+
if (result.status === "removed") {
|
|
11768
|
+
clearAuthBinaryCache();
|
|
11769
|
+
outputLine(` \u2713 Removed: ${local.binaryPath}`);
|
|
11770
|
+
} else {
|
|
11771
|
+
outputLine(" okx-auth is not installed.");
|
|
11772
|
+
}
|
|
11773
|
+
}
|
|
11774
|
+
async function confirmRemoval(force, binaryPath) {
|
|
11775
|
+
if (force) return true;
|
|
11776
|
+
if (!process.stdin.isTTY) {
|
|
11777
|
+
errorLine(" Error: stdin is not a TTY. Use --force to skip confirmation.");
|
|
11778
|
+
process.exitCode = 1;
|
|
11779
|
+
return false;
|
|
11780
|
+
}
|
|
11781
|
+
const confirmed = await askConfirmation(
|
|
11782
|
+
` Remove okx-auth at ${binaryPath}? [y/N] `
|
|
11783
|
+
);
|
|
11784
|
+
if (!confirmed) {
|
|
11785
|
+
outputLine(" Cancelled.");
|
|
11786
|
+
return false;
|
|
11787
|
+
}
|
|
11788
|
+
return true;
|
|
11789
|
+
}
|
|
11790
|
+
function askConfirmation(prompt2) {
|
|
11791
|
+
return new Promise((resolve3) => {
|
|
11792
|
+
const rl = readline.createInterface({
|
|
11793
|
+
input: process.stdin,
|
|
11794
|
+
output: process.stdout
|
|
11795
|
+
});
|
|
11796
|
+
rl.question(prompt2, (answer) => {
|
|
11797
|
+
rl.close();
|
|
11798
|
+
resolve3(answer.trim().toLowerCase() === "y");
|
|
11799
|
+
});
|
|
11800
|
+
});
|
|
11801
|
+
}
|
|
11802
|
+
async function handleAuthCommand(action, _rest, v) {
|
|
11803
|
+
const site = v.site;
|
|
11804
|
+
const json = v.json;
|
|
11805
|
+
const manual = v.manual;
|
|
11806
|
+
const force = v.force;
|
|
11807
|
+
if (["login", "logout", "status"].includes(action)) {
|
|
11808
|
+
await ensureAuthBinaryLatest((msg) => errorLine(` ${msg}`));
|
|
11809
|
+
}
|
|
11810
|
+
switch (action) {
|
|
11811
|
+
case "login":
|
|
11812
|
+
return cmdAuthLogin({ site, manual });
|
|
11813
|
+
case "logout":
|
|
11814
|
+
return cmdAuthLogout();
|
|
11815
|
+
case "status":
|
|
11816
|
+
return cmdAuthStatus({ json });
|
|
11817
|
+
case "install":
|
|
11818
|
+
return cmdAuthInstall(json ?? false);
|
|
11819
|
+
case "install-status":
|
|
11820
|
+
return cmdAuthInstallStatus(json ?? false);
|
|
11821
|
+
case "remove":
|
|
11822
|
+
return cmdAuthRemove(force ?? false, json ?? false);
|
|
11823
|
+
default:
|
|
11824
|
+
errorLine(`Unknown auth command: ${action}`);
|
|
11825
|
+
errorLine("Available: login, logout, status, install, install-status, remove");
|
|
11826
|
+
process.exitCode = 1;
|
|
11827
|
+
}
|
|
11828
|
+
}
|
|
11829
|
+
|
|
11830
|
+
// src/commands/diagnose.ts
|
|
11831
|
+
import dns from "dns/promises";
|
|
11832
|
+
import net from "net";
|
|
11833
|
+
import os5 from "os";
|
|
11834
|
+
import tls from "tls";
|
|
11835
|
+
|
|
11176
11836
|
// src/commands/diagnose-utils.ts
|
|
11837
|
+
import fs4 from "fs";
|
|
11838
|
+
import { createRequire } from "module";
|
|
11177
11839
|
var _require = createRequire(import.meta.url);
|
|
11178
11840
|
function readCliVersion() {
|
|
11179
11841
|
for (const rel of ["../package.json", "../../package.json"]) {
|
|
@@ -11259,7 +11921,7 @@ function sanitize2(value) {
|
|
|
11259
11921
|
import fs5 from "fs";
|
|
11260
11922
|
import path4 from "path";
|
|
11261
11923
|
import os4 from "os";
|
|
11262
|
-
import { spawnSync, spawn } from "child_process";
|
|
11924
|
+
import { spawnSync, spawn as spawn3 } from "child_process";
|
|
11263
11925
|
import { createRequire as createRequire2 } from "module";
|
|
11264
11926
|
import { fileURLToPath } from "url";
|
|
11265
11927
|
var _require2 = createRequire2(import.meta.url);
|
|
@@ -11593,7 +12255,7 @@ async function checkStdioHandshake(entryPath, report) {
|
|
|
11593
12255
|
clearTimeout(timer);
|
|
11594
12256
|
resolve3(passed);
|
|
11595
12257
|
};
|
|
11596
|
-
const child =
|
|
12258
|
+
const child = spawn3(process.execPath, [entryPath], {
|
|
11597
12259
|
stdio: ["pipe", "pipe", "pipe"],
|
|
11598
12260
|
env: { ...process.env }
|
|
11599
12261
|
});
|
|
@@ -11712,7 +12374,7 @@ async function cmdDiagnoseMcp(options = {}) {
|
|
|
11712
12374
|
|
|
11713
12375
|
// src/commands/diagnose.ts
|
|
11714
12376
|
var CLI_VERSION = readCliVersion();
|
|
11715
|
-
var GIT_HASH = true ? "
|
|
12377
|
+
var GIT_HASH = true ? "7705482" : "dev";
|
|
11716
12378
|
function maskKey2(key) {
|
|
11717
12379
|
if (!key) return "(not set)";
|
|
11718
12380
|
if (key.length <= 8) return "****";
|
|
@@ -12047,10 +12709,10 @@ async function runCliChecks(config, profile, outputPath) {
|
|
|
12047
12709
|
}
|
|
12048
12710
|
|
|
12049
12711
|
// src/unknown-command.ts
|
|
12050
|
-
function unknownSubcommand(module, action, knownActions) {
|
|
12712
|
+
function unknownSubcommand(module, action, knownActions, knownPaths = []) {
|
|
12051
12713
|
const actionStr = action ?? "(missing)";
|
|
12052
12714
|
errorLine(`Unknown command: okx ${module} ${actionStr}`);
|
|
12053
|
-
const hint = suggestSubcommand(action, knownActions);
|
|
12715
|
+
const hint = suggestSubcommand(action, knownActions, knownPaths);
|
|
12054
12716
|
if (hint) {
|
|
12055
12717
|
errorLine(` Did you mean: okx ${module} ${hint} ?`);
|
|
12056
12718
|
}
|
|
@@ -12060,12 +12722,12 @@ function unknownSubcommand(module, action, knownActions) {
|
|
|
12060
12722
|
errorLine(` Run 'okx ${module} --help' for full usage.`);
|
|
12061
12723
|
process.exitCode = 1;
|
|
12062
12724
|
}
|
|
12063
|
-
function suggestSubcommand(action, knownActions) {
|
|
12725
|
+
function suggestSubcommand(action, knownActions, knownPaths = []) {
|
|
12064
12726
|
if (!action || !action.includes("-")) return void 0;
|
|
12065
12727
|
const parts = action.split("-");
|
|
12066
12728
|
if (parts.length !== 2) return void 0;
|
|
12067
12729
|
const [a, b] = parts;
|
|
12068
|
-
if (knownActions.includes(b)) {
|
|
12730
|
+
if (knownActions.includes(b) && knownPaths.includes(`${b} ${a}`)) {
|
|
12069
12731
|
return `${b} ${a}`;
|
|
12070
12732
|
}
|
|
12071
12733
|
return void 0;
|
|
@@ -12073,24 +12735,24 @@ function suggestSubcommand(action, knownActions) {
|
|
|
12073
12735
|
|
|
12074
12736
|
// src/commands/upgrade.ts
|
|
12075
12737
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
12076
|
-
import { readFileSync as
|
|
12077
|
-
import { dirname as
|
|
12078
|
-
import { homedir as
|
|
12738
|
+
import { readFileSync as readFileSync9, writeFileSync as writeFileSync8, mkdirSync as mkdirSync11 } from "fs";
|
|
12739
|
+
import { dirname as dirname8, join as join13 } from "path";
|
|
12740
|
+
import { homedir as homedir11 } from "os";
|
|
12079
12741
|
var PACKAGES = ["@okx_ai/okx-trade-mcp", "@okx_ai/okx-trade-cli"];
|
|
12080
|
-
var CACHE_FILE2 =
|
|
12742
|
+
var CACHE_FILE2 = join13(homedir11(), ".okx", "last_check");
|
|
12081
12743
|
var THROTTLE_MS = 12 * 60 * 60 * 1e3;
|
|
12082
|
-
var NPM_BIN =
|
|
12744
|
+
var NPM_BIN = join13(dirname8(process.execPath), process.platform === "win32" ? "npm.cmd" : "npm");
|
|
12083
12745
|
function readLastCheck() {
|
|
12084
12746
|
try {
|
|
12085
|
-
return parseInt(
|
|
12747
|
+
return parseInt(readFileSync9(CACHE_FILE2, "utf-8").trim(), 10) || 0;
|
|
12086
12748
|
} catch {
|
|
12087
12749
|
return 0;
|
|
12088
12750
|
}
|
|
12089
12751
|
}
|
|
12090
12752
|
function writeLastCheck() {
|
|
12091
12753
|
try {
|
|
12092
|
-
|
|
12093
|
-
|
|
12754
|
+
mkdirSync11(join13(homedir11(), ".okx"), { recursive: true });
|
|
12755
|
+
writeFileSync8(CACHE_FILE2, String(Math.floor(Date.now() / 1e3)), "utf-8");
|
|
12094
12756
|
} catch {
|
|
12095
12757
|
}
|
|
12096
12758
|
}
|
|
@@ -13555,22 +14217,18 @@ async function cmdNewsSentimentRank(run, opts) {
|
|
|
13555
14217
|
}
|
|
13556
14218
|
|
|
13557
14219
|
// src/config/loader.ts
|
|
13558
|
-
function loadProfileConfig(opts) {
|
|
13559
|
-
return loadConfig(
|
|
13560
|
-
|
|
13561
|
-
|
|
13562
|
-
|
|
13563
|
-
|
|
13564
|
-
|
|
13565
|
-
|
|
13566
|
-
|
|
13567
|
-
|
|
13568
|
-
|
|
13569
|
-
|
|
13570
|
-
verbose: opts.verbose
|
|
13571
|
-
},
|
|
13572
|
-
{ entryPrefix: opts.entryPrefix, readEnv: opts.readEnv }
|
|
13573
|
-
);
|
|
14220
|
+
async function loadProfileConfig(opts) {
|
|
14221
|
+
return loadConfig({
|
|
14222
|
+
profile: opts.profile,
|
|
14223
|
+
modules: opts.modules,
|
|
14224
|
+
readOnly: opts.readOnly ?? false,
|
|
14225
|
+
demo: opts.demo,
|
|
14226
|
+
live: opts.live,
|
|
14227
|
+
site: opts.site,
|
|
14228
|
+
userAgent: opts.userAgent,
|
|
14229
|
+
sourceTag: opts.sourceTag,
|
|
14230
|
+
verbose: opts.verbose
|
|
14231
|
+
});
|
|
13574
14232
|
}
|
|
13575
14233
|
|
|
13576
14234
|
// src/help.ts
|
|
@@ -13579,13 +14237,12 @@ var HELP_TREE = generateHelpTree();
|
|
|
13579
14237
|
function printGlobalHelp() {
|
|
13580
14238
|
const lines = [
|
|
13581
14239
|
"",
|
|
13582
|
-
`Usage: okx [--profile <name>] [--demo | --live] [--
|
|
14240
|
+
`Usage: okx [--profile <name>] [--demo | --live] [--json] <module> <action> [args...]`,
|
|
13583
14241
|
"",
|
|
13584
14242
|
"Global Options:",
|
|
13585
14243
|
` --profile <name> Use a named profile from ${configFilePath()}`,
|
|
13586
14244
|
" --demo Use simulated trading (demo) mode",
|
|
13587
14245
|
" --live Force live trading mode (overrides profile demo=true; mutually exclusive with --demo)",
|
|
13588
|
-
" --skill <token> OKX skill token for order tagging (from SKILL.md)",
|
|
13589
14246
|
" --json Output raw JSON",
|
|
13590
14247
|
" --env With --json, wrap output as {env, profile, data}",
|
|
13591
14248
|
" --verbose Show detailed network request/response info (stderr)",
|
|
@@ -13718,7 +14375,6 @@ import { parseArgs } from "util";
|
|
|
13718
14375
|
var CLI_OPTIONS = {
|
|
13719
14376
|
profile: { type: "string" },
|
|
13720
14377
|
demo: { type: "boolean", default: false },
|
|
13721
|
-
skill: { type: "string" },
|
|
13722
14378
|
json: { type: "boolean", default: false },
|
|
13723
14379
|
env: { type: "boolean", default: false },
|
|
13724
14380
|
help: { type: "boolean", default: false },
|
|
@@ -13898,6 +14554,9 @@ var CLI_OPTIONS = {
|
|
|
13898
14554
|
dir: { type: "string" },
|
|
13899
14555
|
page: { type: "string" },
|
|
13900
14556
|
format: { type: "string" },
|
|
14557
|
+
// auth
|
|
14558
|
+
site: { type: "string" },
|
|
14559
|
+
manual: { type: "boolean", default: false },
|
|
13901
14560
|
// event contract
|
|
13902
14561
|
underlying: { type: "string" },
|
|
13903
14562
|
seriesId: { type: "string" },
|
|
@@ -17121,14 +17780,14 @@ async function cmdDcdQuoteAndBuy(run, opts) {
|
|
|
17121
17780
|
}
|
|
17122
17781
|
|
|
17123
17782
|
// src/commands/skill.ts
|
|
17124
|
-
import { tmpdir, homedir as
|
|
17125
|
-
import { join as
|
|
17126
|
-
import { mkdirSync as
|
|
17783
|
+
import { tmpdir, homedir as homedir13 } from "os";
|
|
17784
|
+
import { join as join15, dirname as dirname9 } from "path";
|
|
17785
|
+
import { mkdirSync as mkdirSync12, rmSync, existsSync as existsSync10, copyFileSync as copyFileSync2 } from "fs";
|
|
17127
17786
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
17128
17787
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
17129
17788
|
function resolveNpx() {
|
|
17130
|
-
const sibling =
|
|
17131
|
-
if (
|
|
17789
|
+
const sibling = join15(dirname9(process.execPath), "npx");
|
|
17790
|
+
if (existsSync10(sibling)) return sibling;
|
|
17132
17791
|
return "npx";
|
|
17133
17792
|
}
|
|
17134
17793
|
var THIRD_PARTY_INSTALL_NOTICE = "Note: This skill was created by a third-party developer, not by OKX. Review SKILL.md before use.";
|
|
@@ -17181,13 +17840,13 @@ async function cmdSkillCategories(run, json) {
|
|
|
17181
17840
|
outputLine("");
|
|
17182
17841
|
}
|
|
17183
17842
|
async function cmdSkillAdd(name, config, json) {
|
|
17184
|
-
const tmpBase =
|
|
17185
|
-
|
|
17843
|
+
const tmpBase = join15(tmpdir(), `okx-skill-${randomUUID2()}`);
|
|
17844
|
+
mkdirSync12(tmpBase, { recursive: true });
|
|
17186
17845
|
try {
|
|
17187
17846
|
outputLine(`Downloading ${name}...`);
|
|
17188
17847
|
const client = new OkxRestClient(config);
|
|
17189
17848
|
const zipPath = await downloadSkillZip(client, name, tmpBase);
|
|
17190
|
-
const contentDir = await extractSkillZip(zipPath,
|
|
17849
|
+
const contentDir = await extractSkillZip(zipPath, join15(tmpBase, "content"));
|
|
17191
17850
|
const meta = readMetaJson(contentDir);
|
|
17192
17851
|
validateSkillMdExists(contentDir);
|
|
17193
17852
|
outputLine("Installing to detected agents...");
|
|
@@ -17197,7 +17856,7 @@ async function cmdSkillAdd(name, config, json) {
|
|
|
17197
17856
|
timeout: 6e4
|
|
17198
17857
|
});
|
|
17199
17858
|
} catch (e) {
|
|
17200
|
-
const savedZip =
|
|
17859
|
+
const savedZip = join15(process.cwd(), `${name}.zip`);
|
|
17201
17860
|
try {
|
|
17202
17861
|
copyFileSync2(zipPath, savedZip);
|
|
17203
17862
|
} catch {
|
|
@@ -17236,7 +17895,7 @@ function cmdSkillRemove(name, json) {
|
|
|
17236
17895
|
timeout: 6e4
|
|
17237
17896
|
});
|
|
17238
17897
|
} catch {
|
|
17239
|
-
const agentsPath =
|
|
17898
|
+
const agentsPath = join15(homedir13(), ".agents", "skills", name);
|
|
17240
17899
|
try {
|
|
17241
17900
|
rmSync(agentsPath, { recursive: true, force: true });
|
|
17242
17901
|
} catch {
|
|
@@ -17310,14 +17969,14 @@ function printSkillInstallResult(meta, json) {
|
|
|
17310
17969
|
}
|
|
17311
17970
|
|
|
17312
17971
|
// src/commands/pilot.ts
|
|
17313
|
-
import
|
|
17314
|
-
function
|
|
17972
|
+
import readline2 from "readline";
|
|
17973
|
+
function resolveChecksumMatch2(local, cdnChecksum, cdnError) {
|
|
17315
17974
|
if (!local.exists) return "not-installed";
|
|
17316
17975
|
if (cdnError || !cdnChecksum) return "unavailable";
|
|
17317
17976
|
if (cdnChecksum.sha256 === local.sha256) return "match";
|
|
17318
17977
|
return "mismatch";
|
|
17319
17978
|
}
|
|
17320
|
-
function
|
|
17979
|
+
function checksumMatchLabel2(match) {
|
|
17321
17980
|
if (match === "match") return "\u2713 match";
|
|
17322
17981
|
if (match === "mismatch") return "\u2717 mismatch (update available)";
|
|
17323
17982
|
return "CDN unreachable";
|
|
@@ -17330,9 +17989,9 @@ function formatStatusText(local, checksumMatch, cdnChecksum, runtimeMode) {
|
|
|
17330
17989
|
outputLine(` Installed : ${local.exists ? "yes" : "no"}`);
|
|
17331
17990
|
outputLine(` Platform : ${local.platform ?? "(unsupported)"}`);
|
|
17332
17991
|
if (local.exists) {
|
|
17333
|
-
outputLine(` File size : ${
|
|
17992
|
+
outputLine(` File size : ${formatBytes2(local.fileSize)}`);
|
|
17334
17993
|
outputLine(` SHA-256 : ${local.sha256 ?? "(unknown)"}`);
|
|
17335
|
-
outputLine(` CDN check : ${
|
|
17994
|
+
outputLine(` CDN check : ${checksumMatchLabel2(checksumMatch)}`);
|
|
17336
17995
|
if (cdnChecksum) {
|
|
17337
17996
|
outputLine(` CDN source : ${cdnChecksum.source}`);
|
|
17338
17997
|
}
|
|
@@ -17359,7 +18018,7 @@ async function cmdPilotStatus(json, binaryPath) {
|
|
|
17359
18018
|
cdnError = err instanceof Error ? err.message : String(err);
|
|
17360
18019
|
}
|
|
17361
18020
|
}
|
|
17362
|
-
const checksumMatch =
|
|
18021
|
+
const checksumMatch = resolveChecksumMatch2(local, cdnChecksum, cdnError);
|
|
17363
18022
|
if (json) {
|
|
17364
18023
|
outputLine(
|
|
17365
18024
|
JSON.stringify({
|
|
@@ -17425,7 +18084,7 @@ async function cmdPilotRemove(force, json, binaryPath) {
|
|
|
17425
18084
|
process.exitCode = 1;
|
|
17426
18085
|
return;
|
|
17427
18086
|
}
|
|
17428
|
-
const confirmed = await
|
|
18087
|
+
const confirmed = await askConfirmation2(
|
|
17429
18088
|
` Remove Pilot at ${local.binaryPath}? [y/N] `
|
|
17430
18089
|
);
|
|
17431
18090
|
if (!confirmed) {
|
|
@@ -17444,14 +18103,14 @@ async function cmdPilotRemove(force, json, binaryPath) {
|
|
|
17444
18103
|
outputLine(" Pilot is not installed.");
|
|
17445
18104
|
}
|
|
17446
18105
|
}
|
|
17447
|
-
function
|
|
18106
|
+
function formatBytes2(bytes) {
|
|
17448
18107
|
if (bytes < 1024) return `${bytes} B`;
|
|
17449
18108
|
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
17450
18109
|
return `${(bytes / (1024 * 1024)).toFixed(2)} MB`;
|
|
17451
18110
|
}
|
|
17452
|
-
function
|
|
18111
|
+
function askConfirmation2(prompt2) {
|
|
17453
18112
|
return new Promise((resolve3) => {
|
|
17454
|
-
const rl =
|
|
18113
|
+
const rl = readline2.createInterface({
|
|
17455
18114
|
input: process.stdin,
|
|
17456
18115
|
output: process.stdout
|
|
17457
18116
|
});
|
|
@@ -17920,7 +18579,7 @@ async function cmdEventCancel(run, opts) {
|
|
|
17920
18579
|
// src/index.ts
|
|
17921
18580
|
var _require3 = createRequire3(import.meta.url);
|
|
17922
18581
|
var CLI_VERSION2 = _require3("../package.json").version;
|
|
17923
|
-
var GIT_HASH2 = true ? "
|
|
18582
|
+
var GIT_HASH2 = true ? "7705482" : "dev";
|
|
17924
18583
|
function handlePilotCommand(action, json, force, binaryPath) {
|
|
17925
18584
|
if (action === "status") return cmdPilotStatus(json, binaryPath);
|
|
17926
18585
|
if (action === "install") return cmdPilotInstall(json, binaryPath);
|
|
@@ -18250,7 +18909,7 @@ function handleSpotCommand(run, action, rest, v, json) {
|
|
|
18250
18909
|
"algo",
|
|
18251
18910
|
"batch",
|
|
18252
18911
|
"leverage"
|
|
18253
|
-
]);
|
|
18912
|
+
], ["algo place", "algo cancel", "algo amend", "algo trail", "algo orders"]);
|
|
18254
18913
|
}
|
|
18255
18914
|
function handleSwapAlgoCommand(run, subAction, v, json) {
|
|
18256
18915
|
if (subAction === "trail")
|
|
@@ -18395,7 +19054,7 @@ function handleSwapCommand(run, action, rest, v, json) {
|
|
|
18395
19054
|
"leverage",
|
|
18396
19055
|
"algo",
|
|
18397
19056
|
"batch"
|
|
18398
|
-
]);
|
|
19057
|
+
], ["algo place", "algo cancel", "algo amend", "algo trail", "algo orders"]);
|
|
18399
19058
|
}
|
|
18400
19059
|
function handleOptionAlgoCommand(run, subAction, v, json) {
|
|
18401
19060
|
if (subAction === "place")
|
|
@@ -18497,7 +19156,7 @@ function handleOptionCommand(run, action, rest, v, json) {
|
|
|
18497
19156
|
"amend",
|
|
18498
19157
|
"batch-cancel",
|
|
18499
19158
|
"algo"
|
|
18500
|
-
]);
|
|
19159
|
+
], ["algo place", "algo cancel", "algo amend", "algo orders"]);
|
|
18501
19160
|
}
|
|
18502
19161
|
function handleFuturesAlgoCommand(run, subAction, v, json) {
|
|
18503
19162
|
if (subAction === "trail")
|
|
@@ -18642,7 +19301,7 @@ function handleFuturesCommand(run, action, rest, v, json) {
|
|
|
18642
19301
|
"leverage",
|
|
18643
19302
|
"batch",
|
|
18644
19303
|
"algo"
|
|
18645
|
-
]);
|
|
19304
|
+
], ["algo place", "algo cancel", "algo amend", "algo trail", "algo orders"]);
|
|
18646
19305
|
}
|
|
18647
19306
|
function handleBotGridCommand(run, v, rest, json) {
|
|
18648
19307
|
const subAction = rest[0];
|
|
@@ -18963,13 +19622,13 @@ function handleNewsCommand(run, action, rest, v, json) {
|
|
|
18963
19622
|
const period = v.period;
|
|
18964
19623
|
const points = v.points !== void 0 ? Number(v.points) : 24;
|
|
18965
19624
|
const sortBy = v["sort-by"];
|
|
18966
|
-
const
|
|
18967
|
-
const searchOpts = { coins: v.coins, importance: v.importance, platform:
|
|
18968
|
-
const listOpts = { coins: v.coins, importance: v.importance, platform:
|
|
19625
|
+
const platform3 = v.platform;
|
|
19626
|
+
const searchOpts = { coins: v.coins, importance: v.importance, platform: platform3, sentiment: v.sentiment, sortBy, begin, end, language, detailLvl, limit, after, json };
|
|
19627
|
+
const listOpts = { coins: v.coins, importance: v.importance, platform: platform3, begin, end, language, detailLvl, limit, after, json };
|
|
18969
19628
|
const dispatch = {
|
|
18970
19629
|
latest: () => cmdNewsLatest(run, listOpts),
|
|
18971
|
-
important: () => cmdNewsImportant(run, { coins: v.coins, platform:
|
|
18972
|
-
"by-coin": () => cmdNewsByCoin(run, v.coins ?? rest[0], { importance: v.importance, platform:
|
|
19630
|
+
important: () => cmdNewsImportant(run, { coins: v.coins, platform: platform3, begin, end, language, detailLvl, limit, json }),
|
|
19631
|
+
"by-coin": () => cmdNewsByCoin(run, v.coins ?? rest[0], { importance: v.importance, platform: platform3, begin, end, language, detailLvl, limit, json }),
|
|
18973
19632
|
search: () => cmdNewsSearch(run, v.keyword ?? rest[0], searchOpts),
|
|
18974
19633
|
detail: () => cmdNewsDetail(run, rest[0], { language, json }),
|
|
18975
19634
|
platforms: () => cmdNewsPlatforms(run, { json }),
|
|
@@ -19110,7 +19769,7 @@ function wrapRunnerWithLogger(baseRunner, logger, verbose = false) {
|
|
|
19110
19769
|
async function runDiagnose(v) {
|
|
19111
19770
|
let config;
|
|
19112
19771
|
try {
|
|
19113
|
-
config = loadProfileConfig({ profile: v.profile, demo: v.demo, live: v.live, verbose: v.verbose, userAgent: `okx-trade-cli/${CLI_VERSION2}`, sourceTag: "CLI"
|
|
19772
|
+
config = await loadProfileConfig({ profile: v.profile, demo: v.demo, live: v.live, verbose: v.verbose, userAgent: `okx-trade-cli/${CLI_VERSION2}`, sourceTag: "CLI" });
|
|
19114
19773
|
} catch {
|
|
19115
19774
|
}
|
|
19116
19775
|
return cmdDiagnose(config, v.profile ?? "default", { mcp: v.mcp, cli: v.cli, all: v.all, output: v.output });
|
|
@@ -19133,6 +19792,7 @@ function routeManagementCommand(module, action, rest, json, v) {
|
|
|
19133
19792
|
handleSetupCommand(v);
|
|
19134
19793
|
return true;
|
|
19135
19794
|
}
|
|
19795
|
+
if (module === "auth") return handleAuthCommand(action, rest, v);
|
|
19136
19796
|
if (module === "upgrade") return cmdUpgrade(CLI_VERSION2, { beta: v.beta, check: v.check, force: v.force }, json);
|
|
19137
19797
|
if (module === "pilot") {
|
|
19138
19798
|
const r = handlePilotCommand(action, json, v.force ?? false);
|
|
@@ -19165,7 +19825,7 @@ async function main() {
|
|
|
19165
19825
|
const json = v.json ?? false;
|
|
19166
19826
|
const mgmt = routeManagementCommand(module, action, rest, json, v);
|
|
19167
19827
|
if (mgmt !== void 0) return mgmt === true ? void 0 : mgmt;
|
|
19168
|
-
const config = loadProfileConfig({ profile: v.profile, demo: v.demo, live: v.live, verbose: v.verbose, userAgent: `okx-trade-cli/${CLI_VERSION2}`, sourceTag: "CLI"
|
|
19828
|
+
const config = await loadProfileConfig({ profile: v.profile, demo: v.demo, live: v.live, verbose: v.verbose, userAgent: `okx-trade-cli/${CLI_VERSION2}`, sourceTag: "CLI" });
|
|
19169
19829
|
setEnvContext({ demo: config.demo, profile: v.profile ?? "default" });
|
|
19170
19830
|
setJsonEnvEnabled(v.env ?? false);
|
|
19171
19831
|
const client = new OkxRestClient(config);
|