@okx_ai/okx-trade-cli 1.3.1-beta.12 → 1.3.1-beta.14
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 +841 -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);
|
|
@@ -10127,12 +10261,13 @@ function getPlatformDir() {
|
|
|
10127
10261
|
"darwin-arm64": "darwin-arm64",
|
|
10128
10262
|
"darwin-x64": "darwin-x64",
|
|
10129
10263
|
"linux-x64": "linux-x64",
|
|
10264
|
+
"linux-arm64": "linux-x64",
|
|
10130
10265
|
"win32-x64": "win32-x64"
|
|
10131
10266
|
};
|
|
10132
10267
|
return map[`${p}-${a}`] ?? null;
|
|
10133
10268
|
}
|
|
10134
10269
|
function getBinaryName() {
|
|
10135
|
-
return platform() === "win32" ? "okx-
|
|
10270
|
+
return platform() === "win32" ? "okx-doh-resolver.exe" : "okx-doh-resolver";
|
|
10136
10271
|
}
|
|
10137
10272
|
function hashFile(filePath) {
|
|
10138
10273
|
const buf = readFileSync7(filePath);
|
|
@@ -10253,7 +10388,7 @@ async function installDohBinary(destPath, sources = CDN_SOURCES, onProgress) {
|
|
|
10253
10388
|
if (earlyResult) return earlyResult;
|
|
10254
10389
|
const platformDir = getPlatformDir();
|
|
10255
10390
|
const binaryName = getBinaryName();
|
|
10256
|
-
const resolvedDest = destPath ??
|
|
10391
|
+
const resolvedDest = destPath ?? join10(homedir8(), ".okx", "bin", binaryName);
|
|
10257
10392
|
const tmpPath = resolvedDest + ".tmp";
|
|
10258
10393
|
mkdirSync8(dirname6(resolvedDest), { recursive: true });
|
|
10259
10394
|
const localHash = existsSync6(resolvedDest) ? hashFile(resolvedDest) : null;
|
|
@@ -10376,16 +10511,276 @@ function downloadText(url, timeoutMs) {
|
|
|
10376
10511
|
})
|
|
10377
10512
|
);
|
|
10378
10513
|
}
|
|
10514
|
+
var AUTH_CDN_PATH_PREFIX = "/upgradeapp/tools/oauth";
|
|
10515
|
+
function getAuthBinaryName() {
|
|
10516
|
+
return platform2() === "win32" ? "okx-auth.exe" : "okx-auth";
|
|
10517
|
+
}
|
|
10518
|
+
function getAuthStatus(binaryPath, opts) {
|
|
10519
|
+
const resolvedPath = binaryPath ?? getAuthBinaryPath();
|
|
10520
|
+
const platformDir = getPlatformDir();
|
|
10521
|
+
if (!existsSync7(resolvedPath)) {
|
|
10522
|
+
return { binaryPath: resolvedPath, exists: false, platform: platformDir };
|
|
10523
|
+
}
|
|
10524
|
+
if (opts?.skipHash) {
|
|
10525
|
+
return { binaryPath: resolvedPath, exists: true, platform: platformDir };
|
|
10526
|
+
}
|
|
10527
|
+
const { size, sha256 } = hashFile(resolvedPath);
|
|
10528
|
+
return { binaryPath: resolvedPath, exists: true, platform: platformDir, fileSize: size, sha256 };
|
|
10529
|
+
}
|
|
10530
|
+
async function fetchAuthCdnChecksum(sources = CDN_SOURCES, timeoutMs = DOWNLOAD_TIMEOUT_MS) {
|
|
10531
|
+
const platformDir = getPlatformDir();
|
|
10532
|
+
if (!platformDir) return null;
|
|
10533
|
+
const checksumPath = `${AUTH_CDN_PATH_PREFIX}/${platformDir}/checksum.json`;
|
|
10534
|
+
for (const { host, protocol } of sources) {
|
|
10535
|
+
try {
|
|
10536
|
+
const url = `${protocol}://${host}${checksumPath}`;
|
|
10537
|
+
const raw = await downloadText2(url, timeoutMs);
|
|
10538
|
+
const data = JSON.parse(raw);
|
|
10539
|
+
if (typeof data.sha256 !== "string" || typeof data.size !== "number" || typeof data.target !== "string") {
|
|
10540
|
+
continue;
|
|
10541
|
+
}
|
|
10542
|
+
return { sha256: data.sha256, size: data.size, target: data.target, source: host };
|
|
10543
|
+
} catch {
|
|
10544
|
+
}
|
|
10545
|
+
}
|
|
10546
|
+
return null;
|
|
10547
|
+
}
|
|
10548
|
+
async function fetchAndValidateChecksum2(host, protocol, checksumPath, platformDir, timeoutMs, onProgress) {
|
|
10549
|
+
const checksumUrl = `${protocol}://${host}${checksumPath}`;
|
|
10550
|
+
onProgress?.(`Fetching checksum from ${host}...`);
|
|
10551
|
+
const raw = await downloadText2(checksumUrl, timeoutMs);
|
|
10552
|
+
const checksum = JSON.parse(raw);
|
|
10553
|
+
if (typeof checksum.sha256 !== "string" || typeof checksum.size !== "number" || typeof checksum.target !== "string") {
|
|
10554
|
+
throw new Error("Invalid checksum.json: missing sha256, size, or target");
|
|
10555
|
+
}
|
|
10556
|
+
if (checksum.target !== platformDir) {
|
|
10557
|
+
throw new Error(`Target mismatch: expected ${platformDir}, got ${checksum.target}`);
|
|
10558
|
+
}
|
|
10559
|
+
return { sha256: checksum.sha256, size: checksum.size, target: checksum.target };
|
|
10560
|
+
}
|
|
10561
|
+
async function downloadAndVerify2(host, protocol, binaryPath, tmpPath, checksum, timeoutMs, onProgress) {
|
|
10562
|
+
const binaryUrl = `${protocol}://${host}${binaryPath}`;
|
|
10563
|
+
onProgress?.(`Downloading binary from ${host}...`);
|
|
10564
|
+
await download2(binaryUrl, tmpPath, timeoutMs);
|
|
10565
|
+
const actual = hashFile(tmpPath);
|
|
10566
|
+
if (actual.size !== checksum.size) {
|
|
10567
|
+
throw new Error(`Size mismatch: expected ${checksum.size}, got ${actual.size}`);
|
|
10568
|
+
}
|
|
10569
|
+
if (actual.sha256 !== checksum.sha256) {
|
|
10570
|
+
throw new Error(`SHA-256 mismatch: expected ${checksum.sha256}, got ${actual.sha256}`);
|
|
10571
|
+
}
|
|
10572
|
+
}
|
|
10573
|
+
function atomicReplace2(tmpPath, resolvedDest) {
|
|
10574
|
+
if (platform2() === "win32") {
|
|
10575
|
+
try {
|
|
10576
|
+
unlinkSync4(resolvedDest);
|
|
10577
|
+
} catch {
|
|
10578
|
+
}
|
|
10579
|
+
}
|
|
10580
|
+
renameSync4(tmpPath, resolvedDest);
|
|
10581
|
+
if (platform2() !== "win32") {
|
|
10582
|
+
chmodSync2(resolvedDest, 493);
|
|
10583
|
+
}
|
|
10584
|
+
}
|
|
10585
|
+
function installPreChecks2(destPath, sources) {
|
|
10586
|
+
if (!destPath && process.env.OKX_AUTH_BIN) {
|
|
10587
|
+
return { status: "up-to-date", source: "(env override)" };
|
|
10588
|
+
}
|
|
10589
|
+
if (!getPlatformDir()) {
|
|
10590
|
+
return { status: "failed", error: "Unsupported platform" };
|
|
10591
|
+
}
|
|
10592
|
+
if (sources.length === 0) {
|
|
10593
|
+
return { status: "failed", error: "No CDN sources available" };
|
|
10594
|
+
}
|
|
10595
|
+
return null;
|
|
10596
|
+
}
|
|
10597
|
+
function isLocalUpToDate2(localHash, checksum) {
|
|
10598
|
+
return localHash !== null && localHash.size === checksum.size && localHash.sha256 === checksum.sha256;
|
|
10599
|
+
}
|
|
10600
|
+
async function installAuthBinary(destPath, sources = CDN_SOURCES, onProgress) {
|
|
10601
|
+
const earlyResult = installPreChecks2(destPath, sources);
|
|
10602
|
+
if (earlyResult) return earlyResult;
|
|
10603
|
+
const platformDir = getPlatformDir();
|
|
10604
|
+
const binaryName = getAuthBinaryName();
|
|
10605
|
+
const resolvedDest = destPath ?? join11(homedir9(), ".okx", "bin", binaryName);
|
|
10606
|
+
const tmpPath = resolvedDest + ".tmp";
|
|
10607
|
+
mkdirSync9(dirname7(resolvedDest), { recursive: true });
|
|
10608
|
+
const localHash = existsSync7(resolvedDest) ? hashFile(resolvedDest) : null;
|
|
10609
|
+
const checksumPath = `${AUTH_CDN_PATH_PREFIX}/${platformDir}/checksum.json`;
|
|
10610
|
+
const binaryPath = `${AUTH_CDN_PATH_PREFIX}/${platformDir}/${binaryName}`;
|
|
10611
|
+
const errors = [];
|
|
10612
|
+
for (const { host, protocol } of sources) {
|
|
10613
|
+
try {
|
|
10614
|
+
const checksum = await fetchAndValidateChecksum2(
|
|
10615
|
+
host,
|
|
10616
|
+
protocol,
|
|
10617
|
+
checksumPath,
|
|
10618
|
+
platformDir,
|
|
10619
|
+
DOWNLOAD_TIMEOUT_MS,
|
|
10620
|
+
onProgress
|
|
10621
|
+
);
|
|
10622
|
+
if (isLocalUpToDate2(localHash, checksum)) {
|
|
10623
|
+
onProgress?.("Already up to date (checksum match)");
|
|
10624
|
+
return { status: "up-to-date", source: host };
|
|
10625
|
+
}
|
|
10626
|
+
await downloadAndVerify2(host, protocol, binaryPath, tmpPath, checksum, DOWNLOAD_TIMEOUT_MS, onProgress);
|
|
10627
|
+
atomicReplace2(tmpPath, resolvedDest);
|
|
10628
|
+
onProgress?.(`Downloaded and verified from ${host}`);
|
|
10629
|
+
return { status: "installed", source: host };
|
|
10630
|
+
} catch (err) {
|
|
10631
|
+
try {
|
|
10632
|
+
unlinkSync4(tmpPath);
|
|
10633
|
+
} catch {
|
|
10634
|
+
}
|
|
10635
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
10636
|
+
errors.push(`${host}: ${msg}`);
|
|
10637
|
+
onProgress?.(`${host} failed: ${msg}`);
|
|
10638
|
+
}
|
|
10639
|
+
}
|
|
10640
|
+
return { status: "failed", error: `All CDN sources failed:
|
|
10641
|
+
${errors.join("\n")}` };
|
|
10642
|
+
}
|
|
10643
|
+
function removeAuthBinary(binaryPath) {
|
|
10644
|
+
const resolvedPath = binaryPath ?? getAuthBinaryPath();
|
|
10645
|
+
try {
|
|
10646
|
+
unlinkSync4(resolvedPath);
|
|
10647
|
+
return { status: "removed" };
|
|
10648
|
+
} catch (err) {
|
|
10649
|
+
if (err.code === "ENOENT") {
|
|
10650
|
+
return { status: "not-found" };
|
|
10651
|
+
}
|
|
10652
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
10653
|
+
throw new Error(`Failed to remove ${resolvedPath}: ${msg}`);
|
|
10654
|
+
}
|
|
10655
|
+
}
|
|
10656
|
+
function isRedirect2(statusCode) {
|
|
10657
|
+
return statusCode !== void 0 && statusCode >= 300 && statusCode < 400;
|
|
10658
|
+
}
|
|
10659
|
+
function validateRedirect2(res, requestUrl, redirectCount, maxRedirects) {
|
|
10660
|
+
if (redirectCount > maxRedirects) {
|
|
10661
|
+
throw new Error(`Too many redirects (${maxRedirects})`);
|
|
10662
|
+
}
|
|
10663
|
+
const location = res.headers.location;
|
|
10664
|
+
if (requestUrl.startsWith("https") && !location.startsWith("https")) {
|
|
10665
|
+
throw new Error("Refused HTTPS \u2192 HTTP redirect downgrade");
|
|
10666
|
+
}
|
|
10667
|
+
return location;
|
|
10668
|
+
}
|
|
10669
|
+
function fetchResponse2(url, timeoutMs) {
|
|
10670
|
+
return new Promise((resolve3, reject) => {
|
|
10671
|
+
let redirects = 0;
|
|
10672
|
+
const maxRedirects = 5;
|
|
10673
|
+
function doRequest(requestUrl) {
|
|
10674
|
+
const reqFn = requestUrl.startsWith("https") ? httpsGet2 : httpGet2;
|
|
10675
|
+
const req = reqFn(requestUrl, { timeout: timeoutMs }, (res) => {
|
|
10676
|
+
if (isRedirect2(res.statusCode) && res.headers.location) {
|
|
10677
|
+
redirects++;
|
|
10678
|
+
try {
|
|
10679
|
+
const location = validateRedirect2(res, requestUrl, redirects, maxRedirects);
|
|
10680
|
+
res.resume();
|
|
10681
|
+
doRequest(location);
|
|
10682
|
+
} catch (err) {
|
|
10683
|
+
reject(err);
|
|
10684
|
+
}
|
|
10685
|
+
return;
|
|
10686
|
+
}
|
|
10687
|
+
if (res.statusCode !== 200) {
|
|
10688
|
+
reject(new Error(`HTTP ${res.statusCode ?? "unknown"}`));
|
|
10689
|
+
return;
|
|
10690
|
+
}
|
|
10691
|
+
resolve3(res);
|
|
10692
|
+
});
|
|
10693
|
+
req.on("error", reject);
|
|
10694
|
+
req.on("timeout", () => {
|
|
10695
|
+
req.destroy();
|
|
10696
|
+
reject(new Error("Download timed out"));
|
|
10697
|
+
});
|
|
10698
|
+
}
|
|
10699
|
+
doRequest(url);
|
|
10700
|
+
});
|
|
10701
|
+
}
|
|
10702
|
+
function download2(url, destPath, timeoutMs) {
|
|
10703
|
+
return fetchResponse2(url, timeoutMs).then(
|
|
10704
|
+
(res) => new Promise((resolve3, reject) => {
|
|
10705
|
+
const file = createWriteStream3(destPath);
|
|
10706
|
+
res.pipe(file);
|
|
10707
|
+
file.on("finish", () => file.close(() => resolve3()));
|
|
10708
|
+
file.on("error", (err) => {
|
|
10709
|
+
try {
|
|
10710
|
+
unlinkSync4(destPath);
|
|
10711
|
+
} catch {
|
|
10712
|
+
}
|
|
10713
|
+
reject(err);
|
|
10714
|
+
});
|
|
10715
|
+
})
|
|
10716
|
+
);
|
|
10717
|
+
}
|
|
10718
|
+
function downloadText2(url, timeoutMs) {
|
|
10719
|
+
return fetchResponse2(url, timeoutMs).then(
|
|
10720
|
+
(res) => new Promise((resolve3, reject) => {
|
|
10721
|
+
const chunks = [];
|
|
10722
|
+
res.on("data", (chunk) => chunks.push(chunk));
|
|
10723
|
+
res.on("end", () => resolve3(Buffer.concat(chunks).toString("utf8")));
|
|
10724
|
+
res.on("error", reject);
|
|
10725
|
+
})
|
|
10726
|
+
);
|
|
10727
|
+
}
|
|
10728
|
+
var CACHE_PATH = join12(homedir10(), ".okx", "auth-binary-check.json");
|
|
10729
|
+
var CHECK_INTERVAL_MS2 = 2 * 60 * 60 * 1e3;
|
|
10730
|
+
function readCache3() {
|
|
10731
|
+
try {
|
|
10732
|
+
if (!existsSync8(CACHE_PATH)) return null;
|
|
10733
|
+
const data = JSON.parse(readFileSync8(CACHE_PATH, "utf-8"));
|
|
10734
|
+
if (typeof data.cdnSha256 !== "string" || typeof data.checkedAt !== "number") return null;
|
|
10735
|
+
return { cdnSha256: data.cdnSha256, checkedAt: data.checkedAt };
|
|
10736
|
+
} catch {
|
|
10737
|
+
return null;
|
|
10738
|
+
}
|
|
10739
|
+
}
|
|
10740
|
+
function writeCache3(cdnSha256) {
|
|
10741
|
+
try {
|
|
10742
|
+
mkdirSync10(join12(homedir10(), ".okx"), { recursive: true });
|
|
10743
|
+
writeFileSync7(CACHE_PATH, JSON.stringify({ cdnSha256, checkedAt: Date.now() }, null, 2), "utf-8");
|
|
10744
|
+
} catch {
|
|
10745
|
+
}
|
|
10746
|
+
}
|
|
10747
|
+
async function ensureAuthBinaryLatest(onProgress) {
|
|
10748
|
+
if (process.env.OKX_AUTH_BIN) return;
|
|
10749
|
+
const cache = readCache3();
|
|
10750
|
+
if (cache && Date.now() - cache.checkedAt < CHECK_INTERVAL_MS2) return;
|
|
10751
|
+
try {
|
|
10752
|
+
const cdn = await fetchAuthCdnChecksum(void 0, 5e3);
|
|
10753
|
+
if (!cdn) return;
|
|
10754
|
+
const local = getAuthStatus();
|
|
10755
|
+
if (local.exists && local.sha256 === cdn.sha256) {
|
|
10756
|
+
writeCache3(cdn.sha256);
|
|
10757
|
+
return;
|
|
10758
|
+
}
|
|
10759
|
+
onProgress?.("Updating okx-auth binary...");
|
|
10760
|
+
const result = await installAuthBinary(void 0, void 0, onProgress);
|
|
10761
|
+
if (result.status === "installed" || result.status === "up-to-date") {
|
|
10762
|
+
const updated = getAuthStatus();
|
|
10763
|
+
if (updated.sha256) writeCache3(updated.sha256);
|
|
10764
|
+
if (result.status === "installed") {
|
|
10765
|
+
onProgress?.("\u2713 okx-auth updated successfully");
|
|
10766
|
+
}
|
|
10767
|
+
}
|
|
10768
|
+
} catch {
|
|
10769
|
+
}
|
|
10770
|
+
}
|
|
10771
|
+
function updateAuthBinaryCache(sha256) {
|
|
10772
|
+
writeCache3(sha256);
|
|
10773
|
+
}
|
|
10774
|
+
function clearAuthBinaryCache() {
|
|
10775
|
+
try {
|
|
10776
|
+
unlinkSync5(CACHE_PATH);
|
|
10777
|
+
} catch {
|
|
10778
|
+
}
|
|
10779
|
+
}
|
|
10379
10780
|
|
|
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";
|
|
10781
|
+
// src/commands/auth.ts
|
|
10782
|
+
import { spawn as spawn2 } from "child_process";
|
|
10783
|
+
import readline from "readline";
|
|
10389
10784
|
|
|
10390
10785
|
// src/formatter.ts
|
|
10391
10786
|
import { EOL } from "os";
|
|
@@ -10476,7 +10871,282 @@ function markFailedIfSCodeError(data) {
|
|
|
10476
10871
|
}
|
|
10477
10872
|
}
|
|
10478
10873
|
|
|
10874
|
+
// src/commands/auth.ts
|
|
10875
|
+
function runOkxAuth(args) {
|
|
10876
|
+
const binPath = getAuthBinaryPath();
|
|
10877
|
+
return new Promise((resolve3, reject) => {
|
|
10878
|
+
const child = spawn2(binPath, args, {
|
|
10879
|
+
stdio: "inherit"
|
|
10880
|
+
});
|
|
10881
|
+
child.on("error", (err) => {
|
|
10882
|
+
reject(new Error(`Failed to spawn okx-auth: ${err.message}`));
|
|
10883
|
+
});
|
|
10884
|
+
child.on("close", (code) => {
|
|
10885
|
+
resolve3(code ?? 1);
|
|
10886
|
+
});
|
|
10887
|
+
});
|
|
10888
|
+
}
|
|
10889
|
+
function runOkxAuthCapture(args) {
|
|
10890
|
+
const binPath = getAuthBinaryPath();
|
|
10891
|
+
return new Promise((resolve3, reject) => {
|
|
10892
|
+
const child = spawn2(binPath, args, {
|
|
10893
|
+
stdio: ["inherit", "pipe", "inherit"]
|
|
10894
|
+
});
|
|
10895
|
+
const chunks = [];
|
|
10896
|
+
child.stdout.on("data", (chunk) => chunks.push(chunk));
|
|
10897
|
+
child.on("error", (err) => {
|
|
10898
|
+
reject(new Error(`Failed to spawn okx-auth: ${err.message}`));
|
|
10899
|
+
});
|
|
10900
|
+
child.on("close", (code) => {
|
|
10901
|
+
resolve3({
|
|
10902
|
+
code: code ?? 1,
|
|
10903
|
+
stdout: Buffer.concat(chunks).toString("utf-8")
|
|
10904
|
+
});
|
|
10905
|
+
});
|
|
10906
|
+
});
|
|
10907
|
+
}
|
|
10908
|
+
async function cmdAuthLogin(args) {
|
|
10909
|
+
let site = args.site?.trim();
|
|
10910
|
+
if (!site) site = process.env.OKX_SITE?.trim();
|
|
10911
|
+
if (!site) {
|
|
10912
|
+
try {
|
|
10913
|
+
const cfg = readFullConfig();
|
|
10914
|
+
const profileName = cfg.default_profile ?? "default";
|
|
10915
|
+
site = cfg.profiles?.[profileName]?.site?.trim();
|
|
10916
|
+
} catch {
|
|
10917
|
+
}
|
|
10918
|
+
}
|
|
10919
|
+
const cliArgs = ["login"];
|
|
10920
|
+
if (site) cliArgs.push("--site", site);
|
|
10921
|
+
if (args.manual) cliArgs.push("--manual");
|
|
10922
|
+
const code = await runOkxAuth(cliArgs);
|
|
10923
|
+
if (code !== 0) {
|
|
10924
|
+
process.exitCode = code;
|
|
10925
|
+
return;
|
|
10926
|
+
}
|
|
10927
|
+
const explicit = args.site?.trim();
|
|
10928
|
+
if (explicit) {
|
|
10929
|
+
try {
|
|
10930
|
+
const cfg = readFullConfig();
|
|
10931
|
+
const profileName = cfg.default_profile ?? "default";
|
|
10932
|
+
const profiles = { ...cfg.profiles ?? {} };
|
|
10933
|
+
profiles[profileName] = { ...profiles[profileName] ?? {}, site: explicit };
|
|
10934
|
+
writeFullConfig({ ...cfg, default_profile: profileName, profiles });
|
|
10935
|
+
} catch {
|
|
10936
|
+
}
|
|
10937
|
+
}
|
|
10938
|
+
}
|
|
10939
|
+
async function cmdAuthLogout() {
|
|
10940
|
+
const code = await runOkxAuth(["logout"]);
|
|
10941
|
+
if (code !== 0) {
|
|
10942
|
+
process.exitCode = code;
|
|
10943
|
+
}
|
|
10944
|
+
}
|
|
10945
|
+
async function cmdAuthStatus(args) {
|
|
10946
|
+
const cliArgs = ["status"];
|
|
10947
|
+
if (args.json) cliArgs.push("--json");
|
|
10948
|
+
const result = await runOkxAuthCapture(cliArgs);
|
|
10949
|
+
if (result.stdout) {
|
|
10950
|
+
process.stdout.write(result.stdout);
|
|
10951
|
+
}
|
|
10952
|
+
if (result.code !== 0) {
|
|
10953
|
+
process.exitCode = result.code;
|
|
10954
|
+
}
|
|
10955
|
+
}
|
|
10956
|
+
function resolveChecksumMatch(local, cdnChecksum, cdnError) {
|
|
10957
|
+
if (!local.exists) return "not-installed";
|
|
10958
|
+
if (cdnError || !cdnChecksum) return "unavailable";
|
|
10959
|
+
if (cdnChecksum.sha256 === local.sha256) return "match";
|
|
10960
|
+
return "mismatch";
|
|
10961
|
+
}
|
|
10962
|
+
function checksumMatchLabel(match) {
|
|
10963
|
+
if (match === "match") return "\u2713 match";
|
|
10964
|
+
if (match === "mismatch") return "\u2717 mismatch (update available)";
|
|
10965
|
+
return "CDN unreachable";
|
|
10966
|
+
}
|
|
10967
|
+
function formatBytes(bytes) {
|
|
10968
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
10969
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
10970
|
+
return `${(bytes / (1024 * 1024)).toFixed(2)} MB`;
|
|
10971
|
+
}
|
|
10972
|
+
async function cmdAuthInstallStatus(json) {
|
|
10973
|
+
const local = getAuthStatus();
|
|
10974
|
+
let cdnChecksum = null;
|
|
10975
|
+
let cdnError = null;
|
|
10976
|
+
if (local.exists) {
|
|
10977
|
+
try {
|
|
10978
|
+
cdnChecksum = await fetchAuthCdnChecksum(void 0, 5e3);
|
|
10979
|
+
} catch (err) {
|
|
10980
|
+
cdnError = err instanceof Error ? err.message : String(err);
|
|
10981
|
+
}
|
|
10982
|
+
}
|
|
10983
|
+
const checksumMatch = resolveChecksumMatch(local, cdnChecksum, cdnError);
|
|
10984
|
+
if (json) {
|
|
10985
|
+
outputLine(
|
|
10986
|
+
JSON.stringify({
|
|
10987
|
+
binaryPath: local.binaryPath,
|
|
10988
|
+
exists: local.exists,
|
|
10989
|
+
platform: local.platform,
|
|
10990
|
+
fileSize: local.fileSize ?? null,
|
|
10991
|
+
sha256: local.sha256 ?? null,
|
|
10992
|
+
cdnMatch: checksumMatch,
|
|
10993
|
+
cdnSha256: cdnChecksum?.sha256 ?? null,
|
|
10994
|
+
cdnSource: cdnChecksum?.source ?? null
|
|
10995
|
+
})
|
|
10996
|
+
);
|
|
10997
|
+
return;
|
|
10998
|
+
}
|
|
10999
|
+
outputLine("");
|
|
11000
|
+
outputLine(" okx-auth Binary Status");
|
|
11001
|
+
outputLine(" " + "\u2500".repeat(40));
|
|
11002
|
+
outputLine(` Binary path : ${local.binaryPath}`);
|
|
11003
|
+
outputLine(` Installed : ${local.exists ? "yes" : "no"}`);
|
|
11004
|
+
outputLine(` Platform : ${local.platform ?? "(unsupported)"}`);
|
|
11005
|
+
if (local.exists) {
|
|
11006
|
+
outputLine(` File size : ${formatBytes(local.fileSize)}`);
|
|
11007
|
+
outputLine(` SHA-256 : ${local.sha256 ?? "(unknown)"}`);
|
|
11008
|
+
outputLine(` CDN check : ${checksumMatchLabel(checksumMatch)}`);
|
|
11009
|
+
if (cdnChecksum) {
|
|
11010
|
+
outputLine(` CDN source : ${cdnChecksum.source}`);
|
|
11011
|
+
}
|
|
11012
|
+
}
|
|
11013
|
+
outputLine("");
|
|
11014
|
+
}
|
|
11015
|
+
async function cmdAuthInstall(json) {
|
|
11016
|
+
const messages2 = [];
|
|
11017
|
+
const onProgress = (msg) => {
|
|
11018
|
+
if (!json) {
|
|
11019
|
+
outputLine(` ${msg}`);
|
|
11020
|
+
}
|
|
11021
|
+
messages2.push(msg);
|
|
11022
|
+
};
|
|
11023
|
+
if (!json) {
|
|
11024
|
+
outputLine("");
|
|
11025
|
+
outputLine(" Installing okx-auth...");
|
|
11026
|
+
}
|
|
11027
|
+
const result = await installAuthBinary(void 0, void 0, onProgress);
|
|
11028
|
+
if (json) {
|
|
11029
|
+
outputLine(JSON.stringify({ status: result.status, source: result.source ?? null, error: result.error ?? null, messages: messages2 }));
|
|
11030
|
+
if (result.status === "failed") {
|
|
11031
|
+
process.exitCode = 1;
|
|
11032
|
+
}
|
|
11033
|
+
return;
|
|
11034
|
+
}
|
|
11035
|
+
if (result.status === "installed" || result.status === "up-to-date") {
|
|
11036
|
+
const local = getAuthStatus();
|
|
11037
|
+
if (local.sha256) updateAuthBinaryCache(local.sha256);
|
|
11038
|
+
}
|
|
11039
|
+
if (result.status === "installed") {
|
|
11040
|
+
outputLine(` \u2713 okx-auth installed successfully (${result.source ?? ""})`);
|
|
11041
|
+
} else if (result.status === "up-to-date") {
|
|
11042
|
+
outputLine(" \u2713 okx-auth is already up to date");
|
|
11043
|
+
} else {
|
|
11044
|
+
errorLine(` \u2717 Installation failed: ${result.error ?? "unknown error"}`);
|
|
11045
|
+
errorLine(" Hint: check network connectivity or try again later");
|
|
11046
|
+
process.exitCode = 1;
|
|
11047
|
+
}
|
|
11048
|
+
outputLine("");
|
|
11049
|
+
}
|
|
11050
|
+
async function cmdAuthRemove(force, json) {
|
|
11051
|
+
const local = getAuthStatus(void 0, { skipHash: true });
|
|
11052
|
+
if (!local.exists) {
|
|
11053
|
+
if (json) {
|
|
11054
|
+
outputLine(JSON.stringify({ status: "not-installed" }));
|
|
11055
|
+
} else {
|
|
11056
|
+
outputLine(" okx-auth is not installed.");
|
|
11057
|
+
}
|
|
11058
|
+
return;
|
|
11059
|
+
}
|
|
11060
|
+
if (!await confirmRemoval(force, local.binaryPath)) return;
|
|
11061
|
+
let result;
|
|
11062
|
+
try {
|
|
11063
|
+
result = removeAuthBinary();
|
|
11064
|
+
} catch (err) {
|
|
11065
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
11066
|
+
if (json) {
|
|
11067
|
+
outputLine(JSON.stringify({ status: "failed", error: msg }));
|
|
11068
|
+
} else {
|
|
11069
|
+
errorLine(` \u2717 Failed to remove: ${msg}`);
|
|
11070
|
+
}
|
|
11071
|
+
process.exitCode = 1;
|
|
11072
|
+
return;
|
|
11073
|
+
}
|
|
11074
|
+
if (json) {
|
|
11075
|
+
outputLine(JSON.stringify({ status: result.status, path: local.binaryPath }));
|
|
11076
|
+
return;
|
|
11077
|
+
}
|
|
11078
|
+
if (result.status === "removed") {
|
|
11079
|
+
clearAuthBinaryCache();
|
|
11080
|
+
outputLine(` \u2713 Removed: ${local.binaryPath}`);
|
|
11081
|
+
} else {
|
|
11082
|
+
outputLine(" okx-auth is not installed.");
|
|
11083
|
+
}
|
|
11084
|
+
}
|
|
11085
|
+
async function confirmRemoval(force, binaryPath) {
|
|
11086
|
+
if (force) return true;
|
|
11087
|
+
if (!process.stdin.isTTY) {
|
|
11088
|
+
errorLine(" Error: stdin is not a TTY. Use --force to skip confirmation.");
|
|
11089
|
+
process.exitCode = 1;
|
|
11090
|
+
return false;
|
|
11091
|
+
}
|
|
11092
|
+
const confirmed = await askConfirmation(
|
|
11093
|
+
` Remove okx-auth at ${binaryPath}? [y/N] `
|
|
11094
|
+
);
|
|
11095
|
+
if (!confirmed) {
|
|
11096
|
+
outputLine(" Cancelled.");
|
|
11097
|
+
return false;
|
|
11098
|
+
}
|
|
11099
|
+
return true;
|
|
11100
|
+
}
|
|
11101
|
+
function askConfirmation(prompt2) {
|
|
11102
|
+
return new Promise((resolve3) => {
|
|
11103
|
+
const rl = readline.createInterface({
|
|
11104
|
+
input: process.stdin,
|
|
11105
|
+
output: process.stdout
|
|
11106
|
+
});
|
|
11107
|
+
rl.question(prompt2, (answer) => {
|
|
11108
|
+
rl.close();
|
|
11109
|
+
resolve3(answer.trim().toLowerCase() === "y");
|
|
11110
|
+
});
|
|
11111
|
+
});
|
|
11112
|
+
}
|
|
11113
|
+
async function handleAuthCommand(action, _rest, v) {
|
|
11114
|
+
const site = v.site;
|
|
11115
|
+
const json = v.json;
|
|
11116
|
+
const manual = v.manual;
|
|
11117
|
+
const force = v.force;
|
|
11118
|
+
if (["login", "logout", "status"].includes(action)) {
|
|
11119
|
+
await ensureAuthBinaryLatest((msg) => errorLine(` ${msg}`));
|
|
11120
|
+
}
|
|
11121
|
+
switch (action) {
|
|
11122
|
+
case "login":
|
|
11123
|
+
return cmdAuthLogin({ site, manual });
|
|
11124
|
+
case "logout":
|
|
11125
|
+
return cmdAuthLogout();
|
|
11126
|
+
case "status":
|
|
11127
|
+
return cmdAuthStatus({ json });
|
|
11128
|
+
case "install":
|
|
11129
|
+
return cmdAuthInstall(json ?? false);
|
|
11130
|
+
case "install-status":
|
|
11131
|
+
return cmdAuthInstallStatus(json ?? false);
|
|
11132
|
+
case "remove":
|
|
11133
|
+
return cmdAuthRemove(force ?? false, json ?? false);
|
|
11134
|
+
default:
|
|
11135
|
+
errorLine(`Unknown auth command: ${action}`);
|
|
11136
|
+
errorLine("Available: login, logout, status, install, install-status, remove");
|
|
11137
|
+
process.exitCode = 1;
|
|
11138
|
+
}
|
|
11139
|
+
}
|
|
11140
|
+
|
|
11141
|
+
// src/commands/diagnose.ts
|
|
11142
|
+
import dns from "dns/promises";
|
|
11143
|
+
import net from "net";
|
|
11144
|
+
import os5 from "os";
|
|
11145
|
+
import tls from "tls";
|
|
11146
|
+
|
|
10479
11147
|
// src/commands/diagnose-utils.ts
|
|
11148
|
+
import fs4 from "fs";
|
|
11149
|
+
import { createRequire } from "module";
|
|
10480
11150
|
var _require = createRequire(import.meta.url);
|
|
10481
11151
|
function readCliVersion() {
|
|
10482
11152
|
for (const rel of ["../package.json", "../../package.json"]) {
|
|
@@ -10562,7 +11232,7 @@ function sanitize2(value) {
|
|
|
10562
11232
|
import fs5 from "fs";
|
|
10563
11233
|
import path4 from "path";
|
|
10564
11234
|
import os4 from "os";
|
|
10565
|
-
import { spawnSync, spawn } from "child_process";
|
|
11235
|
+
import { spawnSync, spawn as spawn3 } from "child_process";
|
|
10566
11236
|
import { createRequire as createRequire2 } from "module";
|
|
10567
11237
|
import { fileURLToPath } from "url";
|
|
10568
11238
|
var _require2 = createRequire2(import.meta.url);
|
|
@@ -10896,7 +11566,7 @@ async function checkStdioHandshake(entryPath, report) {
|
|
|
10896
11566
|
clearTimeout(timer);
|
|
10897
11567
|
resolve3(passed);
|
|
10898
11568
|
};
|
|
10899
|
-
const child =
|
|
11569
|
+
const child = spawn3(process.execPath, [entryPath], {
|
|
10900
11570
|
stdio: ["pipe", "pipe", "pipe"],
|
|
10901
11571
|
env: { ...process.env }
|
|
10902
11572
|
});
|
|
@@ -11015,7 +11685,7 @@ async function cmdDiagnoseMcp(options = {}) {
|
|
|
11015
11685
|
|
|
11016
11686
|
// src/commands/diagnose.ts
|
|
11017
11687
|
var CLI_VERSION = readCliVersion();
|
|
11018
|
-
var GIT_HASH = true ? "
|
|
11688
|
+
var GIT_HASH = true ? "17e06f4" : "dev";
|
|
11019
11689
|
function maskKey2(key) {
|
|
11020
11690
|
if (!key) return "(not set)";
|
|
11021
11691
|
if (key.length <= 8) return "****";
|
|
@@ -11351,24 +12021,24 @@ async function runCliChecks(config, profile, outputPath) {
|
|
|
11351
12021
|
|
|
11352
12022
|
// src/commands/upgrade.ts
|
|
11353
12023
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
11354
|
-
import { readFileSync as
|
|
11355
|
-
import { dirname as
|
|
11356
|
-
import { homedir as
|
|
12024
|
+
import { readFileSync as readFileSync9, writeFileSync as writeFileSync8, mkdirSync as mkdirSync11 } from "fs";
|
|
12025
|
+
import { dirname as dirname8, join as join13 } from "path";
|
|
12026
|
+
import { homedir as homedir11 } from "os";
|
|
11357
12027
|
var PACKAGES = ["@okx_ai/okx-trade-mcp", "@okx_ai/okx-trade-cli"];
|
|
11358
|
-
var CACHE_FILE2 =
|
|
12028
|
+
var CACHE_FILE2 = join13(homedir11(), ".okx", "last_check");
|
|
11359
12029
|
var THROTTLE_MS = 12 * 60 * 60 * 1e3;
|
|
11360
|
-
var NPM_BIN =
|
|
12030
|
+
var NPM_BIN = join13(dirname8(process.execPath), process.platform === "win32" ? "npm.cmd" : "npm");
|
|
11361
12031
|
function readLastCheck() {
|
|
11362
12032
|
try {
|
|
11363
|
-
return parseInt(
|
|
12033
|
+
return parseInt(readFileSync9(CACHE_FILE2, "utf-8").trim(), 10) || 0;
|
|
11364
12034
|
} catch {
|
|
11365
12035
|
return 0;
|
|
11366
12036
|
}
|
|
11367
12037
|
}
|
|
11368
12038
|
function writeLastCheck() {
|
|
11369
12039
|
try {
|
|
11370
|
-
|
|
11371
|
-
|
|
12040
|
+
mkdirSync11(join13(homedir11(), ".okx"), { recursive: true });
|
|
12041
|
+
writeFileSync8(CACHE_FILE2, String(Math.floor(Date.now() / 1e3)), "utf-8");
|
|
11372
12042
|
} catch {
|
|
11373
12043
|
}
|
|
11374
12044
|
}
|
|
@@ -12339,29 +13009,29 @@ var CLI_REGISTRY = {
|
|
|
12339
13009
|
commands: {
|
|
12340
13010
|
latest: {
|
|
12341
13011
|
toolName: "news_get_latest",
|
|
12342
|
-
usage: "okx news latest [--coins BTC,ETH] [--
|
|
13012
|
+
usage: "okx news latest [--coins BTC,ETH] [--lang zh_CN] [--limit 20]"
|
|
12343
13013
|
},
|
|
12344
13014
|
important: {
|
|
12345
13015
|
toolName: "news_get_latest",
|
|
12346
|
-
usage: "okx news important [--coins BTC,ETH] [--lang
|
|
13016
|
+
usage: "okx news important [--coins BTC,ETH] [--lang zh_CN] [--limit 20]",
|
|
12347
13017
|
description: "Get important/high-impact crypto news"
|
|
12348
13018
|
},
|
|
12349
13019
|
"by-coin": {
|
|
12350
13020
|
toolName: "news_get_by_coin",
|
|
12351
|
-
usage: "okx news by-coin --coins BTC [--importance high] [--
|
|
13021
|
+
usage: "okx news by-coin --coins BTC [--importance high] [--lang zh_CN]"
|
|
12352
13022
|
},
|
|
12353
13023
|
search: {
|
|
12354
13024
|
toolName: "news_search",
|
|
12355
|
-
usage: "okx news search --keyword <term> [--coins BTC] [--sentiment bullish] [--
|
|
13025
|
+
usage: "okx news search --keyword <term> [--coins BTC] [--sentiment bullish] [--lang zh_CN]"
|
|
12356
13026
|
},
|
|
12357
13027
|
detail: {
|
|
12358
13028
|
toolName: "news_get_detail",
|
|
12359
|
-
usage: "okx news detail <id> [--lang
|
|
13029
|
+
usage: "okx news detail <id> [--lang zh_CN]"
|
|
12360
13030
|
},
|
|
12361
|
-
|
|
13031
|
+
domains: {
|
|
12362
13032
|
toolName: "news_get_domains",
|
|
12363
|
-
usage: "okx news
|
|
12364
|
-
description: "List available news
|
|
13033
|
+
usage: "okx news domains",
|
|
13034
|
+
description: "List available news source domains"
|
|
12365
13035
|
},
|
|
12366
13036
|
"coin-sentiment": {
|
|
12367
13037
|
toolName: "news_get_coin_sentiment",
|
|
@@ -12374,7 +13044,7 @@ var CLI_REGISTRY = {
|
|
|
12374
13044
|
},
|
|
12375
13045
|
"by-sentiment": {
|
|
12376
13046
|
toolName: "news_search",
|
|
12377
|
-
usage: "okx news by-sentiment --sentiment bullish [--coins BTC] [--
|
|
13047
|
+
usage: "okx news by-sentiment --sentiment bullish [--coins BTC] [--sort-by latest]",
|
|
12378
13048
|
description: "Browse news filtered by sentiment direction"
|
|
12379
13049
|
},
|
|
12380
13050
|
"sentiment-rank": {
|
|
@@ -12582,7 +13252,6 @@ async function cmdNewsLatest(run, opts) {
|
|
|
12582
13252
|
const result = await run("news_get_latest", {
|
|
12583
13253
|
coins: opts.coins,
|
|
12584
13254
|
importance: opts.importance,
|
|
12585
|
-
platform: opts.platform,
|
|
12586
13255
|
begin: opts.begin,
|
|
12587
13256
|
end: opts.end,
|
|
12588
13257
|
language: opts.language,
|
|
@@ -12609,7 +13278,6 @@ async function cmdNewsImportant(run, opts) {
|
|
|
12609
13278
|
const result = await run("news_get_latest", {
|
|
12610
13279
|
coins: opts.coins,
|
|
12611
13280
|
importance: "high",
|
|
12612
|
-
platform: opts.platform,
|
|
12613
13281
|
begin: opts.begin,
|
|
12614
13282
|
end: opts.end,
|
|
12615
13283
|
language: opts.language,
|
|
@@ -12634,7 +13302,6 @@ async function cmdNewsByCoin(run, coins, opts) {
|
|
|
12634
13302
|
const result = await run("news_get_by_coin", {
|
|
12635
13303
|
coins,
|
|
12636
13304
|
importance: opts.importance,
|
|
12637
|
-
platform: opts.platform,
|
|
12638
13305
|
begin: opts.begin,
|
|
12639
13306
|
end: opts.end,
|
|
12640
13307
|
language: opts.language,
|
|
@@ -12660,7 +13327,6 @@ async function cmdNewsSearch(run, keyword, opts) {
|
|
|
12660
13327
|
keyword: keyword || void 0,
|
|
12661
13328
|
coins: opts.coins,
|
|
12662
13329
|
importance: opts.importance,
|
|
12663
|
-
platform: opts.platform,
|
|
12664
13330
|
sentiment: opts.sentiment,
|
|
12665
13331
|
sortBy: opts.sortBy,
|
|
12666
13332
|
begin: opts.begin,
|
|
@@ -12713,12 +13379,12 @@ async function cmdNewsDetail(run, id, opts) {
|
|
|
12713
13379
|
content
|
|
12714
13380
|
});
|
|
12715
13381
|
}
|
|
12716
|
-
async function
|
|
13382
|
+
async function cmdNewsDomains(run, opts) {
|
|
12717
13383
|
const result = await run("news_get_domains", {});
|
|
12718
13384
|
const raw = getData(result);
|
|
12719
13385
|
const items = raw?.[0]?.["platform"] ?? [];
|
|
12720
13386
|
if (opts.json) return printJson(items);
|
|
12721
|
-
outputLine("Available news
|
|
13387
|
+
outputLine("Available news source domains:");
|
|
12722
13388
|
items.forEach((d) => outputLine(` ${d}`));
|
|
12723
13389
|
}
|
|
12724
13390
|
async function cmdNewsCoinSentiment(run, coins, opts) {
|
|
@@ -12792,7 +13458,7 @@ async function cmdNewsSentimentRank(run, opts) {
|
|
|
12792
13458
|
}
|
|
12793
13459
|
|
|
12794
13460
|
// src/config/loader.ts
|
|
12795
|
-
function loadProfileConfig(opts) {
|
|
13461
|
+
async function loadProfileConfig(opts) {
|
|
12796
13462
|
return loadConfig({
|
|
12797
13463
|
profile: opts.profile,
|
|
12798
13464
|
modules: opts.modules,
|
|
@@ -13100,7 +13766,6 @@ var CLI_OPTIONS = {
|
|
|
13100
13766
|
coins: { type: "string" },
|
|
13101
13767
|
sentiment: { type: "string" },
|
|
13102
13768
|
importance: { type: "string" },
|
|
13103
|
-
platform: { type: "string" },
|
|
13104
13769
|
keyword: { type: "string" },
|
|
13105
13770
|
"detail-lvl": { type: "string" },
|
|
13106
13771
|
period: { type: "string" },
|
|
@@ -13111,6 +13776,9 @@ var CLI_OPTIONS = {
|
|
|
13111
13776
|
dir: { type: "string" },
|
|
13112
13777
|
page: { type: "string" },
|
|
13113
13778
|
format: { type: "string" },
|
|
13779
|
+
// auth
|
|
13780
|
+
site: { type: "string" },
|
|
13781
|
+
manual: { type: "boolean", default: false },
|
|
13114
13782
|
// event contract
|
|
13115
13783
|
underlying: { type: "string" },
|
|
13116
13784
|
seriesId: { type: "string" },
|
|
@@ -16125,14 +16793,14 @@ async function cmdDcdQuoteAndBuy(run, opts) {
|
|
|
16125
16793
|
}
|
|
16126
16794
|
|
|
16127
16795
|
// src/commands/skill.ts
|
|
16128
|
-
import { tmpdir, homedir as
|
|
16129
|
-
import { join as
|
|
16130
|
-
import { mkdirSync as
|
|
16796
|
+
import { tmpdir, homedir as homedir13 } from "os";
|
|
16797
|
+
import { join as join15, dirname as dirname9 } from "path";
|
|
16798
|
+
import { mkdirSync as mkdirSync12, rmSync, existsSync as existsSync10, copyFileSync as copyFileSync2 } from "fs";
|
|
16131
16799
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
16132
16800
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
16133
16801
|
function resolveNpx() {
|
|
16134
|
-
const sibling =
|
|
16135
|
-
if (
|
|
16802
|
+
const sibling = join15(dirname9(process.execPath), "npx");
|
|
16803
|
+
if (existsSync10(sibling)) return sibling;
|
|
16136
16804
|
return "npx";
|
|
16137
16805
|
}
|
|
16138
16806
|
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 +16853,13 @@ async function cmdSkillCategories(run, json) {
|
|
|
16185
16853
|
outputLine("");
|
|
16186
16854
|
}
|
|
16187
16855
|
async function cmdSkillAdd(name, config, json) {
|
|
16188
|
-
const tmpBase =
|
|
16189
|
-
|
|
16856
|
+
const tmpBase = join15(tmpdir(), `okx-skill-${randomUUID2()}`);
|
|
16857
|
+
mkdirSync12(tmpBase, { recursive: true });
|
|
16190
16858
|
try {
|
|
16191
16859
|
outputLine(`Downloading ${name}...`);
|
|
16192
16860
|
const client = new OkxRestClient(config);
|
|
16193
16861
|
const zipPath = await downloadSkillZip(client, name, tmpBase);
|
|
16194
|
-
const contentDir = await extractSkillZip(zipPath,
|
|
16862
|
+
const contentDir = await extractSkillZip(zipPath, join15(tmpBase, "content"));
|
|
16195
16863
|
const meta = readMetaJson(contentDir);
|
|
16196
16864
|
validateSkillMdExists(contentDir);
|
|
16197
16865
|
outputLine("Installing to detected agents...");
|
|
@@ -16201,7 +16869,7 @@ async function cmdSkillAdd(name, config, json) {
|
|
|
16201
16869
|
timeout: 6e4
|
|
16202
16870
|
});
|
|
16203
16871
|
} catch (e) {
|
|
16204
|
-
const savedZip =
|
|
16872
|
+
const savedZip = join15(process.cwd(), `${name}.zip`);
|
|
16205
16873
|
try {
|
|
16206
16874
|
copyFileSync2(zipPath, savedZip);
|
|
16207
16875
|
} catch {
|
|
@@ -16240,7 +16908,7 @@ function cmdSkillRemove(name, json) {
|
|
|
16240
16908
|
timeout: 6e4
|
|
16241
16909
|
});
|
|
16242
16910
|
} catch {
|
|
16243
|
-
const agentsPath =
|
|
16911
|
+
const agentsPath = join15(homedir13(), ".agents", "skills", name);
|
|
16244
16912
|
try {
|
|
16245
16913
|
rmSync(agentsPath, { recursive: true, force: true });
|
|
16246
16914
|
} catch {
|
|
@@ -16314,14 +16982,14 @@ function printSkillInstallResult(meta, json) {
|
|
|
16314
16982
|
}
|
|
16315
16983
|
|
|
16316
16984
|
// src/commands/doh.ts
|
|
16317
|
-
import
|
|
16318
|
-
function
|
|
16985
|
+
import readline2 from "readline";
|
|
16986
|
+
function resolveChecksumMatch2(local, cdnChecksum, cdnError) {
|
|
16319
16987
|
if (!local.exists) return "not-installed";
|
|
16320
16988
|
if (cdnError || !cdnChecksum) return "unavailable";
|
|
16321
16989
|
if (cdnChecksum.sha256 === local.sha256) return "match";
|
|
16322
16990
|
return "mismatch";
|
|
16323
16991
|
}
|
|
16324
|
-
function
|
|
16992
|
+
function checksumMatchLabel2(match) {
|
|
16325
16993
|
if (match === "match") return "\u2713 match";
|
|
16326
16994
|
if (match === "mismatch") return "\u2717 mismatch (update available)";
|
|
16327
16995
|
return "CDN unreachable";
|
|
@@ -16334,9 +17002,9 @@ function formatStatusText(local, checksumMatch, cdnChecksum, runtimeMode) {
|
|
|
16334
17002
|
outputLine(` Installed : ${local.exists ? "yes" : "no"}`);
|
|
16335
17003
|
outputLine(` Platform : ${local.platform ?? "(unsupported)"}`);
|
|
16336
17004
|
if (local.exists) {
|
|
16337
|
-
outputLine(` File size : ${
|
|
17005
|
+
outputLine(` File size : ${formatBytes2(local.fileSize)}`);
|
|
16338
17006
|
outputLine(` SHA-256 : ${local.sha256 ?? "(unknown)"}`);
|
|
16339
|
-
outputLine(` CDN check : ${
|
|
17007
|
+
outputLine(` CDN check : ${checksumMatchLabel2(checksumMatch)}`);
|
|
16340
17008
|
if (cdnChecksum) {
|
|
16341
17009
|
outputLine(` CDN source : ${cdnChecksum.source}`);
|
|
16342
17010
|
}
|
|
@@ -16363,7 +17031,7 @@ async function cmdDohStatus(json, binaryPath) {
|
|
|
16363
17031
|
cdnError = err instanceof Error ? err.message : String(err);
|
|
16364
17032
|
}
|
|
16365
17033
|
}
|
|
16366
|
-
const checksumMatch =
|
|
17034
|
+
const checksumMatch = resolveChecksumMatch2(local, cdnChecksum, cdnError);
|
|
16367
17035
|
if (json) {
|
|
16368
17036
|
outputLine(
|
|
16369
17037
|
JSON.stringify({
|
|
@@ -16429,7 +17097,7 @@ async function cmdDohRemove(force, json, binaryPath) {
|
|
|
16429
17097
|
process.exitCode = 1;
|
|
16430
17098
|
return;
|
|
16431
17099
|
}
|
|
16432
|
-
const confirmed = await
|
|
17100
|
+
const confirmed = await askConfirmation2(
|
|
16433
17101
|
` Remove DoH resolver at ${local.binaryPath}? [y/N] `
|
|
16434
17102
|
);
|
|
16435
17103
|
if (!confirmed) {
|
|
@@ -16448,14 +17116,14 @@ async function cmdDohRemove(force, json, binaryPath) {
|
|
|
16448
17116
|
outputLine(" DoH resolver is not installed.");
|
|
16449
17117
|
}
|
|
16450
17118
|
}
|
|
16451
|
-
function
|
|
17119
|
+
function formatBytes2(bytes) {
|
|
16452
17120
|
if (bytes < 1024) return `${bytes} B`;
|
|
16453
17121
|
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
16454
17122
|
return `${(bytes / (1024 * 1024)).toFixed(2)} MB`;
|
|
16455
17123
|
}
|
|
16456
|
-
function
|
|
17124
|
+
function askConfirmation2(prompt2) {
|
|
16457
17125
|
return new Promise((resolve3) => {
|
|
16458
|
-
const rl =
|
|
17126
|
+
const rl = readline2.createInterface({
|
|
16459
17127
|
input: process.stdin,
|
|
16460
17128
|
output: process.stdout
|
|
16461
17129
|
});
|
|
@@ -16912,7 +17580,7 @@ async function cmdEventCancel(run, opts) {
|
|
|
16912
17580
|
// src/index.ts
|
|
16913
17581
|
var _require3 = createRequire3(import.meta.url);
|
|
16914
17582
|
var CLI_VERSION2 = _require3("../package.json").version;
|
|
16915
|
-
var GIT_HASH2 = true ? "
|
|
17583
|
+
var GIT_HASH2 = true ? "17e06f4" : "dev";
|
|
16916
17584
|
function handleDohCommand(action, json, force, binaryPath) {
|
|
16917
17585
|
if (action === "status") return cmdDohStatus(json, binaryPath);
|
|
16918
17586
|
if (action === "install") return cmdDohInstall(json, binaryPath);
|
|
@@ -17756,22 +18424,21 @@ function handleNewsCommand(run, action, rest, v, json) {
|
|
|
17756
18424
|
const limit = v.limit !== void 0 ? Number(v.limit) : void 0;
|
|
17757
18425
|
const begin = v.begin !== void 0 ? Number(v.begin) : void 0;
|
|
17758
18426
|
const end = v.end !== void 0 ? Number(v.end) : void 0;
|
|
17759
|
-
const language = v.lang ?? "
|
|
18427
|
+
const language = v.lang ?? "en_US";
|
|
17760
18428
|
const detailLvl = v["detail-lvl"];
|
|
17761
18429
|
const after = v.after;
|
|
17762
18430
|
const period = v.period;
|
|
17763
18431
|
const points = v.points !== void 0 ? Number(v.points) : 24;
|
|
17764
18432
|
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 };
|
|
18433
|
+
const searchOpts = { coins: v.coins, importance: v.importance, sentiment: v.sentiment, sortBy, begin, end, language, detailLvl, limit, after, json };
|
|
18434
|
+
const listOpts = { coins: v.coins, importance: v.importance, begin, end, language, detailLvl, limit, after, json };
|
|
17768
18435
|
const dispatch = {
|
|
17769
18436
|
latest: () => cmdNewsLatest(run, listOpts),
|
|
17770
|
-
important: () => cmdNewsImportant(run, { coins: v.coins,
|
|
17771
|
-
"by-coin": () => cmdNewsByCoin(run, v.coins ?? rest[0], { importance: v.importance,
|
|
18437
|
+
important: () => cmdNewsImportant(run, { coins: v.coins, begin, end, language, detailLvl, limit, json }),
|
|
18438
|
+
"by-coin": () => cmdNewsByCoin(run, v.coins ?? rest[0], { importance: v.importance, begin, end, language, detailLvl, limit, json }),
|
|
17772
18439
|
search: () => cmdNewsSearch(run, v.keyword ?? rest[0], searchOpts),
|
|
17773
18440
|
detail: () => cmdNewsDetail(run, rest[0], { language, json }),
|
|
17774
|
-
|
|
18441
|
+
domains: () => cmdNewsDomains(run, { json }),
|
|
17775
18442
|
"coin-sentiment": () => cmdNewsCoinSentiment(run, v.coins ?? rest[0], { period, json }),
|
|
17776
18443
|
"coin-trend": () => cmdNewsCoinTrend(run, v.coins ?? rest[0], { period, points, json }),
|
|
17777
18444
|
// by-sentiment is a convenience wrapper over news_search (no keyword, sentiment filter only)
|
|
@@ -17888,7 +18555,7 @@ function wrapRunnerWithLogger(baseRunner, logger, verbose = false) {
|
|
|
17888
18555
|
async function runDiagnose(v) {
|
|
17889
18556
|
let config;
|
|
17890
18557
|
try {
|
|
17891
|
-
config = loadProfileConfig({ profile: v.profile, demo: v.demo, live: v.live, verbose: v.verbose, userAgent: `okx-trade-cli/${CLI_VERSION2}`, sourceTag: "CLI" });
|
|
18558
|
+
config = await loadProfileConfig({ profile: v.profile, demo: v.demo, live: v.live, verbose: v.verbose, userAgent: `okx-trade-cli/${CLI_VERSION2}`, sourceTag: "CLI" });
|
|
17892
18559
|
} catch {
|
|
17893
18560
|
}
|
|
17894
18561
|
return cmdDiagnose(config, v.profile ?? "default", { mcp: v.mcp, cli: v.cli, all: v.all, output: v.output });
|
|
@@ -17903,24 +18570,13 @@ function printVersion() {
|
|
|
17903
18570
|
}
|
|
17904
18571
|
}
|
|
17905
18572
|
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
|
-
}
|
|
18573
|
+
if (module === "config") return handleConfigCommand(action, rest, json, v.lang, v.force);
|
|
18574
|
+
if (module === "setup") return handleSetupCommand(v);
|
|
18575
|
+
if (module === "auth") return handleAuthCommand(action, rest, v);
|
|
17914
18576
|
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
|
-
}
|
|
18577
|
+
if (module === "doh") return handleDohCommand(action, json, v.force ?? false);
|
|
17919
18578
|
if (module === "diagnose") return runDiagnose(v);
|
|
17920
|
-
if (module === "list-tools")
|
|
17921
|
-
cmdListTools(json);
|
|
17922
|
-
return true;
|
|
17923
|
-
}
|
|
18579
|
+
if (module === "list-tools") return cmdListTools(json);
|
|
17924
18580
|
return void 0;
|
|
17925
18581
|
}
|
|
17926
18582
|
async function main() {
|
|
@@ -17942,8 +18598,8 @@ async function main() {
|
|
|
17942
18598
|
const v = values;
|
|
17943
18599
|
const json = v.json ?? false;
|
|
17944
18600
|
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" });
|
|
18601
|
+
if (mgmt) return mgmt;
|
|
18602
|
+
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
18603
|
setEnvContext({ demo: config.demo, profile: v.profile ?? "default" });
|
|
17948
18604
|
setJsonEnvEnabled(v.env ?? false);
|
|
17949
18605
|
const client = new OkxRestClient(config);
|