@okx_ai/okx-trade-cli 1.3.6-beta.3 → 1.3.7

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
  }
@@ -13562,110 +13894,6 @@ async function handleAuthCommand(action, _rest, v) {
13562
13894
  }
13563
13895
  }
13564
13896
 
13565
- // src/commands/outcomes.ts
13566
- import { spawn as spawn4 } from "child_process";
13567
- import { existsSync as existsSync9 } from "fs";
13568
- import { delimiter, join as join13 } from "path";
13569
- var OUTCOMES_BINARY_NAME = process.platform === "win32" ? "okx-outcomes.exe" : "okx-outcomes";
13570
- function resolveOutcomesBinaryPath() {
13571
- const override = process.env.OKX_OUTCOMES_BIN;
13572
- if (override) {
13573
- if (existsSync9(override)) return override;
13574
- errorLine(
13575
- `Warning: OKX_OUTCOMES_BIN is set to '${override}' but no file exists there; falling back to PATH search.`
13576
- );
13577
- }
13578
- const paths = (process.env.PATH ?? "").split(delimiter);
13579
- for (const dir of paths) {
13580
- if (!dir) continue;
13581
- const full = join13(dir, OUTCOMES_BINARY_NAME);
13582
- if (existsSync9(full)) return full;
13583
- }
13584
- return null;
13585
- }
13586
- function printInstallHint() {
13587
- errorLine("Error: okx-outcomes binary not found in PATH.");
13588
- errorLine("");
13589
- errorLine("Install (macOS / Linux):");
13590
- errorLine(" curl -fsSL https://raw.githubusercontent.com/okx/outcomes/master/install.sh | sh");
13591
- errorLine("");
13592
- errorLine("Install (Windows): download okx-outcomes.exe from");
13593
- errorLine(" https://github.com/okx/outcomes/releases");
13594
- errorLine("and place it on your PATH.");
13595
- errorLine("");
13596
- errorLine("Or set OKX_OUTCOMES_BIN env var to a custom binary path.");
13597
- }
13598
- function runOkxOutcomes(args) {
13599
- const binPath = resolveOutcomesBinaryPath();
13600
- if (!binPath) {
13601
- printInstallHint();
13602
- return Promise.resolve(127);
13603
- }
13604
- return new Promise((resolve3, reject) => {
13605
- const child = spawn4(binPath, args, { stdio: "inherit" });
13606
- child.on(
13607
- "error",
13608
- (err) => reject(new Error(`Failed to spawn okx-outcomes: ${err.message}`))
13609
- );
13610
- child.on("close", (code) => resolve3(code ?? 1));
13611
- });
13612
- }
13613
- function printOutcomesHelp() {
13614
- const lines = [
13615
- "Usage: okx outcomes <command> [args...]",
13616
- "",
13617
- "OKX Outcomes - YES/NO event contract trading via the okx-outcomes binary.",
13618
- "",
13619
- "Common commands:",
13620
- " data events List outcome events",
13621
- " data event <eventId> Get event detail",
13622
- " data event-markets <eventId> Event + all its markets (returns asset ids)",
13623
- " data market <marketId> Get single market detail",
13624
- " data trending List trending events",
13625
- " data ticker <assetId> 24h ticker for an outcome asset",
13626
- " data candles <assetId> OHLCV candles for an outcome asset",
13627
- " search <keyword> Search events/markets",
13628
- " (Note: events/event/market etc. live UNDER the `data` namespace \u2014 calling them",
13629
- " as top-level commands prints the binary's help instead of returning JSON.)",
13630
- "",
13631
- " clob price/prices/midpoint(s)/spread(s)/book(s) --asset <id>",
13632
- " CLOB read-side market data",
13633
- " clob create-order Place limit order (EIP-712 signed)",
13634
- " clob market-order Cross book immediately (IOC/FOK)",
13635
- " clob cancel-oid | cancel-all | heartbeat",
13636
- "",
13637
- " ctf split/merge/redeem Conditional token operations",
13638
- "",
13639
- " account balance/order/orders/positions/closed-positions/trades",
13640
- " HMAC-auth account queries",
13641
- "",
13642
- " wallet show Show derived wallet address",
13643
- " status Health check",
13644
- " setup Interactive .env wizard",
13645
- "",
13646
- "Run 'okx outcomes <command> --help' for command-specific help.",
13647
- "",
13648
- "Note: place flags after the subcommand, e.g. 'okx outcomes events --json'",
13649
- " (or before the module: 'okx --json outcomes events'). Writing",
13650
- " 'okx outcomes --json events' is not supported.",
13651
- "",
13652
- "Requires: curl -fsSL https://raw.githubusercontent.com/okx/outcomes/master/install.sh | sh"
13653
- ];
13654
- for (const line of lines) outputLine(line);
13655
- }
13656
- async function handleOutcomesCommand(action, rest, v) {
13657
- if (!action || action === "--help" || action === "-h" || action === "help") {
13658
- printOutcomesHelp();
13659
- return;
13660
- }
13661
- const forwardArgs = [action, ...rest];
13662
- if (v.json && !forwardArgs.includes("--json") && !forwardArgs.includes("-j")) {
13663
- forwardArgs.push("--json");
13664
- }
13665
- const code = await runOkxOutcomes(forwardArgs);
13666
- if (code !== 0) process.exitCode = code;
13667
- }
13668
-
13669
13897
  // src/commands/diagnose.ts
