@okx_ai/okx-trade-mcp 1.3.3-beta.2 → 1.3.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -22,9 +22,12 @@ import {
22
22
  import { homedir as homedir2 } from "os";
23
23
  import { join as join2, dirname } from "path";
24
24
  import { createHmac } from "crypto";
25
- import { spawn, execFile as execFile2 } from "child_process";
25
+ import { spawn as spawn2, execFile as execFile2 } from "child_process";
26
26
  import { homedir as homedir3 } from "os";
27
27
  import { join as join3 } from "path";
28
+ import { spawn } from "child_process";
29
+ import { createServer } from "net";
30
+ import { randomBytes } from "crypto";
28
31
  import fs from "fs";
29
32
  import path from "path";
30
33
  import os from "os";
@@ -1110,6 +1113,105 @@ var EXIT_CODES = {
1110
1113
  NOT_LOGGED_IN: 2,
1111
1114
  REFRESH_FAILED: 3
1112
1115
  };
1116
+ function finalizeToken(code, token, resolve3, reject) {
1117
+ if (code === EXIT_CODES.SUCCESS) {
1118
+ if (!token) {
1119
+ reject(new AuthenticationError(
1120
+ "okx-auth returned empty token.",
1121
+ "Run `okx auth login` to re-authenticate."
1122
+ ));
1123
+ return;
1124
+ }
1125
+ resolve3(token);
1126
+ return;
1127
+ }
1128
+ if (code === EXIT_CODES.NOT_LOGGED_IN) {
1129
+ reject(new NotLoggedInError());
1130
+ return;
1131
+ }
1132
+ if (code === EXIT_CODES.UNAUTHORIZED_CALLER) {
1133
+ reject(new AuthenticationError(
1134
+ "okx-auth rejected the caller (unauthorized).",
1135
+ "Ensure you are running from a trusted OKX tool."
1136
+ ));
1137
+ return;
1138
+ }
1139
+ if (code === EXIT_CODES.REFRESH_FAILED) {
1140
+ reject(new AuthenticationError(
1141
+ "Token refresh failed.",
1142
+ "Run `okx auth login` to re-authenticate."
1143
+ ));
1144
+ return;
1145
+ }
1146
+ reject(new AuthenticationError(
1147
+ `okx-auth token exited with code ${code}.`,
1148
+ "Run `okx auth login` to re-authenticate."
1149
+ ));
1150
+ }
1151
+ function spawnFailedError(err) {
1152
+ return new ConfigError(
1153
+ `Failed to spawn okx-auth: ${err.message}`,
1154
+ "Ensure the okx-auth binary exists and is executable."
1155
+ );
1156
+ }
1157
+ var WIN_PIPE_PREFIX = String.raw`\\.\pipe\okx-auth-`;
1158
+ function defaultWindowsPipeName() {
1159
+ return WIN_PIPE_PREFIX + randomBytes(32).toString("hex");
1160
+ }
1161
+ function execAuthTokenWindows(binPath, makePipeName = defaultWindowsPipeName) {
1162
+ return new Promise((resolve3, reject) => {
1163
+ const pipeName = makePipeName();
1164
+ const server = createServer();
1165
+ const chunks = [];
1166
+ let connectionMade = false;
1167
+ let pipeClosed = false;
1168
+ let exitCode;
1169
+ let settled = false;
1170
+ const settle = (fn) => {
1171
+ if (settled) return;
1172
+ settled = true;
1173
+ try {
1174
+ server.close();
1175
+ } catch {
1176
+ }
1177
+ fn();
1178
+ };
1179
+ const tryFinalize = () => {
1180
+ if (!pipeClosed || exitCode === void 0) return;
1181
+ const token = Buffer.concat(chunks).toString("utf-8").trim();
1182
+ settle(() => finalizeToken(exitCode, token, resolve3, reject));
1183
+ };
1184
+ server.on("connection", (socket) => {
1185
+ connectionMade = true;
1186
+ socket.on("data", (c) => chunks.push(c));
1187
+ socket.on("end", () => {
1188
+ pipeClosed = true;
1189
+ tryFinalize();
1190
+ });
1191
+ socket.on("error", (err) => settle(() => reject(spawnFailedError(err))));
1192
+ });
1193
+ server.on("error", (err) => settle(() => reject(spawnFailedError(err))));
1194
+ server.listen(pipeName, () => {
1195
+ let child;
1196
+ try {
1197
+ child = spawn(binPath, ["token"], {
1198
+ stdio: ["ignore", "ignore", "inherit"],
1199
+ env: { ...process.env, OKX_AUTH_TOKEN_PIPE: pipeName },
1200
+ windowsHide: true
1201
+ });
1202
+ } catch (err) {
1203
+ settle(() => reject(spawnFailedError(err)));
1204
+ return;
1205
+ }
1206
+ child.on("error", (err) => settle(() => reject(spawnFailedError(err))));
1207
+ child.on("close", (code) => {
1208
+ exitCode = code;
1209
+ if (!connectionMade) pipeClosed = true;
1210
+ tryFinalize();
1211
+ });
1212
+ });
1213
+ });
1214
+ }
1113
1215
  var EXEC_TIMEOUT_MS2 = 5e3;
1114
1216
  var AUTH_BIN_DIR = join3(homedir3(), ".okx", "bin");
1115
1217
  function getAuthBinaryPath() {
@@ -1121,8 +1223,11 @@ function getAuthBinaryPath() {
1121
1223
  }
1122
1224
  function execAuthToken() {
1123
1225
  const binPath = getAuthBinaryPath();
1226
+ return process.platform === "win32" ? execAuthTokenWindows(binPath) : execAuthTokenUnix(binPath);
1227
+ }
1228
+ function execAuthTokenUnix(binPath) {
1124
1229
  return new Promise((resolve3, reject) => {
1125
- const child = spawn(binPath, ["token"], {
1230
+ const child = spawn2(binPath, ["token"], {
1126
1231
  stdio: ["ignore", "ignore", "inherit", "pipe"]
1127
1232
  // stdin stdout stderr fd3 (pipe)
1128
1233
  });
@@ -1130,46 +1235,11 @@ function execAuthToken() {
1130
1235
  const fd3 = child.stdio[3];
1131
1236
  fd3.on("data", (chunk) => chunks.push(chunk));
1132
1237
  child.on("error", (err) => {
1133
- reject(new ConfigError(
1134
- `Failed to spawn okx-auth: ${err.message}`,
1135
- "Ensure the okx-auth binary exists and is executable."
1136
- ));
1238
+ reject(spawnFailedError(err));
1137
1239
  });
1138
1240
  child.on("close", (code) => {
1139
- if (code === EXIT_CODES.SUCCESS) {
1140
- const token = Buffer.concat(chunks).toString("utf-8").trim();
1141
- if (!token) {
1142
- reject(new AuthenticationError(
1143
- "okx-auth returned empty token.",
1144
- "Run `okx auth login` to re-authenticate."
1145
- ));
1146
- return;
1147
- }
1148
- resolve3(token);
1149
- return;
1150
- }
1151
- if (code === EXIT_CODES.NOT_LOGGED_IN) {
1152
- reject(new NotLoggedInError());
1153
- return;
1154
- }
1155
- if (code === EXIT_CODES.UNAUTHORIZED_CALLER) {
1156
- reject(new AuthenticationError(
1157
- "okx-auth rejected the caller (unauthorized).",
1158
- "Ensure you are running from a trusted OKX tool."
1159
- ));
1160
- return;
1161
- }
1162
- if (code === EXIT_CODES.REFRESH_FAILED) {
1163
- reject(new AuthenticationError(
1164
- "Token refresh failed.",
1165
- "Run `okx auth login` to re-authenticate."
1166
- ));
1167
- return;
1168
- }
1169
- reject(new AuthenticationError(
1170
- `okx-auth token exited with code ${code}.`,
1171
- "Run `okx auth login` to re-authenticate."
1172
- ));
1241
+ const token = Buffer.concat(chunks).toString("utf-8").trim();
1242
+ finalizeToken(code, token, resolve3, reject);
1173
1243
  });
1174
1244
  });
1175
1245
  }
@@ -11457,12 +11527,47 @@ function sanitize(value) {
11457
11527
  }
11458
11528
  return value;
11459
11529
  }
11530
+ var PRUNE_MARKER = ".last-prune.json";
11531
+ var PRUNE_INTERVAL_MS = 24 * 60 * 60 * 1e3;
11532
+ var DEFAULT_RETENTION_DAYS = 30;
11533
+ var MS_PER_DAY = 864e5;
11534
+ var LOG_FILE_PATTERN = /^trade-.+\.log$/;
11535
+ function pruneOldLogs(logDir, retentionDays, now) {
11536
+ if (retentionDays === 0) return;
11537
+ try {
11538
+ const markerPath = path2.join(logDir, PRUNE_MARKER);
11539
+ let checkedAt = 0;
11540
+ try {
11541
+ const raw = fs2.readFileSync(markerPath, "utf8");
11542
+ const data = JSON.parse(raw);
11543
+ if (typeof data.checkedAt === "number") {
11544
+ checkedAt = data.checkedAt;
11545
+ }
11546
+ } catch {
11547
+ }
11548
+ if (checkedAt > 0 && now - checkedAt <= PRUNE_INTERVAL_MS) return;
11549
+ const cutoff = now - retentionDays * MS_PER_DAY;
11550
+ for (const name of fs2.readdirSync(logDir).filter((f) => LOG_FILE_PATTERN.test(f))) {
11551
+ try {
11552
+ if (fs2.statSync(path2.join(logDir, name)).mtimeMs < cutoff) {
11553
+ fs2.unlinkSync(path2.join(logDir, name));
11554
+ }
11555
+ } catch {
11556
+ }
11557
+ }
11558
+ fs2.writeFileSync(markerPath, JSON.stringify({ checkedAt: now }), "utf8");
11559
+ } catch {
11560
+ }
11561
+ }
11460
11562
  var TradeLogger = class {
11461
11563
  logLevel;
11462
11564
  logDir;
11463
11565
  constructor(logLevel = "info", logDir) {
11464
11566
  this.logLevel = logLevel;
11465
11567
  this.logDir = logDir ?? path2.join(os2.homedir(), ".okx", "logs");
11568
+ const parsed = parseInt(process.env.OKX_LOG_RETENTION_DAYS ?? "", 10);
11569
+ const retentionDays = isNaN(parsed) || parsed < 0 ? DEFAULT_RETENTION_DAYS : parsed;
11570
+ pruneOldLogs(this.logDir, retentionDays, Date.now());
11466
11571
  }
11467
11572
  getLogPath(date) {
11468
11573
  const d = date ?? /* @__PURE__ */ new Date();
@@ -11647,7 +11752,7 @@ var _require = createRequire(import.meta.url);
11647
11752
  var pkg = _require("../package.json");
11648
11753
  var SERVER_NAME = "okx-trade-mcp";
11649
11754
  var SERVER_VERSION = pkg.version;
11650
- var GIT_HASH = true ? "e8a0930a" : "dev";
11755
+ var GIT_HASH = true ? "e6ad1d1e" : "dev";
11651
11756
 
11652
11757
  // src/server.ts
11653
11758
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
@@ -11747,7 +11852,7 @@ function unknownToolResult(toolName, capabilitySnapshot) {
11747
11852
  capabilitySnapshot
11748
11853
  );
11749
11854
  }
11750
- function createServer(config, logger) {
11855
+ function createServer2(config, logger) {
11751
11856
  const client = new OkxRestClient(config);
11752
11857
  const tools = buildTools(config);
11753
11858
  const toolMap = new Map(tools.map((tool) => [tool.name, tool]));
@@ -11935,7 +12040,7 @@ async function main() {
11935
12040
  sourceTag: "MCP"
11936
12041
  });
11937
12042
  const logger = cli.noLog ? void 0 : new TradeLogger(cli.logLevel);
11938
- const server = createServer(config, logger);
12043
+ const server = createServer2(config, logger);
11939
12044
  const transport = new StdioServerTransport();
11940
12045
  await server.connect(transport);
11941
12046
  }