@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 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 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";
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 readFileSync4, writeFileSync as writeFileSync4, mkdirSync as mkdirSync5, existsSync as existsSync3 } from "fs";
46
- import { join as join7, dirname as dirname4 } from "path";
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, sep2, end, banNewLines = false) {
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 === sep2) {
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 readFileSync5, writeFileSync as writeFileSync5, mkdirSync as mkdirSync6, existsSync as existsSync4 } from "fs";
877
- import { join as join8 } from "path";
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 join11, dirname as dirname7 } from "path";
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 readFileSync7,
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 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";
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
- setGlobalDispatcher(new EnvHttpProxyAgent());
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((resolve3) => {
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
- resolve3(null);
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
- resolve3(result.data);
966
+ resolve4(result.data);
949
967
  } else {
950
- resolve3(null);
968
+ resolve4(null);
951
969
  }
952
970
  } catch {
953
- resolve3(null);
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, resolve3, reject) {
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
- resolve3(token);
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((resolve3, reject) => {
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, resolve3, reject));
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((resolve3, reject) => {
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, resolve3, reject);
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((resolve3) => {
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
- resolve3(null);
1444
+ resolve4(null);
1427
1445
  return;
1428
1446
  }
1429
1447
  try {
1430
1448
  const result = JSON.parse(stdout);
1431
- resolve3(result);
1449
+ resolve4(result);
1432
1450
  } catch {
1433
- resolve3(null);
1451
+ resolve4(null);
1434
1452
  }
1435
1453
  }
1436
1454
  );
1437
1455
  });
1438
1456
  }
1439
1457
  function sleep(ms) {
1440
- return new Promise((resolve3) => {
1441
- setTimeout(resolve3, ms);
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 = resolve2(targetDir);
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 = join5(contentDir, "_meta.json");
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 = readFileSync2(metaPath, "utf-8");
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 = join5(contentDir, "SKILL.md");
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 = join6(homedir4(), ".okx", "skills", "registry.json");
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 = readFileSync3(registryPath, "utf-8");
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 join7(homedir5(), ".okx", "config.toml");
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 = readFileSync4(path42, "utf-8");
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 = join8(homedir6(), ".okx", "update-check.json");
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(readFileSync5(CACHE_FILE, "utf-8"));
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(join8(homedir6(), ".okx"), { recursive: true });
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(join8(dir, ".npmrc"));
12662
+ add(join10(dir, ".npmrc"));
12400
12663
  if (dir === root) break;
12401
- const parent = join8(dir, "..");
12664
+ const parent = join10(dir, "..");
12402
12665
  if (parent === dir) break;
12403
12666
  dir = parent;
12404
12667
  }
12405
- add(join8(homedir6(), ".npmrc"));
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 = readFileSync5(filePath, "utf-8").split(/\r?\n/);
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((resolve3, reject) => {
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
- reject(new Error(`HTTP ${res.statusCode ?? "unknown"}`));
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
- resolve3(res);
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((resolve3, reject) => {
13064
+ (res) => new Promise((resolve4, reject) => {
12790
13065
  const file = createWriteStream2(destPath);
12791
13066
  res.pipe(file);
12792
- file.on("finish", () => file.close(() => resolve3()));
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((resolve3, reject) => {
13080
+ (res) => new Promise((resolve4, reject) => {
12806
13081
  const chunks = [];
12807
13082
  res.on("data", (chunk) => chunks.push(chunk));
12808
- res.on("end", () => resolve3(Buffer.concat(chunks).toString("utf8")));
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 = readFileSync7(filePath);
13112
+ const buf = readFileSync9(filePath);
12838
13113
  return {
12839
13114
  size: buf.byteLength,
12840
- sha256: createHash("sha256").update(buf).digest("hex")
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 ?? join10(homedir8(), ".okx", "bin", binaryName);
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 = getPlatformDir();
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 = getPlatformDir();
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 ?? join11(homedir9(), ".okx", "bin", binaryName);
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
- 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
- }
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 { status: "failed", error: `All CDN sources failed:
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 = join12(homedir10(), ".okx", "auth-binary-check.json");
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(readFileSync8(CACHE_PATH, "utf-8"));
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(join12(homedir10(), ".okx"), { recursive: true });
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((resolve3, reject) => {
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
- resolve3(code ?? 1);
13637
+ resolve4(code ?? 1);
13306
13638
  });
13307
13639
  });
13308
13640
  }
13309
13641
  function runOkxAuthCapture(args) {
13310
13642
  const binPath = getAuthBinaryPath();
13311
- return new Promise((resolve3, reject) => {
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
- resolve3({
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((resolve3) => {
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
- resolve3(answer.trim().toLowerCase() === "y");
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 sep2 = "\u2500".repeat(52);
13922
+ const sep3 = "\u2500".repeat(52);
13591
13923
  outputLine("");
13592
- outputLine(` \u2500\u2500 Diagnostic Report (copy & share) ${sep2.slice(35)}`);
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(` ${sep2}`);
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 sep2 = "\u2500".repeat(52);
13934
+ const sep3 = "\u2500".repeat(52);
13603
13935
  const lines = [
13604
- `\u2500\u2500 Diagnostic Report (copy & share) ${sep2.slice(35)}`
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(sep2, "");
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((resolve3) => {
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
- resolve3(passed);
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 ? "f632d898" : "dev";
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((resolve3) => {
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
- resolve3({ ok: true, ms: Date.now() - t0 });
14469
+ resolve4({ ok: true, ms: Date.now() - t0 });
14138
14470
  });
14139
14471
  socket.once("timeout", () => {
14140
14472
  cleanup();
14141
- resolve3({ ok: false, ms: Date.now() - t0, error: `timed out after ${timeoutMs}ms` });
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
- resolve3({ ok: false, ms: Date.now() - t0, error: err.message });
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 readFileSync9, writeFileSync as writeFileSync8, mkdirSync as mkdirSync11 } from "fs";
14474
- import { dirname as dirname8, join as join13 } from "path";
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 = join13(homedir11(), ".okx", "last_check");
14809
+ var CACHE_FILE2 = join15(homedir11(), ".okx", "last_check");
14478
14810
  var THROTTLE_MS = 12 * 60 * 60 * 1e3;
14479
- var NPM_BIN = join13(dirname8(process.execPath), process.platform === "win32" ? "npm.cmd" : "npm");
14811
+ var NPM_BIN = join15(dirname8(process.execPath), process.platform === "win32" ? "npm.cmd" : "npm");
14480
14812
  function readLastCheck() {
14481
14813
  try {
14482
- return parseInt(readFileSync9(CACHE_FILE2, "utf-8").trim(), 10) || 0;
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(join13(homedir11(), ".okx"), { recursive: true });
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((resolve3) => rl.question(question, resolve3));
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 join15, dirname as dirname9 } from "path";
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 = join15(dirname9(process.execPath), "npx");
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 cmdSkillAdd(name, config, json, exec = execFileSync2) {
19981
- const tmpBase = join15(tmpdir(), `okx-skill-${randomUUID2()}`);
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 downloadSkillZip(client, name, tmpBase);
19987
- const contentDir = await extractSkillZip(zipPath, join15(tmpBase, "content"));
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 = join15(process.cwd(), `${name}.zip`);
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
- upsertSkillRecord(meta);
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 = join15(homedir13(), ".agents", "skills", name);
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((resolve3) => {
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
- resolve3(answer.trim().toLowerCase() === "y");
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 ? "f632d898" : "dev";
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) {