13670
13898
  import dns from "dns/promises";
13671
13899
  import net from "net";
@@ -13691,26 +13919,26 @@ var Report = class {
13691
13919
  this.lines.push({ key, value });
13692
13920
  }
13693
13921
  print() {
13694
- const sep2 = "\u2500".repeat(52);
13922
+ const sep3 = "\u2500".repeat(52);
13695
13923
  outputLine("");
13696
- outputLine(` \u2500\u2500 Diagnostic Report (copy & share) ${sep2.slice(35)}`);
13924
+ outputLine(` \u2500\u2500 Diagnostic Report (copy & share) ${sep3.slice(35)}`);
13697
13925
  for (const { key, value } of this.lines) {
13698
13926
  outputLine(` ${key.padEnd(14)} ${value}`);
13699
13927
  }
13700
- outputLine(` ${sep2}`);
13928
+ outputLine(` ${sep3}`);
13701
13929
  outputLine("");
13702
13930
  }
13703
13931
  /** Write report to a file path, returns true on success. */
13704
13932
  writeToFile(filePath) {
13705
13933
  try {
13706
- const sep2 = "\u2500".repeat(52);
13934
+ const sep3 = "\u2500".repeat(52);
13707
13935
  const lines = [
13708
- `\u2500\u2500 Diagnostic Report (copy & share) ${sep2.slice(35)}`
13936
+ `\u2500\u2500 Diagnostic Report (copy & share) ${sep3.slice(35)}`
13709
13937
  ];
13710
13938
  for (const { key, value } of this.lines) {
13711
13939
  lines.push(`${key.padEnd(14)} ${value}`);
13712
13940
  }
13713
- lines.push(sep2, "");
13941
+ lines.push(sep3, "");
13714
13942
  fs4.writeFileSync(filePath, lines.join("\n"), "utf8");
13715
13943
  return true;
13716
13944
  } catch (_e) {
@@ -13760,7 +13988,7 @@ function sanitize2(value) {
13760
13988
  import fs5 from "fs";
13761
13989
  import path4 from "path";
13762
13990
  import os4 from "os";
13763
- import { spawnSync, spawn as spawn5 } from "child_process";
13991
+ import { spawnSync, spawn as spawn4 } from "child_process";
13764
13992
  import { createRequire as createRequire2 } from "module";
13765
13993
  import { fileURLToPath } from "url";
13766
13994
  var _require2 = createRequire2(import.meta.url);
@@ -14086,15 +14314,15 @@ async function checkStdioHandshake(entryPath, report) {
14086
14314
  clientInfo: { name: "okx-diagnose", version: "1.0" }
14087
14315
  }
14088
14316
  });
14089
- return new Promise((resolve3) => {
14317
+ return new Promise((resolve4) => {
14090
14318
  let settled = false;
14091
14319
  const settle = (passed) => {
14092
14320
  if (settled) return;
14093
14321
  settled = true;
14094
14322
  clearTimeout(timer);
14095
- resolve3(passed);
14323
+ resolve4(passed);
14096
14324
  };
14097
- const child = spawn5(process.execPath, [entryPath], {
14325
+ const child = spawn4(process.execPath, [entryPath], {
14098
14326
  stdio: ["pipe", "pipe", "pipe"],
14099
14327
  env: { ...process.env }
14100
14328
  });
@@ -14213,7 +14441,7 @@ async function cmdDiagnoseMcp(options = {}) {
14213
14441
 
14214
14442
  // src/commands/diagnose.ts
14215
14443
  var CLI_VERSION = readCliVersion();
14216
- var GIT_HASH = true ? "0614700e" : "dev";
14444
+ var GIT_HASH = true ? "2c1da5c1" : "dev";
14217
14445
  function maskKey2(key) {
14218
14446
  if (!key) return "(not set)";
14219
14447
  if (key.length <= 8) return "****";
@@ -14230,7 +14458,7 @@ async function checkDns(hostname) {
14230
14458
  }
14231
14459
  async function checkSocket(createFn, successEvent, timeoutMs) {
14232
14460
  const t0 = Date.now();
14233
- return new Promise((resolve3) => {
14461
+ return new Promise((resolve4) => {
14234
14462
  const socket = createFn();
14235
14463
  const cleanup = () => {
14236
14464
  socket.removeAllListeners();
@@ -14238,15 +14466,15 @@ async function checkSocket(createFn, successEvent, timeoutMs) {
14238
14466
  };
14239
14467
  socket.once(successEvent, () => {
14240
14468
  cleanup();
14241
- resolve3({ ok: true, ms: Date.now() - t0 });
14469
+ resolve4({ ok: true, ms: Date.now() - t0 });
14242
14470
  });
14243
14471
  socket.once("timeout", () => {
14244
14472
  cleanup();
14245
- 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` });
14246
14474
  });
14247
14475
  socket.once("error", (err) => {
14248
14476
  cleanup();
14249
- resolve3({ ok: false, ms: Date.now() - t0, error: err.message });
14477
+ resolve4({ ok: false, ms: Date.now() - t0, error: err.message });
14250
14478
  });
14251
14479
  });
14252
14480
  }
@@ -14574,23 +14802,23 @@ function suggestSubcommand(action, knownActions, knownPaths = []) {
14574
14802
 
14575
14803
  // src/commands/upgrade.ts
14576
14804
  import { spawnSync as spawnSync2 } from "child_process";
14577
- import { readFileSync as readFileSync9, writeFileSync as writeFileSync8, mkdirSync as mkdirSync11 } from "fs";
14578
- import { dirname as dirname8, join as join14 } 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";
14579
14807
  import { homedir as homedir11 } from "os";
14580
14808
  var PACKAGES = ["@okx_ai/okx-trade-mcp", "@okx_ai/okx-trade-cli"];
14581
- var CACHE_FILE2 = join14(homedir11(), ".okx", "last_check");
14809
+ var CACHE_FILE2 = join15(homedir11(), ".okx", "last_check");
14582
14810
  var THROTTLE_MS = 12 * 60 * 60 * 1e3;
14583
- var NPM_BIN = join14(dirname8(process.execPath), process.platform === "win32" ? "npm.cmd" : "npm");
14811
+ var NPM_BIN = join15(dirname8(process.execPath), process.platform === "win32" ? "npm.cmd" : "npm");
14584
14812
  function readLastCheck() {
14585
14813
  try {
14586
- return parseInt(readFileSync9(CACHE_FILE2, "utf-8").trim(), 10) || 0;
14814
+ return parseInt(readFileSync11(CACHE_FILE2, "utf-8").trim(), 10) || 0;
14587
14815
  } catch {
14588
14816
  return 0;
14589
14817
  }
14590
14818
  }
14591
14819
  function writeLastCheck() {
14592
14820
  try {
14593
- mkdirSync11(join14(homedir11(), ".okx"), { recursive: true });
14821
+ mkdirSync11(join15(homedir11(), ".okx"), { recursive: true });
14594
14822
  writeFileSync8(CACHE_FILE2, String(Math.floor(Date.now() / 1e3)), "utf-8");
14595
14823
  } catch {
14596
14824
  }
@@ -15746,63 +15974,6 @@ var CLI_REGISTRY = {
15746
15974
  description: "Upgrade okx CLI and MCP server to the latest stable version",
15747
15975
  usage: "okx upgrade [--check] [--beta] [--force] [--json]"
15748
15976
  },
15749
- // ── outcomes ───────────────────────────────────────────────────────────────
15750
- // External binary wrapper - forwards to the okx-outcomes binary (formerly okx-predict).
15751
- // All toolName=null because outcomes commands are not exposed as MCP tools.
15752
- outcomes: {
15753
- description: "OKX Outcomes markets (YES/NO event contracts) via external okx-outcomes binary",
15754
- commands: {
15755
- // Note: events / event / event-markets / market / trending / ticker /
15756
- // candles live UNDER the `data` namespace in the upstream binary.
15757
- // Calling them as top-level commands prints the binary's help text
15758
- // instead of returning JSON. Always invoke as `okx outcomes data <cmd>`.
15759
- data: {
15760
- toolName: null,
15761
- usage: "okx outcomes data <events|event|event-markets|market|trending|ticker|candles> [args...]",
15762
- description: "Public market data namespace: events, event(-markets), market, trending, ticker, candles"
15763
- },
15764
- search: {
15765
- toolName: null,
15766
- usage: "okx outcomes search <keyword> [--limit <n>] [--cursor <c>]",
15767
- description: "Search events/markets by keyword"
15768
- },
15769
- account: {
15770
- toolName: null,
15771
- usage: "okx outcomes account <balance|order|orders|positions|closed-positions|trades>",
15772
- description: "HMAC-auth account queries"
15773
- },
15774
- clob: {
15775
- toolName: null,
15776
- usage: "okx outcomes clob <price|prices|midpoint|midpoints|spread|spreads|book|books|order|orders|trades|create-order|market-order|cancel-oid|cancel-all|heartbeat>",
15777
- description: "CLOB market data (--asset) + EIP-712 signed order operations"
15778
- },
15779
- ctf: {
15780
- toolName: null,
15781
- usage: "okx outcomes ctf <split|merge|redeem> --market <id> [--amount <xp>]",
15782
- description: "Conditional Token Framework: split xp into YES/NO, merge, redeem"
15783
- },
15784
- wallet: {
15785
- toolName: null,
15786
- usage: "okx outcomes wallet show",
15787
- description: "Show derived wallet address (from PREDICTIONS_AGENT_PRIVATE_KEY)"
15788
- },
15789
- status: {
15790
- toolName: null,
15791
- usage: "okx outcomes status [--json]",
15792
- description: "Health check: API + balance reachability"
15793
- },
15794
- setup: {
15795
- toolName: null,
15796
- usage: "okx outcomes setup",
15797
- description: "Interactive .env wizard for PREDICTIONS_* env vars"
15798
- },
15799
- shell: {
15800
- toolName: null,
15801
- usage: "okx outcomes shell",
15802
- description: "Interactive REPL (do not invoke from agent context)"
15803
- }
15804
- }
15805
- },
15806
15977
  // ── list-tools ──────────────────────────────────────────────────────────────
15807
15978
  "list-tools": {
15808
15979
  description: "List all available tools and their parameters (use --json for machine-readable output)",
@@ -16675,30 +16846,6 @@ function parseCli(argv) {
16675
16846
  }
16676
16847
  return { values, positionals };
16677
16848
  }
16678
- function peekFirstPositional(argv) {
16679
- const booleanFlags = /* @__PURE__ */ new Set();
16680
- for (const [name, opt] of Object.entries(CLI_OPTIONS)) {
16681
- const o = opt;
16682
- if (o.type === "boolean") {
16683
- booleanFlags.add(name);
16684
- if (o.short) booleanFlags.add(o.short);
16685
- }
16686
- }
16687
- for (let i = 0; i < argv.length; i++) {
16688
- const tok = argv[i];
16689
- if (tok === "--") {
16690
- const next2 = argv[i + 1];
16691
- return next2 === void 0 ? void 0 : { module: next2, idx: i + 1 };
16692
- }
16693
- if (!tok.startsWith("-")) return { module: tok, idx: i };
16694
- if (tok.includes("=")) continue;
16695
- const name = tok.replace(/^--?/, "");
16696
- if (booleanFlags.has(name)) continue;
16697
- const next = argv[i + 1];
16698
- if (next !== void 0 && !next.startsWith("-")) i++;
16699
- }
16700
- return void 0;
16701
- }
16702
16849
 
16703
16850
  // src/commands/market.ts
16704
16851
  function getData2(result) {
@@ -18678,7 +18825,7 @@ Config saved to ${p}
18678
18825
  }
18679
18826
  };
18680
18827
  function prompt(rl, question) {
18681
- return new Promise((resolve3) => rl.question(question, resolve3));
18828
+ return new Promise((resolve4) => rl.question(question, resolve4));
18682
18829
  }
18683
18830
  function cmdConfigShow(json) {
18684
18831
  const config = readFullConfig();
@@ -20101,18 +20248,21 @@ async function cmdDcdQuoteAndBuy(run, opts) {
20101
20248
 
20102
20249
  // src/commands/skill.ts
20103
20250
  import { tmpdir, homedir as homedir13 } from "os";
20104
- import { join as join16, dirname as dirname9 } from "path";
20105
- import { mkdirSync as mkdirSync12, rmSync, existsSync as existsSync11, copyFileSync as copyFileSync2 } from "fs";
20251
+ import { join as join17, dirname as dirname9 } from "path";
20252
+ import { mkdirSync as mkdirSync12, rmSync, existsSync as existsSync10, copyFileSync as copyFileSync2 } from "fs";
20106
20253
  import { execFileSync as execFileSync2 } from "child_process";
20107
20254
  import { randomUUID as randomUUID2 } from "crypto";
20108
20255
  function resolveNpx() {
20109
- const sibling = join16(dirname9(process.execPath), "npx");
20110
- if (existsSync11(sibling)) return sibling;
20256
+ const sibling = join17(dirname9(process.execPath), "npx");
20257
+ if (existsSync10(sibling)) return sibling;
20111
20258
  return "npx";
20112
20259
  }
20113
20260
  function npxEnv() {
20114
20261
  return { ...process.env, NO_COLOR: "1", FORCE_COLOR: "0" };
20115
20262
  }
20263
+ function getSkillContentDir(name) {
20264
+ return join17(homedir13(), ".agents", "skills", name);
20265
+ }
20116
20266
  var THIRD_PARTY_INSTALL_NOTICE = "Note: This skill was created by a third-party developer, not by OKX. Review SKILL.md before use.";
20117
20267
  async function cmdSkillSearch(run, opts) {
20118
20268
  const args = {};
@@ -20162,16 +20312,56 @@ async function cmdSkillCategories(run, json) {
20162
20312
  }
20163
20313
  outputLine("");
20164
20314
  }
20165
- async function cmdSkillAdd(name, config, json, exec = execFileSync2) {
20166
- const tmpBase = join16(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()}`);
20167
20331
  mkdirSync12(tmpBase, { recursive: true });
20168
20332
  try {
20169
20333
  outputLine(`Downloading ${name}...`);
20170
20334
  const client = new OkxRestClient(config);
20171
- const zipPath = await downloadSkillZip(client, name, tmpBase);
20172
- const contentDir = await extractSkillZip(zipPath, join16(tmpBase, "content"));
20335
+ const zipPath = await _download(client, name, tmpBase);
20336
+ const contentDir = await _extract(zipPath, join17(tmpBase, "content"));
20173
20337
  const meta = readMetaJson(contentDir);
20174
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
+ }
20175
20365
  outputLine("Installing to detected agents...");
20176
20366
  try {
20177
20367
  exec(resolveNpx(), ["skills", "add", contentDir, "-y", "-g"], {
@@ -20180,7 +20370,7 @@ async function cmdSkillAdd(name, config, json, exec = execFileSync2) {
20180
20370
  env: npxEnv()
20181
20371
  });
20182
20372
  } catch (e) {
20183
- const savedZip = join16(process.cwd(), `${name}.zip`);
20373
+ const savedZip = join17(process.cwd(), `${name}.zip`);
20184
20374
  try {
20185
20375
  copyFileSync2(zipPath, savedZip);
20186
20376
  } catch {
@@ -20189,7 +20379,8 @@ async function cmdSkillAdd(name, config, json, exec = execFileSync2) {
20189
20379
  errorLine(`You can manually install from: ${savedZip}`);
20190
20380
  throw e;
20191
20381
  }
20192
- upsertSkillRecord(meta);
20382
+ const registryStatus = verifyResult.status === "failed" && force ? "bypassed" : verifyResult.status;
20383
+ upsertSkillRecord(meta, void 0, registryStatus);
20193
20384
  printSkillInstallResult(meta, json);
20194
20385
  } finally {
20195
20386
  rmSync(tmpBase, { recursive: true, force: true });
@@ -20220,7 +20411,7 @@ function cmdSkillRemove(name, json, exec = execFileSync2) {
20220
20411
  env: npxEnv()
20221
20412
  });
20222
20413
  } catch {
20223
- const agentsPath = join16(homedir13(), ".agents", "skills", name);
20414
+ const agentsPath = getSkillContentDir(name);
20224
20415
  try {
20225
20416
  rmSync(agentsPath, { recursive: true, force: true });
20226
20417
  } catch {
@@ -20284,6 +20475,59 @@ function cmdSkillList(json) {
20284
20475
  outputLine("");
20285
20476
  outputLine(`${skills.length} skills installed.`);
20286
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
+ }
20287
20531
  function printSkillInstallResult(meta, json) {
20288
20532
  if (json) {
20289
20533
  outputLine(JSON.stringify({ name: meta.name, version: meta.version, status: "installed" }, null, 2));
@@ -20434,14 +20678,14 @@ function formatBytes2(bytes) {
20434
20678
  return `${(bytes / (1024 * 1024)).toFixed(2)} MB`;
20435
20679
  }
20436
20680
  function askConfirmation2(prompt2) {
20437
- return new Promise((resolve3) => {
20681
+ return new Promise((resolve4) => {
20438
20682
  const rl = readline2.createInterface({
20439
20683
  input: process.stdin,
20440
20684
  output: process.stdout
20441
20685
  });
20442
20686
  rl.question(prompt2, (answer) => {
20443
20687
  rl.close();
20444
- resolve3(answer.trim().toLowerCase() === "y");
20688
+ resolve4(answer.trim().toLowerCase() === "y");
20445
20689
  });
20446
20690
  });
20447
20691
  }
@@ -20904,7 +21148,7 @@ async function cmdEventCancel(run, opts) {
20904
21148
  // src/index.ts
20905
21149
  var _require3 = createRequire3(import.meta.url);
20906
21150
  var CLI_VERSION2 = _require3("../package.json").version;
20907
- var GIT_HASH2 = true ? "0614700e" : "dev";
21151
+ var GIT_HASH2 = true ? "2c1da5c1" : "dev";
20908
21152
  function handlePilotCommand(action, json, force, binaryPath) {
20909
21153
  if (action === "status") return cmdPilotStatus(json, binaryPath);
20910
21154
  if (action === "install") return cmdPilotInstall(json, binaryPath);
@@ -22216,9 +22460,13 @@ function requireSkillName(rest, usage) {
22216
22460
  }
22217
22461
  return name;
22218
22462
  }
22219
- function handleSkillAdd(rest, config, json) {
22463
+ function handleSkillAdd(rest, v, config, json) {
22220
22464
  const n = requireSkillName(rest, "Usage: okx skill add <name>");
22221
- 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);
22222
22470
  }
22223
22471
  function handleSkillDownload(rest, v, config, json) {
22224
22472
  const n = requireSkillName(rest, "Usage: okx skill download <name> [--dir <path>] [--format zip|skill]");
@@ -22237,12 +22485,13 @@ function handleSkillCommand(run, action, rest, v, json, config) {
22237
22485
  if (action === "search") return cmdSkillSearch(run, { keyword: rest[0] ?? v.keyword, categories: v.categories, page: v.page, limit: v.limit, json });
22238
22486
  if (action === "categories") return cmdSkillCategories(run, json);
22239
22487
  if (action === "list") return cmdSkillList(json);
22240
- if (action === "add") return handleSkillAdd(rest, config, json);
22488
+ if (action === "add") return handleSkillAdd(rest, v, config, json);
22241
22489
  if (action === "download") return handleSkillDownload(rest, v, config, json);
22242
22490
  if (action === "remove") return handleSkillRemove(rest, json);
22243
22491
  if (action === "check") return handleSkillCheck(run, rest, json);
22492
+ if (action === "verify") return handleSkillVerify(rest, config, json);
22244
22493
  errorLine(`Unknown skill command: ${action}`);
22245
- errorLine("Valid: search, categories, add, download, remove, check, list");
22494
+ errorLine("Valid: search, categories, add, download, remove, check, list, verify");
22246
22495
  process.exitCode = 1;
22247
22496
  }
22248
22497
  function handleEventCommand(run, action, rest, v, json) {
@@ -22372,16 +22621,7 @@ async function main() {
22372
22621
  err: (m) => process.stderr.write(m)
22373
22622
  });
22374
22623
  checkForUpdates("@okx_ai/okx-trade-cli", CLI_VERSION2);
22375
- const rawArgv = process.argv.slice(2);
22376
- const peek = peekFirstPositional(rawArgv);
22377
- if (peek?.module === "outcomes") {
22378
- const after = rawArgv.slice(peek.idx + 1);
22379
- const action2 = after[0];
22380
- const rest2 = after.slice(1);
22381
- const json2 = rawArgv.includes("--json") || rawArgv.includes("-j");
22382
- return handleOutcomesCommand(action2, rest2, { json: json2 });
22383
- }
22384
- const { values, positionals } = parseCli(rawArgv);
22624
+ const { values, positionals } = parseCli(process.argv.slice(2));
22385
22625
  if (values.version) {
22386
22626
  printVersion();
22387
22627
  return;