@okx_ai/okx-trade-cli 1.3.6-beta.2 → 1.3.6-beta.3
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 +338 -578
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -34,22 +34,16 @@ import os from "os";
|
|
|
34
34
|
import { writeFileSync as writeFileSync2, renameSync as renameSync2, unlinkSync as unlinkSync2, mkdirSync as mkdirSync2 } from "fs";
|
|
35
35
|
import { join as join4, resolve, basename, sep } from "path";
|
|
36
36
|
import { randomUUID } from "crypto";
|
|
37
|
-
import { createHash } from "crypto";
|
|
38
|
-
import { readFileSync as readFileSync2, readdirSync, statSync } from "fs";
|
|
39
|
-
import { join as join5 } from "path";
|
|
40
|
-
import { createHash as createHash2, createPublicKey, verify as cryptoVerify } from "crypto";
|
|
41
|
-
import { readFileSync as readFileSync3 } from "fs";
|
|
42
|
-
import { join as join6, resolve as resolve2, sep as sep2 } from "path";
|
|
43
37
|
import yauzl from "yauzl";
|
|
44
38
|
import { createWriteStream, mkdirSync as mkdirSync3 } from "fs";
|
|
45
|
-
import { resolve as
|
|
46
|
-
import { readFileSync as
|
|
47
|
-
import { join as
|
|
48
|
-
import { readFileSync as
|
|
49
|
-
import { join as
|
|
39
|
+
import { resolve as resolve2, dirname as dirname2 } from "path";
|
|
40
|
+
import { readFileSync as readFileSync2, existsSync } from "fs";
|
|
41
|
+
import { join as join5 } from "path";
|
|
42
|
+
import { readFileSync as readFileSync3, writeFileSync as writeFileSync3, mkdirSync as mkdirSync4, existsSync as existsSync2 } from "fs";
|
|
43
|
+
import { join as join6, dirname as dirname3 } from "path";
|
|
50
44
|
import { homedir as homedir4 } from "os";
|
|
51
|
-
import { readFileSync as
|
|
52
|
-
import { join as
|
|
45
|
+
import { readFileSync as readFileSync4, writeFileSync as writeFileSync4, mkdirSync as mkdirSync5, existsSync as existsSync3 } from "fs";
|
|
46
|
+
import { join as join7, dirname as dirname4 } from "path";
|
|
53
47
|
import { homedir as homedir5 } from "os";
|
|
54
48
|
|
|
55
49
|
// ../../node_modules/.pnpm/smol-toml@1.6.0/node_modules/smol-toml/dist/error.js
|
|
@@ -127,7 +121,7 @@ function skipVoid(str, ptr, banNewLines, banComments) {
|
|
|
127
121
|
ptr++;
|
|
128
122
|
return banComments || c !== "#" ? ptr : skipVoid(str, skipComment(str, ptr), banNewLines);
|
|
129
123
|
}
|
|
130
|
-
function skipUntil(str, ptr,
|
|
124
|
+
function skipUntil(str, ptr, sep2, end, banNewLines = false) {
|
|
131
125
|
if (!end) {
|
|
132
126
|
ptr = indexOfNewline(str, ptr);
|
|
133
127
|
return ptr < 0 ? str.length : ptr;
|
|
@@ -136,7 +130,7 @@ function skipUntil(str, ptr, sep3, end, banNewLines = false) {
|
|
|
136
130
|
let c = str[i];
|
|
137
131
|
if (c === "#") {
|
|
138
132
|
i = indexOfNewline(str, i);
|
|
139
|
-
} else if (c ===
|
|
133
|
+
} else if (c === sep2) {
|
|
140
134
|
return i + 1;
|
|
141
135
|
} else if (c === end || banNewLines && (c === "\n" || c === "\r" && str[i + 1] === "\n")) {
|
|
142
136
|
return i;
|
|
@@ -879,8 +873,8 @@ function stringify(obj, { maxDepth = 1e3, numbersAsFloat = false } = {}) {
|
|
|
879
873
|
}
|
|
880
874
|
|
|
881
875
|
// ../core/dist/index.js
|
|
882
|
-
import { readFileSync as
|
|
883
|
-
import { join as
|
|
876
|
+
import { readFileSync as readFileSync5, writeFileSync as writeFileSync5, mkdirSync as mkdirSync6, existsSync as existsSync4 } from "fs";
|
|
877
|
+
import { join as join8 } from "path";
|
|
884
878
|
import { homedir as homedir6 } from "os";
|
|
885
879
|
import fs2 from "fs";
|
|
886
880
|
import path2 from "path";
|
|
@@ -897,37 +891,25 @@ import {
|
|
|
897
891
|
renameSync as renameSync4
|
|
898
892
|
} from "fs";
|
|
899
893
|
import { homedir as homedir9, platform as platform2 } from "os";
|
|
900
|
-
import { join as
|
|
894
|
+
import { join as join11, dirname as dirname7 } from "path";
|
|
901
895
|
import { createWriteStream as createWriteStream2, unlinkSync as unlinkSync3 } from "fs";
|
|
902
896
|
import { get as httpsGet } from "https";
|
|
903
897
|
import { get as httpGet } from "http";
|
|
904
898
|
import {
|
|
905
|
-
readFileSync as
|
|
899
|
+
readFileSync as readFileSync7,
|
|
906
900
|
mkdirSync as mkdirSync8,
|
|
907
901
|
chmodSync,
|
|
908
902
|
existsSync as existsSync6,
|
|
909
903
|
unlinkSync as unlinkSync4,
|
|
910
904
|
renameSync as renameSync3
|
|
911
905
|
} from "fs";
|
|
912
|
-
import { createHash
|
|
906
|
+
import { createHash } from "crypto";
|
|
913
907
|
import { homedir as homedir8, platform, arch } from "os";
|
|
914
|
-
import { join as
|
|
915
|
-
import { readFileSync as
|
|
916
|
-
import { join as
|
|
908
|
+
import { join as join10, dirname as dirname6 } from "path";
|
|
909
|
+
import { readFileSync as readFileSync8, writeFileSync as writeFileSync7, mkdirSync as mkdirSync10, unlinkSync as unlinkSync6, existsSync as existsSync8 } from "fs";
|
|
910
|
+
import { join as join12 } from "path";
|
|
917
911
|
import { homedir as homedir10 } from "os";
|
|
918
|
-
|
|
919
|
-
return Boolean(
|
|
920
|
-
env.HTTPS_PROXY || env.https_proxy || env.HTTP_PROXY || env.http_proxy
|
|
921
|
-
);
|
|
922
|
-
}
|
|
923
|
-
function installEnvProxyDispatcher(deps = {}) {
|
|
924
|
-
const env = deps.env ?? process.env;
|
|
925
|
-
if (!hasProxyEnv(env)) return false;
|
|
926
|
-
const register = deps.register ?? (() => setGlobalDispatcher(new EnvHttpProxyAgent()));
|
|
927
|
-
register();
|
|
928
|
-
return true;
|
|
929
|
-
}
|
|
930
|
-
installEnvProxyDispatcher();
|
|
912
|
+
setGlobalDispatcher(new EnvHttpProxyAgent());
|
|
931
913
|
var EXEC_TIMEOUT_MS = 3e4;
|
|
932
914
|
var ALLOWED_DOMAIN_RE = /^[\w.-]+\.okx\.com$/;
|
|
933
915
|
var PILOT_BIN_DIR = join(homedir(), ".okx", "bin");
|
|
@@ -950,25 +932,25 @@ function execPilotBinary(domain, exclude = [], userAgent) {
|
|
|
950
932
|
if (userAgent) {
|
|
951
933
|
args.push("--user-agent", userAgent);
|
|
952
934
|
}
|
|
953
|
-
return new Promise((
|
|
935
|
+
return new Promise((resolve3) => {
|
|
954
936
|
execFile(
|
|
955
937
|
binPath,
|
|
956
938
|
args,
|
|
957
939
|
{ timeout: EXEC_TIMEOUT_MS, encoding: "utf-8" },
|
|
958
940
|
(error, stdout) => {
|
|
959
941
|
if (error) {
|
|
960
|
-
|
|
942
|
+
resolve3(null);
|
|
961
943
|
return;
|
|
962
944
|
}
|
|
963
945
|
try {
|
|
964
946
|
const result = JSON.parse(stdout);
|
|
965
947
|
if (result.code === 0 && result.data) {
|
|
966
|
-
|
|
948
|
+
resolve3(result.data);
|
|
967
949
|
} else {
|
|
968
|
-
|
|
950
|
+
resolve3(null);
|
|
969
951
|
}
|
|
970
952
|
} catch {
|
|
971
|
-
|
|
953
|
+
resolve3(null);
|
|
972
954
|
}
|
|
973
955
|
}
|
|
974
956
|
);
|
|
@@ -1302,7 +1284,7 @@ var EXIT_CODES = {
|
|
|
1302
1284
|
NOT_LOGGED_IN: 2,
|
|
1303
1285
|
REFRESH_FAILED: 3
|
|
1304
1286
|
};
|
|
1305
|
-
function finalizeToken(code, token,
|
|
1287
|
+
function finalizeToken(code, token, resolve3, reject) {
|
|
1306
1288
|
if (code === EXIT_CODES.SUCCESS) {
|
|
1307
1289
|
if (!token) {
|
|
1308
1290
|
reject(new AuthenticationError(
|
|
@@ -1311,7 +1293,7 @@ function finalizeToken(code, token, resolve4, reject) {
|
|
|
1311
1293
|
));
|
|
1312
1294
|
return;
|
|
1313
1295
|
}
|
|
1314
|
-
|
|
1296
|
+
resolve3(token);
|
|
1315
1297
|
return;
|
|
1316
1298
|
}
|
|
1317
1299
|
if (code === EXIT_CODES.NOT_LOGGED_IN) {
|
|
@@ -1348,7 +1330,7 @@ function defaultWindowsPipeName() {
|
|
|
1348
1330
|
return WIN_PIPE_PREFIX + randomBytes(32).toString("hex");
|
|
1349
1331
|
}
|
|
1350
1332
|
function execAuthTokenWindows(binPath, makePipeName = defaultWindowsPipeName) {
|
|
1351
|
-
return new Promise((
|
|
1333
|
+
return new Promise((resolve3, reject) => {
|
|
1352
1334
|
const pipeName = makePipeName();
|
|
1353
1335
|
const server = createServer();
|
|
1354
1336
|
const chunks = [];
|
|
@@ -1368,7 +1350,7 @@ function execAuthTokenWindows(binPath, makePipeName = defaultWindowsPipeName) {
|
|
|
1368
1350
|
const tryFinalize = () => {
|
|
1369
1351
|
if (!pipeClosed || exitCode === void 0) return;
|
|
1370
1352
|
const token = Buffer.concat(chunks).toString("utf-8").trim();
|
|
1371
|
-
settle(() => finalizeToken(exitCode, token,
|
|
1353
|
+
settle(() => finalizeToken(exitCode, token, resolve3, reject));
|
|
1372
1354
|
};
|
|
1373
1355
|
server.on("connection", (socket) => {
|
|
1374
1356
|
connectionMade = true;
|
|
@@ -1415,7 +1397,7 @@ function execAuthToken() {
|
|
|
1415
1397
|
return process.platform === "win32" ? execAuthTokenWindows(binPath) : execAuthTokenUnix(binPath);
|
|
1416
1398
|
}
|
|
1417
1399
|
function execAuthTokenUnix(binPath) {
|
|
1418
|
-
return new Promise((
|
|
1400
|
+
return new Promise((resolve3, reject) => {
|
|
1419
1401
|
const child = spawn2(binPath, ["token"], {
|
|
1420
1402
|
stdio: ["ignore", "ignore", "inherit", "pipe"]
|
|
1421
1403
|
// stdin stdout stderr fd3 (pipe)
|
|
@@ -1428,35 +1410,35 @@ function execAuthTokenUnix(binPath) {
|
|
|
1428
1410
|
});
|
|
1429
1411
|
child.on("close", (code) => {
|
|
1430
1412
|
const token = Buffer.concat(chunks).toString("utf-8").trim();
|
|
1431
|
-
finalizeToken(code, token,
|
|
1413
|
+
finalizeToken(code, token, resolve3, reject);
|
|
1432
1414
|
});
|
|
1433
1415
|
});
|
|
1434
1416
|
}
|
|
1435
1417
|
function execAuthStatus() {
|
|
1436
1418
|
const binPath = getAuthBinaryPath();
|
|
1437
|
-
return new Promise((
|
|
1419
|
+
return new Promise((resolve3) => {
|
|
1438
1420
|
execFile2(
|
|
1439
1421
|
binPath,
|
|
1440
1422
|
["status", "--json"],
|
|
1441
1423
|
{ timeout: EXEC_TIMEOUT_MS2, encoding: "utf-8" },
|
|
1442
1424
|
(error, stdout) => {
|
|
1443
1425
|
if (error) {
|
|
1444
|
-
|
|
1426
|
+
resolve3(null);
|
|
1445
1427
|
return;
|
|
1446
1428
|
}
|
|
1447
1429
|
try {
|
|
1448
1430
|
const result = JSON.parse(stdout);
|
|
1449
|
-
|
|
1431
|
+
resolve3(result);
|
|
1450
1432
|
} catch {
|
|
1451
|
-
|
|
1433
|
+
resolve3(null);
|
|
1452
1434
|
}
|
|
1453
1435
|
}
|
|
1454
1436
|
);
|
|
1455
1437
|
});
|
|
1456
1438
|
}
|
|
1457
1439
|
function sleep(ms) {
|
|
1458
|
-
return new Promise((
|
|
1459
|
-
setTimeout(
|
|
1440
|
+
return new Promise((resolve3) => {
|
|
1441
|
+
setTimeout(resolve3, ms);
|
|
1460
1442
|
});
|
|
1461
1443
|
}
|
|
1462
1444
|
var RateLimiter = class {
|
|
@@ -4282,223 +4264,6 @@ async function downloadSkillZip(client, name, targetDir, format = "zip") {
|
|
|
4282
4264
|
const filePath = safeWriteFile(targetDir, fileName, result.data);
|
|
4283
4265
|
return filePath;
|
|
4284
4266
|
}
|
|
4285
|
-
function listFilesRecursive(dir, base = "") {
|
|
4286
|
-
const results = [];
|
|
4287
|
-
for (const entry of readdirSync(dir)) {
|
|
4288
|
-
const fullPath = join5(dir, entry);
|
|
4289
|
-
const relPath = base ? `${base}/${entry}` : entry;
|
|
4290
|
-
if (statSync(fullPath).isDirectory()) {
|
|
4291
|
-
results.push(...listFilesRecursive(fullPath, relPath));
|
|
4292
|
-
} else {
|
|
4293
|
-
results.push(relPath);
|
|
4294
|
-
}
|
|
4295
|
-
}
|
|
4296
|
-
return results;
|
|
4297
|
-
}
|
|
4298
|
-
function computeFileHashes(contentDir) {
|
|
4299
|
-
const hashes = {};
|
|
4300
|
-
for (const relPath of listFilesRecursive(contentDir)) {
|
|
4301
|
-
if (relPath === "_meta.json") continue;
|
|
4302
|
-
const bytes = readFileSync2(join5(contentDir, relPath));
|
|
4303
|
-
hashes[relPath] = "sha256:" + createHash("sha256").update(bytes).digest("hex");
|
|
4304
|
-
}
|
|
4305
|
-
return hashes;
|
|
4306
|
-
}
|
|
4307
|
-
async function getPublicKey(client, keyId) {
|
|
4308
|
-
try {
|
|
4309
|
-
const query = {};
|
|
4310
|
-
if (keyId !== void 0) query.keyId = keyId;
|
|
4311
|
-
const result = await client.privateGet(
|
|
4312
|
-
"/api/v5/skill/signing-key",
|
|
4313
|
-
query
|
|
4314
|
-
);
|
|
4315
|
-
if (!result.data || result.data.status !== "active") return null;
|
|
4316
|
-
return result.data.publicKey;
|
|
4317
|
-
} catch (e) {
|
|
4318
|
-
if (e instanceof ConfigError) throw e;
|
|
4319
|
-
return null;
|
|
4320
|
-
}
|
|
4321
|
-
}
|
|
4322
|
-
var ED25519_SPKI_HEADER = Buffer.from("302a300506032b6570032100", "hex");
|
|
4323
|
-
function parseSSHPublicKey(base64) {
|
|
4324
|
-
try {
|
|
4325
|
-
const buf = Buffer.from(base64, "base64");
|
|
4326
|
-
let offset = 0;
|
|
4327
|
-
if (buf.length < 4) return null;
|
|
4328
|
-
const algLen = buf.readUInt32BE(offset);
|
|
4329
|
-
offset += 4;
|
|
4330
|
-
if (offset + algLen + 4 > buf.length) return null;
|
|
4331
|
-
if (buf.subarray(offset, offset + algLen).toString("ascii") !== "ssh-ed25519") return null;
|
|
4332
|
-
offset += algLen;
|
|
4333
|
-
const keyLen = buf.readUInt32BE(offset);
|
|
4334
|
-
offset += 4;
|
|
4335
|
-
if (keyLen !== 32 || offset + keyLen > buf.length) return null;
|
|
4336
|
-
return buf.subarray(offset, offset + keyLen);
|
|
4337
|
-
} catch {
|
|
4338
|
-
return null;
|
|
4339
|
-
}
|
|
4340
|
-
}
|
|
4341
|
-
async function verifySkillSignature(contentDir, signing, opts) {
|
|
4342
|
-
if (!signing) {
|
|
4343
|
-
const fallback = await tryServerFallback(contentDir, opts);
|
|
4344
|
-
if (fallback?.status === "verified_by_server") return fallback;
|
|
4345
|
-
return {
|
|
4346
|
-
status: "failed",
|
|
4347
|
-
error: fallback?.error ?? "Skill is not signed",
|
|
4348
|
-
...fallback?.mismatched?.length && { mismatched: fallback.mismatched }
|
|
4349
|
-
};
|
|
4350
|
-
}
|
|
4351
|
-
const publicKeyBase64 = opts?.fetchPublicKey ? await opts.fetchPublicKey(signing.public_key_id) ?? "" : "";
|
|
4352
|
-
if (!publicKeyBase64) {
|
|
4353
|
-
return localFail(
|
|
4354
|
-
contentDir,
|
|
4355
|
-
opts,
|
|
4356
|
-
`Unknown signing key: ${signing.public_key_id}. Please update CLI.`,
|
|
4357
|
-
"Unknown signing key \u2014 consider updating CLI"
|
|
4358
|
-
);
|
|
4359
|
-
}
|
|
4360
|
-
const rawKey = parseSSHPublicKey(publicKeyBase64);
|
|
4361
|
-
if (!rawKey) {
|
|
4362
|
-
return localFail(
|
|
4363
|
-
contentDir,
|
|
4364
|
-
opts,
|
|
4365
|
-
`Malformed public key for key ID: ${signing.public_key_id}`,
|
|
4366
|
-
"Malformed public key from server \u2014 consider updating CLI",
|
|
4367
|
-
signing.public_key_id
|
|
4368
|
-
);
|
|
4369
|
-
}
|
|
4370
|
-
if (!ed25519Verify(signing, rawKey)) {
|
|
4371
|
-
return localFail(
|
|
4372
|
-
contentDir,
|
|
4373
|
-
opts,
|
|
4374
|
-
"Invalid signature",
|
|
4375
|
-
"Signature mismatch \u2014 key may be outdated or file was modified",
|
|
4376
|
-
signing.public_key_id
|
|
4377
|
-
);
|
|
4378
|
-
}
|
|
4379
|
-
const bindingError = checkNameVersionBinding(signing, opts);
|
|
4380
|
-
if (bindingError) {
|
|
4381
|
-
return { status: "failed", error: bindingError, publicKeyId: signing.public_key_id };
|
|
4382
|
-
}
|
|
4383
|
-
const integrityFailure = await checkFileIntegrity(signing, contentDir, opts);
|
|
4384
|
-
if (integrityFailure) return integrityFailure;
|
|
4385
|
-
const signedFiles = new Set(Object.keys(signing.files));
|
|
4386
|
-
const extraFiles = listFilesRecursive(contentDir).filter(
|
|
4387
|
-
(f) => f !== "_meta.json" && !signedFiles.has(f)
|
|
4388
|
-
);
|
|
4389
|
-
return { status: "verified", publicKeyId: signing.public_key_id, filesChecked: signedFiles.size, extraFiles };
|
|
4390
|
-
}
|
|
4391
|
-
async function localFail(contentDir, opts, localError, serverHint, keyId) {
|
|
4392
|
-
const fallback = await tryServerFallback(contentDir, opts);
|
|
4393
|
-
if (fallback?.status === "verified_by_server") {
|
|
4394
|
-
return { ...fallback, error: serverHint };
|
|
4395
|
-
}
|
|
4396
|
-
return {
|
|
4397
|
-
status: "failed",
|
|
4398
|
-
error: localError,
|
|
4399
|
-
...keyId && { publicKeyId: keyId },
|
|
4400
|
-
...fallback?.mismatched?.length && { mismatched: fallback.mismatched }
|
|
4401
|
-
};
|
|
4402
|
-
}
|
|
4403
|
-
function ed25519Verify(signing, rawKey) {
|
|
4404
|
-
const payloadObj = { files: signing.files, public_key_id: signing.public_key_id };
|
|
4405
|
-
if (signing.name !== void 0) payloadObj.name = signing.name;
|
|
4406
|
-
if (signing.version !== void 0) payloadObj.version = signing.version;
|
|
4407
|
-
const message = Buffer.from(canonicalize(payloadObj), "utf-8");
|
|
4408
|
-
const sig = Buffer.from(signing.signature, "base64");
|
|
4409
|
-
const spkiKey = Buffer.concat([ED25519_SPKI_HEADER, rawKey]);
|
|
4410
|
-
const keyObj = createPublicKey({ key: spkiKey, format: "der", type: "spki" });
|
|
4411
|
-
return cryptoVerify(null, message, keyObj, sig);
|
|
4412
|
-
}
|
|
4413
|
-
function checkNameVersionBinding(signing, opts) {
|
|
4414
|
-
if (signing.name !== void 0 && opts?.skillName !== void 0 && signing.name !== opts.skillName) {
|
|
4415
|
-
return `Skill name mismatch: signature binds "${signing.name}" but installing as "${opts.skillName}"`;
|
|
4416
|
-
}
|
|
4417
|
-
if (signing.version !== void 0 && opts?.skillVersion !== void 0 && signing.version !== opts.skillVersion) {
|
|
4418
|
-
return `Skill version mismatch: signature binds "${signing.version}" but package declares "${opts.skillVersion}"`;
|
|
4419
|
-
}
|
|
4420
|
-
return null;
|
|
4421
|
-
}
|
|
4422
|
-
async function checkFileIntegrity(signing, contentDir, opts) {
|
|
4423
|
-
const resolvedContentDir = resolve2(contentDir);
|
|
4424
|
-
for (const [filename, expectedHash] of Object.entries(signing.files)) {
|
|
4425
|
-
const resolvedPath = resolve2(join6(contentDir, filename));
|
|
4426
|
-
if (!resolvedPath.startsWith(resolvedContentDir + sep2)) {
|
|
4427
|
-
return { status: "failed", error: `Path traversal detected in signing manifest: ${filename}`, publicKeyId: signing.public_key_id };
|
|
4428
|
-
}
|
|
4429
|
-
let bytes;
|
|
4430
|
-
try {
|
|
4431
|
-
bytes = readFileSync3(resolvedPath);
|
|
4432
|
-
} catch {
|
|
4433
|
-
return { status: "failed", error: `File missing: ${filename}`, publicKeyId: signing.public_key_id };
|
|
4434
|
-
}
|
|
4435
|
-
const actual = "sha256:" + createHash2("sha256").update(bytes).digest("hex");
|
|
4436
|
-
if (actual !== expectedHash) {
|
|
4437
|
-
return localFail(
|
|
4438
|
-
contentDir,
|
|
4439
|
-
opts,
|
|
4440
|
-
`File integrity check failed: ${filename}`,
|
|
4441
|
-
"_meta.json may be corrupted",
|
|
4442
|
-
signing.public_key_id
|
|
4443
|
-
);
|
|
4444
|
-
}
|
|
4445
|
-
}
|
|
4446
|
-
return null;
|
|
4447
|
-
}
|
|
4448
|
-
async function tryServerFallback(contentDir, opts) {
|
|
4449
|
-
if (!opts?.serverSideVerify || !opts.skillName) return null;
|
|
4450
|
-
const fileHashes = computeFileHashes(contentDir);
|
|
4451
|
-
const result = await opts.serverSideVerify(
|
|
4452
|
-
opts.skillName,
|
|
4453
|
-
opts.skillVersion,
|
|
4454
|
-
fileHashes
|
|
4455
|
-
);
|
|
4456
|
-
if (!result) return null;
|
|
4457
|
-
if (result.verified) {
|
|
4458
|
-
return {
|
|
4459
|
-
status: "verified_by_server",
|
|
4460
|
-
filesChecked: Object.keys(fileHashes).length,
|
|
4461
|
-
serverVersion: result.version
|
|
4462
|
-
};
|
|
4463
|
-
}
|
|
4464
|
-
return {
|
|
4465
|
-
status: "failed",
|
|
4466
|
-
filesChecked: Object.keys(fileHashes).length,
|
|
4467
|
-
mismatched: result.mismatched,
|
|
4468
|
-
...result.message && { error: result.message }
|
|
4469
|
-
};
|
|
4470
|
-
}
|
|
4471
|
-
function canonicalize(obj) {
|
|
4472
|
-
return JSON.stringify(deepSortKeys(obj));
|
|
4473
|
-
}
|
|
4474
|
-
function compareKeys(a, b) {
|
|
4475
|
-
if (a < b) return -1;
|
|
4476
|
-
if (a > b) return 1;
|
|
4477
|
-
return 0;
|
|
4478
|
-
}
|
|
4479
|
-
function deepSortKeys(val) {
|
|
4480
|
-
if (val === null || typeof val !== "object") return val;
|
|
4481
|
-
if (Array.isArray(val)) return val.map(deepSortKeys);
|
|
4482
|
-
const sorted = {};
|
|
4483
|
-
for (const key of Object.keys(val).sort(compareKeys)) {
|
|
4484
|
-
sorted[key] = deepSortKeys(val[key]);
|
|
4485
|
-
}
|
|
4486
|
-
return sorted;
|
|
4487
|
-
}
|
|
4488
|
-
async function serverSideVerify(client, skillName, version, files) {
|
|
4489
|
-
try {
|
|
4490
|
-
const body = { skillName, files };
|
|
4491
|
-
if (version !== void 0) body.version = version;
|
|
4492
|
-
const result = await client.privatePost(
|
|
4493
|
-
"/api/v5/skill/verify",
|
|
4494
|
-
body
|
|
4495
|
-
);
|
|
4496
|
-
return result.data;
|
|
4497
|
-
} catch (e) {
|
|
4498
|
-
if (e instanceof ConfigError) throw e;
|
|
4499
|
-
return null;
|
|
4500
|
-
}
|
|
4501
|
-
}
|
|
4502
4267
|
var DEFAULT_MAX_TOTAL_BYTES = 100 * 1024 * 1024;
|
|
4503
4268
|
var DEFAULT_MAX_FILES = 1e3;
|
|
4504
4269
|
var DEFAULT_MAX_COMPRESSION_RATIO = 100;
|
|
@@ -4537,7 +4302,7 @@ async function extractSkillZip(zipPath, targetDir, limits) {
|
|
|
4537
4302
|
const maxTotalBytes = limits?.maxTotalBytes ?? DEFAULT_MAX_TOTAL_BYTES;
|
|
4538
4303
|
const maxFiles = limits?.maxFiles ?? DEFAULT_MAX_FILES;
|
|
4539
4304
|
const maxCompressionRatio = limits?.maxCompressionRatio ?? DEFAULT_MAX_COMPRESSION_RATIO;
|
|
4540
|
-
const resolvedTarget =
|
|
4305
|
+
const resolvedTarget = resolve2(targetDir);
|
|
4541
4306
|
mkdirSync3(resolvedTarget, { recursive: true });
|
|
4542
4307
|
return new Promise((resolvePromise, reject) => {
|
|
4543
4308
|
yauzl.open(zipPath, { lazyEntries: true }, (err, zipfile) => {
|
|
@@ -4577,11 +4342,11 @@ async function extractSkillZip(zipPath, targetDir, limits) {
|
|
|
4577
4342
|
});
|
|
4578
4343
|
}
|
|
4579
4344
|
function readMetaJson(contentDir) {
|
|
4580
|
-
const metaPath =
|
|
4345
|
+
const metaPath = join5(contentDir, "_meta.json");
|
|
4581
4346
|
if (!existsSync(metaPath)) {
|
|
4582
4347
|
throw new Error(`_meta.json not found in ${contentDir}. Invalid skill package.`);
|
|
4583
4348
|
}
|
|
4584
|
-
const raw =
|
|
4349
|
+
const raw = readFileSync2(metaPath, "utf-8");
|
|
4585
4350
|
let parsed;
|
|
4586
4351
|
try {
|
|
4587
4352
|
parsed = JSON.parse(raw);
|
|
@@ -4599,49 +4364,22 @@ function readMetaJson(contentDir) {
|
|
|
4599
4364
|
name: String(meta.name),
|
|
4600
4365
|
version: String(meta.version),
|
|
4601
4366
|
title: typeof meta.title === "string" ? meta.title : "",
|
|
4602
|
-
description: typeof meta.description === "string" ? meta.description : ""
|
|
4603
|
-
signing: parseSigningBlock(meta.signing)
|
|
4604
|
-
};
|
|
4605
|
-
}
|
|
4606
|
-
function tryReadMetaJson(contentDir) {
|
|
4607
|
-
try {
|
|
4608
|
-
return readMetaJson(contentDir);
|
|
4609
|
-
} catch {
|
|
4610
|
-
return null;
|
|
4611
|
-
}
|
|
4612
|
-
}
|
|
4613
|
-
function parseSigningBlock(raw) {
|
|
4614
|
-
if (!raw || typeof raw !== "object") return void 0;
|
|
4615
|
-
const s = raw;
|
|
4616
|
-
if (typeof s.signature !== "string" || typeof s.public_key_id !== "string" || !s.files) {
|
|
4617
|
-
return void 0;
|
|
4618
|
-
}
|
|
4619
|
-
const rawFiles = s.files;
|
|
4620
|
-
const files = {};
|
|
4621
|
-
for (const [key, value] of Object.entries(rawFiles)) {
|
|
4622
|
-
if (typeof value === "string") files[key] = value;
|
|
4623
|
-
}
|
|
4624
|
-
return {
|
|
4625
|
-
signature: s.signature,
|
|
4626
|
-
public_key_id: s.public_key_id,
|
|
4627
|
-
files,
|
|
4628
|
-
...typeof s.name === "string" && { name: s.name },
|
|
4629
|
-
...typeof s.version === "string" && { version: s.version }
|
|
4367
|
+
description: typeof meta.description === "string" ? meta.description : ""
|
|
4630
4368
|
};
|
|
4631
4369
|
}
|
|
4632
4370
|
function validateSkillMdExists(contentDir) {
|
|
4633
|
-
const skillMdPath =
|
|
4371
|
+
const skillMdPath = join5(contentDir, "SKILL.md");
|
|
4634
4372
|
if (!existsSync(skillMdPath)) {
|
|
4635
4373
|
throw new Error(`SKILL.md not found in ${contentDir}. Invalid skill package.`);
|
|
4636
4374
|
}
|
|
4637
4375
|
}
|
|
4638
|
-
var DEFAULT_REGISTRY_PATH =
|
|
4376
|
+
var DEFAULT_REGISTRY_PATH = join6(homedir4(), ".okx", "skills", "registry.json");
|
|
4639
4377
|
function readRegistry(registryPath = DEFAULT_REGISTRY_PATH) {
|
|
4640
4378
|
if (!existsSync2(registryPath)) {
|
|
4641
4379
|
return { version: 1, skills: {} };
|
|
4642
4380
|
}
|
|
4643
4381
|
try {
|
|
4644
|
-
const raw =
|
|
4382
|
+
const raw = readFileSync3(registryPath, "utf-8");
|
|
4645
4383
|
return JSON.parse(raw);
|
|
4646
4384
|
} catch {
|
|
4647
4385
|
return { version: 1, skills: {} };
|
|
@@ -4651,7 +4389,7 @@ function writeRegistry(registry, registryPath = DEFAULT_REGISTRY_PATH) {
|
|
|
4651
4389
|
mkdirSync4(dirname3(registryPath), { recursive: true });
|
|
4652
4390
|
writeFileSync3(registryPath, JSON.stringify(registry, null, 2) + "\n", "utf-8");
|
|
4653
4391
|
}
|
|
4654
|
-
function upsertSkillRecord(meta, registryPath = DEFAULT_REGISTRY_PATH
|
|
4392
|
+
function upsertSkillRecord(meta, registryPath = DEFAULT_REGISTRY_PATH) {
|
|
4655
4393
|
const registry = readRegistry(registryPath);
|
|
4656
4394
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
4657
4395
|
const existing = registry.skills[meta.name];
|
|
@@ -4661,8 +4399,7 @@ function upsertSkillRecord(meta, registryPath = DEFAULT_REGISTRY_PATH, verificat
|
|
|
4661
4399
|
description: meta.description,
|
|
4662
4400
|
installedAt: existing?.installedAt ?? now,
|
|
4663
4401
|
updatedAt: now,
|
|
4664
|
-
source: "marketplace"
|
|
4665
|
-
...verification !== void 0 && { verification }
|
|
4402
|
+
source: "marketplace"
|
|
4666
4403
|
};
|
|
4667
4404
|
writeRegistry(registry, registryPath);
|
|
4668
4405
|
}
|
|
@@ -12458,12 +12195,12 @@ function createToolRunner(client, config) {
|
|
|
12458
12195
|
};
|
|
12459
12196
|
}
|
|
12460
12197
|
function configFilePath() {
|
|
12461
|
-
return
|
|
12198
|
+
return join7(homedir5(), ".okx", "config.toml");
|
|
12462
12199
|
}
|
|
12463
12200
|
function readFullConfig() {
|
|
12464
12201
|
const path42 = configFilePath();
|
|
12465
12202
|
if (!existsSync3(path42)) return { profiles: {} };
|
|
12466
|
-
const raw =
|
|
12203
|
+
const raw = readFileSync4(path42, "utf-8");
|
|
12467
12204
|
try {
|
|
12468
12205
|
return parse(raw);
|
|
12469
12206
|
} catch (err) {
|
|
@@ -12613,7 +12350,7 @@ async function loadConfig(cli) {
|
|
|
12613
12350
|
verbose: cli.verbose ?? false
|
|
12614
12351
|
};
|
|
12615
12352
|
}
|
|
12616
|
-
var CACHE_FILE =
|
|
12353
|
+
var CACHE_FILE = join8(homedir6(), ".okx", "update-check.json");
|
|
12617
12354
|
var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
12618
12355
|
var NEGATIVE_CHECK_INTERVAL_MS = 60 * 60 * 1e3;
|
|
12619
12356
|
var DEFAULT_REGISTRY = "https://registry.npmjs.org/";
|
|
@@ -12621,7 +12358,7 @@ var FETCH_TIMEOUT_MS = 3e3;
|
|
|
12621
12358
|
function readCache2() {
|
|
12622
12359
|
try {
|
|
12623
12360
|
if (existsSync4(CACHE_FILE)) {
|
|
12624
|
-
return JSON.parse(
|
|
12361
|
+
return JSON.parse(readFileSync5(CACHE_FILE, "utf-8"));
|
|
12625
12362
|
}
|
|
12626
12363
|
} catch {
|
|
12627
12364
|
}
|
|
@@ -12629,7 +12366,7 @@ function readCache2() {
|
|
|
12629
12366
|
}
|
|
12630
12367
|
function writeCache2(cache) {
|
|
12631
12368
|
try {
|
|
12632
|
-
mkdirSync6(
|
|
12369
|
+
mkdirSync6(join8(homedir6(), ".okx"), { recursive: true });
|
|
12633
12370
|
writeFileSync5(CACHE_FILE, JSON.stringify(cache, null, 2), "utf-8");
|
|
12634
12371
|
} catch {
|
|
12635
12372
|
}
|
|
@@ -12659,13 +12396,13 @@ function buildNpmrcCandidates() {
|
|
|
12659
12396
|
let dir = process.cwd();
|
|
12660
12397
|
const root = dir.startsWith("/") ? "/" : dir.slice(0, 3);
|
|
12661
12398
|
while (true) {
|
|
12662
|
-
add(
|
|
12399
|
+
add(join8(dir, ".npmrc"));
|
|
12663
12400
|
if (dir === root) break;
|
|
12664
|
-
const parent =
|
|
12401
|
+
const parent = join8(dir, "..");
|
|
12665
12402
|
if (parent === dir) break;
|
|
12666
12403
|
dir = parent;
|
|
12667
12404
|
}
|
|
12668
|
-
add(
|
|
12405
|
+
add(join8(homedir6(), ".npmrc"));
|
|
12669
12406
|
if (process.platform !== "win32") {
|
|
12670
12407
|
add("/etc/npmrc");
|
|
12671
12408
|
}
|
|
@@ -12674,7 +12411,7 @@ function buildNpmrcCandidates() {
|
|
|
12674
12411
|
function readNpmrcRegistry(filePath) {
|
|
12675
12412
|
try {
|
|
12676
12413
|
if (!existsSync4(filePath)) return null;
|
|
12677
|
-
const lines =
|
|
12414
|
+
const lines = readFileSync5(filePath, "utf-8").split(/\r?\n/);
|
|
12678
12415
|
for (const line of lines) {
|
|
12679
12416
|
const trimmed = line.trim();
|
|
12680
12417
|
if (trimmed.startsWith("#") || !trimmed.includes("=")) continue;
|
|
@@ -13001,14 +12738,6 @@ function runSetup(options) {
|
|
|
13001
12738
|
`);
|
|
13002
12739
|
}
|
|
13003
12740
|
}
|
|
13004
|
-
var HttpStatusError = class extends Error {
|
|
13005
|
-
statusCode;
|
|
13006
|
-
constructor(statusCode) {
|
|
13007
|
-
super(`HTTP ${statusCode}`);
|
|
13008
|
-
this.name = "HttpStatusError";
|
|
13009
|
-
this.statusCode = statusCode;
|
|
13010
|
-
}
|
|
13011
|
-
};
|
|
13012
12741
|
function isRedirect(statusCode) {
|
|
13013
12742
|
return statusCode !== void 0 && statusCode >= 300 && statusCode < 400;
|
|
13014
12743
|
}
|
|
@@ -13023,7 +12752,7 @@ function validateRedirect(res, requestUrl, redirectCount, maxRedirects) {
|
|
|
13023
12752
|
return location;
|
|
13024
12753
|
}
|
|
13025
12754
|
function fetchResponse(url, timeoutMs) {
|
|
13026
|
-
return new Promise((
|
|
12755
|
+
return new Promise((resolve3, reject) => {
|
|
13027
12756
|
let redirects = 0;
|
|
13028
12757
|
const maxRedirects = 5;
|
|
13029
12758
|
function doRequest(requestUrl) {
|
|
@@ -13041,14 +12770,10 @@ function fetchResponse(url, timeoutMs) {
|
|
|
13041
12770
|
return;
|
|
13042
12771
|
}
|
|
13043
12772
|
if (res.statusCode !== 200) {
|
|
13044
|
-
|
|
13045
|
-
reject(new Error("HTTP unknown"));
|
|
13046
|
-
} else {
|
|
13047
|
-
reject(new HttpStatusError(res.statusCode));
|
|
13048
|
-
}
|
|
12773
|
+
reject(new Error(`HTTP ${res.statusCode ?? "unknown"}`));
|
|
13049
12774
|
return;
|
|
13050
12775
|
}
|
|
13051
|
-
|
|
12776
|
+
resolve3(res);
|
|
13052
12777
|
});
|
|
13053
12778
|
req.on("error", reject);
|
|
13054
12779
|
req.on("timeout", () => {
|
|
@@ -13061,10 +12786,10 @@ function fetchResponse(url, timeoutMs) {
|
|
|
13061
12786
|
}
|
|
13062
12787
|
function download(url, destPath, timeoutMs) {
|
|
13063
12788
|
return fetchResponse(url, timeoutMs).then(
|
|
13064
|
-
(res) => new Promise((
|
|
12789
|
+
(res) => new Promise((resolve3, reject) => {
|
|
13065
12790
|
const file = createWriteStream2(destPath);
|
|
13066
12791
|
res.pipe(file);
|
|
13067
|
-
file.on("finish", () => file.close(() =>
|
|
12792
|
+
file.on("finish", () => file.close(() => resolve3()));
|
|
13068
12793
|
file.on("error", (err) => {
|
|
13069
12794
|
try {
|
|
13070
12795
|
unlinkSync3(destPath);
|
|
@@ -13077,10 +12802,10 @@ function download(url, destPath, timeoutMs) {
|
|
|
13077
12802
|
}
|
|
13078
12803
|
function downloadText(url, timeoutMs) {
|
|
13079
12804
|
return fetchResponse(url, timeoutMs).then(
|
|
13080
|
-
(res) => new Promise((
|
|
12805
|
+
(res) => new Promise((resolve3, reject) => {
|
|
13081
12806
|
const chunks = [];
|
|
13082
12807
|
res.on("data", (chunk) => chunks.push(chunk));
|
|
13083
|
-
res.on("end", () =>
|
|
12808
|
+
res.on("end", () => resolve3(Buffer.concat(chunks).toString("utf8")));
|
|
13084
12809
|
res.on("error", reject);
|
|
13085
12810
|
})
|
|
13086
12811
|
);
|
|
@@ -13109,10 +12834,10 @@ function getBinaryName() {
|
|
|
13109
12834
|
return platform() === "win32" ? "okx-pilot.exe" : "okx-pilot";
|
|
13110
12835
|
}
|
|
13111
12836
|
function hashFile(filePath) {
|
|
13112
|
-
const buf =
|
|
12837
|
+
const buf = readFileSync7(filePath);
|
|
13113
12838
|
return {
|
|
13114
12839
|
size: buf.byteLength,
|
|
13115
|
-
sha256:
|
|
12840
|
+
sha256: createHash("sha256").update(buf).digest("hex")
|
|
13116
12841
|
};
|
|
13117
12842
|
}
|
|
13118
12843
|
function getPilotStatus(binaryPath, opts) {
|
|
@@ -13227,7 +12952,7 @@ async function installPilotBinary(destPath, sources = CDN_SOURCES, onProgress) {
|
|
|
13227
12952
|
if (earlyResult) return earlyResult;
|
|
13228
12953
|
const platformDir = getPlatformDir();
|
|
13229
12954
|
const binaryName = getBinaryName();
|
|
13230
|
-
const resolvedDest = destPath ??
|
|
12955
|
+
const resolvedDest = destPath ?? join10(homedir8(), ".okx", "bin", binaryName);
|
|
13231
12956
|
const tmpPath = resolvedDest + ".tmp";
|
|
13232
12957
|
mkdirSync8(dirname6(resolvedDest), { recursive: true });
|
|
13233
12958
|
const localHash = existsSync6(resolvedDest) ? hashFile(resolvedDest) : null;
|
|
@@ -13279,41 +13004,9 @@ function removePilotBinary(binaryPath) {
|
|
|
13279
13004
|
}
|
|
13280
13005
|
}
|
|
13281
13006
|
var AUTH_CDN_PATH_PREFIX = "/upgradeapp/tools/oauth";
|
|
13282
|
-
var LINUX_ARM64_FALLBACK_DIR = "linux-x64";
|
|
13283
13007
|
function getAuthBinaryName() {
|
|
13284
13008
|
return platform2() === "win32" ? "okx-auth.exe" : "okx-auth";
|
|
13285
13009
|
}
|
|
13286
|
-
async function _resolveAuthPlatformFromNative(native, sources, timeoutMs) {
|
|
13287
|
-
if (native !== "linux-arm64") {
|
|
13288
|
-
return native;
|
|
13289
|
-
}
|
|
13290
|
-
const checksumPath = `${AUTH_CDN_PATH_PREFIX}/linux-arm64/checksum.json`;
|
|
13291
|
-
let got404 = false;
|
|
13292
|
-
for (const { host, protocol } of sources) {
|
|
13293
|
-
const url = `${protocol}://${host}${checksumPath}`;
|
|
13294
|
-
try {
|
|
13295
|
-
await downloadText(url, timeoutMs);
|
|
13296
|
-
return "linux-arm64";
|
|
13297
|
-
} catch (err) {
|
|
13298
|
-
if (err instanceof HttpStatusError && err.statusCode === 404) {
|
|
13299
|
-
got404 = true;
|
|
13300
|
-
break;
|
|
13301
|
-
}
|
|
13302
|
-
}
|
|
13303
|
-
}
|
|
13304
|
-
if (got404) {
|
|
13305
|
-
console.warn(
|
|
13306
|
-
"[okx-auth] Native linux-arm64 binary not yet on CDN. Falling back to linux-x64 binary \u2014 x86_64 emulation (binfmt_misc/Rosetta) required."
|
|
13307
|
-
);
|
|
13308
|
-
return LINUX_ARM64_FALLBACK_DIR;
|
|
13309
|
-
}
|
|
13310
|
-
return "linux-arm64";
|
|
13311
|
-
}
|
|
13312
|
-
async function resolveAuthPlatformDir(sources = CDN_SOURCES, timeoutMs = DOWNLOAD_TIMEOUT_MS) {
|
|
13313
|
-
const native = getPlatformDir();
|
|
13314
|
-
if (!native) return null;
|
|
13315
|
-
return _resolveAuthPlatformFromNative(native, sources, timeoutMs);
|
|
13316
|
-
}
|
|
13317
13010
|
function getAuthStatus(binaryPath, opts) {
|
|
13318
13011
|
const resolvedPath = binaryPath ?? getAuthBinaryPath();
|
|
13319
13012
|
const platformDir = getPlatformDir();
|
|
@@ -13327,7 +13020,7 @@ function getAuthStatus(binaryPath, opts) {
|
|
|
13327
13020
|
return { binaryPath: resolvedPath, exists: true, platform: platformDir, fileSize: size, sha256 };
|
|
13328
13021
|
}
|
|
13329
13022
|
async function fetchAuthCdnChecksum(sources = CDN_SOURCES, timeoutMs = DOWNLOAD_TIMEOUT_MS) {
|
|
13330
|
-
const platformDir =
|
|
13023
|
+
const platformDir = getPlatformDir();
|
|
13331
13024
|
if (!platformDir) return null;
|
|
13332
13025
|
const checksumPath = `${AUTH_CDN_PATH_PREFIX}/${platformDir}/checksum.json`;
|
|
13333
13026
|
for (const { host, protocol } of sources) {
|
|
@@ -13396,51 +13089,12 @@ function installPreChecks2(destPath, sources) {
|
|
|
13396
13089
|
function isLocalUpToDate2(localHash, checksum) {
|
|
13397
13090
|
return localHash !== null && localHash.size === checksum.size && localHash.sha256 === checksum.sha256;
|
|
13398
13091
|
}
|
|
13399
|
-
async function tryInstallFromOneSource(ctx) {
|
|
13400
|
-
const { host, protocol, platformDir, checksumPath, binaryPath, tmpPath, resolvedDest, localHash, onProgress } = ctx;
|
|
13401
|
-
try {
|
|
13402
|
-
const checksum = await fetchAndValidateChecksum2(
|
|
13403
|
-
host,
|
|
13404
|
-
protocol,
|
|
13405
|
-
checksumPath,
|
|
13406
|
-
platformDir,
|
|
13407
|
-
DOWNLOAD_TIMEOUT_MS,
|
|
13408
|
-
onProgress
|
|
13409
|
-
);
|
|
13410
|
-
if (isLocalUpToDate2(localHash, checksum)) {
|
|
13411
|
-
onProgress?.("Already up to date (checksum match)");
|
|
13412
|
-
return { kind: "done", result: { status: "up-to-date", source: host } };
|
|
13413
|
-
}
|
|
13414
|
-
await downloadAndVerify2(host, protocol, binaryPath, tmpPath, checksum, DOWNLOAD_TIMEOUT_MS, onProgress);
|
|
13415
|
-
atomicReplace2(tmpPath, resolvedDest);
|
|
13416
|
-
onProgress?.(`Downloaded and verified from ${host}`);
|
|
13417
|
-
return { kind: "done", result: { status: "installed", source: host } };
|
|
13418
|
-
} catch (err) {
|
|
13419
|
-
try {
|
|
13420
|
-
unlinkSync5(tmpPath);
|
|
13421
|
-
} catch {
|
|
13422
|
-
}
|
|
13423
|
-
const errObj = err instanceof Error ? err : new Error(String(err));
|
|
13424
|
-
onProgress?.(`${host} failed: ${errObj.message}`);
|
|
13425
|
-
return { kind: "next", err: errObj };
|
|
13426
|
-
}
|
|
13427
|
-
}
|
|
13428
|
-
function buildInstallFailureResult(errors) {
|
|
13429
|
-
const formatted = errors.map(({ host, err }) => `${host}: ${err.message}`).join("\n");
|
|
13430
|
-
const allHttpErrors = errors.length > 0 && errors.every(({ err }) => err instanceof HttpStatusError);
|
|
13431
|
-
const prefix = allHttpErrors ? "okx-auth binary not available on CDN for this platform" : "All CDN sources failed";
|
|
13432
|
-
return { status: "failed", error: `${prefix}:
|
|
13433
|
-
${formatted}` };
|
|
13434
|
-
}
|
|
13435
13092
|
async function installAuthBinary(destPath, sources = CDN_SOURCES, onProgress) {
|
|
13436
13093
|
const earlyResult = installPreChecks2(destPath, sources);
|
|
13437
13094
|
if (earlyResult) return earlyResult;
|
|
13438
|
-
const platformDir =
|
|
13439
|
-
if (!platformDir) {
|
|
13440
|
-
return { status: "failed", error: "Unsupported platform" };
|
|
13441
|
-
}
|
|
13095
|
+
const platformDir = getPlatformDir();
|
|
13442
13096
|
const binaryName = getAuthBinaryName();
|
|
13443
|
-
const resolvedDest = destPath ??
|
|
13097
|
+
const resolvedDest = destPath ?? join11(homedir9(), ".okx", "bin", binaryName);
|
|
13444
13098
|
const tmpPath = resolvedDest + ".tmp";
|
|
13445
13099
|
mkdirSync9(dirname7(resolvedDest), { recursive: true });
|
|
13446
13100
|
const localHash = existsSync7(resolvedDest) ? hashFile(resolvedDest) : null;
|
|
@@ -13448,21 +13102,35 @@ async function installAuthBinary(destPath, sources = CDN_SOURCES, onProgress) {
|
|
|
13448
13102
|
const binaryPath = `${AUTH_CDN_PATH_PREFIX}/${platformDir}/${binaryName}`;
|
|
13449
13103
|
const errors = [];
|
|
13450
13104
|
for (const { host, protocol } of sources) {
|
|
13451
|
-
|
|
13452
|
-
|
|
13453
|
-
|
|
13454
|
-
|
|
13455
|
-
|
|
13456
|
-
|
|
13457
|
-
|
|
13458
|
-
|
|
13459
|
-
|
|
13460
|
-
|
|
13461
|
-
|
|
13462
|
-
|
|
13463
|
-
|
|
13105
|
+
try {
|
|
13106
|
+
const checksum = await fetchAndValidateChecksum2(
|
|
13107
|
+
host,
|
|
13108
|
+
protocol,
|
|
13109
|
+
checksumPath,
|
|
13110
|
+
platformDir,
|
|
13111
|
+
DOWNLOAD_TIMEOUT_MS,
|
|
13112
|
+
onProgress
|
|
13113
|
+
);
|
|
13114
|
+
if (isLocalUpToDate2(localHash, checksum)) {
|
|
13115
|
+
onProgress?.("Already up to date (checksum match)");
|
|
13116
|
+
return { status: "up-to-date", source: host };
|
|
13117
|
+
}
|
|
13118
|
+
await downloadAndVerify2(host, protocol, binaryPath, tmpPath, checksum, DOWNLOAD_TIMEOUT_MS, onProgress);
|
|
13119
|
+
atomicReplace2(tmpPath, resolvedDest);
|
|
13120
|
+
onProgress?.(`Downloaded and verified from ${host}`);
|
|
13121
|
+
return { status: "installed", source: host };
|
|
13122
|
+
} catch (err) {
|
|
13123
|
+
try {
|
|
13124
|
+
unlinkSync5(tmpPath);
|
|
13125
|
+
} catch {
|
|
13126
|
+
}
|
|
13127
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
13128
|
+
errors.push(`${host}: ${msg}`);
|
|
13129
|
+
onProgress?.(`${host} failed: ${msg}`);
|
|
13130
|
+
}
|
|
13464
13131
|
}
|
|
13465
|
-
return
|
|
13132
|
+
return { status: "failed", error: `All CDN sources failed:
|
|
13133
|
+
${errors.join("\n")}` };
|
|
13466
13134
|
}
|
|
13467
13135
|
function removeAuthBinary(binaryPath) {
|
|
13468
13136
|
const resolvedPath = binaryPath ?? getAuthBinaryPath();
|
|
@@ -13477,12 +13145,12 @@ function removeAuthBinary(binaryPath) {
|
|
|
13477
13145
|
throw new Error(`Failed to remove ${resolvedPath}: ${msg}`);
|
|
13478
13146
|
}
|
|
13479
13147
|
}
|
|
13480
|
-
var CACHE_PATH =
|
|
13148
|
+
var CACHE_PATH = join12(homedir10(), ".okx", "auth-binary-check.json");
|
|
13481
13149
|
var CHECK_INTERVAL_MS2 = 2 * 60 * 60 * 1e3;
|
|
13482
13150
|
function readCache3() {
|
|
13483
13151
|
try {
|
|
13484
13152
|
if (!existsSync8(CACHE_PATH)) return null;
|
|
13485
|
-
const data = JSON.parse(
|
|
13153
|
+
const data = JSON.parse(readFileSync8(CACHE_PATH, "utf-8"));
|
|
13486
13154
|
if (typeof data.cdnSha256 !== "string" || typeof data.checkedAt !== "number") return null;
|
|
13487
13155
|
return { cdnSha256: data.cdnSha256, checkedAt: data.checkedAt };
|
|
13488
13156
|
} catch {
|
|
@@ -13491,7 +13159,7 @@ function readCache3() {
|
|
|
13491
13159
|
}
|
|
13492
13160
|
function writeCache3(cdnSha256) {
|
|
13493
13161
|
try {
|
|
13494
|
-
mkdirSync10(
|
|
13162
|
+
mkdirSync10(join12(homedir10(), ".okx"), { recursive: true });
|
|
13495
13163
|
writeFileSync7(CACHE_PATH, JSON.stringify({ cdnSha256, checkedAt: Date.now() }, null, 2), "utf-8");
|
|
13496
13164
|
} catch {
|
|
13497
13165
|
}
|
|
@@ -13626,7 +13294,7 @@ function markFailedIfSCodeError(data) {
|
|
|
13626
13294
|
// src/commands/auth.ts
|
|
13627
13295
|
function runOkxAuth(args) {
|
|
13628
13296
|
const binPath = getAuthBinaryPath();
|
|
13629
|
-
return new Promise((
|
|
13297
|
+
return new Promise((resolve3, reject) => {
|
|
13630
13298
|
const child = spawn3(binPath, args, {
|
|
13631
13299
|
stdio: "inherit"
|
|
13632
13300
|
});
|
|
@@ -13634,13 +13302,13 @@ function runOkxAuth(args) {
|
|
|
13634
13302
|
reject(new Error(`Failed to spawn okx-auth: ${err.message}`));
|
|
13635
13303
|
});
|
|
13636
13304
|
child.on("close", (code) => {
|
|
13637
|
-
|
|
13305
|
+
resolve3(code ?? 1);
|
|
13638
13306
|
});
|
|
13639
13307
|
});
|
|
13640
13308
|
}
|
|
13641
13309
|
function runOkxAuthCapture(args) {
|
|
13642
13310
|
const binPath = getAuthBinaryPath();
|
|
13643
|
-
return new Promise((
|
|
13311
|
+
return new Promise((resolve3, reject) => {
|
|
13644
13312
|
const child = spawn3(binPath, args, {
|
|
13645
13313
|
stdio: ["inherit", "pipe", "inherit"]
|
|
13646
13314
|
});
|
|
@@ -13650,7 +13318,7 @@ function runOkxAuthCapture(args) {
|
|
|
13650
13318
|
reject(new Error(`Failed to spawn okx-auth: ${err.message}`));
|
|
13651
13319
|
});
|
|
13652
13320
|
child.on("close", (code) => {
|
|
13653
|
-
|
|
13321
|
+
resolve3({
|
|
13654
13322
|
code: code ?? 1,
|
|
13655
13323
|
stdout: Buffer.concat(chunks).toString("utf-8")
|
|
13656
13324
|
});
|
|
@@ -13855,14 +13523,14 @@ async function confirmRemoval(force, binaryPath) {
|
|
|
13855
13523
|
return true;
|
|
13856
13524
|
}
|
|
13857
13525
|
function askConfirmation(prompt2) {
|
|
13858
|
-
return new Promise((
|
|
13526
|
+
return new Promise((resolve3) => {
|
|
13859
13527
|
const rl = readline.createInterface({
|
|
13860
13528
|
input: process.stdin,
|
|
13861
13529
|
output: process.stdout
|
|
13862
13530
|
});
|
|
13863
13531
|
rl.question(prompt2, (answer) => {
|
|
13864
13532
|
rl.close();
|
|
13865
|
-
|
|
13533
|
+
resolve3(answer.trim().toLowerCase() === "y");
|
|
13866
13534
|
});
|
|
13867
13535
|
});
|
|
13868
13536
|
}
|
|
@@ -13894,6 +13562,110 @@ async function handleAuthCommand(action, _rest, v) {
|
|
|
13894
13562
|
}
|
|
13895
13563
|
}
|
|
13896
13564
|
|
|
13565
|
+
// src/commands/outcomes.ts
|
|
13566
|
+
import { spawn as spawn4 } from "child_process";
|
|
13567
|
+
import { existsSync as existsSync9 } from "fs";
|
|
13568
|
+
import { delimiter, join as join13 } from "path";
|
|
13569
|
+
var OUTCOMES_BINARY_NAME = process.platform === "win32" ? "okx-outcomes.exe" : "okx-outcomes";
|
|
13570
|
+
function resolveOutcomesBinaryPath() {
|
|
13571
|
+
const override = process.env.OKX_OUTCOMES_BIN;
|
|
13572
|
+
if (override) {
|
|
13573
|
+
if (existsSync9(override)) return override;
|
|
13574
|
+
errorLine(
|
|
13575
|
+
`Warning: OKX_OUTCOMES_BIN is set to '${override}' but no file exists there; falling back to PATH search.`
|
|
13576
|
+
);
|
|
13577
|
+
}
|
|
13578
|
+
const paths = (process.env.PATH ?? "").split(delimiter);
|
|
13579
|
+
for (const dir of paths) {
|
|
13580
|
+
if (!dir) continue;
|
|
13581
|
+
const full = join13(dir, OUTCOMES_BINARY_NAME);
|
|
13582
|
+
if (existsSync9(full)) return full;
|
|
13583
|
+
}
|
|
13584
|
+
return null;
|
|
13585
|
+
}
|
|
13586
|
+
function printInstallHint() {
|
|
13587
|
+
errorLine("Error: okx-outcomes binary not found in PATH.");
|
|
13588
|
+
errorLine("");
|
|
13589
|
+
errorLine("Install (macOS / Linux):");
|
|
13590
|
+
errorLine(" curl -fsSL https://raw.githubusercontent.com/okx/outcomes/master/install.sh | sh");
|
|
13591
|
+
errorLine("");
|
|
13592
|
+
errorLine("Install (Windows): download okx-outcomes.exe from");
|
|
13593
|
+
errorLine(" https://github.com/okx/outcomes/releases");
|
|
13594
|
+
errorLine("and place it on your PATH.");
|
|
13595
|
+
errorLine("");
|
|
13596
|
+
errorLine("Or set OKX_OUTCOMES_BIN env var to a custom binary path.");
|
|
13597
|
+
}
|
|
13598
|
+
function runOkxOutcomes(args) {
|
|
13599
|
+
const binPath = resolveOutcomesBinaryPath();
|
|
13600
|
+
if (!binPath) {
|
|
13601
|
+
printInstallHint();
|
|
13602
|
+
return Promise.resolve(127);
|
|
13603
|
+
}
|
|
13604
|
+
return new Promise((resolve3, reject) => {
|
|
13605
|
+
const child = spawn4(binPath, args, { stdio: "inherit" });
|
|
13606
|
+
child.on(
|
|
13607
|
+
"error",
|
|
13608
|
+
(err) => reject(new Error(`Failed to spawn okx-outcomes: ${err.message}`))
|
|
13609
|
+
);
|
|
13610
|
+
child.on("close", (code) => resolve3(code ?? 1));
|
|
13611
|
+
});
|
|
13612
|
+
}
|
|
13613
|
+
function printOutcomesHelp() {
|
|
13614
|
+
const lines = [
|
|
13615
|
+
"Usage: okx outcomes <command> [args...]",
|
|
13616
|
+
"",
|
|
13617
|
+
"OKX Outcomes - YES/NO event contract trading via the okx-outcomes binary.",
|
|
13618
|
+
"",
|
|
13619
|
+
"Common commands:",
|
|
13620
|
+
" data events List outcome events",
|
|
13621
|
+
" data event <eventId> Get event detail",
|
|
13622
|
+
" data event-markets <eventId> Event + all its markets (returns asset ids)",
|
|
13623
|
+
" data market <marketId> Get single market detail",
|
|
13624
|
+
" data trending List trending events",
|
|
13625
|
+
" data ticker <assetId> 24h ticker for an outcome asset",
|
|
13626
|
+
" data candles <assetId> OHLCV candles for an outcome asset",
|
|
13627
|
+
" search <keyword> Search events/markets",
|
|
13628
|
+
" (Note: events/event/market etc. live UNDER the `data` namespace \u2014 calling them",
|
|
13629
|
+
" as top-level commands prints the binary's help instead of returning JSON.)",
|
|
13630
|
+
"",
|
|
13631
|
+
" clob price/prices/midpoint(s)/spread(s)/book(s) --asset <id>",
|
|
13632
|
+
" CLOB read-side market data",
|
|
13633
|
+
" clob create-order Place limit order (EIP-712 signed)",
|
|
13634
|
+
" clob market-order Cross book immediately (IOC/FOK)",
|
|
13635
|
+
" clob cancel-oid | cancel-all | heartbeat",
|
|
13636
|
+
"",
|
|
13637
|
+
" ctf split/merge/redeem Conditional token operations",
|
|
13638
|
+
"",
|
|
13639
|
+
" account balance/order/orders/positions/closed-positions/trades",
|
|
13640
|
+
" HMAC-auth account queries",
|
|
13641
|
+
"",
|
|
13642
|
+
" wallet show Show derived wallet address",
|
|
13643
|
+
" status Health check",
|
|
13644
|
+
" setup Interactive .env wizard",
|
|
13645
|
+
"",
|
|
13646
|
+
"Run 'okx outcomes <command> --help' for command-specific help.",
|
|
13647
|
+
"",
|
|
13648
|
+
"Note: place flags after the subcommand, e.g. 'okx outcomes events --json'",
|
|
13649
|
+
" (or before the module: 'okx --json outcomes events'). Writing",
|
|
13650
|
+
" 'okx outcomes --json events' is not supported.",
|
|
13651
|
+
"",
|
|
13652
|
+
"Requires: curl -fsSL https://raw.githubusercontent.com/okx/outcomes/master/install.sh | sh"
|
|
13653
|
+
];
|
|
13654
|
+
for (const line of lines) outputLine(line);
|
|
13655
|
+
}
|
|
13656
|
+
async function handleOutcomesCommand(action, rest, v) {
|
|
13657
|
+
if (!action || action === "--help" || action === "-h" || action === "help") {
|
|
13658
|
+
printOutcomesHelp();
|
|
13659
|
+
return;
|
|
13660
|
+
}
|
|
13661
|
+
const forwardArgs = [action, ...rest];
|
|
13662
|
+
if (v.json && !forwardArgs.includes("--json") && !forwardArgs.includes("-j")) {
|
|
13663
|
+
forwardArgs.push("--json");
|
|
13664
|
+
}
|
|
13665
|
+
const code = await runOkxOutcomes(forwardArgs);
|
|
13666
|
+
if (code !== 0) process.exitCode = code;
|
|
13667
|
+
}
|
|
13668
|
+
|
|
13897
13669
|
// src/commands/diagnose.ts
|
|
13898
13670
|
import dns from "dns/promises";
|
|
13899
13671
|
import net from "net";
|
|
@@ -13919,26 +13691,26 @@ var Report = class {
|
|
|
13919
13691
|
this.lines.push({ key, value });
|
|
13920
13692
|
}
|
|
13921
13693
|
print() {
|
|
13922
|
-
const
|
|
13694
|
+
const sep2 = "\u2500".repeat(52);
|
|
13923
13695
|
outputLine("");
|
|
13924
|
-
outputLine(` \u2500\u2500 Diagnostic Report (copy & share) ${
|
|
13696
|
+
outputLine(` \u2500\u2500 Diagnostic Report (copy & share) ${sep2.slice(35)}`);
|
|
13925
13697
|
for (const { key, value } of this.lines) {
|
|
13926
13698
|
outputLine(` ${key.padEnd(14)} ${value}`);
|
|
13927
13699
|
}
|
|
13928
|
-
outputLine(` ${
|
|
13700
|
+
outputLine(` ${sep2}`);
|
|
13929
13701
|
outputLine("");
|
|
13930
13702
|
}
|
|
13931
13703
|
/** Write report to a file path, returns true on success. */
|
|
13932
13704
|
writeToFile(filePath) {
|
|
13933
13705
|
try {
|
|
13934
|
-
const
|
|
13706
|
+
const sep2 = "\u2500".repeat(52);
|
|
13935
13707
|
const lines = [
|
|
13936
|
-
`\u2500\u2500 Diagnostic Report (copy & share) ${
|
|
13708
|
+
`\u2500\u2500 Diagnostic Report (copy & share) ${sep2.slice(35)}`
|
|
13937
13709
|
];
|
|
13938
13710
|
for (const { key, value } of this.lines) {
|
|
13939
13711
|
lines.push(`${key.padEnd(14)} ${value}`);
|
|
13940
13712
|
}
|
|
13941
|
-
lines.push(
|
|
13713
|
+
lines.push(sep2, "");
|
|
13942
13714
|
fs4.writeFileSync(filePath, lines.join("\n"), "utf8");
|
|
13943
13715
|
return true;
|
|
13944
13716
|
} catch (_e) {
|
|
@@ -13988,7 +13760,7 @@ function sanitize2(value) {
|
|
|
13988
13760
|
import fs5 from "fs";
|
|
13989
13761
|
import path4 from "path";
|
|
13990
13762
|
import os4 from "os";
|
|
13991
|
-
import { spawnSync, spawn as
|
|
13763
|
+
import { spawnSync, spawn as spawn5 } from "child_process";
|
|
13992
13764
|
import { createRequire as createRequire2 } from "module";
|
|
13993
13765
|
import { fileURLToPath } from "url";
|
|
13994
13766
|
var _require2 = createRequire2(import.meta.url);
|
|
@@ -14314,15 +14086,15 @@ async function checkStdioHandshake(entryPath, report) {
|
|
|
14314
14086
|
clientInfo: { name: "okx-diagnose", version: "1.0" }
|
|
14315
14087
|
}
|
|
14316
14088
|
});
|
|
14317
|
-
return new Promise((
|
|
14089
|
+
return new Promise((resolve3) => {
|
|
14318
14090
|
let settled = false;
|
|
14319
14091
|
const settle = (passed) => {
|
|
14320
14092
|
if (settled) return;
|
|
14321
14093
|
settled = true;
|
|
14322
14094
|
clearTimeout(timer);
|
|
14323
|
-
|
|
14095
|
+
resolve3(passed);
|
|
14324
14096
|
};
|
|
14325
|
-
const child =
|
|
14097
|
+
const child = spawn5(process.execPath, [entryPath], {
|
|
14326
14098
|
stdio: ["pipe", "pipe", "pipe"],
|
|
14327
14099
|
env: { ...process.env }
|
|
14328
14100
|
});
|
|
@@ -14441,7 +14213,7 @@ async function cmdDiagnoseMcp(options = {}) {
|
|
|
14441
14213
|
|
|
14442
14214
|
// src/commands/diagnose.ts
|
|
14443
14215
|
var CLI_VERSION = readCliVersion();
|
|
14444
|
-
var GIT_HASH = true ? "
|
|
14216
|
+
var GIT_HASH = true ? "0614700e" : "dev";
|
|
14445
14217
|
function maskKey2(key) {
|
|
14446
14218
|
if (!key) return "(not set)";
|
|
14447
14219
|
if (key.length <= 8) return "****";
|
|
@@ -14458,7 +14230,7 @@ async function checkDns(hostname) {
|
|
|
14458
14230
|
}
|
|
14459
14231
|
async function checkSocket(createFn, successEvent, timeoutMs) {
|
|
14460
14232
|
const t0 = Date.now();
|
|
14461
|
-
return new Promise((
|
|
14233
|
+
return new Promise((resolve3) => {
|
|
14462
14234
|
const socket = createFn();
|
|
14463
14235
|
const cleanup = () => {
|
|
14464
14236
|
socket.removeAllListeners();
|
|
@@ -14466,15 +14238,15 @@ async function checkSocket(createFn, successEvent, timeoutMs) {
|
|
|
14466
14238
|
};
|
|
14467
14239
|
socket.once(successEvent, () => {
|
|
14468
14240
|
cleanup();
|
|
14469
|
-
|
|
14241
|
+
resolve3({ ok: true, ms: Date.now() - t0 });
|
|
14470
14242
|
});
|
|
14471
14243
|
socket.once("timeout", () => {
|
|
14472
14244
|
cleanup();
|
|
14473
|
-
|
|
14245
|
+
resolve3({ ok: false, ms: Date.now() - t0, error: `timed out after ${timeoutMs}ms` });
|
|
14474
14246
|
});
|
|
14475
14247
|
socket.once("error", (err) => {
|
|
14476
14248
|
cleanup();
|
|
14477
|
-
|
|
14249
|
+
resolve3({ ok: false, ms: Date.now() - t0, error: err.message });
|
|
14478
14250
|
});
|
|
14479
14251
|
});
|
|
14480
14252
|
}
|
|
@@ -14802,23 +14574,23 @@ function suggestSubcommand(action, knownActions, knownPaths = []) {
|
|
|
14802
14574
|
|
|
14803
14575
|
// src/commands/upgrade.ts
|
|
14804
14576
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
14805
|
-
import { readFileSync as
|
|
14806
|
-
import { dirname as dirname8, join as
|
|
14577
|
+
import { readFileSync as readFileSync9, writeFileSync as writeFileSync8, mkdirSync as mkdirSync11 } from "fs";
|
|
14578
|
+
import { dirname as dirname8, join as join14 } from "path";
|
|
14807
14579
|
import { homedir as homedir11 } from "os";
|
|
14808
14580
|
var PACKAGES = ["@okx_ai/okx-trade-mcp", "@okx_ai/okx-trade-cli"];
|
|
14809
|
-
var CACHE_FILE2 =
|
|
14581
|
+
var CACHE_FILE2 = join14(homedir11(), ".okx", "last_check");
|
|
14810
14582
|
var THROTTLE_MS = 12 * 60 * 60 * 1e3;
|
|
14811
|
-
var NPM_BIN =
|
|
14583
|
+
var NPM_BIN = join14(dirname8(process.execPath), process.platform === "win32" ? "npm.cmd" : "npm");
|
|
14812
14584
|
function readLastCheck() {
|
|
14813
14585
|
try {
|
|
14814
|
-
return parseInt(
|
|
14586
|
+
return parseInt(readFileSync9(CACHE_FILE2, "utf-8").trim(), 10) || 0;
|
|
14815
14587
|
} catch {
|
|
14816
14588
|
return 0;
|
|
14817
14589
|
}
|
|
14818
14590
|
}
|
|
14819
14591
|
function writeLastCheck() {
|
|
14820
14592
|
try {
|
|
14821
|
-
mkdirSync11(
|
|
14593
|
+
mkdirSync11(join14(homedir11(), ".okx"), { recursive: true });
|
|
14822
14594
|
writeFileSync8(CACHE_FILE2, String(Math.floor(Date.now() / 1e3)), "utf-8");
|
|
14823
14595
|
} catch {
|
|
14824
14596
|
}
|
|
@@ -15974,6 +15746,63 @@ var CLI_REGISTRY = {
|
|
|
15974
15746
|
description: "Upgrade okx CLI and MCP server to the latest stable version",
|
|
15975
15747
|
usage: "okx upgrade [--check] [--beta] [--force] [--json]"
|
|
15976
15748
|
},
|
|
15749
|
+
// ── outcomes ───────────────────────────────────────────────────────────────
|
|
15750
|
+
// External binary wrapper - forwards to the okx-outcomes binary (formerly okx-predict).
|
|
15751
|
+
// All toolName=null because outcomes commands are not exposed as MCP tools.
|
|
15752
|
+
outcomes: {
|
|
15753
|
+
description: "OKX Outcomes markets (YES/NO event contracts) via external okx-outcomes binary",
|
|
15754
|
+
commands: {
|
|
15755
|
+
// Note: events / event / event-markets / market / trending / ticker /
|
|
15756
|
+
// candles live UNDER the `data` namespace in the upstream binary.
|
|
15757
|
+
// Calling them as top-level commands prints the binary's help text
|
|
15758
|
+
// instead of returning JSON. Always invoke as `okx outcomes data <cmd>`.
|
|
15759
|
+
data: {
|
|
15760
|
+
toolName: null,
|
|
15761
|
+
usage: "okx outcomes data <events|event|event-markets|market|trending|ticker|candles> [args...]",
|
|
15762
|
+
description: "Public market data namespace: events, event(-markets), market, trending, ticker, candles"
|
|
15763
|
+
},
|
|
15764
|
+
search: {
|
|
15765
|
+
toolName: null,
|
|
15766
|
+
usage: "okx outcomes search <keyword> [--limit <n>] [--cursor <c>]",
|
|
15767
|
+
description: "Search events/markets by keyword"
|
|
15768
|
+
},
|
|
15769
|
+
account: {
|
|
15770
|
+
toolName: null,
|
|
15771
|
+
usage: "okx outcomes account <balance|order|orders|positions|closed-positions|trades>",
|
|
15772
|
+
description: "HMAC-auth account queries"
|
|
15773
|
+
},
|
|
15774
|
+
clob: {
|
|
15775
|
+
toolName: null,
|
|
15776
|
+
usage: "okx outcomes clob <price|prices|midpoint|midpoints|spread|spreads|book|books|order|orders|trades|create-order|market-order|cancel-oid|cancel-all|heartbeat>",
|
|
15777
|
+
description: "CLOB market data (--asset) + EIP-712 signed order operations"
|
|
15778
|
+
},
|
|
15779
|
+
ctf: {
|
|
15780
|
+
toolName: null,
|
|
15781
|
+
usage: "okx outcomes ctf <split|merge|redeem> --market <id> [--amount <xp>]",
|
|
15782
|
+
description: "Conditional Token Framework: split xp into YES/NO, merge, redeem"
|
|
15783
|
+
},
|
|
15784
|
+
wallet: {
|
|
15785
|
+
toolName: null,
|
|
15786
|
+
usage: "okx outcomes wallet show",
|
|
15787
|
+
description: "Show derived wallet address (from PREDICTIONS_AGENT_PRIVATE_KEY)"
|
|
15788
|
+
},
|
|
15789
|
+
status: {
|
|
15790
|
+
toolName: null,
|
|
15791
|
+
usage: "okx outcomes status [--json]",
|
|
15792
|
+
description: "Health check: API + balance reachability"
|
|
15793
|
+
},
|
|
15794
|
+
setup: {
|
|
15795
|
+
toolName: null,
|
|
15796
|
+
usage: "okx outcomes setup",
|
|
15797
|
+
description: "Interactive .env wizard for PREDICTIONS_* env vars"
|
|
15798
|
+
},
|
|
15799
|
+
shell: {
|
|
15800
|
+
toolName: null,
|
|
15801
|
+
usage: "okx outcomes shell",
|
|
15802
|
+
description: "Interactive REPL (do not invoke from agent context)"
|
|
15803
|
+
}
|
|
15804
|
+
}
|
|
15805
|
+
},
|
|
15977
15806
|
// ── list-tools ──────────────────────────────────────────────────────────────
|
|
15978
15807
|
"list-tools": {
|
|
15979
15808
|
description: "List all available tools and their parameters (use --json for machine-readable output)",
|
|
@@ -16846,6 +16675,30 @@ function parseCli(argv) {
|
|
|
16846
16675
|
}
|
|
16847
16676
|
return { values, positionals };
|
|
16848
16677
|
}
|
|
16678
|
+
function peekFirstPositional(argv) {
|
|
16679
|
+
const booleanFlags = /* @__PURE__ */ new Set();
|
|
16680
|
+
for (const [name, opt] of Object.entries(CLI_OPTIONS)) {
|
|
16681
|
+
const o = opt;
|
|
16682
|
+
if (o.type === "boolean") {
|
|
16683
|
+
booleanFlags.add(name);
|
|
16684
|
+
if (o.short) booleanFlags.add(o.short);
|
|
16685
|
+
}
|
|
16686
|
+
}
|
|
16687
|
+
for (let i = 0; i < argv.length; i++) {
|
|
16688
|
+
const tok = argv[i];
|
|
16689
|
+
if (tok === "--") {
|
|
16690
|
+
const next2 = argv[i + 1];
|
|
16691
|
+
return next2 === void 0 ? void 0 : { module: next2, idx: i + 1 };
|
|
16692
|
+
}
|
|
16693
|
+
if (!tok.startsWith("-")) return { module: tok, idx: i };
|
|
16694
|
+
if (tok.includes("=")) continue;
|
|
16695
|
+
const name = tok.replace(/^--?/, "");
|
|
16696
|
+
if (booleanFlags.has(name)) continue;
|
|
16697
|
+
const next = argv[i + 1];
|
|
16698
|
+
if (next !== void 0 && !next.startsWith("-")) i++;
|
|
16699
|
+
}
|
|
16700
|
+
return void 0;
|
|
16701
|
+
}
|
|
16849
16702
|
|
|
16850
16703
|
// src/commands/market.ts
|
|
16851
16704
|
function getData2(result) {
|
|
@@ -18825,7 +18678,7 @@ Config saved to ${p}
|
|
|
18825
18678
|
}
|
|
18826
18679
|
};
|
|
18827
18680
|
function prompt(rl, question) {
|
|
18828
|
-
return new Promise((
|
|
18681
|
+
return new Promise((resolve3) => rl.question(question, resolve3));
|
|
18829
18682
|
}
|
|
18830
18683
|
function cmdConfigShow(json) {
|
|
18831
18684
|
const config = readFullConfig();
|
|
@@ -20248,21 +20101,18 @@ async function cmdDcdQuoteAndBuy(run, opts) {
|
|
|
20248
20101
|
|
|
20249
20102
|
// src/commands/skill.ts
|
|
20250
20103
|
import { tmpdir, homedir as homedir13 } from "os";
|
|
20251
|
-
import { join as
|
|
20252
|
-
import { mkdirSync as mkdirSync12, rmSync, existsSync as
|
|
20104
|
+
import { join as join16, dirname as dirname9 } from "path";
|
|
20105
|
+
import { mkdirSync as mkdirSync12, rmSync, existsSync as existsSync11, copyFileSync as copyFileSync2 } from "fs";
|
|
20253
20106
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
20254
20107
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
20255
20108
|
function resolveNpx() {
|
|
20256
|
-
const sibling =
|
|
20257
|
-
if (
|
|
20109
|
+
const sibling = join16(dirname9(process.execPath), "npx");
|
|
20110
|
+
if (existsSync11(sibling)) return sibling;
|
|
20258
20111
|
return "npx";
|
|
20259
20112
|
}
|
|
20260
20113
|
function npxEnv() {
|
|
20261
20114
|
return { ...process.env, NO_COLOR: "1", FORCE_COLOR: "0" };
|
|
20262
20115
|
}
|
|
20263
|
-
function getSkillContentDir(name) {
|
|
20264
|
-
return join17(homedir13(), ".agents", "skills", name);
|
|
20265
|
-
}
|
|
20266
20116
|
var THIRD_PARTY_INSTALL_NOTICE = "Note: This skill was created by a third-party developer, not by OKX. Review SKILL.md before use.";
|
|
20267
20117
|
async function cmdSkillSearch(run, opts) {
|
|
20268
20118
|
const args = {};
|
|
@@ -20312,56 +20162,16 @@ async function cmdSkillCategories(run, json) {
|
|
|
20312
20162
|
}
|
|
20313
20163
|
outputLine("");
|
|
20314
20164
|
}
|
|
20315
|
-
async function
|
|
20316
|
-
|
|
20317
|
-
return await fn();
|
|
20318
|
-
} catch (e) {
|
|
20319
|
-
if (e instanceof ConfigError) {
|
|
20320
|
-
throw new Error(
|
|
20321
|
-
`Signature verification requires authentication \u2014 run \`okx auth login\` first, or use --force to bypass.`
|
|
20322
|
-
);
|
|
20323
|
-
}
|
|
20324
|
-
throw e;
|
|
20325
|
-
}
|
|
20326
|
-
}
|
|
20327
|
-
async function cmdSkillAdd(name, config, json, force = false, exec = execFileSync2, _deps) {
|
|
20328
|
-
const _download = _deps?.download ?? downloadSkillZip;
|
|
20329
|
-
const _extract = _deps?.extract ?? extractSkillZip;
|
|
20330
|
-
const tmpBase = join17(tmpdir(), `okx-skill-${randomUUID2()}`);
|
|
20165
|
+
async function cmdSkillAdd(name, config, json, exec = execFileSync2) {
|
|
20166
|
+
const tmpBase = join16(tmpdir(), `okx-skill-${randomUUID2()}`);
|
|
20331
20167
|
mkdirSync12(tmpBase, { recursive: true });
|
|
20332
20168
|
try {
|
|
20333
20169
|
outputLine(`Downloading ${name}...`);
|
|
20334
20170
|
const client = new OkxRestClient(config);
|
|
20335
|
-
const zipPath = await
|
|
20336
|
-
const contentDir = await
|
|
20171
|
+
const zipPath = await downloadSkillZip(client, name, tmpBase);
|
|
20172
|
+
const contentDir = await extractSkillZip(zipPath, join16(tmpBase, "content"));
|
|
20337
20173
|
const meta = readMetaJson(contentDir);
|
|
20338
20174
|
validateSkillMdExists(contentDir);
|
|
20339
|
-
outputLine("Verifying signature...");
|
|
20340
|
-
const verifyResult = await wrapVerify(
|
|
20341
|
-
() => verifySkillSignature(contentDir, meta.signing, {
|
|
20342
|
-
fetchPublicKey: (keyId) => getPublicKey(client, keyId),
|
|
20343
|
-
serverSideVerify: (sName, version, files) => serverSideVerify(client, sName, version, files),
|
|
20344
|
-
skillName: meta.name,
|
|
20345
|
-
skillVersion: meta.version
|
|
20346
|
-
})
|
|
20347
|
-
);
|
|
20348
|
-
if (verifyResult.status === "failed") {
|
|
20349
|
-
if (!force) {
|
|
20350
|
-
throw new Error(`Signature verification failed: ${verifyResult.error ?? "unknown error"}. Use --force to install anyway.`);
|
|
20351
|
-
}
|
|
20352
|
-
process.stderr.write(`WARNING: Signature verification failed \u2014 ${verifyResult.error ?? "unknown"}. Installing anyway (--force).
|
|
20353
|
-
`);
|
|
20354
|
-
} else if (verifyResult.status === "verified_by_server") {
|
|
20355
|
-
if (!json) {
|
|
20356
|
-
outputLine(` Verified by server (v${verifyResult.serverVersion ?? "?"})`);
|
|
20357
|
-
if (verifyResult.error) outputLine(` Note: ${verifyResult.error}`);
|
|
20358
|
-
}
|
|
20359
|
-
} else if (!json) {
|
|
20360
|
-
outputLine(` Signature verified (key: ${verifyResult.publicKeyId}, files: ${verifyResult.filesChecked})`);
|
|
20361
|
-
if (verifyResult.extraFiles?.length) {
|
|
20362
|
-
outputLine(` Note: ${verifyResult.extraFiles.length} extra unsigned file(s) present`);
|
|
20363
|
-
}
|
|
20364
|
-
}
|
|
20365
20175
|
outputLine("Installing to detected agents...");
|
|
20366
20176
|
try {
|
|
20367
20177
|
exec(resolveNpx(), ["skills", "add", contentDir, "-y", "-g"], {
|
|
@@ -20370,7 +20180,7 @@ async function cmdSkillAdd(name, config, json, force = false, exec = execFileSyn
|
|
|
20370
20180
|
env: npxEnv()
|
|
20371
20181
|
});
|
|
20372
20182
|
} catch (e) {
|
|
20373
|
-
const savedZip =
|
|
20183
|
+
const savedZip = join16(process.cwd(), `${name}.zip`);
|
|
20374
20184
|
try {
|
|
20375
20185
|
copyFileSync2(zipPath, savedZip);
|
|
20376
20186
|
} catch {
|
|
@@ -20379,8 +20189,7 @@ async function cmdSkillAdd(name, config, json, force = false, exec = execFileSyn
|
|
|
20379
20189
|
errorLine(`You can manually install from: ${savedZip}`);
|
|
20380
20190
|
throw e;
|
|
20381
20191
|
}
|
|
20382
|
-
|
|
20383
|
-
upsertSkillRecord(meta, void 0, registryStatus);
|
|
20192
|
+
upsertSkillRecord(meta);
|
|
20384
20193
|
printSkillInstallResult(meta, json);
|
|
20385
20194
|
} finally {
|
|
20386
20195
|
rmSync(tmpBase, { recursive: true, force: true });
|
|
@@ -20411,7 +20220,7 @@ function cmdSkillRemove(name, json, exec = execFileSync2) {
|
|
|
20411
20220
|
env: npxEnv()
|
|
20412
20221
|
});
|
|
20413
20222
|
} catch {
|
|
20414
|
-
const agentsPath =
|
|
20223
|
+
const agentsPath = join16(homedir13(), ".agents", "skills", name);
|
|
20415
20224
|
try {
|
|
20416
20225
|
rmSync(agentsPath, { recursive: true, force: true });
|
|
20417
20226
|
} catch {
|
|
@@ -20475,59 +20284,6 @@ function cmdSkillList(json) {
|
|
|
20475
20284
|
outputLine("");
|
|
20476
20285
|
outputLine(`${skills.length} skills installed.`);
|
|
20477
20286
|
}
|
|
20478
|
-
async function cmdSkillVerify(name, config, json) {
|
|
20479
|
-
const record = getSkillRecord(name);
|
|
20480
|
-
if (!record) {
|
|
20481
|
-
errorLine(`Skill "${name}" is not installed.`);
|
|
20482
|
-
process.exitCode = 1;
|
|
20483
|
-
return;
|
|
20484
|
-
}
|
|
20485
|
-
const contentDir = getSkillContentDir(name);
|
|
20486
|
-
if (!existsSync10(contentDir)) {
|
|
20487
|
-
errorLine(`Skill content directory not found: ${contentDir}`);
|
|
20488
|
-
errorLine(`Try reinstalling with: okx skill add ${name}`);
|
|
20489
|
-
process.exitCode = 1;
|
|
20490
|
-
return;
|
|
20491
|
-
}
|
|
20492
|
-
const meta = tryReadMetaJson(contentDir);
|
|
20493
|
-
const client = new OkxRestClient(config);
|
|
20494
|
-
let result;
|
|
20495
|
-
try {
|
|
20496
|
-
result = await wrapVerify(
|
|
20497
|
-
() => verifySkillSignature(contentDir, meta?.signing, {
|
|
20498
|
-
fetchPublicKey: (keyId) => getPublicKey(client, keyId),
|
|
20499
|
-
serverSideVerify: (sName, version, files) => serverSideVerify(client, sName, version, files),
|
|
20500
|
-
skillName: name,
|
|
20501
|
-
skillVersion: meta?.version
|
|
20502
|
-
})
|
|
20503
|
-
);
|
|
20504
|
-
} catch (e) {
|
|
20505
|
-
errorLine(e instanceof Error ? e.message : String(e));
|
|
20506
|
-
process.exitCode = 1;
|
|
20507
|
-
return;
|
|
20508
|
-
}
|
|
20509
|
-
if (meta) {
|
|
20510
|
-
upsertSkillRecord(meta, void 0, result.status);
|
|
20511
|
-
}
|
|
20512
|
-
if (result.status === "failed") {
|
|
20513
|
-
process.exitCode = 1;
|
|
20514
|
-
}
|
|
20515
|
-
if (json) {
|
|
20516
|
-
outputLine(JSON.stringify(result, null, 2));
|
|
20517
|
-
return;
|
|
20518
|
-
}
|
|
20519
|
-
if (result.status === "verified") {
|
|
20520
|
-
outputLine(`\u2713 ${name}: signature verified (key: ${result.publicKeyId}, files: ${result.filesChecked})`);
|
|
20521
|
-
if (result.extraFiles?.length) {
|
|
20522
|
-
outputLine(` Note: ${result.extraFiles.length} extra unsigned file(s) present`);
|
|
20523
|
-
}
|
|
20524
|
-
} else if (result.status === "verified_by_server") {
|
|
20525
|
-
outputLine(`\u2713 ${name}: verified by server (v${result.serverVersion ?? "?"})`);
|
|
20526
|
-
if (result.error) outputLine(` Note: ${result.error}`);
|
|
20527
|
-
} else if (result.status === "failed") {
|
|
20528
|
-
errorLine(`\u2717 ${name}: verification failed \u2014 ${result.error ?? "unknown"}`);
|
|
20529
|
-
}
|
|
20530
|
-
}
|
|
20531
20287
|
function printSkillInstallResult(meta, json) {
|
|
20532
20288
|
if (json) {
|
|
20533
20289
|
outputLine(JSON.stringify({ name: meta.name, version: meta.version, status: "installed" }, null, 2));
|
|
@@ -20678,14 +20434,14 @@ function formatBytes2(bytes) {
|
|
|
20678
20434
|
return `${(bytes / (1024 * 1024)).toFixed(2)} MB`;
|
|
20679
20435
|
}
|
|
20680
20436
|
function askConfirmation2(prompt2) {
|
|
20681
|
-
return new Promise((
|
|
20437
|
+
return new Promise((resolve3) => {
|
|
20682
20438
|
const rl = readline2.createInterface({
|
|
20683
20439
|
input: process.stdin,
|
|
20684
20440
|
output: process.stdout
|
|
20685
20441
|
});
|
|
20686
20442
|
rl.question(prompt2, (answer) => {
|
|
20687
20443
|
rl.close();
|
|
20688
|
-
|
|
20444
|
+
resolve3(answer.trim().toLowerCase() === "y");
|
|
20689
20445
|
});
|
|
20690
20446
|
});
|
|
20691
20447
|
}
|
|
@@ -21148,7 +20904,7 @@ async function cmdEventCancel(run, opts) {
|
|
|
21148
20904
|
// src/index.ts
|
|
21149
20905
|
var _require3 = createRequire3(import.meta.url);
|
|
21150
20906
|
var CLI_VERSION2 = _require3("../package.json").version;
|
|
21151
|
-
var GIT_HASH2 = true ? "
|
|
20907
|
+
var GIT_HASH2 = true ? "0614700e" : "dev";
|
|
21152
20908
|
function handlePilotCommand(action, json, force, binaryPath) {
|
|
21153
20909
|
if (action === "status") return cmdPilotStatus(json, binaryPath);
|
|
21154
20910
|
if (action === "install") return cmdPilotInstall(json, binaryPath);
|
|
@@ -22460,13 +22216,9 @@ function requireSkillName(rest, usage) {
|
|
|
22460
22216
|
}
|
|
22461
22217
|
return name;
|
|
22462
22218
|
}
|
|
22463
|
-
function handleSkillAdd(rest,
|
|
22219
|
+
function handleSkillAdd(rest, config, json) {
|
|
22464
22220
|
const n = requireSkillName(rest, "Usage: okx skill add <name>");
|
|
22465
|
-
if (n) return cmdSkillAdd(n, config, json
|
|
22466
|
-
}
|
|
22467
|
-
function handleSkillVerify(rest, config, json) {
|
|
22468
|
-
const n = requireSkillName(rest, "Usage: okx skill verify <name>");
|
|
22469
|
-
if (n) return cmdSkillVerify(n, config, json);
|
|
22221
|
+
if (n) return cmdSkillAdd(n, config, json);
|
|
22470
22222
|
}
|
|
22471
22223
|
function handleSkillDownload(rest, v, config, json) {
|
|
22472
22224
|
const n = requireSkillName(rest, "Usage: okx skill download <name> [--dir <path>] [--format zip|skill]");
|
|
@@ -22485,13 +22237,12 @@ function handleSkillCommand(run, action, rest, v, json, config) {
|
|
|
22485
22237
|
if (action === "search") return cmdSkillSearch(run, { keyword: rest[0] ?? v.keyword, categories: v.categories, page: v.page, limit: v.limit, json });
|
|
22486
22238
|
if (action === "categories") return cmdSkillCategories(run, json);
|
|
22487
22239
|
if (action === "list") return cmdSkillList(json);
|
|
22488
|
-
if (action === "add") return handleSkillAdd(rest,
|
|
22240
|
+
if (action === "add") return handleSkillAdd(rest, config, json);
|
|
22489
22241
|
if (action === "download") return handleSkillDownload(rest, v, config, json);
|
|
22490
22242
|
if (action === "remove") return handleSkillRemove(rest, json);
|
|
22491
22243
|
if (action === "check") return handleSkillCheck(run, rest, json);
|
|
22492
|
-
if (action === "verify") return handleSkillVerify(rest, config, json);
|
|
22493
22244
|
errorLine(`Unknown skill command: ${action}`);
|
|
22494
|
-
errorLine("Valid: search, categories, add, download, remove, check, list
|
|
22245
|
+
errorLine("Valid: search, categories, add, download, remove, check, list");
|
|
22495
22246
|
process.exitCode = 1;
|
|
22496
22247
|
}
|
|
22497
22248
|
function handleEventCommand(run, action, rest, v, json) {
|
|
@@ -22621,7 +22372,16 @@ async function main() {
|
|
|
22621
22372
|
err: (m) => process.stderr.write(m)
|
|
22622
22373
|
});
|
|
22623
22374
|
checkForUpdates("@okx_ai/okx-trade-cli", CLI_VERSION2);
|
|
22624
|
-
const
|
|
22375
|
+
const rawArgv = process.argv.slice(2);
|
|
22376
|
+
const peek = peekFirstPositional(rawArgv);
|
|
22377
|
+
if (peek?.module === "outcomes") {
|
|
22378
|
+
const after = rawArgv.slice(peek.idx + 1);
|
|
22379
|
+
const action2 = after[0];
|
|
22380
|
+
const rest2 = after.slice(1);
|
|
22381
|
+
const json2 = rawArgv.includes("--json") || rawArgv.includes("-j");
|
|
22382
|
+
return handleOutcomesCommand(action2, rest2, { json: json2 });
|
|
22383
|
+
}
|
|
22384
|
+
const { values, positionals } = parseCli(rawArgv);
|
|
22625
22385
|
if (values.version) {
|
|
22626
22386
|
printVersion();
|
|
22627
22387
|
return;
|