@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 +148 -43
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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 =
|
|
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(
|
|
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
|
-
|
|
1140
|
-
|
|
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 ? "
|
|
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
|
|
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 =
|
|
12043
|
+
const server = createServer2(config, logger);
|
|
11939
12044
|
const transport = new StdioServerTransport();
|
|
11940
12045
|
await server.connect(transport);
|
|
11941
12046
|
}
|