desic-okx-agent 0.1.9 → 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.
- package/README.md +46 -4
- package/dist/account/private-websocket.d.ts +1 -0
- package/dist/account/private-websocket.js +27 -2
- package/dist/account/private-websocket.js.map +1 -1
- package/dist/cli/doctor.d.ts +14 -0
- package/dist/cli/doctor.js +133 -0
- package/dist/cli/doctor.js.map +1 -0
- package/dist/cli/index.js +185 -9
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/prompts.d.ts +1 -0
- package/dist/cli/prompts.js +6 -0
- package/dist/cli/prompts.js.map +1 -1
- package/dist/cli/render.d.ts +8 -0
- package/dist/cli/render.js +73 -0
- package/dist/cli/render.js.map +1 -0
- package/dist/market/store.d.ts +1 -0
- package/dist/market/store.js +24 -0
- package/dist/market/store.js.map +1 -1
- package/dist/market/websocket.d.ts +1 -0
- package/dist/market/websocket.js +13 -0
- package/dist/market/websocket.js.map +1 -1
- package/dist/runtime/client.d.ts +1 -0
- package/dist/runtime/client.js +46 -2
- package/dist/runtime/client.js.map +1 -1
- package/dist/runtime/server.js +21 -2
- package/dist/runtime/server.js.map +1 -1
- package/dist/setup/installer.d.ts +21 -0
- package/dist/setup/installer.js +113 -27
- package/dist/setup/installer.js.map +1 -1
- package/dist/storage/database.d.ts +2 -0
- package/dist/storage/database.js +23 -0
- package/dist/storage/database.js.map +1 -1
- package/dist/tools/catalog.js +1 -1
- package/dist/tools/catalog.js.map +1 -1
- package/dist/tools/help.d.ts +10 -0
- package/dist/tools/help.js +79 -0
- package/dist/tools/help.js.map +1 -0
- package/dist/update/service.d.ts +10 -0
- package/dist/update/service.js +104 -0
- package/dist/update/service.js.map +1 -0
- package/package.json +1 -1
- package/skills/okx-news-intelligence/SKILL.md +2 -0
- package/skills/okx-smart-money-analysis/SKILL.md +2 -0
package/README.md
CHANGED
|
@@ -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
|
|
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:
|
|
@@ -101,10 +101,15 @@ Useful Runtime commands:
|
|
|
101
101
|
|
|
102
102
|
```bash
|
|
103
103
|
desic-okx status
|
|
104
|
+
desic-okx status --json
|
|
105
|
+
desic-okx doctor
|
|
104
106
|
desic-okx tools
|
|
107
|
+
desic-okx tool market_get_decision_snapshot
|
|
105
108
|
desic-okx stop
|
|
106
109
|
```
|
|
107
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
|
+
|
|
108
113
|
## Connect Codex
|
|
109
114
|
|
|
110
115
|
The recommended option is `desic-okx setup`, which also installs all Skills. To register only the local stdio MCP server manually:
|
|
@@ -169,6 +174,14 @@ Included skills:
|
|
|
169
174
|
|
|
170
175
|
Restart the client if a newly installed skill does not appear.
|
|
171
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
|
+
|
|
172
185
|
## Configure an OKX account
|
|
173
186
|
|
|
174
187
|
Public tools work without credentials. Account and trading tools use a named account alias and never accept credentials as tool arguments.
|
|
@@ -178,9 +191,15 @@ Add and verify an account interactively:
|
|
|
178
191
|
```bash
|
|
179
192
|
desic-okx account add --name demo --environment demo
|
|
180
193
|
desic-okx account verify --name demo
|
|
194
|
+
desic-okx account verify --all
|
|
181
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
|
|
182
199
|
```
|
|
183
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
|
+
|
|
184
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:
|
|
185
204
|
|
|
186
205
|
```bash
|
|
@@ -199,6 +218,29 @@ OKX_ENVIRONMENT=demo|live
|
|
|
199
218
|
|
|
200
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.
|
|
201
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
|
+
|
|
202
244
|
## Proxy
|
|
203
245
|
|
|
204
246
|
REST and every public, business, and private WebSocket connection use the same proxy resolution order:
|
|
@@ -256,7 +298,7 @@ Use an OKX Demo account first. Trading software can lose money, and the project
|
|
|
256
298
|
|
|
257
299
|
## Experimental intelligence
|
|
258
300
|
|
|
259
|
-
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.
|
|
260
302
|
|
|
261
303
|
## Development
|
|
262
304
|
|
|
@@ -284,7 +326,7 @@ Normal tests never submit orders.
|
|
|
284
326
|
|
|
285
327
|
## Status
|
|
286
328
|
|
|
287
|
-
This repository is currently pre-release software at version `0.
|
|
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.
|
|
288
330
|
|
|
289
331
|
## License
|
|
290
332
|
|
|
@@ -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]) => ({
|
|
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", () =>
|
|
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":"AAAA,OAAO,MAAM,MAAM,aAAa,CAAC;AACjC,OAAO,SAAS,MAAM,IAAI,CAAC;AAC3B,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAIpD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;
|
|
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":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACjD,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAClD,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AACrF,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACjE,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,mBAAmB,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACxE,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAiBtD,MAAM,CAAC,KAAK,UAAU,SAAS;IAC7B,MAAM,MAAM,GAAkB,EAAE,CAAC;IACjC,IAAI,MAAqC,CAAC;IAC1C,IAAI,CAAC;QACH,MAAM,GAAG,UAAU,EAAE,CAAC;QACtB,MAAM,UAAU,GAAG,gBAAgB,EAAE,CAAC;QACtC,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,eAAe,EAAE,MAAM,EAAE,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,OAAO,EAAE,UAAU,CAAC,OAAO,EAAE,CAAC,CAAC;IACnH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,eAAe,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QACrF,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC;IACxB,CAAC;IAED,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC,SAAS,EAAE,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACjE,MAAM,CAAC,IAAI,CAAC;YACV,IAAI,EAAE,SAAS;YACf,MAAM,EAAE,MAAM,CAAC,eAAe,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM;YAChD,OAAO,EAAE,MAAM,CAAC,eAAe,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,OAAO,eAAe,MAAM,CAAC,MAAM,YAAY,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,OAAO,aAAa;SAC7H,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,wBAAwB,YAAY,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC;IAC3G,CAAC;IAED,MAAM,OAAO,GAAG,mBAAmB,EAAE,CAAC;IACtC,MAAM,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,aAAa,KAAK,IAAI,CAAC,CAAC;IACzE,MAAM,CAAC,IAAI,CAAC;QACV,IAAI,EAAE,aAAa;QACnB,MAAM,EAAE,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM;QAC3C,OAAO,EAAE,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,gDAAgD;KACjI,CAAC,CAAC;IAEH,MAAM,qBAAqB,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,eAAe,KAAK,IAAI,IAAI,IAAI,CAAC,eAAe,GAAG,CAAC,CAAC;SAC9G,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC,MAAM,EAAqC,EAAE,CAAC,MAAM,KAAK,OAAO,IAAI,MAAM,KAAK,aAAa,CAAC,CAAC;IACpI,IAAI,qBAAqB,CAAC,MAAM,EAAE,CAAC;QACjC,MAAM,MAAM,GAAG,UAAU,CAAC,qBAAqB,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;QACnE,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,CAAC,KAAK,GAAG,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;QACzF,MAAM,CAAC,IAAI,CAAC;YACV,IAAI,EAAE,QAAQ;YACd,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM;YACjC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,sCAAsC,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC,GAAG,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC,6BAA6B;SAC5J,CAAC,CAAC;IACL,CAAC;SAAM,CAAC;QACN,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,8CAA8C,EAAE,CAAC,CAAC;IAC3G,CAAC;IAED,MAAM,KAAK,GAAG,YAAY,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC7C,MAAM,OAAO,GAAG,MAAM,oBAAoB,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACtD,MAAM,CAAC,IAAI,CAAC;QACV,IAAI,EAAE,aAAa;QACnB,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM;QACpC,OAAO,EAAE,OAAO,CAAC,EAAE;YACjB,CAAC,CAAC,GAAG,YAAY,CAAC,KAAK,CAAC,UAAU,OAAO,CAAC,IAAI,CAAC,SAAS,iBAAiB,OAAO,CAAC,SAAS,CAAC,SAAS,IAAI;YACxG,CAAC,CAAC,GAAG,YAAY,CAAC,KAAK,CAAC,UAAU,OAAO,CAAC,IAAI,CAAC,KAAK,IAAI,QAAQ,eAAe,OAAO,CAAC,SAAS,CAAC,KAAK,IAAI,QAAQ,EAAE;KACvH,CAAC,CAAC;IAEH,IAAI,OAAO,CAAC,EAAE,EAAE,CAAC;QACf,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,MAAM,aAAa,CAAC,OAAO,EAAE,CAAC;YAC9C,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,MAAM,EAAE,CAAC;YACtC,MAAM,WAAW,GAAG,MAAM,OAAO,CAAC,WAAW,EAAE,CAAC;YAChD,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,CAAC,OAAO,KAAK,eAAe,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,OAAO,EAAE,WAAW,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,SAAS,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,YAAY,cAAc,CAAC,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YAC3N,MAAM,SAAS,GAAG,MAAM,CAAE,WAAW,CAAC,QAAgD,EAAE,SAAS,IAAI,SAAS,CAAC,CAAC;YAChH,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,KAAK,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,OAAO,EAAE,aAAa,SAAS,EAAE,EAAE,CAAC,CAAC;YACjH,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,mBAAmB,EAAE,EAAE,MAAM,EAAE,eAAe,EAAE,CAAuE,CAAC;YAC1J,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,sBAAsB,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,IAAI,UAAU,CAAC,SAAS,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,IAAI,SAAS,CAAC,IAAI,EAAE,CAAC,CAAC;QACnL,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QACjF,CAAC;IACH,CAAC;IAED,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IAC9C,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC;QACrB,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,6DAA6D,EAAE,CAAC,CAAC;IAC5H,CAAC;SAAM,IAAI,OAAO,CAAC,EAAE,EAAE,CAAC;QACtB,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC,iBAAiB,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC;QAC3D,MAAM,OAAO,GAAG,IAAI,cAAc,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QACnD,KAAK,MAAM,IAAI,IAAI,QAAQ,EAAE,CAAC;YAC5B,IAAI,CAAC;gBACH,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;gBAC5C,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,WAAW,IAAI,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,WAAW,iBAAiB,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,IAAI,iBAAiB,CAAC,EAAE,EAAE,CAAC,CAAC;YAC7K,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,WAAW,IAAI,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;YACzF,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC;AACxB,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,WAAyB;IACpD,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,WAAW,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC;IACnF,MAAM,KAAK,GAAG,CAAC,kBAAkB,EAAE,EAAE,CAAC,CAAC;IACvC,KAAK,MAAM,KAAK,IAAI,WAAW,CAAC,MAAM,EAAE,CAAC;QACvC,KAAK,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;IACvG,CAAC;IACD,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,WAAW,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC;IAC7D,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED,SAAS,gBAAgB;IACvB,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,WAAW,CAAC;QAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,yCAAyC,EAAE,CAAC;IAC7G,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO;QAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,WAAW,EAAE,CAAC;IAChF,MAAM,IAAI,GAAG,EAAE,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC,IAAI,GAAG,KAAK,CAAC;IACnD,OAAO,IAAI,GAAG,KAAK;QACjB,CAAC,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,WAAW,8BAA8B,EAAE;QAC1E,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,WAAW,oBAAoB,EAAE,CAAC;AACpE,CAAC;AAED,SAAS,MAAM,CAAC,MAAqB;IACnC,OAAO,EAAE,EAAE,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,KAAK,MAAM,CAAC,EAAE,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,MAAM,EAAE,CAAC;AAC3H,CAAC;AAED,SAAS,cAAc,CAAC,EAAU;IAChC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC;QAAE,OAAO,SAAS,CAAC;IAC3C,IAAI,EAAE,GAAG,MAAM;QAAE,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,KAAK,CAAC,GAAG,CAAC;IACrD,IAAI,EAAE,GAAG,SAAS;QAAE,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,MAAM,CAAC,GAAG,CAAC;IACzD,OAAO,GAAG,CAAC,EAAE,GAAG,SAAS,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC;AAC3C,CAAC;AAED,SAAS,YAAY,CAAC,KAAc;IAClC,OAAO,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAChE,CAAC"}
|
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,9 +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 { ask, askSecret } from "./prompts.js";
|
|
14
|
-
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";
|
|
15
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";
|
|
16
21
|
const program = new Command()
|
|
17
22
|
.name("desic-okx")
|
|
18
23
|
.description("Independent Desic runtime, MCP server, CLI, and skills for OKX")
|
|
@@ -38,9 +43,26 @@ program.command("stop")
|
|
|
38
43
|
});
|
|
39
44
|
program.command("status")
|
|
40
45
|
.description("Show local runtime status")
|
|
41
|
-
.
|
|
46
|
+
.option("--json", "Print machine-readable JSON")
|
|
47
|
+
.action(async (options) => {
|
|
42
48
|
const client = await RuntimeClient.connect({ start: false });
|
|
43
|
-
|
|
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;
|
|
44
66
|
});
|
|
45
67
|
program.command("mcp")
|
|
46
68
|
.description("Run the stdio MCP adapter")
|
|
@@ -91,6 +113,61 @@ program.command("tools")
|
|
|
91
113
|
const client = await RuntimeClient.connect();
|
|
92
114
|
print(await client.tools());
|
|
93
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
|
+
});
|
|
94
171
|
program.command("config-path")
|
|
95
172
|
.description("Print the active configuration path")
|
|
96
173
|
.action(() => {
|
|
@@ -112,11 +189,13 @@ account.command("add")
|
|
|
112
189
|
if (!apiKey || !secretKey || !passphrase)
|
|
113
190
|
throw new Error("All three credential fields are required");
|
|
114
191
|
const config = loadStoredConfig();
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
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);
|
|
118
197
|
await stopForReload();
|
|
119
|
-
print({ saved: true, name, environment: environmentValue, configPath: CONFIG_PATH });
|
|
198
|
+
print({ saved: true, name, environment: environmentValue, permissions: verified.data, configPath: CONFIG_PATH });
|
|
120
199
|
});
|
|
121
200
|
account.command("list")
|
|
122
201
|
.description("List configured account aliases")
|
|
@@ -124,11 +203,87 @@ account.command("list")
|
|
|
124
203
|
account.command("verify")
|
|
125
204
|
.description("Verify one account and show OKX-reported permissions")
|
|
126
205
|
.option("--name <name>", "account alias")
|
|
206
|
+
.option("--all", "Verify all configured accounts")
|
|
127
207
|
.action(async (options) => {
|
|
128
208
|
const config = loadConfig();
|
|
129
209
|
const client = new OkxClient(OKX_REST_BASE_URL, resolveProxy(config.proxy.url).url);
|
|
130
210
|
const service = new AccountService(config, client);
|
|
131
|
-
|
|
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 });
|
|
132
287
|
});
|
|
133
288
|
account.command("remove")
|
|
134
289
|
.description("Remove one account")
|
|
@@ -160,6 +315,27 @@ async function stopForReload() {
|
|
|
160
315
|
}
|
|
161
316
|
catch { }
|
|
162
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
|
+
}
|
|
163
339
|
function print(value) {
|
|
164
340
|
process.stdout.write(`${JSON.stringify(value, null, 2)}\n`);
|
|
165
341
|
}
|