desic-okx-agent 0.1.8 → 0.2.0

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.
Files changed (50) hide show
  1. package/README.md +48 -5
  2. package/dist/account/private-websocket.d.ts +1 -0
  3. package/dist/account/private-websocket.js +27 -2
  4. package/dist/account/private-websocket.js.map +1 -1
  5. package/dist/cli/doctor.d.ts +14 -0
  6. package/dist/cli/doctor.js +133 -0
  7. package/dist/cli/doctor.js.map +1 -0
  8. package/dist/cli/index.js +190 -11
  9. package/dist/cli/index.js.map +1 -1
  10. package/dist/cli/prompts.d.ts +1 -0
  11. package/dist/cli/prompts.js +6 -0
  12. package/dist/cli/prompts.js.map +1 -1
  13. package/dist/cli/render.d.ts +8 -0
  14. package/dist/cli/render.js +73 -0
  15. package/dist/cli/render.js.map +1 -0
  16. package/dist/market/store.d.ts +1 -0
  17. package/dist/market/store.js +24 -0
  18. package/dist/market/store.js.map +1 -1
  19. package/dist/market/websocket.d.ts +1 -0
  20. package/dist/market/websocket.js +13 -0
  21. package/dist/market/websocket.js.map +1 -1
  22. package/dist/network/connectivity.d.ts +3 -0
  23. package/dist/network/connectivity.js +34 -14
  24. package/dist/network/connectivity.js.map +1 -1
  25. package/dist/network/proxy.js +3 -0
  26. package/dist/network/proxy.js.map +1 -1
  27. package/dist/runtime/client.d.ts +1 -0
  28. package/dist/runtime/client.js +46 -2
  29. package/dist/runtime/client.js.map +1 -1
  30. package/dist/runtime/server.js +21 -2
  31. package/dist/runtime/server.js.map +1 -1
  32. package/dist/setup/installer.d.ts +21 -0
  33. package/dist/setup/installer.js +113 -27
  34. package/dist/setup/installer.js.map +1 -1
  35. package/dist/setup/wizard.js +10 -4
  36. package/dist/setup/wizard.js.map +1 -1
  37. package/dist/storage/database.d.ts +2 -0
  38. package/dist/storage/database.js +23 -0
  39. package/dist/storage/database.js.map +1 -1
  40. package/dist/tools/catalog.js +1 -1
  41. package/dist/tools/catalog.js.map +1 -1
  42. package/dist/tools/help.d.ts +10 -0
  43. package/dist/tools/help.js +79 -0
  44. package/dist/tools/help.js.map +1 -0
  45. package/dist/update/service.d.ts +10 -0
  46. package/dist/update/service.js +104 -0
  47. package/dist/update/service.js.map +1 -0
  48. package/package.json +1 -1
  49. package/skills/okx-news-intelligence/SKILL.md +2 -0
  50. package/skills/okx-smart-money-analysis/SKILL.md +2 -0
package/README.md CHANGED
@@ -47,7 +47,7 @@ The terminal guide lets you select Codex, Claude Code, Cursor, VS Code / GitHub
47
47
 
48
48
  For Codex and Claude Code, setup also installs all bundled Skills automatically. Cursor, VS Code / GitHub Copilot, and Cline receive MCP configuration only because they do not share a portable `SKILL.md` installation format.
49
49
 
50
- Setup then checks both OKX REST and WebSocket connectivity. When direct and detected system-proxy connections fail, it offers to test and save an HTTP proxy URL, retry, or continue without network access.
50
+ Setup then checks both OKX REST and WebSocket connectivity. Transient TLS and network failures are retried before setup asks for action. When direct and detected system-proxy connections still fail, it offers to test and save an HTTP proxy URL, retry, or continue without network access.
51
51
 
52
52
  For automation, choose explicit targets without opening the UI:
53
53
 
@@ -60,7 +60,7 @@ desic-okx setup --targets codex --yes --skip-network-check
60
60
 
61
61
  Non-interactive setup exits with an error when the network check fails. Use `--skip-network-check` only when connectivity will be configured later.
62
62
 
63
- Restart the selected client after setup. The MCP server starts the local Runtime automatically when it is first used.
63
+ Restart the selected client after setup. The MCP server starts the local Runtime automatically when it is first used. Run `desic-okx doctor` at any time to verify the package, client configuration, Skills, network, Runtime, SQLite, public market data, and configured accounts.
64
64
 
65
65
  ## Ask an AI to install it
66
66
 
