@okx_ai/okx-trade-cli 1.3.1-beta.12 → 1.3.1-beta.13
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 +818 -185
- 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,25 +21,28 @@ 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
|
-
// ../../node_modules
|
|
45
|
+
// ../../node_modules/smol-toml/dist/error.js
|
|
43
46
|
function getLineColFromPtr(string, ptr) {
|
|
44
47
|
let lines = string.slice(0, ptr).split(/\r\n|\n|\r/g);
|
|
45
48
|
return [lines.length, lines.pop().length + 1];
|
|
@@ -79,7 +82,7 @@ ${codeblock}`, options);
|
|
|
79
82
|
}
|
|
80
83
|
};
|
|
81
84
|
|
|
82
|
-
// ../../node_modules
|
|
85
|
+
// ../../node_modules/smol-toml/dist/util.js
|
|
83
86
|
function isEscaped(str, ptr) {
|
|
84
87
|
let i = 0;
|
|
85
88
|
while (str[ptr - ++i] === "\\")
|
|
@@ -110,9 +113,14 @@ function skipComment(str, ptr) {
|
|
|
110
113
|
}
|
|
111
114
|
function skipVoid(str, ptr, banNewLines, banComments) {
|
|
112
115
|
let c;
|
|
113
|
-
while (
|
|
114
|
-
ptr
|
|
115
|
-
|
|
116
|
+
while (1) {
|
|
117
|
+
while ((c = str[ptr]) === " " || c === " " || !banNewLines && (c === "\n" || c === "\r" && str[ptr + 1] === "\n"))
|
|
118
|
+
ptr++;
|
|
119
|
+
if (banComments || c !== "#")
|
|
120
|
+
break;
|
|
121
|
+
ptr = skipComment(str, ptr);
|
|
122
|
+
}
|
|
123
|
+
return ptr;
|
|
116
124
|
}
|
|
117
125
|
function skipUntil(str, ptr, sep2, end, banNewLines = false) {
|
|
118
126
|
if (!end) {
|
|
@@ -153,7 +161,7 @@ function getStringEnd(str, seek) {
|
|
|
153
161
|
return seek;
|
|
154
162
|
}
|
|
155
163
|
|
|
156
|
-
// ../../node_modules
|
|
164
|
+
// ../../node_modules/smol-toml/dist/date.js
|
|
157
165
|
var DATE_TIME_RE = /^(\d{4}-\d{2}-\d{2})?[T ]?(?:(\d{2}):\d{2}(?::\d{2}(?:\.\d+)?)?)?(Z|[-+]\d{2}:\d{2})?$/i;
|
|
158
166
|
var TomlDate = class _TomlDate extends Date {
|
|
159
167
|
#hasDate = false;
|
|
@@ -245,7 +253,7 @@ var TomlDate = class _TomlDate extends Date {
|
|
|
245
253
|
}
|
|
246
254
|
};
|
|
247
255
|
|
|
248
|
-
// ../../node_modules
|
|
256
|
+
// ../../node_modules/smol-toml/dist/primitive.js
|
|
249
257
|
var INT_REGEX = /^((0x[0-9a-fA-F](_?[0-9a-fA-F])*)|(([+-]|0[ob])?\d(_?\d)*))$/;
|
|
250
258
|
var FLOAT_REGEX = /^[+-]?\d(_?\d)*(\.\d(_?\d)*)?([eE][+-]?\d(_?\d)*)?$/;
|
|
251
259
|
var LEADING_ZERO = /^[+-]?0[0-9_]/;
|
|
@@ -384,7 +392,7 @@ function parseValue(value, toml, ptr, integersAsBigInt) {
|
|
|
384
392
|
return date;
|
|
385
393
|
}
|
|
386
394
|
|
|
387
|
-
// ../../node_modules
|
|
395
|
+
// ../../node_modules/smol-toml/dist/extract.js
|
|
388
396
|
function sliceAndTrimEndOf(str, startPtr, endPtr) {
|
|
389
397
|
let value = str.slice(startPtr, endPtr);
|
|
390
398
|
let commentIdx = value.indexOf("#");
|
|
@@ -451,7 +459,7 @@ function extractValue(str, ptr, end, depth, integersAsBigInt) {
|
|
|
451
459
|
];
|
|
452
460
|
}
|
|
453
461
|
|
|
454
|
-
// ../../node_modules
|
|
462
|
+
// ../../node_modules/smol-toml/dist/struct.js
|
|
455
463
|
var KEY_PART_RE = /^[a-zA-Z0-9-_]+[ \t]*$/;
|
|
456
464
|
function parseKey(str, ptr, end = "=") {
|
|
457
465
|
let dot = ptr - 1;
|
|
@@ -599,7 +607,7 @@ function parseArray(str, ptr, depth, integersAsBigInt) {
|
|
|
599
607
|
return [res, ptr];
|
|
600
608
|
}
|
|
601
609
|
|
|
602
|
-
// ../../node_modules
|
|
610
|
+
// ../../node_modules/smol-toml/dist/parse.js
|
|
603
611
|
function peekTable(key, table, meta, type) {
|
|
604
612
|
let t = table;
|
|
605
613
|
let m = meta;
|
|
@@ -724,7 +732,7 @@ function parse(toml, { maxDepth = 1e3, integersAsBigInt } = {}) {
|
|
|
724
732
|
return res;
|
|
725
733
|
}
|
|
726
734
|
|
|
727
|
-
// ../../node_modules
|
|
735
|
+
// ../../node_modules/smol-toml/dist/stringify.js
|
|
728
736
|
var BARE_KEY = /^[a-z0-9-_]+$/i;
|
|
729
737
|
function extendedTypeOf(obj) {
|
|
730
738
|
let type = typeof obj;
|
|
@@ -867,8 +875,8 @@ function stringify(obj, { maxDepth = 1e3, numbersAsFloat = false } = {}) {
|
|
|
867
875
|
|
|
868
876
|
// ../core/dist/index.js
|
|
869
877
|
import { readFileSync as readFileSync5, writeFileSync as writeFileSync5, mkdirSync as mkdirSync6, existsSync as existsSync4 } from "fs";
|
|
870
|
-
import { join as
|
|
871
|
-
import { homedir as
|
|
878
|
+
import { join as join8 } from "path";
|
|
879
|
+
import { homedir as homedir6 } from "os";
|
|
872
880
|
import fs2 from "fs";
|
|
873
881
|
import path2 from "path";
|
|
874
882
|
import os2 from "os";
|
|
@@ -876,6 +884,18 @@ import * as fs3 from "fs";
|
|
|
876
884
|
import * as path3 from "path";
|
|
877
885
|
import * as os3 from "os";
|
|
878
886
|
import { execFileSync } from "child_process";
|
|
887
|
+
import {
|
|
888
|
+
createWriteStream as createWriteStream3,
|
|
889
|
+
mkdirSync as mkdirSync9,
|
|
890
|
+
chmodSync as chmodSync2,
|
|
891
|
+
existsSync as existsSync7,
|
|
892
|
+
unlinkSync as unlinkSync4,
|
|
893
|
+
renameSync as renameSync4
|
|
894
|
+
} from "fs";
|
|
895
|
+
import { homedir as homedir9, platform as platform2 } from "os";
|
|
896
|
+
import { join as join11, dirname as dirname7 } from "path";
|
|
897
|
+
import { get as httpsGet2 } from "https";
|
|
898
|
+
import { get as httpGet2 } from "http";
|
|
879
899
|
import {
|
|
880
900
|
readFileSync as readFileSync7,
|
|
881
901
|
createWriteStream as createWriteStream2,
|
|
@@ -886,10 +906,13 @@ import {
|
|
|
886
906
|
renameSync as renameSync3
|
|
887
907
|
} from "fs";
|
|
888
908
|
import { createHash } from "crypto";
|
|
889
|
-
import { homedir as
|
|
890
|
-
import { join as
|
|
909
|
+
import { homedir as homedir8, platform, arch } from "os";
|
|
910
|
+
import { join as join10, dirname as dirname6 } from "path";
|
|
891
911
|
import { get as httpsGet } from "https";
|
|
892
912
|
import { get as httpGet } from "http";
|
|
913
|
+
import { readFileSync as readFileSync8, writeFileSync as writeFileSync7, mkdirSync as mkdirSync10, unlinkSync as unlinkSync5, existsSync as existsSync8 } from "fs";
|
|
914
|
+
import { join as join12 } from "path";
|
|
915
|
+
import { homedir as homedir10 } from "os";
|
|
893
916
|
var EXEC_TIMEOUT_MS = 3e4;
|
|
894
917
|
var ALLOWED_DOMAIN_RE = /^[\w.-]+\.okx\.com$/;
|
|
895
918
|
var DOH_BIN_DIR = join(homedir(), ".okx", "bin");
|
|
@@ -898,7 +921,7 @@ function getDohBinaryPath() {
|
|
|
898
921
|
return process.env.OKX_DOH_BINARY_PATH;
|
|
899
922
|
}
|
|
900
923
|
const ext = process.platform === "win32" ? ".exe" : "";
|
|
901
|
-
return join(DOH_BIN_DIR, `okx-
|
|
924
|
+
return join(DOH_BIN_DIR, `okx-doh-resolver${ext}`);
|
|
902
925
|
}
|
|
903
926
|
function execDohBinary(domain, exclude = [], userAgent) {
|
|
904
927
|
if (!ALLOWED_DOMAIN_RE.test(domain)) {
|
|
@@ -969,7 +992,7 @@ function classifyAndCache(node, hostname, failedNodes, cachePath) {
|
|
|
969
992
|
if (!node) {
|
|
970
993
|
return { mode: null, node: null };
|
|
971
994
|
}
|
|
972
|
-
if (node.ip === hostname
|
|
995
|
+
if (node.ip === hostname || node.host === hostname) {
|
|
973
996
|
writeCache(hostname, {
|
|
974
997
|
mode: "direct",
|
|
975
998
|
node: null,
|
|
@@ -1201,6 +1224,11 @@ var ConfigError = class extends OkxMcpError {
|
|
|
1201
1224
|
super("ConfigError", message, { suggestion });
|
|
1202
1225
|
}
|
|
1203
1226
|
};
|
|
1227
|
+
var NotLoggedInError = class extends ConfigError {
|
|
1228
|
+
constructor(suggestion = "Run `okx auth login` to authenticate.") {
|
|
1229
|
+
super("Not logged in.", suggestion);
|
|
1230
|
+
}
|
|
1231
|
+
};
|
|
1204
1232
|
var ValidationError = class extends OkxMcpError {
|
|
1205
1233
|
constructor(message, suggestion) {
|
|
1206
1234
|
super("ValidationError", message, { suggestion });
|
|
@@ -1253,6 +1281,97 @@ function toToolErrorPayload(error, fallbackEndpoint) {
|
|
|
1253
1281
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1254
1282
|
};
|
|
1255
1283
|
}
|
|
1284
|
+
var EXIT_CODES = {
|
|
1285
|
+
SUCCESS: 0,
|
|
1286
|
+
UNAUTHORIZED_CALLER: 1,
|
|
1287
|
+
NOT_LOGGED_IN: 2,
|
|
1288
|
+
REFRESH_FAILED: 3
|
|
1289
|
+
};
|
|
1290
|
+
var EXEC_TIMEOUT_MS2 = 5e3;
|
|
1291
|
+
var AUTH_BIN_DIR = join3(homedir3(), ".okx", "bin");
|
|
1292
|
+
function getAuthBinaryPath() {
|
|
1293
|
+
if (process.env.OKX_AUTH_BIN) {
|
|
1294
|
+
return process.env.OKX_AUTH_BIN;
|
|
1295
|
+
}
|
|
1296
|
+
const ext = process.platform === "win32" ? ".exe" : "";
|
|
1297
|
+
return join3(AUTH_BIN_DIR, `okx-auth${ext}`);
|
|
1298
|
+
}
|
|
1299
|
+
function execAuthToken() {
|
|
1300
|
+
const binPath = getAuthBinaryPath();
|
|
1301
|
+
return new Promise((resolve3, reject) => {
|
|
1302
|
+
const child = spawn(binPath, ["token"], {
|
|
1303
|
+
stdio: ["ignore", "ignore", "inherit", "pipe"]
|
|
1304
|
+
// stdin stdout stderr fd3 (pipe)
|
|
1305
|
+
});
|
|
1306
|
+
const chunks = [];
|
|
1307
|
+
const fd3 = child.stdio[3];
|
|
1308
|
+
fd3.on("data", (chunk) => chunks.push(chunk));
|
|
1309
|
+
child.on("error", (err) => {
|
|
1310
|
+
reject(new ConfigError(
|
|
1311
|
+
`Failed to spawn okx-auth: ${err.message}`,
|
|
1312
|
+
"Ensure the okx-auth binary exists and is executable."
|
|
1313
|
+
));
|
|
1314
|
+
});
|
|
1315
|
+
child.on("close", (code) => {
|
|
1316
|
+
if (code === EXIT_CODES.SUCCESS) {
|
|
1317
|
+
const token = Buffer.concat(chunks).toString("utf-8").trim();
|
|
1318
|
+
if (!token) {
|
|
1319
|
+
reject(new AuthenticationError(
|
|
1320
|
+
"okx-auth returned empty token.",
|
|
1321
|
+
"Run `okx auth login` to re-authenticate."
|
|
1322
|
+
));
|
|
1323
|
+
return;
|
|
1324
|
+
}
|
|
1325
|
+
resolve3(token);
|
|
1326
|
+
return;
|
|
1327
|
+
}
|
|
1328
|
+
if (code === EXIT_CODES.NOT_LOGGED_IN) {
|
|
1329
|
+
reject(new NotLoggedInError());
|
|
1330
|
+
return;
|
|
1331
|
+
}
|
|
1332
|
+
if (code === EXIT_CODES.UNAUTHORIZED_CALLER) {
|
|
1333
|
+
reject(new AuthenticationError(
|
|
1334
|
+
"okx-auth rejected the caller (unauthorized).",
|
|
1335
|
+
"Ensure you are running from a trusted OKX tool."
|
|
1336
|
+
));
|
|
1337
|
+
return;
|
|
1338
|
+
}
|
|
1339
|
+
if (code === EXIT_CODES.REFRESH_FAILED) {
|
|
1340
|
+
reject(new AuthenticationError(
|
|
1341
|
+
"Token refresh failed.",
|
|
1342
|
+
"Run `okx auth login` to re-authenticate."
|
|
1343
|
+
));
|
|
1344
|
+
return;
|
|
1345
|
+
}
|
|
1346
|
+
reject(new AuthenticationError(
|
|
1347
|
+
`okx-auth token exited with code ${code}.`,
|
|
1348
|
+
"Run `okx auth login` to re-authenticate."
|
|
1349
|
+
));
|
|
1350
|
+
});
|
|
1351
|
+
});
|
|
1352
|
+
}
|
|
1353
|
+
function execAuthStatus() {
|
|
1354
|
+
const binPath = getAuthBinaryPath();
|
|
1355
|
+
return new Promise((resolve3) => {
|
|
1356
|
+
execFile2(
|
|
1357
|
+
binPath,
|
|
1358
|
+
["status", "--json"],
|
|
1359
|
+
{ timeout: EXEC_TIMEOUT_MS2, encoding: "utf-8" },
|
|
1360
|
+
(error, stdout) => {
|
|
1361
|
+
if (error) {
|
|
1362
|
+
resolve3(null);
|
|
1363
|
+
return;
|
|
1364
|
+
}
|
|
1365
|
+
try {
|
|
1366
|
+
const result = JSON.parse(stdout);
|
|
1367
|
+
resolve3(result);
|
|
1368
|
+
} catch {
|
|
1369
|
+
resolve3(null);
|
|
1370
|
+
}
|
|
1371
|
+
}
|
|
1372
|
+
);
|
|
1373
|
+
});
|
|
1374
|
+
}
|
|
1256
1375
|
function sleep(ms) {
|
|
1257
1376
|
return new Promise((resolve3) => {
|
|
1258
1377
|
setTimeout(resolve3, ms);
|
|
@@ -1331,10 +1450,10 @@ var OKX_CODE_BEHAVIORS = {
|
|
|
1331
1450
|
"50011": { retry: true, suggestion: "Rate limited. Back off and retry after a delay." },
|
|
1332
1451
|
"50061": { retry: true, suggestion: "Too many connections. Reduce request frequency and retry." },
|
|
1333
1452
|
// Server temporarily unavailable → retryable
|
|
1334
|
-
"50001": { retry: true, suggestion: "
|
|
1335
|
-
"50004": { retry: true, suggestion: "Endpoint
|
|
1453
|
+
"50001": { retry: true, suggestion: "OKX system upgrade in progress. Retry in a few minutes." },
|
|
1454
|
+
"50004": { retry: true, suggestion: "Endpoint temporarily unavailable. Retry later." },
|
|
1336
1455
|
"50013": { retry: true, suggestion: "System busy. Retry after 1-2 seconds." },
|
|
1337
|
-
"50026": { retry: true, suggestion: "
|
|
1456
|
+
"50026": { retry: true, suggestion: "Order book system upgrading. Retry in a few minutes." },
|
|
1338
1457
|
// Region / compliance restriction → do not retry
|
|
1339
1458
|
"51155": { retry: false, suggestion: "Feature unavailable in your region (site: {site}). Verify your site setting matches your account registration region. Available sites: global, eea, us. Do not retry." },
|
|
1340
1459
|
"51734": { retry: false, suggestion: "Feature not supported for your KYC country (site: {site}). Verify your site setting matches your account registration region. Available sites: global, eea, us. Do not retry." },
|
|
@@ -1384,6 +1503,7 @@ function maskKey(key) {
|
|
|
1384
1503
|
if (key.length <= 8) return "***";
|
|
1385
1504
|
return `${key.slice(0, 3)}***${key.slice(-3)}`;
|
|
1386
1505
|
}
|
|
1506
|
+
var TOKEN_CACHE_TTL_MS = 6e4;
|
|
1387
1507
|
function vlog2(message) {
|
|
1388
1508
|
process.stderr.write(`[verbose] ${message}
|
|
1389
1509
|
`);
|
|
@@ -1392,6 +1512,8 @@ var OkxRestClient = class _OkxRestClient {
|
|
|
1392
1512
|
config;
|
|
1393
1513
|
rateLimiter;
|
|
1394
1514
|
dispatcher;
|
|
1515
|
+
cachedAccessToken;
|
|
1516
|
+
cachedAccessTokenAt = 0;
|
|
1395
1517
|
doh;
|
|
1396
1518
|
constructor(config) {
|
|
1397
1519
|
this.config = config;
|
|
@@ -1406,6 +1528,51 @@ var OkxRestClient = class _OkxRestClient {
|
|
|
1406
1528
|
hasCustomProxy: !!config.proxyUrl
|
|
1407
1529
|
});
|
|
1408
1530
|
}
|
|
1531
|
+
/**
|
|
1532
|
+
* Resolve OAuth access token via the okx-auth binary (fd3 pipe).
|
|
1533
|
+
* Caches the token for 60 s to avoid spawning the binary on every
|
|
1534
|
+
* request. The binary handles refresh internally (300s TTL lead),
|
|
1535
|
+
* so periodic re-calls let it serve a fresh token when needed.
|
|
1536
|
+
* Returns null when not logged in.
|
|
1537
|
+
*/
|
|
1538
|
+
async resolveAccessToken() {
|
|
1539
|
+
if (this.cachedAccessToken && Date.now() - this.cachedAccessTokenAt < TOKEN_CACHE_TTL_MS) {
|
|
1540
|
+
return this.cachedAccessToken;
|
|
1541
|
+
}
|
|
1542
|
+
try {
|
|
1543
|
+
const token = await execAuthToken();
|
|
1544
|
+
this.cachedAccessToken = token;
|
|
1545
|
+
this.cachedAccessTokenAt = Date.now();
|
|
1546
|
+
return token;
|
|
1547
|
+
} catch (e) {
|
|
1548
|
+
if (e instanceof NotLoggedInError) {
|
|
1549
|
+
return null;
|
|
1550
|
+
}
|
|
1551
|
+
throw e;
|
|
1552
|
+
}
|
|
1553
|
+
}
|
|
1554
|
+
/**
|
|
1555
|
+
* Dynamic auth — determines auth method per request.
|
|
1556
|
+
*
|
|
1557
|
+
* 1. API key in config → HMAC signing (no OAuth fallback)
|
|
1558
|
+
* 2. OAuth token via okx-auth binary → Bearer token
|
|
1559
|
+
* 3. Neither → throw ConfigError
|
|
1560
|
+
*/
|
|
1561
|
+
async applyAuth(headers, method, requestPath, bodyJson, timestamp) {
|
|
1562
|
+
if (this.config.apiKey && this.config.secretKey && this.config.passphrase) {
|
|
1563
|
+
this.setAuthHeaders(headers, method, requestPath, bodyJson, timestamp);
|
|
1564
|
+
return;
|
|
1565
|
+
}
|
|
1566
|
+
const accessToken = await this.resolveAccessToken();
|
|
1567
|
+
if (accessToken) {
|
|
1568
|
+
headers.set("Authorization", `Bearer ${accessToken}`);
|
|
1569
|
+
return;
|
|
1570
|
+
}
|
|
1571
|
+
throw new ConfigError(
|
|
1572
|
+
"No credentials found.",
|
|
1573
|
+
"Run `okx auth login` to authenticate, or configure API key credentials."
|
|
1574
|
+
);
|
|
1575
|
+
}
|
|
1409
1576
|
/** The canonical base URL for this client (e.g. https://www.okx.com). */
|
|
1410
1577
|
get baseUrl() {
|
|
1411
1578
|
return this.config.baseUrl;
|
|
@@ -1463,18 +1630,6 @@ var OkxRestClient = class _OkxRestClient {
|
|
|
1463
1630
|
});
|
|
1464
1631
|
}
|
|
1465
1632
|
setAuthHeaders(headers, method, requestPath, bodyJson, timestamp) {
|
|
1466
|
-
if (!this.config.hasAuth) {
|
|
1467
|
-
throw new ConfigError(
|
|
1468
|
-
"Private endpoint requires API credentials.",
|
|
1469
|
-
"Configure OKX_API_KEY, OKX_SECRET_KEY and OKX_PASSPHRASE."
|
|
1470
|
-
);
|
|
1471
|
-
}
|
|
1472
|
-
if (!this.config.apiKey || !this.config.secretKey || !this.config.passphrase) {
|
|
1473
|
-
throw new ConfigError(
|
|
1474
|
-
"Invalid private API credentials state.",
|
|
1475
|
-
"Ensure all OKX credentials are set."
|
|
1476
|
-
);
|
|
1477
|
-
}
|
|
1478
1633
|
const payload = `${timestamp}${method.toUpperCase()}${requestPath}${bodyJson}`;
|
|
1479
1634
|
const signature = signOkxPayload(payload, this.config.secretKey);
|
|
1480
1635
|
headers.set("OK-ACCESS-KEY", this.config.apiKey);
|
|
@@ -1589,7 +1744,7 @@ var OkxRestClient = class _OkxRestClient {
|
|
|
1589
1744
|
const conn = this.doh.getConnectionParams();
|
|
1590
1745
|
this.logRequest("POST", `${conn.baseUrl}${path42}`, "private");
|
|
1591
1746
|
const reqConfig = { method: "POST", path: path42, auth: "private" };
|
|
1592
|
-
const headers = this.buildHeaders(reqConfig, path42, bodyJson, getNow());
|
|
1747
|
+
const headers = await this.buildHeaders(reqConfig, path42, bodyJson, getNow());
|
|
1593
1748
|
if (conn.userAgent) {
|
|
1594
1749
|
headers.set("User-Agent", conn.userAgent);
|
|
1595
1750
|
}
|
|
@@ -1706,7 +1861,7 @@ var OkxRestClient = class _OkxRestClient {
|
|
|
1706
1861
|
// Header building
|
|
1707
1862
|
// ---------------------------------------------------------------------------
|
|
1708
1863
|
/** Build HTTP headers. reqConfig.extraHeaders must NOT contain auth keys (OK-ACCESS-*). */
|
|
1709
|
-
buildHeaders(reqConfig, requestPath, bodyJson, timestamp) {
|
|
1864
|
+
async buildHeaders(reqConfig, requestPath, bodyJson, timestamp) {
|
|
1710
1865
|
const headers = new Headers({
|
|
1711
1866
|
"Content-Type": "application/json",
|
|
1712
1867
|
Accept: "application/json"
|
|
@@ -1715,7 +1870,7 @@ var OkxRestClient = class _OkxRestClient {
|
|
|
1715
1870
|
headers.set("User-Agent", this.config.userAgent);
|
|
1716
1871
|
}
|
|
1717
1872
|
if (reqConfig.auth === "private") {
|
|
1718
|
-
this.
|
|
1873
|
+
await this.applyAuth(headers, reqConfig.method, requestPath, bodyJson, timestamp);
|
|
1719
1874
|
}
|
|
1720
1875
|
const useSimulated = reqConfig.simulatedTrading !== void 0 ? reqConfig.simulatedTrading : this.config.demo;
|
|
1721
1876
|
if (useSimulated) {
|
|
@@ -1769,7 +1924,7 @@ var OkxRestClient = class _OkxRestClient {
|
|
|
1769
1924
|
if (reqConfig.rateLimit) {
|
|
1770
1925
|
await this.rateLimiter.consume(reqConfig.rateLimit);
|
|
1771
1926
|
}
|
|
1772
|
-
const headers = this.buildHeaders(reqConfig, requestPath, bodyJson, timestamp);
|
|
1927
|
+
const headers = await this.buildHeaders(reqConfig, requestPath, bodyJson, timestamp);
|
|
1773
1928
|
if (conn.userAgent) {
|
|
1774
1929
|
headers.set("User-Agent", conn.userAgent);
|
|
1775
1930
|
}
|
|
@@ -2192,7 +2347,7 @@ var OKX_SITES = {
|
|
|
2192
2347
|
},
|
|
2193
2348
|
us: {
|
|
2194
2349
|
label: "US",
|
|
2195
|
-
apiBaseUrl: "https://
|
|
2350
|
+
apiBaseUrl: "https://us.okx.com",
|
|
2196
2351
|
webUrl: "https://app.okx.com"
|
|
2197
2352
|
}
|
|
2198
2353
|
};
|
|
@@ -3722,7 +3877,7 @@ function safeWriteFile(targetDir, fileName, data) {
|
|
|
3722
3877
|
throw new Error(`Invalid file name: "${fileName}"`);
|
|
3723
3878
|
}
|
|
3724
3879
|
const resolvedDir = resolve(targetDir);
|
|
3725
|
-
const filePath =
|
|
3880
|
+
const filePath = join4(resolvedDir, safeName);
|
|
3726
3881
|
const resolvedPath = resolve(filePath);
|
|
3727
3882
|
if (!resolvedPath.startsWith(resolvedDir + sep)) {
|
|
3728
3883
|
throw new Error(`Path traversal detected: "${fileName}" resolves outside target directory`);
|
|
@@ -3850,7 +4005,7 @@ async function extractSkillZip(zipPath, targetDir, limits) {
|
|
|
3850
4005
|
});
|
|
3851
4006
|
}
|
|
3852
4007
|
function readMetaJson(contentDir) {
|
|
3853
|
-
const metaPath =
|
|
4008
|
+
const metaPath = join5(contentDir, "_meta.json");
|
|
3854
4009
|
if (!existsSync(metaPath)) {
|
|
3855
4010
|
throw new Error(`_meta.json not found in ${contentDir}. Invalid skill package.`);
|
|
3856
4011
|
}
|
|
@@ -3876,12 +4031,12 @@ function readMetaJson(contentDir) {
|
|
|
3876
4031
|
};
|
|
3877
4032
|
}
|
|
3878
4033
|
function validateSkillMdExists(contentDir) {
|
|
3879
|
-
const skillMdPath =
|
|
4034
|
+
const skillMdPath = join5(contentDir, "SKILL.md");
|
|
3880
4035
|
if (!existsSync(skillMdPath)) {
|
|
3881
4036
|
throw new Error(`SKILL.md not found in ${contentDir}. Invalid skill package.`);
|
|
3882
4037
|
}
|
|
3883
4038
|
}
|
|
3884
|
-
var DEFAULT_REGISTRY_PATH =
|
|
4039
|
+
var DEFAULT_REGISTRY_PATH = join6(homedir4(), ".okx", "skills", "registry.json");
|
|
3885
4040
|
function readRegistry(registryPath = DEFAULT_REGISTRY_PATH) {
|
|
3886
4041
|
if (!existsSync2(registryPath)) {
|
|
3887
4042
|
return { version: 1, skills: {} };
|
|
@@ -5219,7 +5374,7 @@ function registerOnchainEarnTools() {
|
|
|
5219
5374
|
];
|
|
5220
5375
|
}
|
|
5221
5376
|
var DCD_CODE_BEHAVIORS = {
|
|
5222
|
-
"50001": { retry: true, suggestion: "
|
|
5377
|
+
"50001": { retry: true, suggestion: "DCD service is down. Retry in a few minutes." },
|
|
5223
5378
|
"50002": { retry: false, suggestion: "Invalid JSON in request body. This is likely a bug \u2014 check request parameters." },
|
|
5224
5379
|
"50014": { retry: false, suggestion: "Missing required parameter. Check that all required fields are provided." },
|
|
5225
5380
|
"50016": { retry: false, suggestion: "notionalCcy does not match productId option type. Use baseCcy for CALL, quoteCcy for PUT." },
|
|
@@ -7837,40 +7992,38 @@ var NEWS_DETAIL = "/api/v5/orbit/news-detail";
|
|
|
7837
7992
|
var NEWS_DOMAINS = "/api/v5/orbit/news-platform";
|
|
7838
7993
|
var SENTIMENT_QUERY = "/api/v5/orbit/currency-sentiment-query";
|
|
7839
7994
|
var SENTIMENT_RANKING = "/api/v5/orbit/currency-sentiment-ranking";
|
|
7840
|
-
var NEWS_LANGUAGE = ["
|
|
7995
|
+
var NEWS_LANGUAGE = ["en_US", "zh_CN"];
|
|
7841
7996
|
function langHeader(lang) {
|
|
7842
|
-
|
|
7843
|
-
return { "Accept-Language":
|
|
7997
|
+
const resolved = lang === "zh_CN" ? "zh_CN" : "en_US";
|
|
7998
|
+
return { "Accept-Language": resolved };
|
|
7844
7999
|
}
|
|
7845
8000
|
var NEWS_DETAIL_LVL = ["brief", "summary", "full"];
|
|
7846
|
-
var NEWS_IMPORTANCE = ["high", "low"];
|
|
8001
|
+
var NEWS_IMPORTANCE = ["high", "medium", "low"];
|
|
7847
8002
|
var NEWS_SENTIMENT = ["bullish", "bearish", "neutral"];
|
|
7848
8003
|
var NEWS_SORT = ["latest", "relevant"];
|
|
7849
8004
|
var SENTIMENT_PERIOD = ["1h", "4h", "24h"];
|
|
7850
8005
|
var D_COINS_NEWS = 'Comma-separated uppercase ticker symbols (e.g. "BTC,ETH"). Normalize names/aliases to standard tickers.';
|
|
7851
8006
|
var D_COINS_SENTIMENT = 'Comma-separated uppercase ticker symbols, max 20 (e.g. "BTC,ETH"). Normalize names/aliases to standard tickers.';
|
|
7852
|
-
var D_LANGUAGE = "Content language:
|
|
7853
|
-
var D_BEGIN = "Start time, Unix epoch milliseconds.
|
|
8007
|
+
var D_LANGUAGE = "Content language: zh_CN or en_US. Infer from user's message. No server default.";
|
|
8008
|
+
var D_BEGIN = "Start time, Unix epoch milliseconds. Parse relative time if given (e.g. 'yesterday', 'last 7 days').";
|
|
7854
8009
|
var D_END = "End time, Unix epoch milliseconds. Parse relative time if given. Omit for no upper bound.";
|
|
7855
|
-
var D_IMPORTANCE = "Importance filter: high (server default)
|
|
7856
|
-
var D_PLATFORM = "Filter by news source. Use values from news_get_domains (e.g. blockbeats, odaily_flash). Omit for all sources.";
|
|
8010
|
+
var D_IMPORTANCE = "Importance filter: high (server default), medium, low. Omit unless user wants broader coverage.";
|
|
7857
8011
|
var D_LIMIT = "Number of results (default 10, max 50).";
|
|
7858
8012
|
function registerNewsTools() {
|
|
7859
|
-
|
|
8013
|
+
return [
|
|
7860
8014
|
// -----------------------------------------------------------------------
|
|
7861
8015
|
// News browsing tools
|
|
7862
8016
|
// -----------------------------------------------------------------------
|
|
7863
8017
|
{
|
|
7864
8018
|
name: "news_get_latest",
|
|
7865
8019
|
module: "news",
|
|
7866
|
-
description: "Get crypto news sorted by time. Omitting importance still returns only high-importance news (server default). Pass importance='low' explicitly to broaden results. Use when user asks 'what happened recently', 'latest news', 'any big news today', or wants to browse without a keyword. For coin-specific news, use news_get_by_coin instead.",
|
|
8020
|
+
description: "Get crypto news sorted by time. Omitting importance still returns only high-importance news (server default). Pass importance='medium' or 'low' explicitly to broaden results. Use when user asks 'what happened recently', 'latest news', 'any big news today', or wants to browse without a keyword. For coin-specific news, use news_get_by_coin instead.",
|
|
7867
8021
|
isWrite: false,
|
|
7868
8022
|
inputSchema: {
|
|
7869
8023
|
type: "object",
|
|
7870
8024
|
properties: {
|
|
7871
8025
|
coins: { type: "string", description: D_COINS_NEWS + " Optional." },
|
|
7872
8026
|
importance: { type: "string", enum: [...NEWS_IMPORTANCE], description: D_IMPORTANCE },
|
|
7873
|
-
platform: { type: "string", description: D_PLATFORM },
|
|
7874
8027
|
begin: { type: "number", description: D_BEGIN },
|
|
7875
8028
|
end: { type: "number", description: D_END },
|
|
7876
8029
|
language: { type: "string", enum: [...NEWS_LANGUAGE], description: D_LANGUAGE },
|
|
@@ -7891,7 +8044,6 @@ function registerNewsTools() {
|
|
|
7891
8044
|
compactObject({
|
|
7892
8045
|
sortBy: "latest",
|
|
7893
8046
|
importance: readString(args, "importance"),
|
|
7894
|
-
platform: readString(args, "platform"),
|
|
7895
8047
|
ccyList: readString(args, "coins"),
|
|
7896
8048
|
begin: readNumber(args, "begin"),
|
|
7897
8049
|
end: readNumber(args, "end"),
|
|
@@ -7915,7 +8067,6 @@ function registerNewsTools() {
|
|
|
7915
8067
|
properties: {
|
|
7916
8068
|
coins: { type: "string", description: D_COINS_NEWS + " Required." },
|
|
7917
8069
|
importance: { type: "string", enum: [...NEWS_IMPORTANCE], description: D_IMPORTANCE },
|
|
7918
|
-
platform: { type: "string", description: D_PLATFORM },
|
|
7919
8070
|
begin: { type: "number", description: D_BEGIN },
|
|
7920
8071
|
end: { type: "number", description: D_END },
|
|
7921
8072
|
language: { type: "string", enum: [...NEWS_LANGUAGE], description: D_LANGUAGE },
|
|
@@ -7936,7 +8087,6 @@ function registerNewsTools() {
|
|
|
7936
8087
|
sortBy: "latest",
|
|
7937
8088
|
ccyList: coins,
|
|
7938
8089
|
importance: readString(args, "importance"),
|
|
7939
|
-
platform: readString(args, "platform"),
|
|
7940
8090
|
begin: readNumber(args, "begin"),
|
|
7941
8091
|
end: readNumber(args, "end"),
|
|
7942
8092
|
detailLvl: readString(args, "detailLvl"),
|
|
@@ -7962,7 +8112,6 @@ function registerNewsTools() {
|
|
|
7962
8112
|
},
|
|
7963
8113
|
coins: { type: "string", description: D_COINS_NEWS + " Optional." },
|
|
7964
8114
|
importance: { type: "string", enum: [...NEWS_IMPORTANCE], description: D_IMPORTANCE },
|
|
7965
|
-
platform: { type: "string", description: D_PLATFORM },
|
|
7966
8115
|
sentiment: {
|
|
7967
8116
|
type: "string",
|
|
7968
8117
|
enum: [...NEWS_SENTIMENT],
|
|
@@ -7990,7 +8139,6 @@ function registerNewsTools() {
|
|
|
7990
8139
|
keyword: readString(args, "keyword") || void 0,
|
|
7991
8140
|
sortBy: readString(args, "sortBy") ?? "relevant",
|
|
7992
8141
|
importance: readString(args, "importance"),
|
|
7993
|
-
platform: readString(args, "platform"),
|
|
7994
8142
|
ccyList: readString(args, "coins"),
|
|
7995
8143
|
sentiment: readString(args, "sentiment"),
|
|
7996
8144
|
begin: readNumber(args, "begin"),
|
|
@@ -8136,24 +8284,6 @@ function registerNewsTools() {
|
|
|
8136
8284
|
}
|
|
8137
8285
|
}
|
|
8138
8286
|
];
|
|
8139
|
-
const domainsIdx = tools.findIndex((t) => t.name === "news_get_domains");
|
|
8140
|
-
if (domainsIdx === -1) throw new Error("news_get_domains not found in tools list");
|
|
8141
|
-
const [domainsTool] = tools.splice(domainsIdx, 1);
|
|
8142
|
-
return [...tools.map(withNewsDemoGuard), domainsTool];
|
|
8143
|
-
}
|
|
8144
|
-
var NEWS_DEMO_MESSAGE = "News features are not available in demo/simulated trading mode.";
|
|
8145
|
-
var NEWS_DEMO_SUGGESTION = "Switch to a live profile to use News features.";
|
|
8146
|
-
function withNewsDemoGuard(tool) {
|
|
8147
|
-
const originalHandler = tool.handler;
|
|
8148
|
-
return {
|
|
8149
|
-
...tool,
|
|
8150
|
-
handler: async (args, context) => {
|
|
8151
|
-
if (context.config.demo) {
|
|
8152
|
-
throw new ConfigError(NEWS_DEMO_MESSAGE, NEWS_DEMO_SUGGESTION);
|
|
8153
|
-
}
|
|
8154
|
-
return originalHandler(args, context);
|
|
8155
|
-
}
|
|
8156
|
-
};
|
|
8157
8287
|
}
|
|
8158
8288
|
function registerOptionAlgoTools() {
|
|
8159
8289
|
return [
|
|
@@ -9671,7 +9801,7 @@ function createToolRunner(client, config) {
|
|
|
9671
9801
|
};
|
|
9672
9802
|
}
|
|
9673
9803
|
function configFilePath() {
|
|
9674
|
-
return
|
|
9804
|
+
return join7(homedir5(), ".okx", "config.toml");
|
|
9675
9805
|
}
|
|
9676
9806
|
function readFullConfig() {
|
|
9677
9807
|
const path42 = configFilePath();
|
|
@@ -9690,11 +9820,6 @@ Or re-run: okx config init`
|
|
|
9690
9820
|
);
|
|
9691
9821
|
}
|
|
9692
9822
|
}
|
|
9693
|
-
function readTomlProfile(profileName) {
|
|
9694
|
-
const config = readFullConfig();
|
|
9695
|
-
const name = profileName ?? config.default_profile ?? "default";
|
|
9696
|
-
return config.profiles?.[name] ?? {};
|
|
9697
|
-
}
|
|
9698
9823
|
var CONFIG_HEADER = `# OKX Trade Kit Configuration
|
|
9699
9824
|
# If editing manually, wrap values containing special chars in quotes:
|
|
9700
9825
|
# passphrase = 'value' (if value contains # \\ ")
|
|
@@ -9743,18 +9868,24 @@ function parseModuleList(rawModules) {
|
|
|
9743
9868
|
}
|
|
9744
9869
|
return Array.from(deduped);
|
|
9745
9870
|
}
|
|
9746
|
-
function loadCredentials(toml) {
|
|
9871
|
+
async function loadCredentials(toml) {
|
|
9747
9872
|
const apiKey = process.env.OKX_API_KEY?.trim() ?? toml.api_key;
|
|
9748
9873
|
const secretKey = process.env.OKX_SECRET_KEY?.trim() ?? toml.secret_key;
|
|
9749
9874
|
const passphrase = process.env.OKX_PASSPHRASE?.trim() ?? toml.passphrase;
|
|
9750
|
-
const
|
|
9875
|
+
const hasApiKey = Boolean(apiKey && secretKey && passphrase);
|
|
9751
9876
|
const partialAuth = Boolean(apiKey) || Boolean(secretKey) || Boolean(passphrase);
|
|
9752
|
-
if (partialAuth && !
|
|
9877
|
+
if (partialAuth && !hasApiKey) {
|
|
9753
9878
|
throw new ConfigError(
|
|
9754
9879
|
"Partial API credentials detected.",
|
|
9755
9880
|
"Set OKX_API_KEY, OKX_SECRET_KEY and OKX_PASSPHRASE together (env vars or config.toml profile)."
|
|
9756
9881
|
);
|
|
9757
9882
|
}
|
|
9883
|
+
let hasOAuth = false;
|
|
9884
|
+
if (!hasApiKey) {
|
|
9885
|
+
const status = await execAuthStatus();
|
|
9886
|
+
hasOAuth = status?.status === "logged_in";
|
|
9887
|
+
}
|
|
9888
|
+
const hasAuth = hasOAuth || hasApiKey;
|
|
9758
9889
|
return { apiKey, secretKey, passphrase, hasAuth };
|
|
9759
9890
|
}
|
|
9760
9891
|
function resolveSite(cliSite, tomlSite) {
|
|
@@ -9788,9 +9919,11 @@ function resolveDemo(cli, toml) {
|
|
|
9788
9919
|
if (cli.demo === true) return true;
|
|
9789
9920
|
return process.env.OKX_DEMO === "1" || process.env.OKX_DEMO === "true" || (toml.demo ?? false);
|
|
9790
9921
|
}
|
|
9791
|
-
function loadConfig(cli) {
|
|
9792
|
-
const
|
|
9793
|
-
const
|
|
9922
|
+
async function loadConfig(cli) {
|
|
9923
|
+
const config = readFullConfig();
|
|
9924
|
+
const profileName = cli.profile ?? config.default_profile ?? "default";
|
|
9925
|
+
const toml = config.profiles?.[profileName] ?? {};
|
|
9926
|
+
const creds = await loadCredentials(toml);
|
|
9794
9927
|
const demo = resolveDemo(cli, toml);
|
|
9795
9928
|
const site = resolveSite(cli.site, toml.site);
|
|
9796
9929
|
const baseUrl = resolveBaseUrl(site, toml.base_url);
|
|
@@ -9810,6 +9943,7 @@ function loadConfig(cli) {
|
|
|
9810
9943
|
}
|
|
9811
9944
|
return {
|
|
9812
9945
|
...creds,
|
|
9946
|
+
profile: profileName,
|
|
9813
9947
|
baseUrl,
|
|
9814
9948
|
timeoutMs: Math.floor(rawTimeout),
|
|
9815
9949
|
modules: parseModuleList(cli.modules),
|
|
@@ -9822,7 +9956,7 @@ function loadConfig(cli) {
|
|
|
9822
9956
|
verbose: cli.verbose ?? false
|
|
9823
9957
|
};
|
|
9824
9958
|
}
|
|
9825
|
-
var CACHE_FILE =
|
|
9959
|
+
var CACHE_FILE = join8(homedir6(), ".okx", "update-check.json");
|
|
9826
9960
|
var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
9827
9961
|
function readCache2() {
|
|
9828
9962
|
try {
|
|
@@ -9835,7 +9969,7 @@ function readCache2() {
|
|
|
9835
9969
|
}
|
|
9836
9970
|
function writeCache2(cache) {
|
|
9837
9971
|
try {
|
|
9838
|
-
mkdirSync6(
|
|
9972
|
+
mkdirSync6(join8(homedir6(), ".okx"), { recursive: true });
|
|
9839
9973
|
writeFileSync5(CACHE_FILE, JSON.stringify(cache, null, 2), "utf-8");
|
|
9840
9974
|
} catch {
|
|
9841
9975
|
}
|
|
@@ -10005,13 +10139,13 @@ function findMsStoreClaudePath() {
|
|
|
10005
10139
|
}
|
|
10006
10140
|
function getConfigPath(client) {
|
|
10007
10141
|
const home = os3.homedir();
|
|
10008
|
-
const
|
|
10142
|
+
const platform3 = process.platform;
|
|
10009
10143
|
switch (client) {
|
|
10010
10144
|
case "claude-desktop":
|
|
10011
|
-
if (
|
|
10145
|
+
if (platform3 === "win32") {
|
|
10012
10146
|
return findMsStoreClaudePath() ?? path3.join(appData(), "Claude", CLAUDE_CONFIG_FILE);
|
|
10013
10147
|
}
|
|
10014
|
-
if (
|
|
10148
|
+
if (platform3 === "darwin") {
|
|
10015
10149
|
return path3.join(home, "Library", "Application Support", "Claude", CLAUDE_CONFIG_FILE);
|
|
10016
10150
|
}
|
|
10017
10151
|
return path3.join(process.env.XDG_CONFIG_HOME ?? path3.join(home, ".config"), "Claude", CLAUDE_CONFIG_FILE);
|
|
@@ -10132,7 +10266,7 @@ function getPlatformDir() {
|
|
|
10132
10266
|
return map[`${p}-${a}`] ?? null;
|
|
10133
10267
|
}
|
|
10134
10268
|
function getBinaryName() {
|
|
10135
|
-
return platform() === "win32" ? "okx-
|
|
10269
|
+
return platform() === "win32" ? "okx-doh-resolver.exe" : "okx-doh-resolver";
|
|
10136
10270
|
}
|
|
10137
10271
|
function hashFile(filePath) {
|
|
10138
10272
|
const buf = readFileSync7(filePath);
|
|
@@ -10253,7 +10387,7 @@ async function installDohBinary(destPath, sources = CDN_SOURCES, onProgress) {
|
|
|
10253
10387
|
if (earlyResult) return earlyResult;
|
|
10254
10388
|
const platformDir = getPlatformDir();
|
|
10255
10389
|
const binaryName = getBinaryName();
|
|
10256
|
-
const resolvedDest = destPath ??
|
|
10390
|
+
const resolvedDest = destPath ?? join10(homedir8(), ".okx", "bin", binaryName);
|
|
10257
10391
|
const tmpPath = resolvedDest + ".tmp";
|
|
10258
10392
|
mkdirSync8(dirname6(resolvedDest), { recursive: true });
|
|
10259
10393
|
const localHash = existsSync6(resolvedDest) ? hashFile(resolvedDest) : null;
|
|
@@ -10376,16 +10510,276 @@ function downloadText(url, timeoutMs) {
|
|
|
10376
10510
|
})
|
|
10377
10511
|
);
|
|
10378
10512
|
}
|
|
10513
|
+
var AUTH_CDN_PATH_PREFIX = "/upgradeapp/tools/oauth";
|
|
10514
|
+
function getAuthBinaryName() {
|
|
10515
|
+
return platform2() === "win32" ? "okx-auth.exe" : "okx-auth";
|
|
10516
|
+
}
|
|
10517
|
+
function getAuthStatus(binaryPath, opts) {
|
|
10518
|
+
const resolvedPath = binaryPath ?? getAuthBinaryPath();
|
|
10519
|
+
const platformDir = getPlatformDir();
|
|
10520
|
+
if (!existsSync7(resolvedPath)) {
|
|
10521
|
+
return { binaryPath: resolvedPath, exists: false, platform: platformDir };
|
|
10522
|
+
}
|
|
10523
|
+
if (opts?.skipHash) {
|
|
10524
|
+
return { binaryPath: resolvedPath, exists: true, platform: platformDir };
|
|
10525
|
+
}
|
|
10526
|
+
const { size, sha256 } = hashFile(resolvedPath);
|
|
10527
|
+
return { binaryPath: resolvedPath, exists: true, platform: platformDir, fileSize: size, sha256 };
|
|
10528
|
+
}
|
|
10529
|
+
async function fetchAuthCdnChecksum(sources = CDN_SOURCES, timeoutMs = DOWNLOAD_TIMEOUT_MS) {
|
|
10530
|
+
const platformDir = getPlatformDir();
|
|
10531
|
+
if (!platformDir) return null;
|
|
10532
|
+
const checksumPath = `${AUTH_CDN_PATH_PREFIX}/${platformDir}/checksum.json`;
|
|
10533
|
+
for (const { host, protocol } of sources) {
|
|
10534
|
+
try {
|
|
10535
|
+
const url = `${protocol}://${host}${checksumPath}`;
|
|
10536
|
+
const raw = await downloadText2(url, timeoutMs);
|
|
10537
|
+
const data = JSON.parse(raw);
|
|
10538
|
+
if (typeof data.sha256 !== "string" || typeof data.size !== "number" || typeof data.target !== "string") {
|
|
10539
|
+
continue;
|
|
10540
|
+
}
|
|
10541
|
+
return { sha256: data.sha256, size: data.size, target: data.target, source: host };
|
|
10542
|
+
} catch {
|
|
10543
|
+
}
|
|
10544
|
+
}
|
|
10545
|
+
return null;
|
|
10546
|
+
}
|
|
10547
|
+
async function fetchAndValidateChecksum2(host, protocol, checksumPath, platformDir, timeoutMs, onProgress) {
|
|
10548
|
+
const checksumUrl = `${protocol}://${host}${checksumPath}`;
|
|
10549
|
+
onProgress?.(`Fetching checksum from ${host}...`);
|
|
10550
|
+
const raw = await downloadText2(checksumUrl, timeoutMs);
|
|
10551
|
+
const checksum = JSON.parse(raw);
|
|
10552
|
+
if (typeof checksum.sha256 !== "string" || typeof checksum.size !== "number" || typeof checksum.target !== "string") {
|
|
10553
|
+
throw new Error("Invalid checksum.json: missing sha256, size, or target");
|
|
10554
|
+
}
|
|
10555
|
+
if (checksum.target !== platformDir) {
|
|
10556
|
+
throw new Error(`Target mismatch: expected ${platformDir}, got ${checksum.target}`);
|
|
10557
|
+
}
|
|
10558
|
+
return { sha256: checksum.sha256, size: checksum.size, target: checksum.target };
|
|
10559
|
+
}
|
|
10560
|
+
async function downloadAndVerify2(host, protocol, binaryPath, tmpPath, checksum, timeoutMs, onProgress) {
|
|
10561
|
+
const binaryUrl = `${protocol}://${host}${binaryPath}`;
|
|
10562
|
+
onProgress?.(`Downloading binary from ${host}...`);
|
|
10563
|
+
await download2(binaryUrl, tmpPath, timeoutMs);
|
|
10564
|
+
const actual = hashFile(tmpPath);
|
|
10565
|
+
if (actual.size !== checksum.size) {
|
|
10566
|
+
throw new Error(`Size mismatch: expected ${checksum.size}, got ${actual.size}`);
|
|
10567
|
+
}
|
|
10568
|
+
if (actual.sha256 !== checksum.sha256) {
|
|
10569
|
+
throw new Error(`SHA-256 mismatch: expected ${checksum.sha256}, got ${actual.sha256}`);
|
|
10570
|
+
}
|
|
10571
|
+
}
|
|
10572
|
+
function atomicReplace2(tmpPath, resolvedDest) {
|
|
10573
|
+
if (platform2() === "win32") {
|
|
10574
|
+
try {
|
|
10575
|
+
unlinkSync4(resolvedDest);
|
|
10576
|
+
} catch {
|
|
10577
|
+
}
|
|
10578
|
+
}
|
|
10579
|
+
renameSync4(tmpPath, resolvedDest);
|
|
10580
|
+
if (platform2() !== "win32") {
|
|
10581
|
+
chmodSync2(resolvedDest, 493);
|
|
10582
|
+
}
|
|
10583
|
+
}
|
|
10584
|
+
function installPreChecks2(destPath, sources) {
|
|
10585
|
+
if (!destPath && process.env.OKX_AUTH_BIN) {
|
|
10586
|
+
return { status: "up-to-date", source: "(env override)" };
|
|
10587
|
+
}
|
|
10588
|
+
if (!getPlatformDir()) {
|
|
10589
|
+
return { status: "failed", error: "Unsupported platform" };
|
|
10590
|
+
}
|
|
10591
|
+
if (sources.length === 0) {
|
|
10592
|
+
return { status: "failed", error: "No CDN sources available" };
|
|
10593
|
+
}
|
|
10594
|
+
return null;
|
|
10595
|
+
}
|
|
10596
|
+
function isLocalUpToDate2(localHash, checksum) {
|
|
10597
|
+
return localHash !== null && localHash.size === checksum.size && localHash.sha256 === checksum.sha256;
|
|
10598
|
+
}
|
|
10599
|
+
async function installAuthBinary(destPath, sources = CDN_SOURCES, onProgress) {
|
|
10600
|
+
const earlyResult = installPreChecks2(destPath, sources);
|
|
10601
|
+
if (earlyResult) return earlyResult;
|
|
10602
|
+
const platformDir = getPlatformDir();
|
|
10603
|
+
const binaryName = getAuthBinaryName();
|
|
10604
|
+
const resolvedDest = destPath ?? join11(homedir9(), ".okx", "bin", binaryName);
|
|
10605
|
+
const tmpPath = resolvedDest + ".tmp";
|
|
10606
|
+
mkdirSync9(dirname7(resolvedDest), { recursive: true });
|
|
10607
|
+
const localHash = existsSync7(resolvedDest) ? hashFile(resolvedDest) : null;
|
|
10608
|
+
const checksumPath = `${AUTH_CDN_PATH_PREFIX}/${platformDir}/checksum.json`;
|
|
10609
|
+
const binaryPath = `${AUTH_CDN_PATH_PREFIX}/${platformDir}/${binaryName}`;
|
|
10610
|
+
const errors = [];
|
|
10611
|
+
for (const { host, protocol } of sources) {
|
|
10612
|
+
try {
|
|
10613
|
+
const checksum = await fetchAndValidateChecksum2(
|
|
10614
|
+
host,
|
|
10615
|
+
protocol,
|
|
10616
|
+
checksumPath,
|
|
10617
|
+
platformDir,
|
|
10618
|
+
DOWNLOAD_TIMEOUT_MS,
|
|
10619
|
+
onProgress
|
|
10620
|
+
);
|
|
10621
|
+
if (isLocalUpToDate2(localHash, checksum)) {
|
|
10622
|
+
onProgress?.("Already up to date (checksum match)");
|
|
10623
|
+
return { status: "up-to-date", source: host };
|
|
10624
|
+
}
|
|
10625
|
+
await downloadAndVerify2(host, protocol, binaryPath, tmpPath, checksum, DOWNLOAD_TIMEOUT_MS, onProgress);
|
|
10626
|
+
atomicReplace2(tmpPath, resolvedDest);
|
|
10627
|
+
onProgress?.(`Downloaded and verified from ${host}`);
|
|
10628
|
+
return { status: "installed", source: host };
|
|
10629
|
+
} catch (err) {
|
|
10630
|
+
try {
|
|
10631
|
+
unlinkSync4(tmpPath);
|
|
10632
|
+
} catch {
|
|
10633
|
+
}
|
|
10634
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
10635
|
+
errors.push(`${host}: ${msg}`);
|
|
10636
|
+
onProgress?.(`${host} failed: ${msg}`);
|
|
10637
|
+
}
|
|
10638
|
+
}
|
|
10639
|
+
return { status: "failed", error: `All CDN sources failed:
|
|
10640
|
+
${errors.join("\n")}` };
|
|
10641
|
+
}
|
|
10642
|
+
function removeAuthBinary(binaryPath) {
|
|
10643
|
+
const resolvedPath = binaryPath ?? getAuthBinaryPath();
|
|
10644
|
+
try {
|
|
10645
|
+
unlinkSync4(resolvedPath);
|
|
10646
|
+
return { status: "removed" };
|
|
10647
|
+
} catch (err) {
|
|
10648
|
+
if (err.code === "ENOENT") {
|
|
10649
|
+
return { status: "not-found" };
|
|
10650
|
+
}
|
|
10651
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
10652
|
+
throw new Error(`Failed to remove ${resolvedPath}: ${msg}`);
|
|
10653
|
+
}
|
|
10654
|
+
}
|
|
10655
|
+
function isRedirect2(statusCode) {
|
|
10656
|
+
return statusCode !== void 0 && statusCode >= 300 && statusCode < 400;
|
|
10657
|
+
}
|
|
10658
|
+
function validateRedirect2(res, requestUrl, redirectCount, maxRedirects) {
|
|
10659
|
+
if (redirectCount > maxRedirects) {
|
|
10660
|
+
throw new Error(`Too many redirects (${maxRedirects})`);
|
|
10661
|
+
}
|
|
10662
|
+
const location = res.headers.location;
|
|
10663
|
+
if (requestUrl.startsWith("https") && !location.startsWith("https")) {
|
|
10664
|
+
throw new Error("Refused HTTPS \u2192 HTTP redirect downgrade");
|
|
10665
|
+
}
|
|
10666
|
+
return location;
|
|
10667
|
+
}
|
|
10668
|
+
function fetchResponse2(url, timeoutMs) {
|
|
10669
|
+
return new Promise((resolve3, reject) => {
|
|
10670
|
+
let redirects = 0;
|
|
10671
|
+
const maxRedirects = 5;
|
|
10672
|
+
function doRequest(requestUrl) {
|
|
10673
|
+
const reqFn = requestUrl.startsWith("https") ? httpsGet2 : httpGet2;
|
|
10674
|
+
const req = reqFn(requestUrl, { timeout: timeoutMs }, (res) => {
|
|
10675
|
+
if (isRedirect2(res.statusCode) && res.headers.location) {
|
|
10676
|
+
redirects++;
|
|
10677
|
+
try {
|
|
10678
|
+
const location = validateRedirect2(res, requestUrl, redirects, maxRedirects);
|
|
10679
|
+
res.resume();
|
|
10680
|
+
doRequest(location);
|
|
10681
|
+
} catch (err) {
|
|
10682
|
+
reject(err);
|
|
10683
|
+
}
|
|
10684
|
+
return;
|
|
10685
|
+
}
|
|
10686
|
+
if (res.statusCode !== 200) {
|
|
10687
|
+
reject(new Error(`HTTP ${res.statusCode ?? "unknown"}`));
|
|
10688
|
+
return;
|
|
10689
|
+
}
|
|
10690
|
+
resolve3(res);
|
|
10691
|
+
});
|
|
10692
|
+
req.on("error", reject);
|
|
10693
|
+
req.on("timeout", () => {
|
|
10694
|
+
req.destroy();
|
|
10695
|
+
reject(new Error("Download timed out"));
|
|
10696
|
+
});
|
|
10697
|
+
}
|
|
10698
|
+
doRequest(url);
|
|
10699
|
+
});
|
|
10700
|
+
}
|
|
10701
|
+
function download2(url, destPath, timeoutMs) {
|
|
10702
|
+
return fetchResponse2(url, timeoutMs).then(
|
|
10703
|
+
(res) => new Promise((resolve3, reject) => {
|
|
10704
|
+
const file = createWriteStream3(destPath);
|
|
10705
|
+
res.pipe(file);
|
|
10706
|
+
file.on("finish", () => file.close(() => resolve3()));
|
|
10707
|
+
file.on("error", (err) => {
|
|
10708
|
+
try {
|
|
10709
|
+
unlinkSync4(destPath);
|
|
10710
|
+
} catch {
|
|
10711
|
+
}
|
|
10712
|
+
reject(err);
|
|
10713
|
+
});
|
|
10714
|
+
})
|
|
10715
|
+
);
|
|
10716
|
+
}
|
|
10717
|
+
function downloadText2(url, timeoutMs) {
|
|
10718
|
+
return fetchResponse2(url, timeoutMs).then(
|
|
10719
|
+
(res) => new Promise((resolve3, reject) => {
|
|
10720
|
+
const chunks = [];
|
|
10721
|
+
res.on("data", (chunk) => chunks.push(chunk));
|
|
10722
|
+
res.on("end", () => resolve3(Buffer.concat(chunks).toString("utf8")));
|
|
10723
|
+
res.on("error", reject);
|
|
10724
|
+
})
|
|
10725
|
+
);
|
|
10726
|
+
}
|
|
10727
|
+
var CACHE_PATH = join12(homedir10(), ".okx", "auth-binary-check.json");
|
|
10728
|
+
var CHECK_INTERVAL_MS2 = 2 * 60 * 60 * 1e3;
|
|
10729
|
+
function readCache3() {
|
|
10730
|
+
try {
|
|
10731
|
+
if (!existsSync8(CACHE_PATH)) return null;
|
|
10732
|
+
const data = JSON.parse(readFileSync8(CACHE_PATH, "utf-8"));
|
|
10733
|
+
if (typeof data.cdnSha256 !== "string" || typeof data.checkedAt !== "number") return null;
|
|
10734
|
+
return { cdnSha256: data.cdnSha256, checkedAt: data.checkedAt };
|
|
10735
|
+
} catch {
|
|
10736
|
+
return null;
|
|
10737
|
+
}
|
|
10738
|
+
}
|
|
10739
|
+
function writeCache3(cdnSha256) {
|
|
10740
|
+
try {
|
|
10741
|
+
mkdirSync10(join12(homedir10(), ".okx"), { recursive: true });
|
|
10742
|
+
writeFileSync7(CACHE_PATH, JSON.stringify({ cdnSha256, checkedAt: Date.now() }, null, 2), "utf-8");
|
|
10743
|
+
} catch {
|
|
10744
|
+
}
|
|
10745
|
+
}
|
|
10746
|
+
async function ensureAuthBinaryLatest(onProgress) {
|
|
10747
|
+
if (process.env.OKX_AUTH_BIN) return;
|
|
10748
|
+
const cache = readCache3();
|
|
10749
|
+
if (cache && Date.now() - cache.checkedAt < CHECK_INTERVAL_MS2) return;
|
|
10750
|
+
try {
|
|
10751
|
+
const cdn = await fetchAuthCdnChecksum(void 0, 5e3);
|
|
10752
|
+
if (!cdn) return;
|
|
10753
|
+
const local = getAuthStatus();
|
|
10754
|
+
if (local.exists && local.sha256 === cdn.sha256) {
|
|
10755
|
+
writeCache3(cdn.sha256);
|
|
10756
|
+
return;
|
|
10757
|
+
}
|
|
10758
|
+
onProgress?.("Updating okx-auth binary...");
|
|
10759
|
+
const result = await installAuthBinary(void 0, void 0, onProgress);
|
|
10760
|
+
if (result.status === "installed" || result.status === "up-to-date") {
|
|
10761
|
+
const updated = getAuthStatus();
|
|
10762
|
+
if (updated.sha256) writeCache3(updated.sha256);
|
|
10763
|
+
if (result.status === "installed") {
|
|
10764
|
+
onProgress?.("\u2713 okx-auth updated successfully");
|
|
10765
|
+
}
|
|
10766
|
+
}
|
|
10767
|
+
} catch {
|
|
10768
|
+
}
|
|
10769
|
+
}
|
|
10770
|
+
function updateAuthBinaryCache(sha256) {
|
|
10771
|
+
writeCache3(sha256);
|
|
10772
|
+
}
|
|
10773
|
+
function clearAuthBinaryCache() {
|
|
10774
|
+
try {
|
|
10775
|
+
unlinkSync5(CACHE_PATH);
|
|
10776
|
+
} catch {
|
|
10777
|
+
}
|
|
10778
|
+
}
|
|
10379
10779
|
|
|
10380
|
-
// src/commands/
|
|
10381
|
-
import
|
|
10382
|
-
import
|
|
10383
|
-
import os5 from "os";
|
|
10384
|
-
import tls from "tls";
|
|
10385
|
-
|
|
10386
|
-
// src/commands/diagnose-utils.ts
|
|
10387
|
-
import fs4 from "fs";
|
|
10388
|
-
import { createRequire } from "module";
|
|
10780
|
+
// src/commands/auth.ts
|
|
10781
|
+
import { spawn as spawn2 } from "child_process";
|
|
10782
|
+
import readline from "readline";
|
|
10389
10783
|
|
|
10390
10784
|
// src/formatter.ts
|
|
10391
10785
|
import { EOL } from "os";
|
|
@@ -10476,7 +10870,260 @@ function markFailedIfSCodeError(data) {
|
|
|
10476
10870
|
}
|
|
10477
10871
|
}
|
|
10478
10872
|
|
|
10873
|
+
// src/commands/auth.ts
|
|
10874
|
+
function runOkxAuth(args) {
|
|
10875
|
+
const binPath = getAuthBinaryPath();
|
|
10876
|
+
return new Promise((resolve3, reject) => {
|
|
10877
|
+
const child = spawn2(binPath, args, {
|
|
10878
|
+
stdio: "inherit"
|
|
10879
|
+
});
|
|
10880
|
+
child.on("error", (err) => {
|
|
10881
|
+
reject(new Error(`Failed to spawn okx-auth: ${err.message}`));
|
|
10882
|
+
});
|
|
10883
|
+
child.on("close", (code) => {
|
|
10884
|
+
resolve3(code ?? 1);
|
|
10885
|
+
});
|
|
10886
|
+
});
|
|
10887
|
+
}
|
|
10888
|
+
function runOkxAuthCapture(args) {
|
|
10889
|
+
const binPath = getAuthBinaryPath();
|
|
10890
|
+
return new Promise((resolve3, reject) => {
|
|
10891
|
+
const child = spawn2(binPath, args, {
|
|
10892
|
+
stdio: ["inherit", "pipe", "inherit"]
|
|
10893
|
+
});
|
|
10894
|
+
const chunks = [];
|
|
10895
|
+
child.stdout.on("data", (chunk) => chunks.push(chunk));
|
|
10896
|
+
child.on("error", (err) => {
|
|
10897
|
+
reject(new Error(`Failed to spawn okx-auth: ${err.message}`));
|
|
10898
|
+
});
|
|
10899
|
+
child.on("close", (code) => {
|
|
10900
|
+
resolve3({
|
|
10901
|
+
code: code ?? 1,
|
|
10902
|
+
stdout: Buffer.concat(chunks).toString("utf-8")
|
|
10903
|
+
});
|
|
10904
|
+
});
|
|
10905
|
+
});
|
|
10906
|
+
}
|
|
10907
|
+
async function cmdAuthLogin(args) {
|
|
10908
|
+
const cliArgs = ["login"];
|
|
10909
|
+
if (args.site) cliArgs.push("--site", args.site);
|
|
10910
|
+
if (args.manual) cliArgs.push("--manual");
|
|
10911
|
+
const code = await runOkxAuth(cliArgs);
|
|
10912
|
+
if (code !== 0) {
|
|
10913
|
+
process.exitCode = code;
|
|
10914
|
+
}
|
|
10915
|
+
}
|
|
10916
|
+
async function cmdAuthLogout() {
|
|
10917
|
+
const code = await runOkxAuth(["logout"]);
|
|
10918
|
+
if (code !== 0) {
|
|
10919
|
+
process.exitCode = code;
|
|
10920
|
+
}
|
|
10921
|
+
}
|
|
10922
|
+
async function cmdAuthStatus(args) {
|
|
10923
|
+
const cliArgs = ["status"];
|
|
10924
|
+
if (args.json) cliArgs.push("--json");
|
|
10925
|
+
const result = await runOkxAuthCapture(cliArgs);
|
|
10926
|
+
if (result.stdout) {
|
|
10927
|
+
process.stdout.write(result.stdout);
|
|
10928
|
+
}
|
|
10929
|
+
if (result.code !== 0) {
|
|
10930
|
+
process.exitCode = result.code;
|
|
10931
|
+
}
|
|
10932
|
+
}
|
|
10933
|
+
function resolveChecksumMatch(local, cdnChecksum, cdnError) {
|
|
10934
|
+
if (!local.exists) return "not-installed";
|
|
10935
|
+
if (cdnError || !cdnChecksum) return "unavailable";
|
|
10936
|
+
if (cdnChecksum.sha256 === local.sha256) return "match";
|
|
10937
|
+
return "mismatch";
|
|
10938
|
+
}
|
|
10939
|
+
function checksumMatchLabel(match) {
|
|
10940
|
+
if (match === "match") return "\u2713 match";
|
|
10941
|
+
if (match === "mismatch") return "\u2717 mismatch (update available)";
|
|
10942
|
+
return "CDN unreachable";
|
|
10943
|
+
}
|
|
10944
|
+
function formatBytes(bytes) {
|
|
10945
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
10946
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
10947
|
+
return `${(bytes / (1024 * 1024)).toFixed(2)} MB`;
|
|
10948
|
+
}
|
|
10949
|
+
async function cmdAuthInstallStatus(json) {
|
|
10950
|
+
const local = getAuthStatus();
|
|
10951
|
+
let cdnChecksum = null;
|
|
10952
|
+
let cdnError = null;
|
|
10953
|
+
if (local.exists) {
|
|
10954
|
+
try {
|
|
10955
|
+
cdnChecksum = await fetchAuthCdnChecksum(void 0, 5e3);
|
|
10956
|
+
} catch (err) {
|
|
10957
|
+
cdnError = err instanceof Error ? err.message : String(err);
|
|
10958
|
+
}
|
|
10959
|
+
}
|
|
10960
|
+
const checksumMatch = resolveChecksumMatch(local, cdnChecksum, cdnError);
|
|
10961
|
+
if (json) {
|
|
10962
|
+
outputLine(
|
|
10963
|
+
JSON.stringify({
|
|
10964
|
+
binaryPath: local.binaryPath,
|
|
10965
|
+
exists: local.exists,
|
|
10966
|
+
platform: local.platform,
|
|
10967
|
+
fileSize: local.fileSize ?? null,
|
|
10968
|
+
sha256: local.sha256 ?? null,
|
|
10969
|
+
cdnMatch: checksumMatch,
|
|
10970
|
+
cdnSha256: cdnChecksum?.sha256 ?? null,
|
|
10971
|
+
cdnSource: cdnChecksum?.source ?? null
|
|
10972
|
+
})
|
|
10973
|
+
);
|
|
10974
|
+
return;
|
|
10975
|
+
}
|
|
10976
|
+
outputLine("");
|
|
10977
|
+
outputLine(" okx-auth Binary Status");
|
|
10978
|
+
outputLine(" " + "\u2500".repeat(40));
|
|
10979
|
+
outputLine(` Binary path : ${local.binaryPath}`);
|
|
10980
|
+
outputLine(` Installed : ${local.exists ? "yes" : "no"}`);
|
|
10981
|
+
outputLine(` Platform : ${local.platform ?? "(unsupported)"}`);
|
|
10982
|
+
if (local.exists) {
|
|
10983
|
+
outputLine(` File size : ${formatBytes(local.fileSize)}`);
|
|
10984
|
+
outputLine(` SHA-256 : ${local.sha256 ?? "(unknown)"}`);
|
|
10985
|
+
outputLine(` CDN check : ${checksumMatchLabel(checksumMatch)}`);
|
|
10986
|
+
if (cdnChecksum) {
|
|
10987
|
+
outputLine(` CDN source : ${cdnChecksum.source}`);
|
|
10988
|
+
}
|
|
10989
|
+
}
|
|
10990
|
+
outputLine("");
|
|
10991
|
+
}
|
|
10992
|
+
async function cmdAuthInstall(json) {
|
|
10993
|
+
const messages2 = [];
|
|
10994
|
+
const onProgress = (msg) => {
|
|
10995
|
+
if (!json) {
|
|
10996
|
+
outputLine(` ${msg}`);
|
|
10997
|
+
}
|
|
10998
|
+
messages2.push(msg);
|
|
10999
|
+
};
|
|
11000
|
+
if (!json) {
|
|
11001
|
+
outputLine("");
|
|
11002
|
+
outputLine(" Installing okx-auth...");
|
|
11003
|
+
}
|
|
11004
|
+
const result = await installAuthBinary(void 0, void 0, onProgress);
|
|
11005
|
+
if (json) {
|
|
11006
|
+
outputLine(JSON.stringify({ status: result.status, source: result.source ?? null, error: result.error ?? null, messages: messages2 }));
|
|
11007
|
+
if (result.status === "failed") {
|
|
11008
|
+
process.exitCode = 1;
|
|
11009
|
+
}
|
|
11010
|
+
return;
|
|
11011
|
+
}
|
|
11012
|
+
if (result.status === "installed" || result.status === "up-to-date") {
|
|
11013
|
+
const local = getAuthStatus();
|
|
11014
|
+
if (local.sha256) updateAuthBinaryCache(local.sha256);
|
|
11015
|
+
}
|
|
11016
|
+
if (result.status === "installed") {
|
|
11017
|
+
outputLine(` \u2713 okx-auth installed successfully (${result.source ?? ""})`);
|
|
11018
|
+
} else if (result.status === "up-to-date") {
|
|
11019
|
+
outputLine(" \u2713 okx-auth is already up to date");
|
|
11020
|
+
} else {
|
|
11021
|
+
errorLine(` \u2717 Installation failed: ${result.error ?? "unknown error"}`);
|
|
11022
|
+
errorLine(" Hint: check network connectivity or try again later");
|
|
11023
|
+
process.exitCode = 1;
|
|
11024
|
+
}
|
|
11025
|
+
outputLine("");
|
|
11026
|
+
}
|
|
11027
|
+
async function cmdAuthRemove(force, json) {
|
|
11028
|
+
const local = getAuthStatus(void 0, { skipHash: true });
|
|
11029
|
+
if (!local.exists) {
|
|
11030
|
+
if (json) {
|
|
11031
|
+
outputLine(JSON.stringify({ status: "not-installed" }));
|
|
11032
|
+
} else {
|
|
11033
|
+
outputLine(" okx-auth is not installed.");
|
|
11034
|
+
}
|
|
11035
|
+
return;
|
|
11036
|
+
}
|
|
11037
|
+
if (!await confirmRemoval(force, local.binaryPath)) return;
|
|
11038
|
+
let result;
|
|
11039
|
+
try {
|
|
11040
|
+
result = removeAuthBinary();
|
|
11041
|
+
} catch (err) {
|
|
11042
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
11043
|
+
if (json) {
|
|
11044
|
+
outputLine(JSON.stringify({ status: "failed", error: msg }));
|
|
11045
|
+
} else {
|
|
11046
|
+
errorLine(` \u2717 Failed to remove: ${msg}`);
|
|
11047
|
+
}
|
|
11048
|
+
process.exitCode = 1;
|
|
11049
|
+
return;
|
|
11050
|
+
}
|
|
11051
|
+
if (json) {
|
|
11052
|
+
outputLine(JSON.stringify({ status: result.status, path: local.binaryPath }));
|
|
11053
|
+
return;
|
|
11054
|
+
}
|
|
11055
|
+
if (result.status === "removed") {
|
|
11056
|
+
clearAuthBinaryCache();
|
|
11057
|
+
outputLine(` \u2713 Removed: ${local.binaryPath}`);
|
|
11058
|
+
} else {
|
|
11059
|
+
outputLine(" okx-auth is not installed.");
|
|
11060
|
+
}
|
|
11061
|
+
}
|
|
11062
|
+
async function confirmRemoval(force, binaryPath) {
|
|
11063
|
+
if (force) return true;
|
|
11064
|
+
if (!process.stdin.isTTY) {
|
|
11065
|
+
errorLine(" Error: stdin is not a TTY. Use --force to skip confirmation.");
|
|
11066
|
+
process.exitCode = 1;
|
|
11067
|
+
return false;
|
|
11068
|
+
}
|
|
11069
|
+
const confirmed = await askConfirmation(
|
|
11070
|
+
` Remove okx-auth at ${binaryPath}? [y/N] `
|
|
11071
|
+
);
|
|
11072
|
+
if (!confirmed) {
|
|
11073
|
+
outputLine(" Cancelled.");
|
|
11074
|
+
return false;
|
|
11075
|
+
}
|
|
11076
|
+
return true;
|
|
11077
|
+
}
|
|
11078
|
+
function askConfirmation(prompt2) {
|
|
11079
|
+
return new Promise((resolve3) => {
|
|
11080
|
+
const rl = readline.createInterface({
|
|
11081
|
+
input: process.stdin,
|
|
11082
|
+
output: process.stdout
|
|
11083
|
+
});
|
|
11084
|
+
rl.question(prompt2, (answer) => {
|
|
11085
|
+
rl.close();
|
|
11086
|
+
resolve3(answer.trim().toLowerCase() === "y");
|
|
11087
|
+
});
|
|
11088
|
+
});
|
|
11089
|
+
}
|
|
11090
|
+
async function handleAuthCommand(action, _rest, v) {
|
|
11091
|
+
const site = v.site;
|
|
11092
|
+
const json = v.json;
|
|
11093
|
+
const manual = v.manual;
|
|
11094
|
+
const force = v.force;
|
|
11095
|
+
if (["login", "logout", "status"].includes(action)) {
|
|
11096
|
+
await ensureAuthBinaryLatest((msg) => errorLine(` ${msg}`));
|
|
11097
|
+
}
|
|
11098
|
+
switch (action) {
|
|
11099
|
+
case "login":
|
|
11100
|
+
return cmdAuthLogin({ site, manual });
|
|
11101
|
+
case "logout":
|
|
11102
|
+
return cmdAuthLogout();
|
|
11103
|
+
case "status":
|
|
11104
|
+
return cmdAuthStatus({ json });
|
|
11105
|
+
case "install":
|
|
11106
|
+
return cmdAuthInstall(json ?? false);
|
|
11107
|
+
case "install-status":
|
|
11108
|
+
return cmdAuthInstallStatus(json ?? false);
|
|
11109
|
+
case "remove":
|
|
11110
|
+
return cmdAuthRemove(force ?? false, json ?? false);
|
|
11111
|
+
default:
|
|
11112
|
+
errorLine(`Unknown auth command: ${action}`);
|
|
11113
|
+
errorLine("Available: login, logout, status, install, install-status, remove");
|
|
11114
|
+
process.exitCode = 1;
|
|
11115
|
+
}
|
|
11116
|
+
}
|
|
11117
|
+
|
|
11118
|
+
// src/commands/diagnose.ts
|
|
11119
|
+
import dns from "dns/promises";
|
|
11120
|
+
import net from "net";
|
|
11121
|
+
import os5 from "os";
|
|
11122
|
+
import tls from "tls";
|
|
11123
|
+
|
|
10479
11124
|
// src/commands/diagnose-utils.ts
|
|
11125
|
+
import fs4 from "fs";
|
|
11126
|
+
import { createRequire } from "module";
|
|
10480
11127
|
var _require = createRequire(import.meta.url);
|
|
10481
11128
|
function readCliVersion() {
|
|
10482
11129
|
for (const rel of ["../package.json", "../../package.json"]) {
|
|
@@ -10562,7 +11209,7 @@ function sanitize2(value) {
|
|
|
10562
11209
|
import fs5 from "fs";
|
|
10563
11210
|
import path4 from "path";
|
|
10564
11211
|
import os4 from "os";
|
|
10565
|
-
import { spawnSync, spawn } from "child_process";
|
|
11212
|
+
import { spawnSync, spawn as spawn3 } from "child_process";
|
|
10566
11213
|
import { createRequire as createRequire2 } from "module";
|
|
10567
11214
|
import { fileURLToPath } from "url";
|
|
10568
11215
|
var _require2 = createRequire2(import.meta.url);
|
|
@@ -10896,7 +11543,7 @@ async function checkStdioHandshake(entryPath, report) {
|
|
|
10896
11543
|
clearTimeout(timer);
|
|
10897
11544
|
resolve3(passed);
|
|
10898
11545
|
};
|
|
10899
|
-
const child =
|
|
11546
|
+
const child = spawn3(process.execPath, [entryPath], {
|
|
10900
11547
|
stdio: ["pipe", "pipe", "pipe"],
|
|
10901
11548
|
env: { ...process.env }
|
|
10902
11549
|
});
|
|
@@ -11015,7 +11662,7 @@ async function cmdDiagnoseMcp(options = {}) {
|
|
|
11015
11662
|
|
|
11016
11663
|
// src/commands/diagnose.ts
|
|
11017
11664
|
var CLI_VERSION = readCliVersion();
|
|
11018
|
-
var GIT_HASH = true ? "
|
|
11665
|
+
var GIT_HASH = true ? "17e06f4" : "dev";
|
|
11019
11666
|
function maskKey2(key) {
|
|
11020
11667
|
if (!key) return "(not set)";
|
|
11021
11668
|
if (key.length <= 8) return "****";
|
|
@@ -11351,24 +11998,24 @@ async function runCliChecks(config, profile, outputPath) {
|
|
|
11351
11998
|
|
|
11352
11999
|
// src/commands/upgrade.ts
|
|
11353
12000
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
11354
|
-
import { readFileSync as
|
|
11355
|
-
import { dirname as
|
|
11356
|
-
import { homedir as
|
|
12001
|
+
import { readFileSync as readFileSync9, writeFileSync as writeFileSync8, mkdirSync as mkdirSync11 } from "fs";
|
|
12002
|
+
import { dirname as dirname8, join as join13 } from "path";
|
|
12003
|
+
import { homedir as homedir11 } from "os";
|
|
11357
12004
|
var PACKAGES = ["@okx_ai/okx-trade-mcp", "@okx_ai/okx-trade-cli"];
|
|
11358
|
-
var CACHE_FILE2 =
|
|
12005
|
+
var CACHE_FILE2 = join13(homedir11(), ".okx", "last_check");
|
|
11359
12006
|
var THROTTLE_MS = 12 * 60 * 60 * 1e3;
|
|
11360
|
-
var NPM_BIN =
|
|
12007
|
+
var NPM_BIN = join13(dirname8(process.execPath), process.platform === "win32" ? "npm.cmd" : "npm");
|
|
11361
12008
|
function readLastCheck() {
|
|
11362
12009
|
try {
|
|
11363
|
-
return parseInt(
|
|
12010
|
+
return parseInt(readFileSync9(CACHE_FILE2, "utf-8").trim(), 10) || 0;
|
|
11364
12011
|
} catch {
|
|
11365
12012
|
return 0;
|
|
11366
12013
|
}
|
|
11367
12014
|
}
|
|
11368
12015
|
function writeLastCheck() {
|
|
11369
12016
|
try {
|
|
11370
|
-
|
|
11371
|
-
|
|
12017
|
+
mkdirSync11(join13(homedir11(), ".okx"), { recursive: true });
|
|
12018
|
+
writeFileSync8(CACHE_FILE2, String(Math.floor(Date.now() / 1e3)), "utf-8");
|
|
11372
12019
|
} catch {
|
|
11373
12020
|
}
|
|
11374
12021
|
}
|
|
@@ -12339,29 +12986,29 @@ var CLI_REGISTRY = {
|
|
|
12339
12986
|
commands: {
|
|
12340
12987
|
latest: {
|
|
12341
12988
|
toolName: "news_get_latest",
|
|
12342
|
-
usage: "okx news latest [--coins BTC,ETH] [--
|
|
12989
|
+
usage: "okx news latest [--coins BTC,ETH] [--lang zh_CN] [--limit 20]"
|
|
12343
12990
|
},
|
|
12344
12991
|
important: {
|
|
12345
12992
|
toolName: "news_get_latest",
|
|
12346
|
-
usage: "okx news important [--coins BTC,ETH] [--lang
|
|
12993
|
+
usage: "okx news important [--coins BTC,ETH] [--lang zh_CN] [--limit 20]",
|
|
12347
12994
|
description: "Get important/high-impact crypto news"
|
|
12348
12995
|
},
|
|
12349
12996
|
"by-coin": {
|
|
12350
12997
|
toolName: "news_get_by_coin",
|
|
12351
|
-
usage: "okx news by-coin --coins BTC [--importance high] [--
|
|
12998
|
+
usage: "okx news by-coin --coins BTC [--importance high] [--lang zh_CN]"
|
|
12352
12999
|
},
|
|
12353
13000
|
search: {
|
|
12354
13001
|
toolName: "news_search",
|
|
12355
|
-
usage: "okx news search --keyword <term> [--coins BTC] [--sentiment bullish] [--
|
|
13002
|
+
usage: "okx news search --keyword <term> [--coins BTC] [--sentiment bullish] [--lang zh_CN]"
|
|
12356
13003
|
},
|
|
12357
13004
|
detail: {
|
|
12358
13005
|
toolName: "news_get_detail",
|
|
12359
|
-
usage: "okx news detail <id> [--lang
|
|
13006
|
+
usage: "okx news detail <id> [--lang zh_CN]"
|
|
12360
13007
|
},
|
|
12361
|
-
|
|
13008
|
+
domains: {
|
|
12362
13009
|
toolName: "news_get_domains",
|
|
12363
|
-
usage: "okx news
|
|
12364
|
-
description: "List available news
|
|
13010
|
+
usage: "okx news domains",
|
|
13011
|
+
description: "List available news source domains"
|
|
12365
13012
|
},
|
|
12366
13013
|
"coin-sentiment": {
|
|
12367
13014
|
toolName: "news_get_coin_sentiment",
|
|
@@ -12374,7 +13021,7 @@ var CLI_REGISTRY = {
|
|
|
12374
13021
|
},
|
|
12375
13022
|
"by-sentiment": {
|
|
12376
13023
|
toolName: "news_search",
|
|
12377
|
-
usage: "okx news by-sentiment --sentiment bullish [--coins BTC] [--
|
|
13024
|
+
usage: "okx news by-sentiment --sentiment bullish [--coins BTC] [--sort-by latest]",
|
|
12378
13025
|
description: "Browse news filtered by sentiment direction"
|
|
12379
13026
|
},
|
|
12380
13027
|
"sentiment-rank": {
|
|
@@ -12582,7 +13229,6 @@ async function cmdNewsLatest(run, opts) {
|
|
|
12582
13229
|
const result = await run("news_get_latest", {
|
|
12583
13230
|
coins: opts.coins,
|
|
12584
13231
|
importance: opts.importance,
|
|
12585
|
-
platform: opts.platform,
|
|
12586
13232
|
begin: opts.begin,
|
|
12587
13233
|
end: opts.end,
|
|
12588
13234
|
language: opts.language,
|
|
@@ -12609,7 +13255,6 @@ async function cmdNewsImportant(run, opts) {
|
|
|
12609
13255
|
const result = await run("news_get_latest", {
|
|
12610
13256
|
coins: opts.coins,
|
|
12611
13257
|
importance: "high",
|
|
12612
|
-
platform: opts.platform,
|
|
12613
13258
|
begin: opts.begin,
|
|
12614
13259
|
end: opts.end,
|
|
12615
13260
|
language: opts.language,
|
|
@@ -12634,7 +13279,6 @@ async function cmdNewsByCoin(run, coins, opts) {
|
|
|
12634
13279
|
const result = await run("news_get_by_coin", {
|
|
12635
13280
|
coins,
|
|
12636
13281
|
importance: opts.importance,
|
|
12637
|
-
platform: opts.platform,
|
|
12638
13282
|
begin: opts.begin,
|
|
12639
13283
|
end: opts.end,
|
|
12640
13284
|
language: opts.language,
|
|
@@ -12660,7 +13304,6 @@ async function cmdNewsSearch(run, keyword, opts) {
|
|
|
12660
13304
|
keyword: keyword || void 0,
|
|
12661
13305
|
coins: opts.coins,
|
|
12662
13306
|
importance: opts.importance,
|
|
12663
|
-
platform: opts.platform,
|
|
12664
13307
|
sentiment: opts.sentiment,
|
|
12665
13308
|
sortBy: opts.sortBy,
|
|
12666
13309
|
begin: opts.begin,
|
|
@@ -12713,12 +13356,12 @@ async function cmdNewsDetail(run, id, opts) {
|
|
|
12713
13356
|
content
|
|
12714
13357
|
});
|
|
12715
13358
|
}
|
|
12716
|
-
async function
|
|
13359
|
+
async function cmdNewsDomains(run, opts) {
|
|
12717
13360
|
const result = await run("news_get_domains", {});
|
|
12718
13361
|
const raw = getData(result);
|
|
12719
13362
|
const items = raw?.[0]?.["platform"] ?? [];
|
|
12720
13363
|
if (opts.json) return printJson(items);
|
|
12721
|
-
outputLine("Available news
|
|
13364
|
+
outputLine("Available news source domains:");
|
|
12722
13365
|
items.forEach((d) => outputLine(` ${d}`));
|
|
12723
13366
|
}
|
|
12724
13367
|
async function cmdNewsCoinSentiment(run, coins, opts) {
|
|
@@ -12792,7 +13435,7 @@ async function cmdNewsSentimentRank(run, opts) {
|
|
|
12792
13435
|
}
|
|
12793
13436
|
|
|
12794
13437
|
// src/config/loader.ts
|
|
12795
|
-
function loadProfileConfig(opts) {
|
|
13438
|
+
async function loadProfileConfig(opts) {
|
|
12796
13439
|
return loadConfig({
|
|
12797
13440
|
profile: opts.profile,
|
|
12798
13441
|
modules: opts.modules,
|
|
@@ -13100,7 +13743,6 @@ var CLI_OPTIONS = {
|
|
|
13100
13743
|
coins: { type: "string" },
|
|
13101
13744
|
sentiment: { type: "string" },
|
|
13102
13745
|
importance: { type: "string" },
|
|
13103
|
-
platform: { type: "string" },
|
|
13104
13746
|
keyword: { type: "string" },
|
|
13105
13747
|
"detail-lvl": { type: "string" },
|
|
13106
13748
|
period: { type: "string" },
|
|
@@ -13111,6 +13753,9 @@ var CLI_OPTIONS = {
|
|
|
13111
13753
|
dir: { type: "string" },
|
|
13112
13754
|
page: { type: "string" },
|
|
13113
13755
|
format: { type: "string" },
|
|
13756
|
+
// auth
|
|
13757
|
+
site: { type: "string" },
|
|
13758
|
+
manual: { type: "boolean", default: false },
|
|
13114
13759
|
// event contract
|
|
13115
13760
|
underlying: { type: "string" },
|
|
13116
13761
|
seriesId: { type: "string" },
|
|
@@ -16125,14 +16770,14 @@ async function cmdDcdQuoteAndBuy(run, opts) {
|
|
|
16125
16770
|
}
|
|
16126
16771
|
|
|
16127
16772
|
// src/commands/skill.ts
|
|
16128
|
-
import { tmpdir, homedir as
|
|
16129
|
-
import { join as
|
|
16130
|
-
import { mkdirSync as
|
|
16773
|
+
import { tmpdir, homedir as homedir13 } from "os";
|
|
16774
|
+
import { join as join15, dirname as dirname9 } from "path";
|
|
16775
|
+
import { mkdirSync as mkdirSync12, rmSync, existsSync as existsSync10, copyFileSync as copyFileSync2 } from "fs";
|
|
16131
16776
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
16132
16777
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
16133
16778
|
function resolveNpx() {
|
|
16134
|
-
const sibling =
|
|
16135
|
-
if (
|
|
16779
|
+
const sibling = join15(dirname9(process.execPath), "npx");
|
|
16780
|
+
if (existsSync10(sibling)) return sibling;
|
|
16136
16781
|
return "npx";
|
|
16137
16782
|
}
|
|
16138
16783
|
var THIRD_PARTY_INSTALL_NOTICE = "Note: This skill was created by a third-party developer, not by OKX. Review SKILL.md before use.";
|
|
@@ -16185,13 +16830,13 @@ async function cmdSkillCategories(run, json) {
|
|
|
16185
16830
|
outputLine("");
|
|
16186
16831
|
}
|
|
16187
16832
|
async function cmdSkillAdd(name, config, json) {
|
|
16188
|
-
const tmpBase =
|
|
16189
|
-
|
|
16833
|
+
const tmpBase = join15(tmpdir(), `okx-skill-${randomUUID2()}`);
|
|
16834
|
+
mkdirSync12(tmpBase, { recursive: true });
|
|
16190
16835
|
try {
|
|
16191
16836
|
outputLine(`Downloading ${name}...`);
|
|
16192
16837
|
const client = new OkxRestClient(config);
|
|
16193
16838
|
const zipPath = await downloadSkillZip(client, name, tmpBase);
|
|
16194
|
-
const contentDir = await extractSkillZip(zipPath,
|
|
16839
|
+
const contentDir = await extractSkillZip(zipPath, join15(tmpBase, "content"));
|
|
16195
16840
|
const meta = readMetaJson(contentDir);
|
|
16196
16841
|
validateSkillMdExists(contentDir);
|
|
16197
16842
|
outputLine("Installing to detected agents...");
|
|
@@ -16201,7 +16846,7 @@ async function cmdSkillAdd(name, config, json) {
|
|
|
16201
16846
|
timeout: 6e4
|
|
16202
16847
|
});
|
|
16203
16848
|
} catch (e) {
|
|
16204
|
-
const savedZip =
|
|
16849
|
+
const savedZip = join15(process.cwd(), `${name}.zip`);
|
|
16205
16850
|
try {
|
|
16206
16851
|
copyFileSync2(zipPath, savedZip);
|
|
16207
16852
|
} catch {
|
|
@@ -16240,7 +16885,7 @@ function cmdSkillRemove(name, json) {
|
|
|
16240
16885
|
timeout: 6e4
|
|
16241
16886
|
});
|
|
16242
16887
|
} catch {
|
|
16243
|
-
const agentsPath =
|
|
16888
|
+
const agentsPath = join15(homedir13(), ".agents", "skills", name);
|
|
16244
16889
|
try {
|
|
16245
16890
|
rmSync(agentsPath, { recursive: true, force: true });
|
|
16246
16891
|
} catch {
|
|
@@ -16314,14 +16959,14 @@ function printSkillInstallResult(meta, json) {
|
|
|
16314
16959
|
}
|
|
16315
16960
|
|
|
16316
16961
|
// src/commands/doh.ts
|
|
16317
|
-
import
|
|
16318
|
-
function
|
|
16962
|
+
import readline2 from "readline";
|
|
16963
|
+
function resolveChecksumMatch2(local, cdnChecksum, cdnError) {
|
|
16319
16964
|
if (!local.exists) return "not-installed";
|
|
16320
16965
|
if (cdnError || !cdnChecksum) return "unavailable";
|
|
16321
16966
|
if (cdnChecksum.sha256 === local.sha256) return "match";
|
|
16322
16967
|
return "mismatch";
|
|
16323
16968
|
}
|
|
16324
|
-
function
|
|
16969
|
+
function checksumMatchLabel2(match) {
|
|
16325
16970
|
if (match === "match") return "\u2713 match";
|
|
16326
16971
|
if (match === "mismatch") return "\u2717 mismatch (update available)";
|
|
16327
16972
|
return "CDN unreachable";
|
|
@@ -16334,9 +16979,9 @@ function formatStatusText(local, checksumMatch, cdnChecksum, runtimeMode) {
|
|
|
16334
16979
|
outputLine(` Installed : ${local.exists ? "yes" : "no"}`);
|
|
16335
16980
|
outputLine(` Platform : ${local.platform ?? "(unsupported)"}`);
|
|
16336
16981
|
if (local.exists) {
|
|
16337
|
-
outputLine(` File size : ${
|
|
16982
|
+
outputLine(` File size : ${formatBytes2(local.fileSize)}`);
|
|
16338
16983
|
outputLine(` SHA-256 : ${local.sha256 ?? "(unknown)"}`);
|
|
16339
|
-
outputLine(` CDN check : ${
|
|
16984
|
+
outputLine(` CDN check : ${checksumMatchLabel2(checksumMatch)}`);
|
|
16340
16985
|
if (cdnChecksum) {
|
|
16341
16986
|
outputLine(` CDN source : ${cdnChecksum.source}`);
|
|
16342
16987
|
}
|
|
@@ -16363,7 +17008,7 @@ async function cmdDohStatus(json, binaryPath) {
|
|
|
16363
17008
|
cdnError = err instanceof Error ? err.message : String(err);
|
|
16364
17009
|
}
|
|
16365
17010
|
}
|
|
16366
|
-
const checksumMatch =
|
|
17011
|
+
const checksumMatch = resolveChecksumMatch2(local, cdnChecksum, cdnError);
|
|
16367
17012
|
if (json) {
|
|
16368
17013
|
outputLine(
|
|
16369
17014
|
JSON.stringify({
|
|
@@ -16429,7 +17074,7 @@ async function cmdDohRemove(force, json, binaryPath) {
|
|
|
16429
17074
|
process.exitCode = 1;
|
|
16430
17075
|
return;
|
|
16431
17076
|
}
|
|
16432
|
-
const confirmed = await
|
|
17077
|
+
const confirmed = await askConfirmation2(
|
|
16433
17078
|
` Remove DoH resolver at ${local.binaryPath}? [y/N] `
|
|
16434
17079
|
);
|
|
16435
17080
|
if (!confirmed) {
|
|
@@ -16448,14 +17093,14 @@ async function cmdDohRemove(force, json, binaryPath) {
|
|
|
16448
17093
|
outputLine(" DoH resolver is not installed.");
|
|
16449
17094
|
}
|
|
16450
17095
|
}
|
|
16451
|
-
function
|
|
17096
|
+
function formatBytes2(bytes) {
|
|
16452
17097
|
if (bytes < 1024) return `${bytes} B`;
|
|
16453
17098
|
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
16454
17099
|
return `${(bytes / (1024 * 1024)).toFixed(2)} MB`;
|
|
16455
17100
|
}
|
|
16456
|
-
function
|
|
17101
|
+
function askConfirmation2(prompt2) {
|
|
16457
17102
|
return new Promise((resolve3) => {
|
|
16458
|
-
const rl =
|
|
17103
|
+
const rl = readline2.createInterface({
|
|
16459
17104
|
input: process.stdin,
|
|
16460
17105
|
output: process.stdout
|
|
16461
17106
|
});
|
|
@@ -16912,7 +17557,7 @@ async function cmdEventCancel(run, opts) {
|
|
|
16912
17557
|
// src/index.ts
|
|
16913
17558
|
var _require3 = createRequire3(import.meta.url);
|
|
16914
17559
|
var CLI_VERSION2 = _require3("../package.json").version;
|
|
16915
|
-
var GIT_HASH2 = true ? "
|
|
17560
|
+
var GIT_HASH2 = true ? "17e06f4" : "dev";
|
|
16916
17561
|
function handleDohCommand(action, json, force, binaryPath) {
|
|
16917
17562
|
if (action === "status") return cmdDohStatus(json, binaryPath);
|
|
16918
17563
|
if (action === "install") return cmdDohInstall(json, binaryPath);
|
|
@@ -17756,22 +18401,21 @@ function handleNewsCommand(run, action, rest, v, json) {
|
|
|
17756
18401
|
const limit = v.limit !== void 0 ? Number(v.limit) : void 0;
|
|
17757
18402
|
const begin = v.begin !== void 0 ? Number(v.begin) : void 0;
|
|
17758
18403
|
const end = v.end !== void 0 ? Number(v.end) : void 0;
|
|
17759
|
-
const language = v.lang ?? "
|
|
18404
|
+
const language = v.lang ?? "en_US";
|
|
17760
18405
|
const detailLvl = v["detail-lvl"];
|
|
17761
18406
|
const after = v.after;
|
|
17762
18407
|
const period = v.period;
|
|
17763
18408
|
const points = v.points !== void 0 ? Number(v.points) : 24;
|
|
17764
18409
|
const sortBy = v["sort-by"];
|
|
17765
|
-
const
|
|
17766
|
-
const
|
|
17767
|
-
const listOpts = { coins: v.coins, importance: v.importance, platform: platform2, begin, end, language, detailLvl, limit, after, json };
|
|
18410
|
+
const searchOpts = { coins: v.coins, importance: v.importance, sentiment: v.sentiment, sortBy, begin, end, language, detailLvl, limit, after, json };
|
|
18411
|
+
const listOpts = { coins: v.coins, importance: v.importance, begin, end, language, detailLvl, limit, after, json };
|
|
17768
18412
|
const dispatch = {
|
|
17769
18413
|
latest: () => cmdNewsLatest(run, listOpts),
|
|
17770
|
-
important: () => cmdNewsImportant(run, { coins: v.coins,
|
|
17771
|
-
"by-coin": () => cmdNewsByCoin(run, v.coins ?? rest[0], { importance: v.importance,
|
|
18414
|
+
important: () => cmdNewsImportant(run, { coins: v.coins, begin, end, language, detailLvl, limit, json }),
|
|
18415
|
+
"by-coin": () => cmdNewsByCoin(run, v.coins ?? rest[0], { importance: v.importance, begin, end, language, detailLvl, limit, json }),
|
|
17772
18416
|
search: () => cmdNewsSearch(run, v.keyword ?? rest[0], searchOpts),
|
|
17773
18417
|
detail: () => cmdNewsDetail(run, rest[0], { language, json }),
|
|
17774
|
-
|
|
18418
|
+
domains: () => cmdNewsDomains(run, { json }),
|
|
17775
18419
|
"coin-sentiment": () => cmdNewsCoinSentiment(run, v.coins ?? rest[0], { period, json }),
|
|
17776
18420
|
"coin-trend": () => cmdNewsCoinTrend(run, v.coins ?? rest[0], { period, points, json }),
|
|
17777
18421
|
// by-sentiment is a convenience wrapper over news_search (no keyword, sentiment filter only)
|
|
@@ -17888,7 +18532,7 @@ function wrapRunnerWithLogger(baseRunner, logger, verbose = false) {
|
|
|
17888
18532
|
async function runDiagnose(v) {
|
|
17889
18533
|
let config;
|
|
17890
18534
|
try {
|
|
17891
|
-
config = loadProfileConfig({ profile: v.profile, demo: v.demo, live: v.live, verbose: v.verbose, userAgent: `okx-trade-cli/${CLI_VERSION2}`, sourceTag: "CLI" });
|
|
18535
|
+
config = await loadProfileConfig({ profile: v.profile, demo: v.demo, live: v.live, verbose: v.verbose, userAgent: `okx-trade-cli/${CLI_VERSION2}`, sourceTag: "CLI" });
|
|
17892
18536
|
} catch {
|
|
17893
18537
|
}
|
|
17894
18538
|
return cmdDiagnose(config, v.profile ?? "default", { mcp: v.mcp, cli: v.cli, all: v.all, output: v.output });
|
|
@@ -17903,24 +18547,13 @@ function printVersion() {
|
|
|
17903
18547
|
}
|
|
17904
18548
|
}
|
|
17905
18549
|
function routeManagementCommand(module, action, rest, json, v) {
|
|
17906
|
-
if (module === "config")
|
|
17907
|
-
|
|
17908
|
-
|
|
17909
|
-
}
|
|
17910
|
-
if (module === "setup") {
|
|
17911
|
-
handleSetupCommand(v);
|
|
17912
|
-
return true;
|
|
17913
|
-
}
|
|
18550
|
+
if (module === "config") return handleConfigCommand(action, rest, json, v.lang, v.force);
|
|
18551
|
+
if (module === "setup") return handleSetupCommand(v);
|
|
18552
|
+
if (module === "auth") return handleAuthCommand(action, rest, v);
|
|
17914
18553
|
if (module === "upgrade") return cmdUpgrade(CLI_VERSION2, { beta: v.beta, check: v.check, force: v.force }, json);
|
|
17915
|
-
if (module === "doh")
|
|
17916
|
-
const r = handleDohCommand(action, json, v.force ?? false);
|
|
17917
|
-
return r ?? true;
|
|
17918
|
-
}
|
|
18554
|
+
if (module === "doh") return handleDohCommand(action, json, v.force ?? false);
|
|
17919
18555
|
if (module === "diagnose") return runDiagnose(v);
|
|
17920
|
-
if (module === "list-tools")
|
|
17921
|
-
cmdListTools(json);
|
|
17922
|
-
return true;
|
|
17923
|
-
}
|
|
18556
|
+
if (module === "list-tools") return cmdListTools(json);
|
|
17924
18557
|
return void 0;
|
|
17925
18558
|
}
|
|
17926
18559
|
async function main() {
|
|
@@ -17942,8 +18575,8 @@ async function main() {
|
|
|
17942
18575
|
const v = values;
|
|
17943
18576
|
const json = v.json ?? false;
|
|
17944
18577
|
const mgmt = routeManagementCommand(module, action, rest, json, v);
|
|
17945
|
-
if (mgmt
|
|
17946
|
-
const config = loadProfileConfig({ profile: v.profile, demo: v.demo, live: v.live, verbose: v.verbose, userAgent: `okx-trade-cli/${CLI_VERSION2}`, sourceTag: "CLI" });
|
|
18578
|
+
if (mgmt) return mgmt;
|
|
18579
|
+
const config = await loadProfileConfig({ profile: v.profile, demo: v.demo, live: v.live, verbose: v.verbose, userAgent: `okx-trade-cli/${CLI_VERSION2}`, sourceTag: "CLI" });
|
|
17947
18580
|
setEnvContext({ demo: config.demo, profile: v.profile ?? "default" });
|
|
17948
18581
|
setJsonEnvEnabled(v.env ?? false);
|
|
17949
18582
|
const client = new OkxRestClient(config);
|