@okx_ai/okx-trade-cli 1.3.6-beta.1 → 1.3.6-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +573 -139
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -34,16 +34,22 @@ 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";
|
|
37
43
|
import yauzl from "yauzl";
|
|
38
44
|
import { createWriteStream, mkdirSync as mkdirSync3 } from "fs";
|
|
39
|
-
import { resolve as
|
|
40
|
-
import { readFileSync as
|
|
41
|
-
import { join as
|
|
42
|
-
import { readFileSync as
|
|
43
|
-
import { join as
|
|
45
|
+
import { resolve as resolve3, dirname as dirname2 } from "path";
|
|
46
|
+
import { readFileSync as readFileSync4, existsSync } from "fs";
|
|
47
|
+
import { join as join7 } from "path";
|
|
48
|
+
import { readFileSync as readFileSync5, writeFileSync as writeFileSync3, mkdirSync as mkdirSync4, existsSync as existsSync2 } from "fs";
|
|
49
|
+
import { join as join8, dirname as dirname3 } from "path";
|
|
44
50
|
import { homedir as homedir4 } from "os";
|
|
45
|
-
import { readFileSync as
|
|
46
|
-
import { join as
|
|
51
|
+
import { readFileSync as readFileSync6, writeFileSync as writeFileSync4, mkdirSync as mkdirSync5, existsSync as existsSync3 } from "fs";
|
|
52
|
+
import { join as join9, dirname as dirname4 } from "path";
|
|
47
53
|
import { homedir as homedir5 } from "os";
|
|
48
54
|
|
|
49
55
|
// ../../node_modules/.pnpm/smol-toml@1.6.0/node_modules/smol-toml/dist/error.js
|
|
@@ -121,7 +127,7 @@ function skipVoid(str, ptr, banNewLines, banComments) {
|
|
|
121
127
|
ptr++;
|
|
122
128
|
return banComments || c !== "#" ? ptr : skipVoid(str, skipComment(str, ptr), banNewLines);
|
|
123
129
|
}
|
|
124
|
-
function skipUntil(str, ptr,
|
|
130
|
+
function skipUntil(str, ptr, sep3, end, banNewLines = false) {
|
|
125
131
|
if (!end) {
|
|
126
132
|
ptr = indexOfNewline(str, ptr);
|
|
127
133
|
return ptr < 0 ? str.length : ptr;
|
|
@@ -130,7 +136,7 @@ function skipUntil(str, ptr, sep2, end, banNewLines = false) {
|
|
|
130
136
|
let c = str[i];
|
|
131
137
|
if (c === "#") {
|
|
132
138
|
i = indexOfNewline(str, i);
|
|
133
|
-
} else if (c ===
|
|
139
|
+
} else if (c === sep3) {
|
|
134
140
|
return i + 1;
|
|
135
141
|
} else if (c === end || banNewLines && (c === "\n" || c === "\r" && str[i + 1] === "\n")) {
|
|
136
142
|
return i;
|
|
@@ -873,8 +879,8 @@ function stringify(obj, { maxDepth = 1e3, numbersAsFloat = false } = {}) {
|
|
|
873
879
|
}
|
|
874
880
|
|
|
875
881
|
// ../core/dist/index.js
|
|
876
|
-
import { readFileSync as
|
|
877
|
-
import { join as
|
|
882
|
+
import { readFileSync as readFileSync7, writeFileSync as writeFileSync5, mkdirSync as mkdirSync6, existsSync as existsSync4 } from "fs";
|
|
883
|
+
import { join as join10 } from "path";
|
|
878
884
|
import { homedir as homedir6 } from "os";
|
|
879
885
|
import fs2 from "fs";
|
|
880
886
|
import path2 from "path";
|
|
@@ -891,25 +897,37 @@ import {
|
|
|
891
897
|
renameSync as renameSync4
|
|
892
898
|
} from "fs";
|
|
893
899
|
import { homedir as homedir9, platform as platform2 } from "os";
|
|
894
|
-
import { join as
|
|
900
|
+
import { join as join13, dirname as dirname7 } from "path";
|
|
895
901
|
import { createWriteStream as createWriteStream2, unlinkSync as unlinkSync3 } from "fs";
|
|
896
902
|
import { get as httpsGet } from "https";
|
|
897
903
|
import { get as httpGet } from "http";
|
|
898
904
|
import {
|
|
899
|
-
readFileSync as
|
|
905
|
+
readFileSync as readFileSync9,
|
|
900
906
|
mkdirSync as mkdirSync8,
|
|
901
907
|
chmodSync,
|
|
902
908
|
existsSync as existsSync6,
|
|
903
909
|
unlinkSync as unlinkSync4,
|
|
904
910
|
renameSync as renameSync3
|
|
905
911
|
} from "fs";
|
|
906
|
-
import { createHash } from "crypto";
|
|
912
|
+
import { createHash as createHash3 } from "crypto";
|
|
907
913
|
import { homedir as homedir8, platform, arch } from "os";
|
|
908
|
-
import { join as
|
|
909
|
-
import { readFileSync as
|
|
910
|
-
import { join as
|
|
914
|
+
import { join as join12, dirname as dirname6 } from "path";
|
|
915
|
+
import { readFileSync as readFileSync10, writeFileSync as writeFileSync7, mkdirSync as mkdirSync10, unlinkSync as unlinkSync6, existsSync as existsSync8 } from "fs";
|
|
916
|
+
import { join as join14 } from "path";
|
|
911
917
|
import { homedir as homedir10 } from "os";
|
|
912
|
-
|
|
918
|
+
function hasProxyEnv(env = process.env) {
|
|
919
|
+
return Boolean(
|
|
920
|
+
env.HTTPS_PROXY || env.https_proxy || env.HTTP_PROXY || env.http_proxy
|
|
921
|
+
);
|
|
922
|
+
}
|
|
923
|
+
function installEnvProxyDispatcher(deps = {}) {
|
|
924
|
+
const env = deps.env ?? process.env;
|
|
925
|
+
if (!hasProxyEnv(env)) return false;
|
|
926
|
+
const register = deps.register ?? (() => setGlobalDispatcher(new EnvHttpProxyAgent()));
|
|
927
|
+
register();
|
|
928
|
+
return true;
|
|
929
|
+
}
|
|
930
|
+
installEnvProxyDispatcher();
|
|
913
931
|
var EXEC_TIMEOUT_MS = 3e4;
|
|
914
932
|
var ALLOWED_DOMAIN_RE = /^[\w.-]+\.okx\.com$/;
|
|
915
933
|
var PILOT_BIN_DIR = join(homedir(), ".okx", "bin");
|
|
@@ -932,25 +950,25 @@ function execPilotBinary(domain, exclude = [], userAgent) {
|
|
|
932
950
|
if (userAgent) {
|
|
933
951
|
args.push("--user-agent", userAgent);
|
|
934
952
|
}
|
|
935
|
-
return new Promise((
|
|
953
|
+
return new Promise((resolve4) => {
|
|
936
954
|
execFile(
|
|
937
955
|
binPath,
|
|
938
956
|
args,
|
|
939
957
|
{ timeout: EXEC_TIMEOUT_MS, encoding: "utf-8" },
|
|
940
958
|
(error, stdout) => {
|
|
941
959
|
if (error) {
|
|
942
|
-
|
|
960
|
+
resolve4(null);
|
|
943
961
|
return;
|
|
944
962
|
}
|
|
945
963
|
try {
|
|
946
964
|
const result = JSON.parse(stdout);
|
|
947
965
|
if (result.code === 0 && result.data) {
|
|
948
|
-
|
|
966
|
+
resolve4(result.data);
|
|
949
967
|
} else {
|
|
950
|
-
|
|
968
|
+
resolve4(null);
|
|
951
969
|
}
|
|
952
970
|
} catch {
|
|
953
|
-
|
|
971
|
+
resolve4(null);
|
|
954
972
|
}
|
|
955
973
|
}
|
|
956
974
|
);
|
|
@@ -1284,7 +1302,7 @@ var EXIT_CODES = {
|
|
|
1284
1302
|
NOT_LOGGED_IN: 2,
|
|
1285
1303
|
REFRESH_FAILED: 3
|
|
1286
1304
|
};
|
|
1287
|
-
function finalizeToken(code, token,
|
|
1305
|
+
function finalizeToken(code, token, resolve4, reject) {
|
|
1288
1306
|
if (code === EXIT_CODES.SUCCESS) {
|
|
1289
1307
|
if (!token) {
|
|
1290
1308
|
reject(new AuthenticationError(
|
|
@@ -1293,7 +1311,7 @@ function finalizeToken(code, token, resolve3, reject) {
|
|
|
1293
1311
|
));
|
|
1294
1312
|
return;
|
|
1295
1313
|
}
|
|
1296
|
-
|
|
1314
|
+
resolve4(token);
|
|
1297
1315
|
return;
|
|
1298
1316
|
}
|
|
1299
1317
|
if (code === EXIT_CODES.NOT_LOGGED_IN) {
|
|
@@ -1330,7 +1348,7 @@ function defaultWindowsPipeName() {
|
|
|
1330
1348
|
return WIN_PIPE_PREFIX + randomBytes(32).toString("hex");
|
|
1331
1349
|
}
|
|
1332
1350
|
function execAuthTokenWindows(binPath, makePipeName = defaultWindowsPipeName) {
|
|
1333
|
-
return new Promise((
|
|
1351
|
+
return new Promise((resolve4, reject) => {
|
|
1334
1352
|
const pipeName = makePipeName();
|
|
1335
1353
|
const server = createServer();
|
|
1336
1354
|
const chunks = [];
|
|
@@ -1350,7 +1368,7 @@ function execAuthTokenWindows(binPath, makePipeName = defaultWindowsPipeName) {
|
|
|
1350
1368
|
const tryFinalize = () => {
|
|
1351
1369
|
if (!pipeClosed || exitCode === void 0) return;
|
|
1352
1370
|
const token = Buffer.concat(chunks).toString("utf-8").trim();
|
|
1353
|
-
settle(() => finalizeToken(exitCode, token,
|
|
1371
|
+
settle(() => finalizeToken(exitCode, token, resolve4, reject));
|
|
1354
1372
|
};
|
|
1355
1373
|
server.on("connection", (socket) => {
|
|
1356
1374
|
connectionMade = true;
|
|
@@ -1397,7 +1415,7 @@ function execAuthToken() {
|
|
|
1397
1415
|
return process.platform === "win32" ? execAuthTokenWindows(binPath) : execAuthTokenUnix(binPath);
|
|
1398
1416
|
}
|
|
1399
1417
|
function execAuthTokenUnix(binPath) {
|
|
1400
|
-
return new Promise((
|
|
1418
|
+
return new Promise((resolve4, reject) => {
|
|
1401
1419
|
const child = spawn2(binPath, ["token"], {
|
|
1402
1420
|
stdio: ["ignore", "ignore", "inherit", "pipe"]
|
|
1403
1421
|
// stdin stdout stderr fd3 (pipe)
|
|
@@ -1410,35 +1428,35 @@ function execAuthTokenUnix(binPath) {
|
|
|
1410
1428
|
});
|
|
1411
1429
|
child.on("close", (code) => {
|
|
1412
1430
|
const token = Buffer.concat(chunks).toString("utf-8").trim();
|
|
1413
|
-
finalizeToken(code, token,
|
|
1431
|
+
finalizeToken(code, token, resolve4, reject);
|
|
1414
1432
|
});
|
|
1415
1433
|
});
|
|
1416
1434
|
}
|
|
1417
1435
|
function execAuthStatus() {
|
|
1418
1436
|
const binPath = getAuthBinaryPath();
|
|
1419
|
-
return new Promise((
|
|
1437
|
+
return new Promise((resolve4) => {
|
|
1420
1438
|
execFile2(
|
|
1421
1439
|
binPath,
|
|
1422
1440
|
["status", "--json"],
|
|
1423
1441
|
{ timeout: EXEC_TIMEOUT_MS2, encoding: "utf-8" },
|
|
1424
1442
|
(error, stdout) => {
|
|
1425
1443
|
if (error) {
|
|
1426
|
-
|
|
1444
|
+
resolve4(null);
|
|
1427
1445
|
return;
|
|
1428
1446
|
}
|
|
1429
1447
|
try {
|
|
1430
1448
|
const result = JSON.parse(stdout);
|
|
1431
|
-
|
|
1449
|
+
resolve4(result);
|
|
1432
1450
|
} catch {
|
|
1433
|
-
|
|
1451
|
+
resolve4(null);
|
|
1434
1452
|
}
|
|
1435
1453
|
}
|
|
1436
1454
|
);
|
|
1437
1455
|
});
|
|
1438
1456
|
}
|
|
1439
1457
|
function sleep(ms) {
|
|
1440
|
-
return new Promise((
|
|
1441
|
-
setTimeout(
|
|
1458
|
+
return new Promise((resolve4) => {
|
|
1459
|
+
setTimeout(resolve4, ms);
|
|
1442
1460
|
});
|
|
1443
1461
|
}
|
|
1444
1462
|
var RateLimiter = class {
|
|
@@ -4264,6 +4282,223 @@ async function downloadSkillZip(client, name, targetDir, format = "zip") {
|
|
|
4264
4282
|
const filePath = safeWriteFile(targetDir, fileName, result.data);
|
|
4265
4283
|
return filePath;
|
|
4266
4284
|
}
|
|
4285
|
+
function listFilesRecursive(dir, base = "") {
|
|
4286
|
+
const results = [];
|
|
4287
|
+
for (const entry of readdirSync(dir)) {
|
|
4288
|
+
const fullPath = join5(dir, entry);
|
|
4289
|
+
const relPath = base ? `${base}/${entry}` : entry;
|
|
4290
|
+
if (statSync(fullPath).isDirectory()) {
|
|
4291
|
+
results.push(...listFilesRecursive(fullPath, relPath));
|
|
4292
|
+
} else {
|
|
4293
|
+
results.push(relPath);
|
|
4294
|
+
}
|
|
4295
|
+
}
|
|
4296
|
+
return results;
|
|
4297
|
+
}
|
|
4298
|
+
function computeFileHashes(contentDir) {
|
|
4299
|
+
const hashes = {};
|
|
4300
|
+
for (const relPath of listFilesRecursive(contentDir)) {
|
|
4301
|
+
if (relPath === "_meta.json") continue;
|
|
4302
|
+
const bytes = readFileSync2(join5(contentDir, relPath));
|
|
4303
|
+
hashes[relPath] = "sha256:" + createHash("sha256").update(bytes).digest("hex");
|
|
4304
|
+
}
|
|
4305
|
+
return hashes;
|
|
4306
|
+
}
|
|
4307
|
+
async function getPublicKey(client, keyId) {
|
|
4308
|
+
try {
|
|
4309
|
+
const query = {};
|
|
4310
|
+
if (keyId !== void 0) query.keyId = keyId;
|
|
4311
|
+
const result = await client.privateGet(
|
|
4312
|
+
"/api/v5/skill/signing-key",
|
|
4313
|
+
query
|
|
4314
|
+
);
|
|
4315
|
+
if (!result.data || result.data.status !== "active") return null;
|
|
4316
|
+
return result.data.publicKey;
|
|
4317
|
+
} catch (e) {
|
|
4318
|
+
if (e instanceof ConfigError) throw e;
|
|
4319
|
+
return null;
|
|
4320
|
+
}
|
|
4321
|
+
}
|
|
4322
|
+
var ED25519_SPKI_HEADER = Buffer.from("302a300506032b6570032100", "hex");
|
|
4323
|
+
function parseSSHPublicKey(base64) {
|
|
4324
|
+
try {
|
|
4325
|
+
const buf = Buffer.from(base64, "base64");
|
|
4326
|
+
let offset = 0;
|
|
4327
|
+
if (buf.length < 4) return null;
|
|
4328
|
+
const algLen = buf.readUInt32BE(offset);
|
|
4329
|
+
offset += 4;
|
|
4330
|
+
if (offset + algLen + 4 > buf.length) return null;
|
|
4331
|
+
if (buf.subarray(offset, offset + algLen).toString("ascii") !== "ssh-ed25519") return null;
|
|
4332
|
+
offset += algLen;
|
|
4333
|
+
const keyLen = buf.readUInt32BE(offset);
|
|
4334
|
+
offset += 4;
|
|
4335
|
+
if (keyLen !== 32 || offset + keyLen > buf.length) return null;
|
|
4336
|
+
return buf.subarray(offset, offset + keyLen);
|
|
4337
|
+
} catch {
|
|
4338
|
+
return null;
|
|
4339
|
+
}
|
|
4340
|
+
}
|
|
4341
|
+
async function verifySkillSignature(contentDir, signing, opts) {
|
|
4342
|
+
if (!signing) {
|
|
4343
|
+
const fallback = await tryServerFallback(contentDir, opts);
|
|
4344
|
+
if (fallback?.status === "verified_by_server") return fallback;
|
|
4345
|
+
return {
|
|
4346
|
+
status: "failed",
|
|
4347
|
+
error: fallback?.error ?? "Skill is not signed",
|
|
4348
|
+
...fallback?.mismatched?.length && { mismatched: fallback.mismatched }
|
|
4349
|
+
};
|
|
4350
|
+
}
|
|
4351
|
+
const publicKeyBase64 = opts?.fetchPublicKey ? await opts.fetchPublicKey(signing.public_key_id) ?? "" : "";
|
|
4352
|
+
if (!publicKeyBase64) {
|
|
4353
|
+
return localFail(
|
|
4354
|
+
contentDir,
|
|
4355
|
+
opts,
|
|
4356
|
+
`Unknown signing key: ${signing.public_key_id}. Please update CLI.`,
|
|
4357
|
+
"Unknown signing key \u2014 consider updating CLI"
|
|
4358
|
+
);
|
|
4359
|
+
}
|
|
4360
|
+
const rawKey = parseSSHPublicKey(publicKeyBase64);
|
|
4361
|
+
if (!rawKey) {
|
|
4362
|
+
return localFail(
|
|
4363
|
+
contentDir,
|
|
4364
|
+
opts,
|
|
4365
|
+
`Malformed public key for key ID: ${signing.public_key_id}`,
|
|
4366
|
+
"Malformed public key from server \u2014 consider updating CLI",
|
|
4367
|
+
signing.public_key_id
|
|
4368
|
+
);
|
|
4369
|
+
}
|
|
4370
|
+
if (!ed25519Verify(signing, rawKey)) {
|
|
4371
|
+
return localFail(
|
|
4372
|
+
contentDir,
|
|
4373
|
+
opts,
|
|
4374
|
+
"Invalid signature",
|
|
4375
|
+
"Signature mismatch \u2014 key may be outdated or file was modified",
|
|
4376
|
+
signing.public_key_id
|
|
4377
|
+
);
|
|
4378
|
+
}
|
|
4379
|
+
const bindingError = checkNameVersionBinding(signing, opts);
|
|
4380
|
+
if (bindingError) {
|
|
4381
|
+
return { status: "failed", error: bindingError, publicKeyId: signing.public_key_id };
|
|
4382
|
+
}
|
|
4383
|
+
const integrityFailure = await checkFileIntegrity(signing, contentDir, opts);
|
|
4384
|
+
if (integrityFailure) return integrityFailure;
|
|
4385
|
+
const signedFiles = new Set(Object.keys(signing.files));
|
|
4386
|
+
const extraFiles = listFilesRecursive(contentDir).filter(
|
|
4387
|
+
(f) => f !== "_meta.json" && !signedFiles.has(f)
|
|
4388
|
+
);
|
|
4389
|
+
return { status: "verified", publicKeyId: signing.public_key_id, filesChecked: signedFiles.size, extraFiles };
|
|
4390
|
+
}
|
|
4391
|
+
async function localFail(contentDir, opts, localError, serverHint, keyId) {
|
|
4392
|
+
const fallback = await tryServerFallback(contentDir, opts);
|
|
4393
|
+
if (fallback?.status === "verified_by_server") {
|
|
4394
|
+
return { ...fallback, error: serverHint };
|
|
4395
|
+
}
|
|
4396
|
+
return {
|
|
4397
|
+
status: "failed",
|
|
4398
|
+
error: localError,
|
|
4399
|
+
...keyId && { publicKeyId: keyId },
|
|
4400
|
+
...fallback?.mismatched?.length && { mismatched: fallback.mismatched }
|
|
4401
|
+
};
|
|
4402
|
+
}
|
|
4403
|
+
function ed25519Verify(signing, rawKey) {
|
|
4404
|
+
const payloadObj = { files: signing.files, public_key_id: signing.public_key_id };
|
|
4405
|
+
if (signing.name !== void 0) payloadObj.name = signing.name;
|
|
4406
|
+
if (signing.version !== void 0) payloadObj.version = signing.version;
|
|
4407
|
+
const message = Buffer.from(canonicalize(payloadObj), "utf-8");
|
|
4408
|
+
const sig = Buffer.from(signing.signature, "base64");
|
|
4409
|
+
const spkiKey = Buffer.concat([ED25519_SPKI_HEADER, rawKey]);
|
|
4410
|
+
const keyObj = createPublicKey({ key: spkiKey, format: "der", type: "spki" });
|
|
4411
|
+
return cryptoVerify(null, message, keyObj, sig);
|
|
4412
|
+
}
|
|
4413
|
+
function checkNameVersionBinding(signing, opts) {
|
|
4414
|
+
if (signing.name !== void 0 && opts?.skillName !== void 0 && signing.name !== opts.skillName) {
|
|
4415
|
+
return `Skill name mismatch: signature binds "${signing.name}" but installing as "${opts.skillName}"`;
|
|
4416
|
+
}
|
|
4417
|
+
if (signing.version !== void 0 && opts?.skillVersion !== void 0 && signing.version !== opts.skillVersion) {
|
|
4418
|
+
return `Skill version mismatch: signature binds "${signing.version}" but package declares "${opts.skillVersion}"`;
|
|
4419
|
+
}
|
|
4420
|
+
return null;
|
|
4421
|
+
}
|
|
4422
|
+
async function checkFileIntegrity(signing, contentDir, opts) {
|
|
4423
|
+
const resolvedContentDir = resolve2(contentDir);
|
|
4424
|
+
for (const [filename, expectedHash] of Object.entries(signing.files)) {
|
|
4425
|
+
const resolvedPath = resolve2(join6(contentDir, filename));
|
|
4426
|
+
if (!resolvedPath.startsWith(resolvedContentDir + sep2)) {
|
|
4427
|
+
return { status: "failed", error: `Path traversal detected in signing manifest: ${filename}`, publicKeyId: signing.public_key_id };
|
|
4428
|
+
}
|
|
4429
|
+
let bytes;
|
|
4430
|
+
try {
|
|
4431
|
+
bytes = readFileSync3(resolvedPath);
|
|
4432
|
+
} catch {
|
|
4433
|
+
return { status: "failed", error: `File missing: ${filename}`, publicKeyId: signing.public_key_id };
|
|
4434
|
+
}
|
|
4435
|
+
const actual = "sha256:" + createHash2("sha256").update(bytes).digest("hex");
|
|
4436
|
+
if (actual !== expectedHash) {
|
|
4437
|
+
return localFail(
|
|
4438
|
+
contentDir,
|
|
4439
|
+
opts,
|
|
4440
|
+
`File integrity check failed: ${filename}`,
|
|
4441
|
+
"_meta.json may be corrupted",
|
|
4442
|
+
signing.public_key_id
|
|
4443
|
+
);
|
|
4444
|
+
}
|
|
4445
|
+
}
|
|
4446
|
+
return null;
|
|
4447
|
+
}
|
|
4448
|
+
async function tryServerFallback(contentDir, opts) {
|
|
4449
|
+
if (!opts?.serverSideVerify || !opts.skillName) return null;
|
|
4450
|
+
const fileHashes = computeFileHashes(contentDir);
|
|
4451
|
+
const result = await opts.serverSideVerify(
|
|
4452
|
+
opts.skillName,
|
|
4453
|
+
opts.skillVersion,
|
|
4454
|
+
fileHashes
|
|
4455
|
+
);
|
|
4456
|
+
if (!result) return null;
|
|
4457
|
+
if (result.verified) {
|
|
4458
|
+
return {
|
|
4459
|
+
status: "verified_by_server",
|
|
4460
|
+
filesChecked: Object.keys(fileHashes).length,
|
|
4461
|
+
serverVersion: result.version
|
|
4462
|
+
};
|
|
4463
|
+
}
|
|
4464
|
+
return {
|
|
4465
|
+
status: "failed",
|
|
4466
|
+
filesChecked: Object.keys(fileHashes).length,
|
|
4467
|
+
mismatched: result.mismatched,
|
|
4468
|
+
...result.message && { error: result.message }
|
|
4469
|
+
};
|
|
4470
|
+
}
|
|
4471
|
+
function canonicalize(obj) {
|
|
4472
|
+
return JSON.stringify(deepSortKeys(obj));
|
|
4473
|
+
}
|
|
4474
|
+
function compareKeys(a, b) {
|
|
4475
|
+
if (a < b) return -1;
|
|
4476
|
+
if (a > b) return 1;
|
|
4477
|
+
return 0;
|
|
4478
|
+
}
|
|
4479
|
+
function deepSortKeys(val) {
|
|
4480
|
+
if (val === null || typeof val !== "object") return val;
|
|
4481
|
+
if (Array.isArray(val)) return val.map(deepSortKeys);
|
|
4482
|
+
const sorted = {};
|
|
4483
|
+
for (const key of Object.keys(val).sort(compareKeys)) {
|
|
4484
|
+
sorted[key] = deepSortKeys(val[key]);
|
|
4485
|
+
}
|
|
4486
|
+
return sorted;
|
|
4487
|
+
}
|
|
4488
|
+
async function serverSideVerify(client, skillName, version, files) {
|
|
4489
|
+
try {
|
|
4490
|
+
const body = { skillName, files };
|
|
4491
|
+
if (version !== void 0) body.version = version;
|
|
4492
|
+
const result = await client.privatePost(
|
|
4493
|
+
"/api/v5/skill/verify",
|
|
4494
|
+
body
|
|
4495
|
+
);
|
|
4496
|
+
return result.data;
|
|
4497
|
+
} catch (e) {
|
|
4498
|
+
if (e instanceof ConfigError) throw e;
|
|
4499
|
+
return null;
|
|
4500
|
+
}
|
|
4501
|
+
}
|
|
4267
4502
|
var DEFAULT_MAX_TOTAL_BYTES = 100 * 1024 * 1024;
|
|
4268
4503
|
var DEFAULT_MAX_FILES = 1e3;
|
|
4269
4504
|
var DEFAULT_MAX_COMPRESSION_RATIO = 100;
|
|
@@ -4302,7 +4537,7 @@ async function extractSkillZip(zipPath, targetDir, limits) {
|
|
|
4302
4537
|
const maxTotalBytes = limits?.maxTotalBytes ?? DEFAULT_MAX_TOTAL_BYTES;
|
|
4303
4538
|
const maxFiles = limits?.maxFiles ?? DEFAULT_MAX_FILES;
|
|
4304
4539
|
const maxCompressionRatio = limits?.maxCompressionRatio ?? DEFAULT_MAX_COMPRESSION_RATIO;
|
|
4305
|
-
const resolvedTarget =
|
|
4540
|
+
const resolvedTarget = resolve3(targetDir);
|
|
4306
4541
|
mkdirSync3(resolvedTarget, { recursive: true });
|
|
4307
4542
|
return new Promise((resolvePromise, reject) => {
|
|
4308
4543
|
yauzl.open(zipPath, { lazyEntries: true }, (err, zipfile) => {
|
|
@@ -4342,11 +4577,11 @@ async function extractSkillZip(zipPath, targetDir, limits) {
|
|
|
4342
4577
|
});
|
|
4343
4578
|
}
|
|
4344
4579
|
function readMetaJson(contentDir) {
|
|
4345
|
-
const metaPath =
|
|
4580
|
+
const metaPath = join7(contentDir, "_meta.json");
|
|
4346
4581
|
if (!existsSync(metaPath)) {
|
|
4347
4582
|
throw new Error(`_meta.json not found in ${contentDir}. Invalid skill package.`);
|
|
4348
4583
|
}
|
|
4349
|
-
const raw =
|
|
4584
|
+
const raw = readFileSync4(metaPath, "utf-8");
|
|
4350
4585
|
let parsed;
|
|
4351
4586
|
try {
|
|
4352
4587
|
parsed = JSON.parse(raw);
|
|
@@ -4364,22 +4599,49 @@ function readMetaJson(contentDir) {
|
|
|
4364
4599
|
name: String(meta.name),
|
|
4365
4600
|
version: String(meta.version),
|
|
4366
4601
|
title: typeof meta.title === "string" ? meta.title : "",
|
|
4367
|
-
description: typeof meta.description === "string" ? meta.description : ""
|
|
4602
|
+
description: typeof meta.description === "string" ? meta.description : "",
|
|
4603
|
+
signing: parseSigningBlock(meta.signing)
|
|
4604
|
+
};
|
|
4605
|
+
}
|
|
4606
|
+
function tryReadMetaJson(contentDir) {
|
|
4607
|
+
try {
|
|
4608
|
+
return readMetaJson(contentDir);
|
|
4609
|
+
} catch {
|
|
4610
|
+
return null;
|
|
4611
|
+
}
|
|
4612
|
+
}
|
|
4613
|
+
function parseSigningBlock(raw) {
|
|
4614
|
+
if (!raw || typeof raw !== "object") return void 0;
|
|
4615
|
+
const s = raw;
|
|
4616
|
+
if (typeof s.signature !== "string" || typeof s.public_key_id !== "string" || !s.files) {
|
|
4617
|
+
return void 0;
|
|
4618
|
+
}
|
|
4619
|
+
const rawFiles = s.files;
|
|
4620
|
+
const files = {};
|
|
4621
|
+
for (const [key, value] of Object.entries(rawFiles)) {
|
|
4622
|
+
if (typeof value === "string") files[key] = value;
|
|
4623
|
+
}
|
|
4624
|
+
return {
|
|
4625
|
+
signature: s.signature,
|
|
4626
|
+
public_key_id: s.public_key_id,
|
|
4627
|
+
files,
|
|
4628
|
+
...typeof s.name === "string" && { name: s.name },
|
|
4629
|
+
...typeof s.version === "string" && { version: s.version }
|
|
4368
4630
|
};
|
|
4369
4631
|
}
|
|
4370
4632
|
function validateSkillMdExists(contentDir) {
|
|
4371
|
-
const skillMdPath =
|
|
4633
|
+
const skillMdPath = join7(contentDir, "SKILL.md");
|
|
4372
4634
|
if (!existsSync(skillMdPath)) {
|
|
4373
4635
|
throw new Error(`SKILL.md not found in ${contentDir}. Invalid skill package.`);
|
|
4374
4636
|
}
|
|
4375
4637
|
}
|
|
4376
|
-
var DEFAULT_REGISTRY_PATH =
|
|
4638
|
+
var DEFAULT_REGISTRY_PATH = join8(homedir4(), ".okx", "skills", "registry.json");
|
|
4377
4639
|
function readRegistry(registryPath = DEFAULT_REGISTRY_PATH) {
|
|
4378
4640
|
if (!existsSync2(registryPath)) {
|
|
4379
4641
|
return { version: 1, skills: {} };
|
|
4380
4642
|
}
|
|
4381
4643
|
try {
|
|
4382
|
-
const raw =
|
|
4644
|
+
const raw = readFileSync5(registryPath, "utf-8");
|
|
4383
4645
|
return JSON.parse(raw);
|
|
4384
4646
|
} catch {
|
|
4385
4647
|
return { version: 1, skills: {} };
|
|
@@ -4389,7 +4651,7 @@ function writeRegistry(registry, registryPath = DEFAULT_REGISTRY_PATH) {
|
|
|
4389
4651
|
mkdirSync4(dirname3(registryPath), { recursive: true });
|
|
4390
4652
|
writeFileSync3(registryPath, JSON.stringify(registry, null, 2) + "\n", "utf-8");
|
|
4391
4653
|
}
|
|
4392
|
-
function upsertSkillRecord(meta, registryPath = DEFAULT_REGISTRY_PATH) {
|
|
4654
|
+
function upsertSkillRecord(meta, registryPath = DEFAULT_REGISTRY_PATH, verification) {
|
|
4393
4655
|
const registry = readRegistry(registryPath);
|
|
4394
4656
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
4395
4657
|
const existing = registry.skills[meta.name];
|
|
@@ -4399,7 +4661,8 @@ function upsertSkillRecord(meta, registryPath = DEFAULT_REGISTRY_PATH) {
|
|
|
4399
4661
|
description: meta.description,
|
|
4400
4662
|
installedAt: existing?.installedAt ?? now,
|
|
4401
4663
|
updatedAt: now,
|
|
4402
|
-
source: "marketplace"
|
|
4664
|
+
source: "marketplace",
|
|
4665
|
+
...verification !== void 0 && { verification }
|
|
4403
4666
|
};
|
|
4404
4667
|
writeRegistry(registry, registryPath);
|
|
4405
4668
|
}
|
|
@@ -12195,12 +12458,12 @@ function createToolRunner(client, config) {
|
|
|
12195
12458
|
};
|
|
12196
12459
|
}
|
|
12197
12460
|
function configFilePath() {
|
|
12198
|
-
return
|
|
12461
|
+
return join9(homedir5(), ".okx", "config.toml");
|
|
12199
12462
|
}
|
|
12200
12463
|
function readFullConfig() {
|
|
12201
12464
|
const path42 = configFilePath();
|
|
12202
12465
|
if (!existsSync3(path42)) return { profiles: {} };
|
|
12203
|
-
const raw =
|
|
12466
|
+
const raw = readFileSync6(path42, "utf-8");
|
|
12204
12467
|
try {
|
|
12205
12468
|
return parse(raw);
|
|
12206
12469
|
} catch (err) {
|
|
@@ -12350,7 +12613,7 @@ async function loadConfig(cli) {
|
|
|
12350
12613
|
verbose: cli.verbose ?? false
|
|
12351
12614
|
};
|
|
12352
12615
|
}
|
|
12353
|
-
var CACHE_FILE =
|
|
12616
|
+
var CACHE_FILE = join10(homedir6(), ".okx", "update-check.json");
|
|
12354
12617
|
var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
12355
12618
|
var NEGATIVE_CHECK_INTERVAL_MS = 60 * 60 * 1e3;
|
|
12356
12619
|
var DEFAULT_REGISTRY = "https://registry.npmjs.org/";
|
|
@@ -12358,7 +12621,7 @@ var FETCH_TIMEOUT_MS = 3e3;
|
|
|
12358
12621
|
function readCache2() {
|
|
12359
12622
|
try {
|
|
12360
12623
|
if (existsSync4(CACHE_FILE)) {
|
|
12361
|
-
return JSON.parse(
|
|
12624
|
+
return JSON.parse(readFileSync7(CACHE_FILE, "utf-8"));
|
|
12362
12625
|
}
|
|
12363
12626
|
} catch {
|
|
12364
12627
|
}
|
|
@@ -12366,7 +12629,7 @@ function readCache2() {
|
|
|
12366
12629
|
}
|
|
12367
12630
|
function writeCache2(cache) {
|
|
12368
12631
|
try {
|
|
12369
|
-
mkdirSync6(
|
|
12632
|
+
mkdirSync6(join10(homedir6(), ".okx"), { recursive: true });
|
|
12370
12633
|
writeFileSync5(CACHE_FILE, JSON.stringify(cache, null, 2), "utf-8");
|
|
12371
12634
|
} catch {
|
|
12372
12635
|
}
|
|
@@ -12396,13 +12659,13 @@ function buildNpmrcCandidates() {
|
|
|
12396
12659
|
let dir = process.cwd();
|
|
12397
12660
|
const root = dir.startsWith("/") ? "/" : dir.slice(0, 3);
|
|
12398
12661
|
while (true) {
|
|
12399
|
-
add(
|
|
12662
|
+
add(join10(dir, ".npmrc"));
|
|
12400
12663
|
if (dir === root) break;
|
|
12401
|
-
const parent =
|
|
12664
|
+
const parent = join10(dir, "..");
|
|
12402
12665
|
if (parent === dir) break;
|
|
12403
12666
|
dir = parent;
|
|
12404
12667
|
}
|
|
12405
|
-
add(
|
|
12668
|
+
add(join10(homedir6(), ".npmrc"));
|
|
12406
12669
|
if (process.platform !== "win32") {
|
|
12407
12670
|
add("/etc/npmrc");
|
|
12408
12671
|
}
|
|
@@ -12411,7 +12674,7 @@ function buildNpmrcCandidates() {
|
|
|
12411
12674
|
function readNpmrcRegistry(filePath) {
|
|
12412
12675
|
try {
|
|
12413
12676
|
if (!existsSync4(filePath)) return null;
|
|
12414
|
-
const lines =
|
|
12677
|
+
const lines = readFileSync7(filePath, "utf-8").split(/\r?\n/);
|
|
12415
12678
|
for (const line of lines) {
|
|
12416
12679
|
const trimmed = line.trim();
|
|
12417
12680
|
if (trimmed.startsWith("#") || !trimmed.includes("=")) continue;
|
|
@@ -12738,6 +13001,14 @@ function runSetup(options) {
|
|
|
12738
13001
|
`);
|
|
12739
13002
|
}
|
|
12740
13003
|
}
|
|
13004
|
+
var HttpStatusError = class extends Error {
|
|
13005
|
+
statusCode;
|
|
13006
|
+
constructor(statusCode) {
|
|
13007
|
+
super(`HTTP ${statusCode}`);
|
|
13008
|
+
this.name = "HttpStatusError";
|
|
13009
|
+
this.statusCode = statusCode;
|
|
13010
|
+
}
|
|
13011
|
+
};
|
|
12741
13012
|
function isRedirect(statusCode) {
|
|
12742
13013
|
return statusCode !== void 0 && statusCode >= 300 && statusCode < 400;
|
|
12743
13014
|
}
|
|
@@ -12752,7 +13023,7 @@ function validateRedirect(res, requestUrl, redirectCount, maxRedirects) {
|
|
|
12752
13023
|
return location;
|
|
12753
13024
|
}
|
|
12754
13025
|
function fetchResponse(url, timeoutMs) {
|
|
12755
|
-
return new Promise((
|
|
13026
|
+
return new Promise((resolve4, reject) => {
|
|
12756
13027
|
let redirects = 0;
|
|
12757
13028
|
const maxRedirects = 5;
|
|
12758
13029
|
function doRequest(requestUrl) {
|
|
@@ -12770,10 +13041,14 @@ function fetchResponse(url, timeoutMs) {
|
|
|
12770
13041
|
return;
|
|
12771
13042
|
}
|
|
12772
13043
|
if (res.statusCode !== 200) {
|
|
12773
|
-
|
|
13044
|
+
if (res.statusCode === void 0) {
|
|
13045
|
+
reject(new Error("HTTP unknown"));
|
|
13046
|
+
} else {
|
|
13047
|
+
reject(new HttpStatusError(res.statusCode));
|
|
13048
|
+
}
|
|
12774
13049
|
return;
|
|
12775
13050
|
}
|
|
12776
|
-
|
|
13051
|
+
resolve4(res);
|
|
12777
13052
|
});
|
|
12778
13053
|
req.on("error", reject);
|
|
12779
13054
|
req.on("timeout", () => {
|
|
@@ -12786,10 +13061,10 @@ function fetchResponse(url, timeoutMs) {
|
|
|
12786
13061
|
}
|
|
12787
13062
|
function download(url, destPath, timeoutMs) {
|
|
12788
13063
|
return fetchResponse(url, timeoutMs).then(
|
|
12789
|
-
(res) => new Promise((
|
|
13064
|
+
(res) => new Promise((resolve4, reject) => {
|
|
12790
13065
|
const file = createWriteStream2(destPath);
|
|
12791
13066
|
res.pipe(file);
|
|
12792
|
-
file.on("finish", () => file.close(() =>
|
|
13067
|
+
file.on("finish", () => file.close(() => resolve4()));
|
|
12793
13068
|
file.on("error", (err) => {
|
|
12794
13069
|
try {
|
|
12795
13070
|
unlinkSync3(destPath);
|
|
@@ -12802,10 +13077,10 @@ function download(url, destPath, timeoutMs) {
|
|
|
12802
13077
|
}
|
|
12803
13078
|
function downloadText(url, timeoutMs) {
|
|
12804
13079
|
return fetchResponse(url, timeoutMs).then(
|
|
12805
|
-
(res) => new Promise((
|
|
13080
|
+
(res) => new Promise((resolve4, reject) => {
|
|
12806
13081
|
const chunks = [];
|
|
12807
13082
|
res.on("data", (chunk) => chunks.push(chunk));
|
|
12808
|
-
res.on("end", () =>
|
|
13083
|
+
res.on("end", () => resolve4(Buffer.concat(chunks).toString("utf8")));
|
|
12809
13084
|
res.on("error", reject);
|
|
12810
13085
|
})
|
|
12811
13086
|
);
|
|
@@ -12834,10 +13109,10 @@ function getBinaryName() {
|
|
|
12834
13109
|
return platform() === "win32" ? "okx-pilot.exe" : "okx-pilot";
|
|
12835
13110
|
}
|
|
12836
13111
|
function hashFile(filePath) {
|
|
12837
|
-
const buf =
|
|
13112
|
+
const buf = readFileSync9(filePath);
|
|
12838
13113
|
return {
|
|
12839
13114
|
size: buf.byteLength,
|
|
12840
|
-
sha256:
|
|
13115
|
+
sha256: createHash3("sha256").update(buf).digest("hex")
|
|
12841
13116
|
};
|
|
12842
13117
|
}
|
|
12843
13118
|
function getPilotStatus(binaryPath, opts) {
|
|
@@ -12952,7 +13227,7 @@ async function installPilotBinary(destPath, sources = CDN_SOURCES, onProgress) {
|
|
|
12952
13227
|
if (earlyResult) return earlyResult;
|
|
12953
13228
|
const platformDir = getPlatformDir();
|
|
12954
13229
|
const binaryName = getBinaryName();
|
|
12955
|
-
const resolvedDest = destPath ??
|
|
13230
|
+
const resolvedDest = destPath ?? join12(homedir8(), ".okx", "bin", binaryName);
|
|
12956
13231
|
const tmpPath = resolvedDest + ".tmp";
|
|
12957
13232
|
mkdirSync8(dirname6(resolvedDest), { recursive: true });
|
|
12958
13233
|
const localHash = existsSync6(resolvedDest) ? hashFile(resolvedDest) : null;
|
|
@@ -13004,9 +13279,41 @@ function removePilotBinary(binaryPath) {
|
|
|
13004
13279
|
}
|
|
13005
13280
|
}
|
|
13006
13281
|
var AUTH_CDN_PATH_PREFIX = "/upgradeapp/tools/oauth";
|
|
13282
|
+
var LINUX_ARM64_FALLBACK_DIR = "linux-x64";
|
|
13007
13283
|
function getAuthBinaryName() {
|
|
13008
13284
|
return platform2() === "win32" ? "okx-auth.exe" : "okx-auth";
|
|
13009
13285
|
}
|
|
13286
|
+
async function _resolveAuthPlatformFromNative(native, sources, timeoutMs) {
|
|
13287
|
+
if (native !== "linux-arm64") {
|
|
13288
|
+
return native;
|
|
13289
|
+
}
|
|
13290
|
+
const checksumPath = `${AUTH_CDN_PATH_PREFIX}/linux-arm64/checksum.json`;
|
|
13291
|
+
let got404 = false;
|
|
13292
|
+
for (const { host, protocol } of sources) {
|
|
13293
|
+
const url = `${protocol}://${host}${checksumPath}`;
|
|
13294
|
+
try {
|
|
13295
|
+
await downloadText(url, timeoutMs);
|
|
13296
|
+
return "linux-arm64";
|
|
13297
|
+
} catch (err) {
|
|
13298
|
+
if (err instanceof HttpStatusError && err.statusCode === 404) {
|
|
13299
|
+
got404 = true;
|
|
13300
|
+
break;
|
|
13301
|
+
}
|
|
13302
|
+
}
|
|
13303
|
+
}
|
|
13304
|
+
if (got404) {
|
|
13305
|
+
console.warn(
|
|
13306
|
+
"[okx-auth] Native linux-arm64 binary not yet on CDN. Falling back to linux-x64 binary \u2014 x86_64 emulation (binfmt_misc/Rosetta) required."
|
|
13307
|
+
);
|
|
13308
|
+
return LINUX_ARM64_FALLBACK_DIR;
|
|
13309
|
+
}
|
|
13310
|
+
return "linux-arm64";
|
|
13311
|
+
}
|
|
13312
|
+
async function resolveAuthPlatformDir(sources = CDN_SOURCES, timeoutMs = DOWNLOAD_TIMEOUT_MS) {
|
|
13313
|
+
const native = getPlatformDir();
|
|
13314
|
+
if (!native) return null;
|
|
13315
|
+
return _resolveAuthPlatformFromNative(native, sources, timeoutMs);
|
|
13316
|
+
}
|
|
13010
13317
|
function getAuthStatus(binaryPath, opts) {
|
|
13011
13318
|
const resolvedPath = binaryPath ?? getAuthBinaryPath();
|
|
13012
13319
|
const platformDir = getPlatformDir();
|
|
@@ -13020,7 +13327,7 @@ function getAuthStatus(binaryPath, opts) {
|
|
|
13020
13327
|
return { binaryPath: resolvedPath, exists: true, platform: platformDir, fileSize: size, sha256 };
|
|
13021
13328
|
}
|
|
13022
13329
|
async function fetchAuthCdnChecksum(sources = CDN_SOURCES, timeoutMs = DOWNLOAD_TIMEOUT_MS) {
|
|
13023
|
-
const platformDir =
|
|
13330
|
+
const platformDir = await resolveAuthPlatformDir(sources, timeoutMs);
|
|
13024
13331
|
if (!platformDir) return null;
|
|
13025
13332
|
const checksumPath = `${AUTH_CDN_PATH_PREFIX}/${platformDir}/checksum.json`;
|
|
13026
13333
|
for (const { host, protocol } of sources) {
|
|
@@ -13089,12 +13396,51 @@ function installPreChecks2(destPath, sources) {
|
|
|
13089
13396
|
function isLocalUpToDate2(localHash, checksum) {
|
|
13090
13397
|
return localHash !== null && localHash.size === checksum.size && localHash.sha256 === checksum.sha256;
|
|
13091
13398
|
}
|
|
13399
|
+
async function tryInstallFromOneSource(ctx) {
|
|
13400
|
+
const { host, protocol, platformDir, checksumPath, binaryPath, tmpPath, resolvedDest, localHash, onProgress } = ctx;
|
|
13401
|
+
try {
|
|
13402
|
+
const checksum = await fetchAndValidateChecksum2(
|
|
13403
|
+
host,
|
|
13404
|
+
protocol,
|
|
13405
|
+
checksumPath,
|
|
13406
|
+
platformDir,
|
|
13407
|
+
DOWNLOAD_TIMEOUT_MS,
|
|
13408
|
+
onProgress
|
|
13409
|
+
);
|
|
13410
|
+
if (isLocalUpToDate2(localHash, checksum)) {
|
|
13411
|
+
onProgress?.("Already up to date (checksum match)");
|
|
13412
|
+
return { kind: "done", result: { status: "up-to-date", source: host } };
|
|
13413
|
+
}
|
|
13414
|
+
await downloadAndVerify2(host, protocol, binaryPath, tmpPath, checksum, DOWNLOAD_TIMEOUT_MS, onProgress);
|
|
13415
|
+
atomicReplace2(tmpPath, resolvedDest);
|
|
13416
|
+
onProgress?.(`Downloaded and verified from ${host}`);
|
|
13417
|
+
return { kind: "done", result: { status: "installed", source: host } };
|
|
13418
|
+
} catch (err) {
|
|
13419
|
+
try {
|
|
13420
|
+
unlinkSync5(tmpPath);
|
|
13421
|
+
} catch {
|
|
13422
|
+
}
|
|
13423
|
+
const errObj = err instanceof Error ? err : new Error(String(err));
|
|
13424
|
+
onProgress?.(`${host} failed: ${errObj.message}`);
|
|
13425
|
+
return { kind: "next", err: errObj };
|
|
13426
|
+
}
|
|
13427
|
+
}
|
|
13428
|
+
function buildInstallFailureResult(errors) {
|
|
13429
|
+
const formatted = errors.map(({ host, err }) => `${host}: ${err.message}`).join("\n");
|
|
13430
|
+
const allHttpErrors = errors.length > 0 && errors.every(({ err }) => err instanceof HttpStatusError);
|
|
13431
|
+
const prefix = allHttpErrors ? "okx-auth binary not available on CDN for this platform" : "All CDN sources failed";
|
|
13432
|
+
return { status: "failed", error: `${prefix}:
|
|
13433
|
+
${formatted}` };
|
|
13434
|
+
}
|
|
13092
13435
|
async function installAuthBinary(destPath, sources = CDN_SOURCES, onProgress) {
|
|
13093
13436
|
const earlyResult = installPreChecks2(destPath, sources);
|
|
13094
13437
|
if (earlyResult) return earlyResult;
|
|
13095
|
-
const platformDir =
|
|
13438
|
+
const platformDir = await resolveAuthPlatformDir(sources);
|
|
13439
|
+
if (!platformDir) {
|
|
13440
|
+
return { status: "failed", error: "Unsupported platform" };
|
|
13441
|
+
}
|
|
13096
13442
|
const binaryName = getAuthBinaryName();
|
|
13097
|
-
const resolvedDest = destPath ??
|
|
13443
|
+
const resolvedDest = destPath ?? join13(homedir9(), ".okx", "bin", binaryName);
|
|
13098
13444
|
const tmpPath = resolvedDest + ".tmp";
|
|
13099
13445
|
mkdirSync9(dirname7(resolvedDest), { recursive: true });
|
|
13100
13446
|
const localHash = existsSync7(resolvedDest) ? hashFile(resolvedDest) : null;
|
|
@@ -13102,35 +13448,21 @@ async function installAuthBinary(destPath, sources = CDN_SOURCES, onProgress) {
|
|
|
13102
13448
|
const binaryPath = `${AUTH_CDN_PATH_PREFIX}/${platformDir}/${binaryName}`;
|
|
13103
13449
|
const errors = [];
|
|
13104
13450
|
for (const { host, protocol } of sources) {
|
|
13105
|
-
|
|
13106
|
-
|
|
13107
|
-
|
|
13108
|
-
|
|
13109
|
-
|
|
13110
|
-
|
|
13111
|
-
|
|
13112
|
-
|
|
13113
|
-
|
|
13114
|
-
|
|
13115
|
-
|
|
13116
|
-
|
|
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
|
-
}
|
|
13451
|
+
const attempt = await tryInstallFromOneSource({
|
|
13452
|
+
host,
|
|
13453
|
+
protocol,
|
|
13454
|
+
platformDir,
|
|
13455
|
+
checksumPath,
|
|
13456
|
+
binaryPath,
|
|
13457
|
+
tmpPath,
|
|
13458
|
+
resolvedDest,
|
|
13459
|
+
localHash,
|
|
13460
|
+
onProgress
|
|
13461
|
+
});
|
|
13462
|
+
if (attempt.kind === "done") return attempt.result;
|
|
13463
|
+
errors.push({ host, err: attempt.err });
|
|
13131
13464
|
}
|
|
13132
|
-
return
|
|
13133
|
-
${errors.join("\n")}` };
|
|
13465
|
+
return buildInstallFailureResult(errors);
|
|
13134
13466
|
}
|
|
13135
13467
|
function removeAuthBinary(binaryPath) {
|
|
13136
13468
|
const resolvedPath = binaryPath ?? getAuthBinaryPath();
|
|
@@ -13145,12 +13477,12 @@ function removeAuthBinary(binaryPath) {
|
|
|
13145
13477
|
throw new Error(`Failed to remove ${resolvedPath}: ${msg}`);
|
|
13146
13478
|
}
|
|
13147
13479
|
}
|
|
13148
|
-
var CACHE_PATH =
|
|
13480
|
+
var CACHE_PATH = join14(homedir10(), ".okx", "auth-binary-check.json");
|
|
13149
13481
|
var CHECK_INTERVAL_MS2 = 2 * 60 * 60 * 1e3;
|
|
13150
13482
|
function readCache3() {
|
|
13151
13483
|
try {
|
|
13152
13484
|
if (!existsSync8(CACHE_PATH)) return null;
|
|
13153
|
-
const data = JSON.parse(
|
|
13485
|
+
const data = JSON.parse(readFileSync10(CACHE_PATH, "utf-8"));
|
|
13154
13486
|
if (typeof data.cdnSha256 !== "string" || typeof data.checkedAt !== "number") return null;
|
|
13155
13487
|
return { cdnSha256: data.cdnSha256, checkedAt: data.checkedAt };
|
|
13156
13488
|
} catch {
|
|
@@ -13159,7 +13491,7 @@ function readCache3() {
|
|
|
13159
13491
|
}
|
|
13160
13492
|
function writeCache3(cdnSha256) {
|
|
13161
13493
|
try {
|
|
13162
|
-
mkdirSync10(
|
|
13494
|
+
mkdirSync10(join14(homedir10(), ".okx"), { recursive: true });
|
|
13163
13495
|
writeFileSync7(CACHE_PATH, JSON.stringify({ cdnSha256, checkedAt: Date.now() }, null, 2), "utf-8");
|
|
13164
13496
|
} catch {
|
|
13165
13497
|
}
|
|
@@ -13294,7 +13626,7 @@ function markFailedIfSCodeError(data) {
|
|
|
13294
13626
|
// src/commands/auth.ts
|
|
13295
13627
|
function runOkxAuth(args) {
|
|
13296
13628
|
const binPath = getAuthBinaryPath();
|
|
13297
|
-
return new Promise((
|
|
13629
|
+
return new Promise((resolve4, reject) => {
|
|
13298
13630
|
const child = spawn3(binPath, args, {
|
|
13299
13631
|
stdio: "inherit"
|
|
13300
13632
|
});
|
|
@@ -13302,13 +13634,13 @@ function runOkxAuth(args) {
|
|
|
13302
13634
|
reject(new Error(`Failed to spawn okx-auth: ${err.message}`));
|
|
13303
13635
|
});
|
|
13304
13636
|
child.on("close", (code) => {
|
|
13305
|
-
|
|
13637
|
+
resolve4(code ?? 1);
|
|
13306
13638
|
});
|
|
13307
13639
|
});
|
|
13308
13640
|
}
|
|
13309
13641
|
function runOkxAuthCapture(args) {
|
|
13310
13642
|
const binPath = getAuthBinaryPath();
|
|
13311
|
-
return new Promise((
|
|
13643
|
+
return new Promise((resolve4, reject) => {
|
|
13312
13644
|
const child = spawn3(binPath, args, {
|
|
13313
13645
|
stdio: ["inherit", "pipe", "inherit"]
|
|
13314
13646
|
});
|
|
@@ -13318,7 +13650,7 @@ function runOkxAuthCapture(args) {
|
|
|
13318
13650
|
reject(new Error(`Failed to spawn okx-auth: ${err.message}`));
|
|
13319
13651
|
});
|
|
13320
13652
|
child.on("close", (code) => {
|
|
13321
|
-
|
|
13653
|
+
resolve4({
|
|
13322
13654
|
code: code ?? 1,
|
|
13323
13655
|
stdout: Buffer.concat(chunks).toString("utf-8")
|
|
13324
13656
|
});
|
|
@@ -13523,14 +13855,14 @@ async function confirmRemoval(force, binaryPath) {
|
|
|
13523
13855
|
return true;
|
|
13524
13856
|
}
|
|
13525
13857
|
function askConfirmation(prompt2) {
|
|
13526
|
-
return new Promise((
|
|
13858
|
+
return new Promise((resolve4) => {
|
|
13527
13859
|
const rl = readline.createInterface({
|
|
13528
13860
|
input: process.stdin,
|
|
13529
13861
|
output: process.stdout
|
|
13530
13862
|
});
|
|
13531
13863
|
rl.question(prompt2, (answer) => {
|
|
13532
13864
|
rl.close();
|
|
13533
|
-
|
|
13865
|
+
resolve4(answer.trim().toLowerCase() === "y");
|
|
13534
13866
|
});
|
|
13535
13867
|
});
|
|
13536
13868
|
}
|
|
@@ -13587,26 +13919,26 @@ var Report = class {
|
|
|
13587
13919
|
this.lines.push({ key, value });
|
|
13588
13920
|
}
|
|
13589
13921
|
print() {
|
|
13590
|
-
const
|
|
13922
|
+
const sep3 = "\u2500".repeat(52);
|
|
13591
13923
|
outputLine("");
|
|
13592
|
-
outputLine(` \u2500\u2500 Diagnostic Report (copy & share) ${
|
|
13924
|
+
outputLine(` \u2500\u2500 Diagnostic Report (copy & share) ${sep3.slice(35)}`);
|
|
13593
13925
|
for (const { key, value } of this.lines) {
|
|
13594
13926
|
outputLine(` ${key.padEnd(14)} ${value}`);
|
|
13595
13927
|
}
|
|
13596
|
-
outputLine(` ${
|
|
13928
|
+
outputLine(` ${sep3}`);
|
|
13597
13929
|
outputLine("");
|
|
13598
13930
|
}
|
|
13599
13931
|
/** Write report to a file path, returns true on success. */
|
|
13600
13932
|
writeToFile(filePath) {
|
|
13601
13933
|
try {
|
|
13602
|
-
const
|
|
13934
|
+
const sep3 = "\u2500".repeat(52);
|
|
13603
13935
|
const lines = [
|
|
13604
|
-
`\u2500\u2500 Diagnostic Report (copy & share) ${
|
|
13936
|
+
`\u2500\u2500 Diagnostic Report (copy & share) ${sep3.slice(35)}`
|
|
13605
13937
|
];
|
|
13606
13938
|
for (const { key, value } of this.lines) {
|
|
13607
13939
|
lines.push(`${key.padEnd(14)} ${value}`);
|
|
13608
13940
|
}
|
|
13609
|
-
lines.push(
|
|
13941
|
+
lines.push(sep3, "");
|
|
13610
13942
|
fs4.writeFileSync(filePath, lines.join("\n"), "utf8");
|
|
13611
13943
|
return true;
|
|
13612
13944
|
} catch (_e) {
|
|
@@ -13982,13 +14314,13 @@ async function checkStdioHandshake(entryPath, report) {
|
|
|
13982
14314
|
clientInfo: { name: "okx-diagnose", version: "1.0" }
|
|
13983
14315
|
}
|
|
13984
14316
|
});
|
|
13985
|
-
return new Promise((
|
|
14317
|
+
return new Promise((resolve4) => {
|
|
13986
14318
|
let settled = false;
|
|
13987
14319
|
const settle = (passed) => {
|
|
13988
14320
|
if (settled) return;
|
|
13989
14321
|
settled = true;
|
|
13990
14322
|
clearTimeout(timer);
|
|
13991
|
-
|
|
14323
|
+
resolve4(passed);
|
|
13992
14324
|
};
|
|
13993
14325
|
const child = spawn4(process.execPath, [entryPath], {
|
|
13994
14326
|
stdio: ["pipe", "pipe", "pipe"],
|
|
@@ -14109,7 +14441,7 @@ async function cmdDiagnoseMcp(options = {}) {
|
|
|
14109
14441
|
|
|
14110
14442
|
// src/commands/diagnose.ts
|
|
14111
14443
|
var CLI_VERSION = readCliVersion();
|
|
14112
|
-
var GIT_HASH = true ? "
|
|
14444
|
+
var GIT_HASH = true ? "e5abc21f" : "dev";
|
|
14113
14445
|
function maskKey2(key) {
|
|
14114
14446
|
if (!key) return "(not set)";
|
|
14115
14447
|
if (key.length <= 8) return "****";
|
|
@@ -14126,7 +14458,7 @@ async function checkDns(hostname) {
|
|
|
14126
14458
|
}
|
|
14127
14459
|
async function checkSocket(createFn, successEvent, timeoutMs) {
|
|
14128
14460
|
const t0 = Date.now();
|
|
14129
|
-
return new Promise((
|
|
14461
|
+
return new Promise((resolve4) => {
|
|
14130
14462
|
const socket = createFn();
|
|
14131
14463
|
const cleanup = () => {
|
|
14132
14464
|
socket.removeAllListeners();
|
|
@@ -14134,15 +14466,15 @@ async function checkSocket(createFn, successEvent, timeoutMs) {
|
|
|
14134
14466
|
};
|
|
14135
14467
|
socket.once(successEvent, () => {
|
|
14136
14468
|
cleanup();
|
|
14137
|
-
|
|
14469
|
+
resolve4({ ok: true, ms: Date.now() - t0 });
|
|
14138
14470
|
});
|
|
14139
14471
|
socket.once("timeout", () => {
|
|
14140
14472
|
cleanup();
|
|
14141
|
-
|
|
14473
|
+
resolve4({ ok: false, ms: Date.now() - t0, error: `timed out after ${timeoutMs}ms` });
|
|
14142
14474
|
});
|
|
14143
14475
|
socket.once("error", (err) => {
|
|
14144
14476
|
cleanup();
|
|
14145
|
-
|
|
14477
|
+
resolve4({ ok: false, ms: Date.now() - t0, error: err.message });
|
|
14146
14478
|
});
|
|
14147
14479
|
});
|
|
14148
14480
|
}
|
|
@@ -14470,23 +14802,23 @@ function suggestSubcommand(action, knownActions, knownPaths = []) {
|
|
|
14470
14802
|
|
|
14471
14803
|
// src/commands/upgrade.ts
|
|
14472
14804
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
14473
|
-
import { readFileSync as
|
|
14474
|
-
import { dirname as dirname8, join as
|
|
14805
|
+
import { readFileSync as readFileSync11, writeFileSync as writeFileSync8, mkdirSync as mkdirSync11 } from "fs";
|
|
14806
|
+
import { dirname as dirname8, join as join15 } from "path";
|
|
14475
14807
|
import { homedir as homedir11 } from "os";
|
|
14476
14808
|
var PACKAGES = ["@okx_ai/okx-trade-mcp", "@okx_ai/okx-trade-cli"];
|
|
14477
|
-
var CACHE_FILE2 =
|
|
14809
|
+
var CACHE_FILE2 = join15(homedir11(), ".okx", "last_check");
|
|
14478
14810
|
var THROTTLE_MS = 12 * 60 * 60 * 1e3;
|
|
14479
|
-
var NPM_BIN =
|
|
14811
|
+
var NPM_BIN = join15(dirname8(process.execPath), process.platform === "win32" ? "npm.cmd" : "npm");
|
|
14480
14812
|
function readLastCheck() {
|
|
14481
14813
|
try {
|
|
14482
|
-
return parseInt(
|
|
14814
|
+
return parseInt(readFileSync11(CACHE_FILE2, "utf-8").trim(), 10) || 0;
|
|
14483
14815
|
} catch {
|
|
14484
14816
|
return 0;
|
|
14485
14817
|
}
|
|
14486
14818
|
}
|
|
14487
14819
|
function writeLastCheck() {
|
|
14488
14820
|
try {
|
|
14489
|
-
mkdirSync11(
|
|
14821
|
+
mkdirSync11(join15(homedir11(), ".okx"), { recursive: true });
|
|
14490
14822
|
writeFileSync8(CACHE_FILE2, String(Math.floor(Date.now() / 1e3)), "utf-8");
|
|
14491
14823
|
} catch {
|
|
14492
14824
|
}
|
|
@@ -18493,7 +18825,7 @@ Config saved to ${p}
|
|
|
18493
18825
|
}
|
|
18494
18826
|
};
|
|
18495
18827
|
function prompt(rl, question) {
|
|
18496
|
-
return new Promise((
|
|
18828
|
+
return new Promise((resolve4) => rl.question(question, resolve4));
|
|
18497
18829
|
}
|
|
18498
18830
|
function cmdConfigShow(json) {
|
|
18499
18831
|
const config = readFullConfig();
|
|
@@ -19916,18 +20248,21 @@ async function cmdDcdQuoteAndBuy(run, opts) {
|
|
|
19916
20248
|
|
|
19917
20249
|
// src/commands/skill.ts
|
|
19918
20250
|
import { tmpdir, homedir as homedir13 } from "os";
|
|
19919
|
-
import { join as
|
|
20251
|
+
import { join as join17, dirname as dirname9 } from "path";
|
|
19920
20252
|
import { mkdirSync as mkdirSync12, rmSync, existsSync as existsSync10, copyFileSync as copyFileSync2 } from "fs";
|
|
19921
20253
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
19922
20254
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
19923
20255
|
function resolveNpx() {
|
|
19924
|
-
const sibling =
|
|
20256
|
+
const sibling = join17(dirname9(process.execPath), "npx");
|
|
19925
20257
|
if (existsSync10(sibling)) return sibling;
|
|
19926
20258
|
return "npx";
|
|
19927
20259
|
}
|
|
19928
20260
|
function npxEnv() {
|
|
19929
20261
|
return { ...process.env, NO_COLOR: "1", FORCE_COLOR: "0" };
|
|
19930
20262
|
}
|
|
20263
|
+
function getSkillContentDir(name) {
|
|
20264
|
+
return join17(homedir13(), ".agents", "skills", name);
|
|
20265
|
+
}
|
|
19931
20266
|
var THIRD_PARTY_INSTALL_NOTICE = "Note: This skill was created by a third-party developer, not by OKX. Review SKILL.md before use.";
|
|
19932
20267
|
async function cmdSkillSearch(run, opts) {
|
|
19933
20268
|
const args = {};
|
|
@@ -19977,16 +20312,56 @@ async function cmdSkillCategories(run, json) {
|
|
|
19977
20312
|
}
|
|
19978
20313
|
outputLine("");
|
|
19979
20314
|
}
|
|
19980
|
-
async function
|
|
19981
|
-
|
|
20315
|
+
async function wrapVerify(fn) {
|
|
20316
|
+
try {
|
|
20317
|
+
return await fn();
|
|
20318
|
+
} catch (e) {
|
|
20319
|
+
if (e instanceof ConfigError) {
|
|
20320
|
+
throw new Error(
|
|
20321
|
+
`Signature verification requires authentication \u2014 run \`okx auth login\` first, or use --force to bypass.`
|
|
20322
|
+
);
|
|
20323
|
+
}
|
|
20324
|
+
throw e;
|
|
20325
|
+
}
|
|
20326
|
+
}
|
|
20327
|
+
async function cmdSkillAdd(name, config, json, force = false, exec = execFileSync2, _deps) {
|
|
20328
|
+
const _download = _deps?.download ?? downloadSkillZip;
|
|
20329
|
+
const _extract = _deps?.extract ?? extractSkillZip;
|
|
20330
|
+
const tmpBase = join17(tmpdir(), `okx-skill-${randomUUID2()}`);
|
|
19982
20331
|
mkdirSync12(tmpBase, { recursive: true });
|
|
19983
20332
|
try {
|
|
19984
20333
|
outputLine(`Downloading ${name}...`);
|
|
19985
20334
|
const client = new OkxRestClient(config);
|
|
19986
|
-
const zipPath = await
|
|
19987
|
-
const contentDir = await
|
|
20335
|
+
const zipPath = await _download(client, name, tmpBase);
|
|
20336
|
+
const contentDir = await _extract(zipPath, join17(tmpBase, "content"));
|
|
19988
20337
|
const meta = readMetaJson(contentDir);
|
|
19989
20338
|
validateSkillMdExists(contentDir);
|
|
20339
|
+
outputLine("Verifying signature...");
|
|
20340
|
+
const verifyResult = await wrapVerify(
|
|
20341
|
+
() => verifySkillSignature(contentDir, meta.signing, {
|
|
20342
|
+
fetchPublicKey: (keyId) => getPublicKey(client, keyId),
|
|
20343
|
+
serverSideVerify: (sName, version, files) => serverSideVerify(client, sName, version, files),
|
|
20344
|
+
skillName: meta.name,
|
|
20345
|
+
skillVersion: meta.version
|
|
20346
|
+
})
|
|
20347
|
+
);
|
|
20348
|
+
if (verifyResult.status === "failed") {
|
|
20349
|
+
if (!force) {
|
|
20350
|
+
throw new Error(`Signature verification failed: ${verifyResult.error ?? "unknown error"}. Use --force to install anyway.`);
|
|
20351
|
+
}
|
|
20352
|
+
process.stderr.write(`WARNING: Signature verification failed \u2014 ${verifyResult.error ?? "unknown"}. Installing anyway (--force).
|
|
20353
|
+
`);
|
|
20354
|
+
} else if (verifyResult.status === "verified_by_server") {
|
|
20355
|
+
if (!json) {
|
|
20356
|
+
outputLine(` Verified by server (v${verifyResult.serverVersion ?? "?"})`);
|
|
20357
|
+
if (verifyResult.error) outputLine(` Note: ${verifyResult.error}`);
|
|
20358
|
+
}
|
|
20359
|
+
} else if (!json) {
|
|
20360
|
+
outputLine(` Signature verified (key: ${verifyResult.publicKeyId}, files: ${verifyResult.filesChecked})`);
|
|
20361
|
+
if (verifyResult.extraFiles?.length) {
|
|
20362
|
+
outputLine(` Note: ${verifyResult.extraFiles.length} extra unsigned file(s) present`);
|
|
20363
|
+
}
|
|
20364
|
+
}
|
|
19990
20365
|
outputLine("Installing to detected agents...");
|
|
19991
20366
|
try {
|
|
19992
20367
|
exec(resolveNpx(), ["skills", "add", contentDir, "-y", "-g"], {
|
|
@@ -19995,7 +20370,7 @@ async function cmdSkillAdd(name, config, json, exec = execFileSync2) {
|
|
|
19995
20370
|
env: npxEnv()
|
|
19996
20371
|
});
|
|
19997
20372
|
} catch (e) {
|
|
19998
|
-
const savedZip =
|
|
20373
|
+
const savedZip = join17(process.cwd(), `${name}.zip`);
|
|
19999
20374
|
try {
|
|
20000
20375
|
copyFileSync2(zipPath, savedZip);
|
|
20001
20376
|
} catch {
|
|
@@ -20004,7 +20379,8 @@ async function cmdSkillAdd(name, config, json, exec = execFileSync2) {
|
|
|
20004
20379
|
errorLine(`You can manually install from: ${savedZip}`);
|
|
20005
20380
|
throw e;
|
|
20006
20381
|
}
|
|
20007
|
-
|
|
20382
|
+
const registryStatus = verifyResult.status === "failed" && force ? "bypassed" : verifyResult.status;
|
|
20383
|
+
upsertSkillRecord(meta, void 0, registryStatus);
|
|
20008
20384
|
printSkillInstallResult(meta, json);
|
|
20009
20385
|
} finally {
|
|
20010
20386
|
rmSync(tmpBase, { recursive: true, force: true });
|
|
@@ -20035,7 +20411,7 @@ function cmdSkillRemove(name, json, exec = execFileSync2) {
|
|
|
20035
20411
|
env: npxEnv()
|
|
20036
20412
|
});
|
|
20037
20413
|
} catch {
|
|
20038
|
-
const agentsPath =
|
|
20414
|
+
const agentsPath = getSkillContentDir(name);
|
|
20039
20415
|
try {
|
|
20040
20416
|
rmSync(agentsPath, { recursive: true, force: true });
|
|
20041
20417
|
} catch {
|
|
@@ -20099,6 +20475,59 @@ function cmdSkillList(json) {
|
|
|
20099
20475
|
outputLine("");
|
|
20100
20476
|
outputLine(`${skills.length} skills installed.`);
|
|
20101
20477
|
}
|
|
20478
|
+
async function cmdSkillVerify(name, config, json) {
|
|
20479
|
+
const record = getSkillRecord(name);
|
|
20480
|
+
if (!record) {
|
|
20481
|
+
errorLine(`Skill "${name}" is not installed.`);
|
|
20482
|
+
process.exitCode = 1;
|
|
20483
|
+
return;
|
|
20484
|
+
}
|
|
20485
|
+
const contentDir = getSkillContentDir(name);
|
|
20486
|
+
if (!existsSync10(contentDir)) {
|
|
20487
|
+
errorLine(`Skill content directory not found: ${contentDir}`);
|
|
20488
|
+
errorLine(`Try reinstalling with: okx skill add ${name}`);
|
|
20489
|
+
process.exitCode = 1;
|
|
20490
|
+
return;
|
|
20491
|
+
}
|
|
20492
|
+
const meta = tryReadMetaJson(contentDir);
|
|
20493
|
+
const client = new OkxRestClient(config);
|
|
20494
|
+
let result;
|
|
20495
|
+
try {
|
|
20496
|
+
result = await wrapVerify(
|
|
20497
|
+
() => verifySkillSignature(contentDir, meta?.signing, {
|
|
20498
|
+
fetchPublicKey: (keyId) => getPublicKey(client, keyId),
|
|
20499
|
+
serverSideVerify: (sName, version, files) => serverSideVerify(client, sName, version, files),
|
|
20500
|
+
skillName: name,
|
|
20501
|
+
skillVersion: meta?.version
|
|
20502
|
+
})
|
|
20503
|
+
);
|
|
20504
|
+
} catch (e) {
|
|
20505
|
+
errorLine(e instanceof Error ? e.message : String(e));
|
|
20506
|
+
process.exitCode = 1;
|
|
20507
|
+
return;
|
|
20508
|
+
}
|
|
20509
|
+
if (meta) {
|
|
20510
|
+
upsertSkillRecord(meta, void 0, result.status);
|
|
20511
|
+
}
|
|
20512
|
+
if (result.status === "failed") {
|
|
20513
|
+
process.exitCode = 1;
|
|
20514
|
+
}
|
|
20515
|
+
if (json) {
|
|
20516
|
+
outputLine(JSON.stringify(result, null, 2));
|
|
20517
|
+
return;
|
|
20518
|
+
}
|
|
20519
|
+
if (result.status === "verified") {
|
|
20520
|
+
outputLine(`\u2713 ${name}: signature verified (key: ${result.publicKeyId}, files: ${result.filesChecked})`);
|
|
20521
|
+
if (result.extraFiles?.length) {
|
|
20522
|
+
outputLine(` Note: ${result.extraFiles.length} extra unsigned file(s) present`);
|
|
20523
|
+
}
|
|
20524
|
+
} else if (result.status === "verified_by_server") {
|
|
20525
|
+
outputLine(`\u2713 ${name}: verified by server (v${result.serverVersion ?? "?"})`);
|
|
20526
|
+
if (result.error) outputLine(` Note: ${result.error}`);
|
|
20527
|
+
} else if (result.status === "failed") {
|
|
20528
|
+
errorLine(`\u2717 ${name}: verification failed \u2014 ${result.error ?? "unknown"}`);
|
|
20529
|
+
}
|
|
20530
|
+
}
|
|
20102
20531
|
function printSkillInstallResult(meta, json) {
|
|
20103
20532
|
if (json) {
|
|
20104
20533
|
outputLine(JSON.stringify({ name: meta.name, version: meta.version, status: "installed" }, null, 2));
|
|
@@ -20249,14 +20678,14 @@ function formatBytes2(bytes) {
|
|
|
20249
20678
|
return `${(bytes / (1024 * 1024)).toFixed(2)} MB`;
|
|
20250
20679
|
}
|
|
20251
20680
|
function askConfirmation2(prompt2) {
|
|
20252
|
-
return new Promise((
|
|
20681
|
+
return new Promise((resolve4) => {
|
|
20253
20682
|
const rl = readline2.createInterface({
|
|
20254
20683
|
input: process.stdin,
|
|
20255
20684
|
output: process.stdout
|
|
20256
20685
|
});
|
|
20257
20686
|
rl.question(prompt2, (answer) => {
|
|
20258
20687
|
rl.close();
|
|
20259
|
-
|
|
20688
|
+
resolve4(answer.trim().toLowerCase() === "y");
|
|
20260
20689
|
});
|
|
20261
20690
|
});
|
|
20262
20691
|
}
|
|
@@ -20719,7 +21148,7 @@ async function cmdEventCancel(run, opts) {
|
|
|
20719
21148
|
// src/index.ts
|
|
20720
21149
|
var _require3 = createRequire3(import.meta.url);
|
|
20721
21150
|
var CLI_VERSION2 = _require3("../package.json").version;
|
|
20722
|
-
var GIT_HASH2 = true ? "
|
|
21151
|
+
var GIT_HASH2 = true ? "e5abc21f" : "dev";
|
|
20723
21152
|
function handlePilotCommand(action, json, force, binaryPath) {
|
|
20724
21153
|
if (action === "status") return cmdPilotStatus(json, binaryPath);
|
|
20725
21154
|
if (action === "install") return cmdPilotInstall(json, binaryPath);
|
|
@@ -22031,9 +22460,13 @@ function requireSkillName(rest, usage) {
|
|
|
22031
22460
|
}
|
|
22032
22461
|
return name;
|
|
22033
22462
|
}
|
|
22034
|
-
function handleSkillAdd(rest, config, json) {
|
|
22463
|
+
function handleSkillAdd(rest, v, config, json) {
|
|
22035
22464
|
const n = requireSkillName(rest, "Usage: okx skill add <name>");
|
|
22036
|
-
if (n) return cmdSkillAdd(n, config, json);
|
|
22465
|
+
if (n) return cmdSkillAdd(n, config, json, v.force ?? false);
|
|
22466
|
+
}
|
|
22467
|
+
function handleSkillVerify(rest, config, json) {
|
|
22468
|
+
const n = requireSkillName(rest, "Usage: okx skill verify <name>");
|
|
22469
|
+
if (n) return cmdSkillVerify(n, config, json);
|
|
22037
22470
|
}
|
|
22038
22471
|
function handleSkillDownload(rest, v, config, json) {
|
|
22039
22472
|
const n = requireSkillName(rest, "Usage: okx skill download <name> [--dir <path>] [--format zip|skill]");
|
|
@@ -22052,12 +22485,13 @@ function handleSkillCommand(run, action, rest, v, json, config) {
|
|
|
22052
22485
|
if (action === "search") return cmdSkillSearch(run, { keyword: rest[0] ?? v.keyword, categories: v.categories, page: v.page, limit: v.limit, json });
|
|
22053
22486
|
if (action === "categories") return cmdSkillCategories(run, json);
|
|
22054
22487
|
if (action === "list") return cmdSkillList(json);
|
|
22055
|
-
if (action === "add") return handleSkillAdd(rest, config, json);
|
|
22488
|
+
if (action === "add") return handleSkillAdd(rest, v, config, json);
|
|
22056
22489
|
if (action === "download") return handleSkillDownload(rest, v, config, json);
|
|
22057
22490
|
if (action === "remove") return handleSkillRemove(rest, json);
|
|
22058
22491
|
if (action === "check") return handleSkillCheck(run, rest, json);
|
|
22492
|
+
if (action === "verify") return handleSkillVerify(rest, config, json);
|
|
22059
22493
|
errorLine(`Unknown skill command: ${action}`);
|
|
22060
|
-
errorLine("Valid: search, categories, add, download, remove, check, list");
|
|
22494
|
+
errorLine("Valid: search, categories, add, download, remove, check, list, verify");
|
|
22061
22495
|
process.exitCode = 1;
|
|
22062
22496
|
}
|
|
22063
22497
|
function handleEventCommand(run, action, rest, v, json) {
|