@@ -71,7 +71,7 @@ Install Desic OKX Agent for me. Check that Node.js is at least 22.12, then run:
71
71
  npm install --global desic-okx-agent
72
72
  desic-okx setup --targets codex --yes
73
73
 
74
- Verify the installation with `desic-okx status` and `desic-okx tools`. Do not ask for or configure any OKX API credentials. Confirm that public market tools are ready to use.
74
+ Verify the installation with `desic-okx doctor`. Do not ask for or configure any OKX API credentials. Confirm that public market tools are ready to use.
75
75
  ```
76
76
 
77
77
  For development from source:
@@ -95,15 +95,21 @@ desic-okx call market_get_decision_snapshot --json '{"instId":"BTC-USDT-SWAP","b
95
95
  ```
96
96
 
97
97
  The Runtime starts automatically when an MCP client or CLI call needs it. `desic-okx start` is optional.
98
+ The stdio MCP adapter does not load the native SQLite module itself, which avoids holding the package binary open during Windows upgrades. Stop a running Runtime with `desic-okx stop` before upgrading.
98
99
 
99
100
  Useful Runtime commands:
100
101
 
101
102
  ```bash
102
103
  desic-okx status
104
+ desic-okx status --json
105
+ desic-okx doctor
103
106
  desic-okx tools
107
+ desic-okx tool market_get_decision_snapshot
104
108
  desic-okx stop
105
109
  ```
106
110
 
111
+ `status` shows the Runtime version, uptime, redacted proxy route, public/business/private WebSocket state, subscriptions and data ages, accounts, and database size. `doctor` performs active network, SQLite, market-data, account, MCP-client, Skill, and package checks. Both support machine-readable JSON.
112
+
107
113
  ## Connect Codex
108
114
 
109
115
  The recommended option is `desic-okx setup`, which also installs all Skills. To register only the local stdio MCP server manually:
@@ -168,6 +174,14 @@ Included skills:
168
174
 
169
175
  Restart the client if a newly installed skill does not appear.
170
176
 
177
+ Setup synchronizes bundled Skills on every run. Identical Skills are left untouched. Changed copies are backed up before the bundled version is installed. They can also be inspected or synchronized explicitly:
178
+
179
+ ```bash
180
+ desic-okx skills status
181
+ desic-okx skills sync
182
+ desic-okx skills sync --targets codex --dry-run
183
+ ```
184
+
171
185
  ## Configure an OKX account
172
186
 
173
187
  Public tools work without credentials. Account and trading tools use a named account alias and never accept credentials as tool arguments.
@@ -177,9 +191,15 @@ Add and verify an account interactively:
177
191
  ```bash
178
192
  desic-okx account add --name demo --environment demo
179
193
  desic-okx account verify --name demo
194
+ desic-okx account verify --all
180
195
  desic-okx account list
196
+ desic-okx account set-default demo
197
+ desic-okx account rename demo demo-primary
198
+ desic-okx account edit demo-primary
181
199
  ```
182
200
 
201
+ `account add` and `account edit` verify the credentials with OKX before writing the configuration file. Editing credentials remains interactive so secrets are not passed as command-line arguments.
202
+
183
203
  The credential prompts hide input. Credentials are stored in the system configuration directory in `config.json`; Unix permissions are set to `0600`. The active path is shown by:
184
204
 
