okx-trade-cli 1.0.2 → 1.0.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
@@ -6,6 +6,8 @@ import { createRequire } from "module";
6
6
 
7
7
  // ../core/dist/index.js
8
8
  import { createHmac } from "crypto";
9
+ import path from "path";
10
+ import os from "os";
9
11
  import { readFileSync, existsSync } from "fs";
10
12
  import { join } from "path";
11
13
  import { homedir } from "os";
@@ -24,6 +26,7 @@ var OkxMcpError = class extends Error {
24
26
  code;
25
27
  suggestion;
26
28
  endpoint;
29
+ traceId;
27
30
  constructor(type, message, options) {
28
31
  super(message, options?.cause ? { cause: options.cause } : void 0);
29
32
  this.name = type;
@@ -31,6 +34,7 @@ var OkxMcpError = class extends Error {
31
34
  this.code = options?.code;
32
35
  this.suggestion = options?.suggestion;
33
36
  this.endpoint = options?.endpoint;
37
+ this.traceId = options?.traceId;
34
38
  }
35
39
  };
36
40
  var ConfigError = class extends OkxMcpError {
@@ -39,13 +43,13 @@ var ConfigError = class extends OkxMcpError {
39
43
  }
40
44
  };
41
45
  var RateLimitError = class extends OkxMcpError {
42
- constructor(message, suggestion, endpoint) {
43
- super("RateLimitError", message, { suggestion, endpoint });
46
+ constructor(message, suggestion, endpoint, traceId) {
47
+ super("RateLimitError", message, { suggestion, endpoint, traceId });
44
48
  }
45
49
  };
46
50
  var AuthenticationError = class extends OkxMcpError {
47
- constructor(message, suggestion, endpoint) {
48
- super("AuthenticationError", message, { suggestion, endpoint });
51
+ constructor(message, suggestion, endpoint, traceId) {
52
+ super("AuthenticationError", message, { suggestion, endpoint, traceId });
49
53
  }
50
54
  };
51
55
  var OkxApiError = class extends OkxMcpError {
@@ -71,6 +75,7 @@ function toToolErrorPayload(error, fallbackEndpoint) {
71
75
  message: error.message,
72
76
  suggestion: error.suggestion,
73
77
  endpoint: error.endpoint ?? fallbackEndpoint,
78
+ traceId: error.traceId,
74
79
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
75
80
  };
76
81
  }
@@ -154,6 +159,9 @@ var RateLimiter = class {
154
159
  function isDefined(value) {
155
160
  return value !== void 0 && value !== null;
156
161
  }
162
+ function extractTraceId(headers) {
163
+ return headers.get("x-trace-id") ?? headers.get("x-request-id") ?? headers.get("traceid") ?? void 0;
164
+ }
157
165
  function stringifyQueryValue(value) {
158
166
  if (Array.isArray(value)) {
159
167
  return value.map((item) => String(item)).join(",");
@@ -180,28 +188,28 @@ var OkxRestClient = class {
180
188
  constructor(config) {
181
189
  this.config = config;
182
190
  }
183
- async publicGet(path, query, rateLimit) {
191
+ async publicGet(path3, query, rateLimit) {
184
192
  return this.request({
185
193
  method: "GET",
186
- path,
194
+ path: path3,
187
195
  auth: "public",
188
196
  query,
189
197
  rateLimit
190
198
  });
191
199
  }
192
- async privateGet(path, query, rateLimit) {
200
+ async privateGet(path3, query, rateLimit) {
193
201
  return this.request({
194
202
  method: "GET",
195
- path,
203
+ path: path3,
196
204
  auth: "private",
197
205
  query,
198
206
  rateLimit
199
207
  });
200
208
  }
201
- async privatePost(path, body, rateLimit) {
209
+ async privatePost(path3, body, rateLimit) {
202
210
  return this.request({
203
211
  method: "POST",
204
- path,
212
+ path: path3,
205
213
  auth: "private",
206
214
  body,
207
215
  rateLimit
@@ -220,6 +228,9 @@ var OkxRestClient = class {
220
228
  "Content-Type": "application/json",
221
229
  Accept: "application/json"
222
230
  });
231
+ if (this.config.userAgent) {
232
+ headers.set("User-Agent", this.config.userAgent);
233
+ }
223
234
  if (config.auth === "private") {
224
235
  if (!this.config.hasAuth) {
225
236
  throw new ConfigError(
@@ -259,6 +270,7 @@ var OkxRestClient = class {
259
270
  );
260
271
  }
261
272
  const rawText = await response.text();
273
+ const traceId = extractTraceId(response.headers);
262
274
  let parsed;
263
275
  try {
264
276
  parsed = rawText ? JSON.parse(rawText) : {};
@@ -270,7 +282,8 @@ var OkxRestClient = class {
270
282
  {
271
283
  code: String(response.status),
272
284
  endpoint: `${config.method} ${config.path}`,
273
- suggestion: "Verify endpoint path and request parameters."
285
+ suggestion: "Verify endpoint path and request parameters.",
286
+ traceId
274
287
  }
275
288
  );
276
289
  }
@@ -286,7 +299,8 @@ var OkxRestClient = class {
286
299
  {
287
300
  code: String(response.status),
288
301
  endpoint: `${config.method} ${config.path}`,
289
- suggestion: "Retry later or verify endpoint parameters."
302
+ suggestion: "Retry later or verify endpoint parameters.",
303
+ traceId
290
304
  }
291
305
  );
292
306
  }
@@ -297,12 +311,14 @@ var OkxRestClient = class {
297
311
  throw new AuthenticationError(
298
312
  message,
299
313
  "Check API key, secret, passphrase and permissions.",
300
- `${config.method} ${config.path}`
314
+ `${config.method} ${config.path}`,
315
+ traceId
301
316
  );
302
317
  }
303
318
  throw new OkxApiError(message, {
304
319
  code: responseCode,
305
- endpoint: `${config.method} ${config.path}`
320
+ endpoint: `${config.method} ${config.path}`,
321
+ traceId
306
322
  });
307
323
  }
308
324
  return {
@@ -313,21 +329,24 @@ var OkxRestClient = class {
313
329
  };
314
330
  }
315
331
  };
332
+ var DEFAULT_LOG_DIR = path.join(os.homedir(), ".okx", "logs");
316
333
  var OKX_API_BASE_URL = "https://www.okx.com";
317
334
  var MODULES = [
318
335
  "market",
319
336
  "spot",
320
337
  "swap",
321
- "account"
338
+ "futures",
339
+ "account",
340
+ "bot"
322
341
  ];
323
342
  var DEFAULT_MODULES = ["spot", "swap", "account"];
324
343
  function configFilePath() {
325
344
  return join(homedir(), ".okx", "config.toml");
326
345
  }
327
346
  function readTomlProfile(profileName) {
328
- const path = configFilePath();
329
- if (!existsSync(path)) return {};
330
- const raw = readFileSync(path, "utf-8");
347
+ const path3 = configFilePath();
348
+ if (!existsSync(path3)) return {};
349
+ const raw = readFileSync(path3, "utf-8");
331
350
  const config = parse(raw);
332
351
  const name = profileName ?? config.default_profile ?? "default";
333
352
  return config.profiles?.[name] ?? {};
@@ -394,7 +413,8 @@ function loadConfig(cli) {
394
413
  timeoutMs: Math.floor(rawTimeout),
395
414
  modules: parseModuleList(cli.modules),
396
415
  readOnly: cli.readOnly,
397
- demo
416
+ demo,
417
+ userAgent: cli.userAgent
398
418
  };
399
419
  }
400
420
  var CACHE_FILE = join2(homedir2(), ".okx", "update-check.json");
@@ -471,7 +491,8 @@ function loadProfileConfig(opts) {
471
491
  profile: opts.profile,
472
492
  modules: opts.modules,
473
493
  readOnly: opts.readOnly ?? false,
474
- demo: opts.demo ?? false
494
+ demo: opts.demo ?? false,
495
+ userAgent: opts.userAgent
475
496
  });
476
497
  }
477
498
 
@@ -955,13 +976,18 @@ function writeCliConfig(config) {
955
976
 
956
977
  // src/commands/config.ts
957
978
  import { existsSync as existsSync4, readFileSync as readFileSync3 } from "fs";
958
- import { parse as parse2 } from "smol-toml";
979
+ import { parse as parse2, stringify as stringify2 } from "smol-toml";
980
+ import { createInterface } from "readline";
981
+ import { spawnSync } from "child_process";
959
982
  function readFullConfig() {
960
- const path = configFilePath();
961
- if (!existsSync4(path)) return { profiles: {} };
962
- const raw = readFileSync3(path, "utf-8");
983
+ const path3 = configFilePath();
984
+ if (!existsSync4(path3)) return { profiles: {} };
985
+ const raw = readFileSync3(path3, "utf-8");
963
986
  return parse2(raw);
964
987
  }
988
+ function prompt(rl, question) {
989
+ return new Promise((resolve) => rl.question(question, resolve));
990
+ }
965
991
  function cmdConfigShow(json) {
966
992
  const config = readFullConfig();
967
993
  if (json) return printJson(config);
@@ -995,6 +1021,184 @@ function cmdConfigSet(key, value) {
995
1021
  process.exitCode = 1;
996
1022
  }
997
1023
  }
1024
+ async function cmdConfigInit() {
1025
+ const apiUrl = "https://www.okx.com/account/my-api";
1026
+ process.stdout.write("OKX Trade CLI \u2014 \u914D\u7F6E\u5411\u5BFC\n\n");
1027
+ process.stdout.write(`\u8BF7\u524D\u5F80 ${apiUrl} \u521B\u5EFA API Key\uFF08\u9700\u8981 trade \u6743\u9650\uFF09
1028
+
1029
+ `);
1030
+ try {
1031
+ const opener = process.platform === "darwin" ? "open" : "xdg-open";
1032
+ spawnSync(opener, [apiUrl], { stdio: "ignore" });
1033
+ } catch {
1034
+ }
1035
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
1036
+ try {
1037
+ const profileNameRaw = await prompt(rl, "Profile \u540D\u79F0 (\u9ED8\u8BA4: default): ");
1038
+ const profileName = profileNameRaw.trim() || "default";
1039
+ const apiKey = (await prompt(rl, "API Key: ")).trim();
1040
+ if (!apiKey) {
1041
+ process.stderr.write("\u9519\u8BEF: API Key \u4E0D\u80FD\u4E3A\u7A7A\n");
1042
+ process.exitCode = 1;
1043
+ return;
1044
+ }
1045
+ const secretKey = (await prompt(rl, "Secret Key: ")).trim();
1046
+ if (!secretKey) {
1047
+ process.stderr.write("\u9519\u8BEF: Secret Key \u4E0D\u80FD\u4E3A\u7A7A\n");
1048
+ process.exitCode = 1;
1049
+ return;
1050
+ }
1051
+ const passphrase = (await prompt(rl, "Passphrase: ")).trim();
1052
+ if (!passphrase) {
1053
+ process.stderr.write("\u9519\u8BEF: Passphrase \u4E0D\u80FD\u4E3A\u7A7A\n");
1054
+ process.exitCode = 1;
1055
+ return;
1056
+ }
1057
+ const demoRaw = (await prompt(rl, "\u4F7F\u7528\u6A21\u62DF\u76D8\uFF1F(Y/n) ")).trim().toLowerCase();
1058
+ const demo = demoRaw !== "n";
1059
+ if (demo) {
1060
+ process.stdout.write("\u5DF2\u9009\u62E9\u6A21\u62DF\u76D8\u6A21\u5F0F\uFF0C\u53EF\u968F\u65F6\u901A\u8FC7 okx config set \u5207\u6362\u4E3A\u5B9E\u76D8\u3002\n");
1061
+ }
1062
+ const config = readFullConfig();
1063
+ config.profiles[profileName] = { api_key: apiKey, secret_key: secretKey, passphrase, demo };
1064
+ const configPath = configFilePath();
1065
+ try {
1066
+ writeCliConfig(config);
1067
+ process.stdout.write(`
1068
+ \u914D\u7F6E\u5DF2\u4FDD\u5B58\u5230 ${configPath}
1069
+ `);
1070
+ process.stdout.write(`\u4F7F\u7528\u65B9\u5F0F: okx --profile ${profileName} account balance
1071
+ `);
1072
+ if (!config.default_profile) {
1073
+ process.stdout.write(`\u63D0\u793A: \u8FD0\u884C okx config set default_profile ${profileName} \u53EF\u5C06\u5176\u8BBE\u4E3A\u9ED8\u8BA4
1074
+ `);
1075
+ }
1076
+ } catch (err) {
1077
+ const message = err instanceof Error ? err.message : String(err);
1078
+ const isPermission = err instanceof Error && "code" in err && (err.code === "EACCES" || err.code === "EPERM");
1079
+ process.stderr.write(`\u5199\u5165\u914D\u7F6E\u6587\u4EF6\u5931\u8D25: ${message}
1080
+ `);
1081
+ if (isPermission) {
1082
+ process.stderr.write(`\u6743\u9650\u4E0D\u8DB3\uFF0C\u8BF7\u68C0\u67E5 ${configPath} \u53CA\u5176\u7236\u76EE\u5F55\u7684\u8BFB\u5199\u6743\u9650\u3002
1083
+ `);
1084
+ }
1085
+ process.stderr.write("\u8BF7\u624B\u52A8\u5C06\u4EE5\u4E0B\u5185\u5BB9\u5199\u5165 " + configPath + ":\n\n");
1086
+ process.stdout.write(stringify2(config) + "\n");
1087
+ process.exitCode = 1;
1088
+ }
1089
+ } finally {
1090
+ rl.close();
1091
+ }
1092
+ }
1093
+
1094
+ // src/commands/client-setup.ts
1095
+ import * as fs from "fs";
1096
+ import * as path2 from "path";
1097
+ import * as os2 from "os";
1098
+ import * as readline from "readline";
1099
+ var CLIENTS = [
1100
+ {
1101
+ name: "Claude Desktop",
1102
+ configPath: path2.join(os2.homedir(), "Library/Application Support/Claude/claude_desktop_config.json"),
1103
+ mcpKey: "mcpServers"
1104
+ },
1105
+ {
1106
+ name: "Cursor",
1107
+ configPath: path2.join(os2.homedir(), ".cursor/mcp.json"),
1108
+ mcpKey: "mcpServers"
1109
+ },
1110
+ {
1111
+ name: "Windsurf",
1112
+ configPath: path2.join(os2.homedir(), ".codeium/windsurf/mcp_config.json"),
1113
+ mcpKey: "mcpServers"
1114
+ }
1115
+ ];
1116
+ var MCP_ENTRY = {
1117
+ command: "okx-trade-mcp",
1118
+ args: ["--modules", "all"]
1119
+ };
1120
+ var MCP_SERVER_NAME = "okx-trade-mcp";
1121
+ function prompt2(rl, question) {
1122
+ return new Promise((resolve) => {
1123
+ rl.question(question, (answer) => {
1124
+ resolve(answer);
1125
+ });
1126
+ });
1127
+ }
1128
+ async function cmdSetupClients() {
1129
+ const detected = CLIENTS.filter((c) => fs.existsSync(c.configPath));
1130
+ if (detected.length === 0) {
1131
+ process.stdout.write(
1132
+ "No supported IDE/client installations detected.\nChecked:\n" + CLIENTS.map((c) => ` - ${c.name}: ${c.configPath}`).join("\n") + "\n"
1133
+ );
1134
+ return;
1135
+ }
1136
+ process.stdout.write(`Detected ${detected.length} client(s):
1137
+ `);
1138
+ for (const c of detected) {
1139
+ process.stdout.write(` - ${c.name}
1140
+ `);
1141
+ }
1142
+ process.stdout.write("\n");
1143
+ const rl = readline.createInterface({
1144
+ input: process.stdin,
1145
+ output: process.stdout
1146
+ });
1147
+ try {
1148
+ for (const client of detected) {
1149
+ const answer = await prompt2(rl, `Configure ${client.name}? (y/N) `);
1150
+ if (answer.trim().toLowerCase() !== "y") {
1151
+ process.stdout.write(` Skipped ${client.name}.
1152
+ `);
1153
+ continue;
1154
+ }
1155
+ let data = { [client.mcpKey]: {} };
1156
+ if (fs.existsSync(client.configPath)) {
1157
+ const raw = fs.readFileSync(client.configPath, "utf-8");
1158
+ try {
1159
+ data = JSON.parse(raw);
1160
+ } catch {
1161
+ process.stderr.write(
1162
+ ` Error: Failed to parse JSON for ${client.name} at ${client.configPath}. Skipping.
1163
+ `
1164
+ );
1165
+ continue;
1166
+ }
1167
+ }
1168
+ if (typeof data[client.mcpKey] !== "object" || data[client.mcpKey] === null) {
1169
+ data[client.mcpKey] = {};
1170
+ }
1171
+ const servers = data[client.mcpKey];
1172
+ if (Object.prototype.hasOwnProperty.call(servers, MCP_SERVER_NAME)) {
1173
+ process.stdout.write(` Already configured in ${client.name}. Skipping.
1174
+ `);
1175
+ continue;
1176
+ }
1177
+ servers[MCP_SERVER_NAME] = MCP_ENTRY;
1178
+ const jsonOutput = JSON.stringify(data, null, 2);
1179
+ try {
1180
+ fs.writeFileSync(client.configPath, jsonOutput, "utf-8");
1181
+ process.stdout.write(` Configured ${client.name} successfully.
1182
+ `);
1183
+ } catch (err) {
1184
+ const reason = err instanceof Error ? err.message : String(err);
1185
+ process.stderr.write(
1186
+ ` Error: Failed to write config for ${client.name}: ${reason}
1187
+ Add the following to "${client.configPath}" manually:
1188
+
1189
+ "${MCP_SERVER_NAME}": ${JSON.stringify(MCP_ENTRY, null, 2).split("\n").join("\n ")}
1190
+
1191
+ `
1192
+ );
1193
+ }
1194
+ }
1195
+ } finally {
1196
+ rl.close();
1197
+ }
1198
+ process.stdout.write(
1199
+ "\nDone. Please restart any configured IDE/client for the changes to take effect.\n"
1200
+ );
1201
+ }
998
1202
 
999
1203
  // src/index.ts
1000
1204
  var _require = createRequire(import.meta.url);
@@ -1046,8 +1250,10 @@ Commands:
1046
1250
  [--newSlTriggerPx <price>] [--newSlOrdPx <price|-1>]
1047
1251
  swap algo cancel --instId <id> --algoId <id>
1048
1252
 
1253
+ config init
1049
1254
  config show
1050
1255
  config set <key> <value>
1256
+ config setup-clients
1051
1257
  `);
1052
1258
  }
1053
1259
  async function main() {
@@ -1102,14 +1308,16 @@ async function main() {
1102
1308
  const [module, action, ...rest] = positionals;
1103
1309
  const json = values.json ?? false;
1104
1310
  if (module === "config") {
1311
+ if (action === "init") return cmdConfigInit();
1105
1312
  if (action === "show") return cmdConfigShow(json);
1106
1313
  if (action === "set") return cmdConfigSet(rest[0], rest[1]);
1314
+ if (action === "setup-clients") return cmdSetupClients();
1107
1315
  process.stderr.write(`Unknown config command: ${action}
1108
1316
  `);
1109
1317
  process.exitCode = 1;
1110
1318
  return;
1111
1319
  }
1112
- const config = loadProfileConfig({ profile: values.profile });
1320
+ const config = loadProfileConfig({ profile: values.profile, userAgent: `okx-trade-cli/${CLI_VERSION}` });
1113
1321
  const client = new OkxRestClient(config);
1114
1322
  if (module === "market") {
1115
1323
  if (action === "ticker") return cmdMarketTicker(client, rest[0], json);
@@ -1271,8 +1479,12 @@ async function main() {
1271
1479
  main().catch((error) => {
1272
1480
  const payload = toToolErrorPayload(error);
1273
1481
  process.stderr.write(`Error: ${payload.message}
1482
+ `);
1483
+ if (payload.traceId) process.stderr.write(`TraceId: ${payload.traceId}
1274
1484
  `);
1275
1485
  if (payload.suggestion) process.stderr.write(`Hint: ${payload.suggestion}
1486
+ `);
1487
+ process.stderr.write(`Version: okx-trade-cli@${CLI_VERSION}
1276
1488
  `);
1277
1489
  process.exitCode = 1;
1278
1490
  });