185
205
  ```bash
@@ -198,6 +218,29 @@ OKX_ENVIRONMENT=demo|live
198
218
 
199
219
  The three credential values must be provided together. Whether an account can trade is determined by the permissions assigned to its API key in OKX.
200
220
 
221
+ ## Tool help
222
+
223
+ The MCP schemas are available to AI clients. Humans can inspect the same schema, account requirement, and a runnable example from the CLI:
224
+
225
+ ```bash
226
+ desic-okx tool news_search
227
+ desic-okx tool trade_place_order --json
228
+ ```
229
+
230
+ Remote News and Smart Money tools require a configured **live OKX account**. Read-only API permission is sufficient. Local News event and history tools can use previously persisted data where available. This requirement is included in the MCP tool descriptions and relevant Skills.
231
+
232
+ ## Update
233
+
234
+ Check or install the latest npm release:
235
+
236
+ ```bash
237
+ desic-okx update --check
238
+ desic-okx update
239
+ desic-okx update --yes
240
+ ```
241
+
242
+ The updater checks the official npm registry, stops the Runtime, waits for the native SQLite module to be released, and then performs the global npm update. It synchronizes Skills for detected installations and runs Doctor afterward. When a newer CLI or MCP adapter encounters an older running Runtime, it automatically replaces it before handling requests.
243
+
201
244
  ## Proxy
202
245
 
203
246
  REST and every public, business, and private WebSocket connection use the same proxy resolution order:
@@ -255,7 +298,7 @@ Use an OKX Demo account first. Trading software can lose money, and the project
255
298
 
256
299
  ## Experimental intelligence
257
300
 
258
- News and Smart Money use upstream interfaces that may change without notice. Compatibility failures return `CAPABILITY_UNAVAILABLE`, or locally persisted history when available. A failure in these modules does not disable market, account, or trading tools.
301
+ Remote News and Smart Money use upstream interfaces that may change without notice and require a configured live OKX account; read-only API permission is sufficient. Compatibility failures return `CAPABILITY_UNAVAILABLE`, or locally persisted history when available. A failure in these modules does not disable market, account, or trading tools.
259
302
 
260
303
  ## Development
261
304
 
@@ -283,7 +326,7 @@ Normal tests never submit orders.
283
326
 
284
327
  ## Status
285
328
 
286
- This repository is currently pre-release software at version `0.1.8`. Unit, packaging, Skill, and MCP transport checks are automated. Real OKX Demo trading should be validated in the target network environment before a `1.0.0` release.
329
+ This repository is currently pre-release software at version `0.2.0`. Unit, packaging, Skill, and MCP transport checks are automated. Real OKX Demo trading should be validated in the target network environment before a `1.0.0` release.
287
330
 
288
331
  ## License
289
332
 
@@ -7,6 +7,7 @@ export declare class PrivateAccountWebSockets {
7
7
  constructor(config: RuntimeConfig, store: AccountStore);
8
8
  start(): void;
9
9
  stop(): void;
10
+ status(): Array<Record<string, unknown>>;
10
11
  private connect;
11
12
  private reconnect;
12
13
  private message;
@@ -8,7 +8,9 @@ export class PrivateAccountWebSockets {
8
8
  agent;
9
9
  constructor(config, store) {
10
10
  this.store = store;
11
- this.connections = Object.entries(config.accounts).map(([name, account]) => ({ account: { name, ...account }, stopping: false, attempt: 0 }));
11
+ this.connections = Object.entries(config.accounts).map(([name, account]) => ({
12
+ account: { name, ...account }, stopping: false, attempt: 0, authenticated: false
13
+ }));
12
14
  const proxyUrl = resolveProxy(config.proxy.url).url;
13
15
  this.agent = proxyUrl ? new HttpsProxyAgent(proxyUrl) : undefined;
14
16
  }
@@ -26,6 +28,18 @@ export class PrivateAccountWebSockets {
26
28
  connection.socket?.close();
27
29
  }
28
30
  }
31
+ status() {
32
+ const states = ["connecting", "open", "closing", "closed"];
33
+ return this.connections.map((connection) => ({
34
+ account: connection.account.name,
35
+ environment: connection.account.environment,
36
+ state: connection.socket ? states[connection.socket.readyState] ?? "unknown" : "not-started",
37
+ authenticated: connection.authenticated,
38
+ reconnectAttempt: connection.attempt,
39
+ lastMessageAt: connection.lastMessageAt ?? null,
40
+ lastError: connection.lastError ?? null
41
+ }));
42
+ }
29
43
  connect(connection) {
30
44
  if (connection.stopping)
31
45
  return;
@@ -34,10 +48,14 @@ export class PrivateAccountWebSockets {
34
48
  : "wss://ws.okx.com:8443/ws/v5/private";
35
49
  const socket = new WebSocket(url, this.agent ? { agent: this.agent } : undefined);
36
50
  connection.socket = socket;
51
+ connection.authenticated = false;
37
52
  socket.on("open", () => socket.send(JSON.stringify({ op: "login", args: [loginArgs(connection.account)] })));
38
53
  socket.on("message", (buffer) => this.message(connection, buffer.toString()));
39
54
  socket.on("close", () => this.reconnect(connection));
40
- socket.on("error", () => socket.close());
55
+ socket.on("error", (error) => {
56
+ connection.lastError = error.message;
57
+ socket.close();
58
+ });
41
59
  }
42
60
  reconnect(connection) {
43
61
  if (connection.stopping || connection.reconnect)
@@ -55,6 +73,7 @@ export class PrivateAccountWebSockets {
55
73
  message(connection, text) {
56
74
  if (text === "pong")
57
75
  return;
76
+ connection.lastMessageAt = Date.now();
58
77
  let message;
59
78
  try {
60
79
  message = JSON.parse(text);
@@ -64,6 +83,8 @@ export class PrivateAccountWebSockets {
64
83
  }
65
84
  if (message.event === "login" && message.code === "0") {
66
85
  connection.attempt = 0;
86
+ connection.authenticated = true;
87
+ connection.lastError = undefined;
67
88
  connection.socket?.send(JSON.stringify({
68
89
  op: "subscribe",
69
90
  args: [
@@ -80,6 +101,10 @@ export class PrivateAccountWebSockets {
80
101
  connection.heartbeat.unref();
81
102
  return;
82
103
  }
104
+ if (message.event === "login" && message.code !== "0") {
105
+ connection.authenticated = false;
106
+ connection.lastError = String(message.msg ?? `Login failed with code ${String(message.code ?? "unknown")}`);
107
+ }
83
108
  const arg = message.arg;
84
109
  const data = Array.isArray(message.data) ? message.data : [];
85
110
  if (!arg?.channel || !data.length)
@@ -1 +1 @@
1
- {"version":3,"file":"private-websocket.js","sourceRoot":"","sources":["../../src/account/private-websocket.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"private-websocket.js","sourceRoot":"","sources":["../../src/account/private-websocket.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ export type DoctorStatus = "pass" | "warn" | "fail";
2
+ export interface DoctorCheck {
3
+ name: string;
4
+ status: DoctorStatus;
5
+ details: string;
6
+ }
7
+ export interface DoctorReport {
8
+ ok: boolean;
9
+ version: string;
10
+ checkedAt: number;
11
+ checks: DoctorCheck[];
12
+ }
13
+ export declare function runDoctor(): Promise<DoctorReport>;
14
+ export declare function formatDoctor(reportValue: DoctorReport): string;
@@ -0,0 +1,133 @@
1
+ import fs from "node:fs";
2
+ import { AccountService } from "../account/service.js";
3
+ import { loadConfig } from "../config/loader.js";
4
+ import { CONFIG_PATH } from "../config/paths.js";
5
+ import { OkxClient } from "../core/okx-client.js";
6
+ import { PACKAGE_VERSION } from "../core/version.js";
7
+ import { checkOkxConnectivity, OKX_REST_BASE_URL } from "../network/connectivity.js";
8
+ import { displayProxy, resolveProxy } from "../network/proxy.js";
9
+ import { RuntimeClient } from "../runtime/client.js";
10
+ import { inspectSetupTargets, syncSkills } from "../setup/installer.js";
11
+ import { checkForUpdate } from "../update/service.js";
12
+ export async function runDoctor() {
13
+ const checks = [];
14
+ let config;
15
+ try {
16
+ config = loadConfig();
17
+ const permission = configPermission();
18
+ checks.push({ name: "Configuration", status: permission.secure ? "pass" : "warn", details: permission.details });
19
+ }
20
+ catch (error) {
21
+ checks.push({ name: "Configuration", status: "fail", details: errorMessage(error) });
22
+ return report(checks);
23
+ }
24
+ try {
25
+ const update = await checkForUpdate(undefined, config.proxy.url);
26
+ checks.push({
27
+ name: "Package",
28
+ status: update.updateAvailable ? "warn" : "pass",
29
+ details: update.updateAvailable ? `${update.current} installed; ${update.latest} available` : `${update.current} is current`
30
+ });
31
+ }
32
+ catch (error) {
33
+ checks.push({ name: "Package", status: "warn", details: `Could not check npm: ${errorMessage(error)}` });
34
+ }
35
+ const clients = inspectSetupTargets();
36
+ const configured = clients.filter((item) => item.mcpConfigured === true);
37
+ checks.push({
38
+ name: "MCP clients",
39
+ status: configured.length ? "pass" : "warn",
40
+ details: configured.length ? configured.map((item) => item.target).join(", ") : "No supported client configuration was detected"
41
+ });
42
+ const installedSkillTargets = clients.filter((item) => item.skillsInstalled !== null && item.skillsInstalled > 0)
43
+ .map((item) => item.target).filter((target) => target === "codex" || target === "claude-code");
44
+ if (installedSkillTargets.length) {
45
+ const skills = syncSkills(installedSkillTargets, { dryRun: true });
46
+ const pending = skills.reduce((total, item) => total + item.installed + item.updated, 0);
47
+ checks.push({
48
+ name: "Skills",
49
+ status: pending ? "warn" : "pass",
50
+ details: pending ? `${pending} bundled Skills need synchronization` : `${skills.reduce((sum, item) => sum + item.unchanged, 0)} bundled Skills are current`
51
+ });
52
+ }
53
+ else {
54
+ checks.push({ name: "Skills", status: "warn", details: "No Codex or Claude Code Skills were detected" });
55
+ }
56
+ const proxy = resolveProxy(config.proxy.url);
57
+ const network = await checkOkxConnectivity(proxy.url);
58
+ checks.push({
59
+ name: "OKX network",
60
+ status: network.ok ? "pass" : "fail",
61
+ details: network.ok
62
+ ? `${displayProxy(proxy)}; REST ${network.rest.latencyMs}ms, WebSocket ${network.websocket.latencyMs}ms`
63
+ : `${displayProxy(proxy)}; REST ${network.rest.error ?? "failed"}; WebSocket ${network.websocket.error ?? "failed"}`
64
+ });
65
+ if (network.ok) {
66
+ try {
67
+ const runtime = await RuntimeClient.connect();
68
+ const health = await runtime.health();
69
+ const diagnostics = await runtime.diagnostics();
70
+ checks.push({ name: "Runtime", status: health.version === PACKAGE_VERSION ? "pass" : "warn", details: `version ${String(health.version)}, PID ${String(health.pid)}, uptime ${formatDuration(Number(health.uptimeMs))}` });
71
+ const integrity = String(diagnostics.database?.integrity ?? "unknown");
72
+ checks.push({ name: "SQLite", status: integrity === "ok" ? "pass" : "fail", details: `integrity ${integrity}` });
73
+ const ticker = await runtime.call("market_get_ticker", { instId: "BTC-USDT-SWAP" });
74
+ checks.push({ name: "Market data", status: "pass", details: `BTC-USDT-SWAP last ${String(ticker.data?.last ?? "received")}, age ${String(ticker.meta?.ageMs ?? "unknown")}ms` });
75
+ }
76
+ catch (error) {
77
+ checks.push({ name: "Runtime", status: "fail", details: errorMessage(error) });
78
+ }
79
+ }
80
+ const accounts = Object.keys(config.accounts);
81
+ if (!accounts.length) {
82
+ checks.push({ name: "Accounts", status: "warn", details: "No account configured; public market tools remain available" });
83
+ }
84
+ else if (network.ok) {
85
+ const client = new OkxClient(OKX_REST_BASE_URL, proxy.url);
86
+ const service = new AccountService(config, client);
87
+ for (const name of accounts) {
88
+ try {
89
+ const verified = await service.verify(name);
90
+ checks.push({ name: `Account ${name}`, status: "pass", details: `${config.accounts[name]?.environment}; permissions ${String(verified.data.perm ?? "reported by OKX")}` });
91
+ }
92
+ catch (error) {
93
+ checks.push({ name: `Account ${name}`, status: "fail", details: errorMessage(error) });
94
+ }
95
+ }
96
+ }
97
+ return report(checks);
98
+ }
99
+ export function formatDoctor(reportValue) {
100
+ const width = Math.max(...reportValue.checks.map((check) => check.name.length), 6);
101
+ const lines = ["Desic OKX Doctor", ""];
102
+ for (const check of reportValue.checks) {
103
+ lines.push(`${check.status.toUpperCase().padEnd(4)} ${check.name.padEnd(width)} ${check.details}`);
104
+ }
105
+ lines.push("", reportValue.ok ? "Ready" : "Action required");
106
+ return lines.join("\n");
107
+ }
108
+ function configPermission() {
109
+ if (!fs.existsSync(CONFIG_PATH))
110
+ return { secure: true, details: "defaults in use; no config file created" };
111
+ if (process.platform === "win32")
112
+ return { secure: true, details: CONFIG_PATH };
113
+ const mode = fs.statSync(CONFIG_PATH).mode & 0o777;
114
+ return mode & 0o077
115
+ ? { secure: false, details: `${CONFIG_PATH}; permissions should be 0600` }
116
+ : { secure: true, details: `${CONFIG_PATH}; permissions 0600` };
117
+ }
118
+ function report(checks) {
119
+ return { ok: checks.every((check) => check.status !== "fail"), version: PACKAGE_VERSION, checkedAt: Date.now(), checks };
120
+ }
121
+ function formatDuration(ms) {
122
+ if (!Number.isFinite(ms))
123
+ return "unknown";
124
+ if (ms < 60_000)
125
+ return `${Math.round(ms / 1_000)}s`;
126
+ if (ms < 3_600_000)
127
+ return `${Math.round(ms / 60_000)}m`;
128
+ return `${(ms / 3_600_000).toFixed(1)}h`;
129
+ }
130
+ function errorMessage(error) {
131
+ return error instanceof Error ? error.message : String(error);
132
+ }
133
+ //# sourceMappingURL=doctor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../src/cli/doctor.ts"],"names":[],"mappings":"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"}
package/dist/cli/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import { Command } from "commander";
3
+ import * as prompts from "@clack/prompts";
3
4
  import { accountSummaries, loadConfig, loadStoredConfig, saveConfig } from "../config/loader.js";
4
5
  import { CONFIG_PATH } from "../config/paths.js";
5
6
  import { OkxClient } from "../core/okx-client.js";
@@ -10,10 +11,13 @@ import { displayProxy, resolveProxy } from "../network/proxy.js";
10
11
  import { AccountService } from "../account/service.js";
11
12
  import { runMcpServer } from "../mcp/server.js";
12
13
  import { RuntimeClient } from "../runtime/client.js";
13
- import { runRuntimeServer } from "../runtime/server.js";
14
- import { ask, askSecret } from "./prompts.js";
15
- import { SetupCancelledError, parseSetupTargets, SETUP_TARGETS, setupSucceeded, setupSummary, setupTargets } from "../setup/installer.js";
14
+ import { ask, askConfirm, askSecret } from "./prompts.js";
15
+ import { SetupCancelledError, inspectSetupTargets, parseSetupTargets, SETUP_TARGETS, setupSucceeded, setupSummary, setupTargets, syncSkills } from "../setup/installer.js";
16
16
  import { runSetupWizard } from "../setup/wizard.js";
17
+ import { runDoctor, formatDoctor } from "./doctor.js";
18
+ import { formatRuntimeStatus, formatToolHelp } from "./render.js";
19
+ import { getToolHelp } from "../tools/help.js";
20
+ import { checkForUpdate, installLatestVersion, OFFICIAL_NPM_REGISTRY, runInstalledCommand } from "../update/service.js";
17
21
  const program = new Command()
18
22
  .name("desic-okx")
19
23
  .description("Independent Desic runtime, MCP server, CLI, and skills for OKX")
@@ -25,7 +29,11 @@ program.command("start")
25
29
  print(await client.health());
26
30
  });
27
31
  program.command("daemon", { hidden: true })
28
- .action(async () => runRuntimeServer());
32
+ .action(async () => {
33
+ // Keep native SQLite out of long-lived MCP adapter processes on Windows.
34
+ const { runRuntimeServer } = await import("../runtime/server.js");
35
+ await runRuntimeServer();
36
+ });
29
37
  program.command("stop")
30
38
  .description("Stop the local runtime")
31
39
  .action(async () => {
@@ -35,9 +43,26 @@ program.command("stop")
35
43
  });
36
44
  program.command("status")
37
45
  .description("Show local runtime status")
38
- .action(async () => {
46
+ .option("--json", "Print machine-readable JSON")
47
+ .action(async (options) => {
39
48
  const client = await RuntimeClient.connect({ start: false });
40
- print(await client.health());
49
+ const health = await client.health();
50
+ if (options.json || !process.stdout.isTTY)
51
+ print(health);
52
+ else
53
+ process.stdout.write(`${formatRuntimeStatus(health)}\n`);
54
+ });
55
+ program.command("doctor")
56
+ .description("Diagnose installation, network, Runtime, Skills, and accounts")
57
+ .option("--json", "Print machine-readable JSON")
58
+ .action(async (options) => {
59
+ const report = await runDoctor();
60
+ if (options.json || !process.stdout.isTTY)
61
+ print(report);
62
+ else
63
+ process.stdout.write(`${formatDoctor(report)}\n`);
64
+ if (!report.ok)
65
+ process.exitCode = 1;
41
66
  });
42
67
  program.command("mcp")
43
68
  .description("Run the stdio MCP adapter")
@@ -88,6 +113,61 @@ program.command("tools")
88
113
  const client = await RuntimeClient.connect();
89
114
  print(await client.tools());
90
115
  });
116
+ program.command("tool")
117
+ .description("Show one tool's requirements, schema, and example")
118
+ .argument("<name>", "tool name")
119
+ .option("--json", "Print machine-readable JSON")
120
+ .action((name, options) => {
121
+ const help = getToolHelp(name);
122
+ if (!help)
123
+ throw new Error(`Unknown tool '${name}'`);
124
+ if (options.json || !process.stdout.isTTY)
125
+ print(help);
126
+ else
127
+ process.stdout.write(`${formatToolHelp(help)}\n`);
128
+ });
129
+ const skills = program.command("skills").description("Inspect and synchronize bundled Skills");
130
+ skills.command("status")
131
+ .description("Show pending Skill installations and updates")
132
+ .option("--targets <targets>", "codex,claude-code; defaults to detected installations")
133
+ .action((options) => print(syncSkills(resolveSkillTargets(options.targets), { dryRun: true })));
134
+ skills.command("sync")
135
+ .description("Install or update bundled Skills, backing up changed copies")
136
+ .option("--targets <targets>", "codex,claude-code; defaults to detected installations")
137
+ .option("--dry-run", "Show changes without writing")
138
+ .action((options) => print(syncSkills(resolveSkillTargets(options.targets), { dryRun: options.dryRun })));
139
+ program.command("update")
140
+ .description("Check for and install the latest npm release safely")
141
+ .option("--check", "Only check for a newer version")
142
+ .option("--yes", "Install without confirmation")
143
+ .option("--registry <url>", "npm registry", OFFICIAL_NPM_REGISTRY)
144
+ .action(async (options) => {
145
+ const info = await checkForUpdate(options.registry, loadStoredConfig().proxy.url);
146
+ if (options.check || !info.updateAvailable) {
147
+ print(info);
148
+ return;
149
+ }
150
+ let approved = options.yes === true;
151
+ if (!approved && process.stdin.isTTY) {
152
+ const answer = await prompts.confirm({ message: `Update ${info.current} to ${info.latest}?` });
153
+ if (prompts.isCancel(answer) || answer === false)
154
+ throw new SetupCancelledError();
155
+ approved = true;
156
+ }
157
+ if (!approved)
158
+ throw new Error("Use --yes to update in a non-interactive terminal");
159
+ const exitCode = await installLatestVersion(info);
160
+ if (exitCode !== 0)
161
+ throw new Error(`npm exited with code ${exitCode}`);
162
+ process.stdout.write(`Updated to ${info.latest}. Synchronizing installed Skills...\n`);
163
+ const skillExit = await runInstalledCommand(["skills", "sync"]);
164
+ if (skillExit !== 0)
165
+ process.stderr.write("The package was updated, but Skill synchronization needs attention.\n");
166
+ process.stdout.write("Verifying the updated installation...\n");
167
+ const doctorExit = await runInstalledCommand(["doctor"]);
168
+ if (doctorExit !== 0)
169
+ process.stderr.write("The package was updated, but Doctor reported an issue.\n");
170
+ });
91
171
  program.command("config-path")
92
172
  .description("Print the active configuration path")
93
173
  .action(() => {
@@ -109,11 +189,13 @@ account.command("add")
109
189
  if (!apiKey || !secretKey || !passphrase)
110
190
  throw new Error("All three credential fields are required");
111
191
  const config = loadStoredConfig();
112
- config.accounts[name] = { environment: environmentValue, apiKey, secretKey, passphrase };
113
- config.defaultAccount ??= name;
114
- saveConfig(config);
192
+ const candidate = structuredClone(config);
193
+ candidate.accounts[name] = { environment: environmentValue, apiKey, secretKey, passphrase };
194
+ candidate.defaultAccount ??= name;
195
+ const verified = await verifyAccount(candidate, name);
196
+ saveConfig(candidate);
115
197
  await stopForReload();
116
- print({ saved: true, name, environment: environmentValue, configPath: CONFIG_PATH });
198
+ print({ saved: true, name, environment: environmentValue, permissions: verified.data, configPath: CONFIG_PATH });
117
199
  });
118
200
  account.command("list")
119
201
  .description("List configured account aliases")
@@ -121,11 +203,87 @@ account.command("list")
121
203
  account.command("verify")
122
204
  .description("Verify one account and show OKX-reported permissions")
123
205
  .option("--name <name>", "account alias")
206
+ .option("--all", "Verify all configured accounts")
124
207
  .action(async (options) => {
125
208
  const config = loadConfig();
126
209
  const client = new OkxClient(OKX_REST_BASE_URL, resolveProxy(config.proxy.url).url);
127
210
  const service = new AccountService(config, client);
128
- print(await service.verify(options.name));
211
+ if (options.all) {
212
+ const results = await Promise.all(Object.keys(config.accounts).map(async (name) => {
213
+ try {
214
+ return { name, ok: true, result: await service.verify(name) };
215
+ }
216
+ catch (error) {
217
+ return { name, ok: false, error: publicError(error) };
218
+ }
219
+ }));
220
+ print(results);
221
+ if (results.some((result) => !result.ok))
222
+ process.exitCode = 1;
223
+ }
224
+ else {
225
+ print(await service.verify(options.name));
226
+ }
227
+ });
228
+ account.command("set-default")
229
+ .description("Set the default account alias")
230
+ .argument("<name>", "account alias")
231
+ .action(async (name) => {
232
+ const config = loadStoredConfig();
233
+ if (!config.accounts[name])
234
+ throw new Error(`Account '${name}' was not found`);
235
+ config.defaultAccount = name;
236
+ saveConfig(config);
237
+ await stopForReload();
238
+ print({ saved: true, defaultAccount: name });
239
+ });
240
+ account.command("rename")
241
+ .description("Rename an account alias")
242
+ .argument("<oldName>", "current account alias")
243
+ .argument("<newName>", "new account alias")
244
+ .action(async (oldName, newName) => {
245
+ const config = loadStoredConfig();
246
+ if (!config.accounts[oldName])
247
+ throw new Error(`Account '${oldName}' was not found`);
248
+ if (config.accounts[newName])
249
+ throw new Error(`Account '${newName}' already exists`);
250
+ config.accounts[newName] = config.accounts[oldName];
251
+ delete config.accounts[oldName];
252
+ if (config.defaultAccount === oldName)
253
+ config.defaultAccount = newName;
254
+ saveConfig(config);
255
+ await stopForReload();
256
+ print({ renamed: true, from: oldName, to: newName, default: config.defaultAccount === newName });
257
+ });
258
+ account.command("edit")
259
+ .description("Edit an account environment or replace its credentials")
260
+ .argument("<name>", "account alias")
261
+ .option("--environment <environment>", "demo or live")
262
+ .option("--replace-credentials", "Prompt for replacement credentials")
263
+ .action(async (name, options) => {
264
+ const config = loadStoredConfig();
265
+ const current = config.accounts[name];
266
+ if (!current)
267
+ throw new Error(`Account '${name}' was not found`);
268
+ const environment = options.environment?.trim() || await ask("Environment (demo/live)", current.environment);
269
+ if (environment !== "demo" && environment !== "live")
270
+ throw new Error("Environment must be demo or live");
271
+ const normalizedEnvironment = environment;
272
+ const replace = options.replaceCredentials ?? await askConfirm("Replace API credentials", false);
273
+ const updated = { ...current, environment: normalizedEnvironment };
274
+ if (replace) {
275
+ updated.apiKey = await askSecret("API Key");
276
+ updated.secretKey = await askSecret("Secret Key");
277
+ updated.passphrase = await askSecret("Passphrase");
278
+ if (!updated.apiKey || !updated.secretKey || !updated.passphrase)
279
+ throw new Error("All three credential fields are required");
280
+ }
281
+ const candidate = structuredClone(config);
282
+ candidate.accounts[name] = updated;
283
+ const verified = await verifyAccount(candidate, name);
284
+ saveConfig(candidate);
285
+ await stopForReload();
286
+ print({ saved: true, name, environment, permissions: verified.data });
129
287
  });
130
288
  account.command("remove")
131
289
  .description("Remove one account")
@@ -157,6 +315,27 @@ async function stopForReload() {
157
315
  }
158
316
  catch { }
159
317
  }
318
+ async function verifyAccount(config, name) {
319
+ const client = new OkxClient(OKX_REST_BASE_URL, resolveProxy(config.proxy.url).url);
320
+ return new AccountService(config, client).verify(name);
321
+ }
322
+ function parseSkillTargets(value) {
323
+ const targets = value.split(",").map((item) => item.trim()).filter(Boolean);
324
+ const invalid = targets.filter((target) => target !== "codex" && target !== "claude-code");
325
+ if (invalid.length)
326
+ throw new Error(`Unsupported Skill target: ${invalid.join(", ")}`);
327
+ return [...new Set(targets)];
328
+ }
329
+ function resolveSkillTargets(value) {
330
+ if (value)
331
+ return parseSkillTargets(value);
332
+ const detected = inspectSetupTargets()
333
+ .filter((item) => (item.target === "codex" || item.target === "claude-code") && item.skillsInstalled !== null && item.skillsInstalled > 0)
334
+ .map((item) => item.target);
335
+ if (!detected.length)
336
+ throw new Error("No installed Codex or Claude Code Skills were detected; pass --targets to install them");
337
+ return detected;
338
+ }
160
339
  function print(value) {
161
340
  process.stdout.write(`${JSON.stringify(value, null, 2)}\n`);
162
341
  }