@bitkyc08/opencodex 2.7.41 → 2.7.42
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 +4 -0
- package/gui/dist/assets/index-Bl_VBGoI.js +65 -0
- package/gui/dist/assets/index-DfVGuN88.css +1 -0
- package/gui/dist/index.html +2 -2
- package/package.json +1 -1
- package/src/adapters/base.ts +6 -0
- package/src/adapters/kiro-constants.ts +6 -2
- package/src/adapters/kiro-retry.ts +175 -10
- package/src/adapters/kiro.ts +172 -85
- package/src/adapters/mimo-free.ts +1 -0
- package/src/adapters/openai-chat.ts +30 -4
- package/src/adapters/openai-responses.ts +90 -12
- package/src/bridge.ts +91 -43
- package/src/claude/desktop-3p-paths.ts +84 -0
- package/src/claude/desktop-3p.ts +29 -2
- package/src/cli/access.ts +108 -0
- package/src/cli/account-auth.ts +223 -0
- package/src/cli/account.ts +9 -1
- package/src/cli/agent.ts +184 -0
- package/src/cli/combo.ts +119 -0
- package/src/cli/config-command.ts +145 -0
- package/src/cli/debug.ts +20 -8
- package/src/cli/doctor.ts +45 -8
- package/src/cli/help.ts +65 -13
- package/src/cli/index.ts +108 -7
- package/src/cli/integrations.ts +142 -0
- package/src/cli/models-runtime.ts +212 -0
- package/src/cli/models.ts +9 -10
- package/src/cli/observe.ts +117 -0
- package/src/cli/provider-runtime.ts +152 -0
- package/src/cli/provider.ts +23 -1
- package/src/cli/runtime-api.ts +325 -0
- package/src/cli/star-prompt.ts +3 -3
- package/src/cli/status.ts +17 -0
- package/src/cli/system-command.ts +112 -0
- package/src/codex/auth-api.ts +3 -2
- package/src/codex/catalog/aggregation.ts +113 -18
- package/src/codex/catalog/provider-fetch.ts +24 -13
- package/src/codex/catalog/sync.ts +20 -8
- package/src/codex/catalog.ts +2 -1
- package/src/codex/refresh.ts +10 -3
- package/src/codex/routing.ts +21 -32
- package/src/codex/sync.ts +17 -0
- package/src/config.ts +48 -0
- package/src/generated/jawcode-model-metadata.ts +2 -1
- package/src/grok/inject.ts +184 -4
- package/src/grok/status.ts +33 -0
- package/src/lib/retry-after.ts +55 -0
- package/src/lib/windows-elevation.ts +627 -0
- package/src/providers/openai-sidecar.ts +46 -2
- package/src/providers/registry.ts +52 -0
- package/src/server/auth-cors.ts +6 -0
- package/src/server/chat-completions.ts +6 -1
- package/src/server/claude-messages.ts +20 -1
- package/src/server/images.ts +14 -7
- package/src/server/management/agent-settings-routes.ts +10 -4
- package/src/server/management/combo-routes.ts +0 -1
- package/src/server/management/config-routes.ts +0 -1
- package/src/server/management/logs-usage-routes.ts +94 -0
- package/src/server/management/model-routes.ts +0 -1
- package/src/server/management/oauth-account-routes.ts +0 -1
- package/src/server/management/provider-routes.ts +0 -1
- package/src/server/management/shared.ts +0 -1
- package/src/server/management/system-routes.ts +27 -15
- package/src/server/management-api.ts +0 -1
- package/src/server/memory-watchdog.ts +54 -10
- package/src/server/request-log-conversation.ts +168 -0
- package/src/server/request-log.ts +122 -2
- package/src/server/responses/core.ts +76 -13
- package/src/server/responses/passthrough-error.ts +38 -13
- package/src/server/startup-action-control.ts +266 -15
- package/src/service.ts +512 -3
- package/src/storage/cleanup.ts +1538 -0
- package/src/storage/scanner.ts +4 -1
- package/src/types.ts +16 -0
- package/src/update/job.ts +229 -25
- package/src/usage/log.ts +39 -0
- package/src/web-search/loop.ts +8 -1
- package/gui/dist/assets/index-B2J4t3te.css +0 -1
- package/gui/dist/assets/index-BmvM6wRb.js +0 -65
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { readFileSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { getConfigPath, readConfigDiagnostics, saveConfig, validateConfigCandidate } from "../config";
|
|
3
|
+
import type { OcxConfig } from "../types";
|
|
4
|
+
import { CliUsageError, printData, rejectArgs, runCliAction, takeFlag } from "./runtime-api";
|
|
5
|
+
|
|
6
|
+
const USAGE = `Usage:
|
|
7
|
+
ocx config [show] [--json] [--source]
|
|
8
|
+
ocx config get <dot.path> [--json]
|
|
9
|
+
ocx config set <dot.path> <json-or-string> [--json]
|
|
10
|
+
ocx config unset <dot.path> [--json]
|
|
11
|
+
ocx config validate [path|-] [--json]
|
|
12
|
+
ocx config export <path|->
|
|
13
|
+
ocx config import <path|-> --yes [--json]`;
|
|
14
|
+
|
|
15
|
+
const SECRET_KEYS = /^(apiKey|key|accessToken|refreshToken|idToken|token|password|clientSecret)$/i;
|
|
16
|
+
const BLOCKED_SEGMENTS = new Set(["__proto__", "prototype", "constructor"]);
|
|
17
|
+
|
|
18
|
+
function redact(value: unknown, key = ""): unknown {
|
|
19
|
+
if (SECRET_KEYS.test(key) && typeof value === "string") return value ? "********" : value;
|
|
20
|
+
if (Array.isArray(value)) return value.map(item => redact(item));
|
|
21
|
+
if (value && typeof value === "object") {
|
|
22
|
+
return Object.fromEntries(Object.entries(value as Record<string, unknown>).map(([childKey, child]) => [childKey, redact(child, childKey)]));
|
|
23
|
+
}
|
|
24
|
+
return value;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function pathSegments(path: string): string[] {
|
|
28
|
+
const segments = path.split(".").map(part => part.trim()).filter(Boolean);
|
|
29
|
+
if (segments.length === 0 || segments.some(part => BLOCKED_SEGMENTS.has(part))) throw new CliUsageError("invalid config path", USAGE);
|
|
30
|
+
return segments;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function getPath(root: unknown, path: string): unknown {
|
|
34
|
+
let current = root;
|
|
35
|
+
for (const segment of pathSegments(path)) {
|
|
36
|
+
if (!current || typeof current !== "object" || !Object.hasOwn(current, segment)) throw new CliUsageError(`config path not found: ${path}`);
|
|
37
|
+
current = (current as Record<string, unknown>)[segment];
|
|
38
|
+
}
|
|
39
|
+
return current;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function setPath(root: Record<string, unknown>, path: string, value: unknown, remove = false): void {
|
|
43
|
+
const segments = pathSegments(path);
|
|
44
|
+
let current = root;
|
|
45
|
+
for (const segment of segments.slice(0, -1)) {
|
|
46
|
+
const next = current[segment];
|
|
47
|
+
if (!next || typeof next !== "object" || Array.isArray(next)) throw new CliUsageError(`config parent path not found: ${segment}`, USAGE);
|
|
48
|
+
current = next as Record<string, unknown>;
|
|
49
|
+
}
|
|
50
|
+
const leaf = segments.at(-1)!;
|
|
51
|
+
if (remove && !Object.hasOwn(current, leaf)) throw new CliUsageError(`config path not found: ${path}`);
|
|
52
|
+
if (remove) delete current[leaf];
|
|
53
|
+
else current[leaf] = value;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function parseValue(raw: string): unknown {
|
|
57
|
+
try { return JSON.parse(raw); }
|
|
58
|
+
catch { return raw; }
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function loadInput(path: string): unknown {
|
|
62
|
+
const raw = path === "-" ? readFileSync(0, "utf8") : readFileSync(path, "utf8");
|
|
63
|
+
try { return JSON.parse(raw); }
|
|
64
|
+
catch { throw new CliUsageError(`invalid JSON in ${path}`); }
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function validate(value: unknown): OcxConfig {
|
|
68
|
+
const result = validateConfigCandidate(value);
|
|
69
|
+
if (!result.ok) throw new CliUsageError(result.error);
|
|
70
|
+
return result.config;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export async function handleConfigCommand(argv: string[]): Promise<number> {
|
|
74
|
+
return runCliAction(async () => {
|
|
75
|
+
const args = [...argv];
|
|
76
|
+
const action = (args.shift() ?? "show").toLowerCase();
|
|
77
|
+
const wantsJson = takeFlag(args, "--json");
|
|
78
|
+
if (action === "show") {
|
|
79
|
+
const source = takeFlag(args, "--source");
|
|
80
|
+
rejectArgs(args, USAGE);
|
|
81
|
+
const diagnostics = readConfigDiagnostics();
|
|
82
|
+
const config = redact(diagnostics.config);
|
|
83
|
+
const result = source ? { config, source: diagnostics.source, error: diagnostics.error, warnings: diagnostics.warnings ?? [] } : config;
|
|
84
|
+
printData(result, true);
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
if (action === "get") {
|
|
88
|
+
const path = args.shift();
|
|
89
|
+
if (!path) throw new CliUsageError("config path is required", USAGE);
|
|
90
|
+
rejectArgs(args, USAGE);
|
|
91
|
+
const value = redact(getPath(readConfigDiagnostics().config, path), path.split(".").at(-1));
|
|
92
|
+
if (wantsJson || typeof value === "object") console.log(JSON.stringify(value, null, 2));
|
|
93
|
+
else console.log(String(value));
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
if (action === "set" || action === "unset") {
|
|
97
|
+
const path = args.shift();
|
|
98
|
+
const raw = action === "set" ? args.shift() : undefined;
|
|
99
|
+
if (!path || (action === "set" && raw === undefined)) throw new CliUsageError("config path and value are required", USAGE);
|
|
100
|
+
rejectArgs(args, USAGE);
|
|
101
|
+
const candidate = structuredClone(readConfigDiagnostics().config) as unknown as Record<string, unknown>;
|
|
102
|
+
setPath(candidate, path, raw === undefined ? undefined : parseValue(raw), action === "unset");
|
|
103
|
+
const config = validate(candidate);
|
|
104
|
+
const savedValue = action === "unset" ? null : getPath(config, path);
|
|
105
|
+
saveConfig(config);
|
|
106
|
+
printData({ ok: true, path, value: redact(savedValue, path.split(".").at(-1)) }, wantsJson,
|
|
107
|
+
[`${action === "unset" ? "Unset" : "Set"} ${path}.`]);
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
if (action === "validate") {
|
|
111
|
+
const path = args.shift();
|
|
112
|
+
rejectArgs(args, USAGE);
|
|
113
|
+
const result = path ? validateConfigCandidate(loadInput(path)) : (() => {
|
|
114
|
+
const diagnostics = readConfigDiagnostics();
|
|
115
|
+
return diagnostics.error ? { ok: false as const, error: diagnostics.error } : { ok: true as const, config: diagnostics.config };
|
|
116
|
+
})();
|
|
117
|
+
printData(result.ok ? { ok: true, source: path ?? getConfigPath() } : result, wantsJson,
|
|
118
|
+
[result.ok ? "Config is valid." : `Config is invalid: ${result.error}`]);
|
|
119
|
+
if (!result.ok) process.exitCode = 1;
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
if (action === "export") {
|
|
123
|
+
const path = args.shift();
|
|
124
|
+
if (!path) throw new CliUsageError("export path is required", USAGE);
|
|
125
|
+
rejectArgs(args, USAGE);
|
|
126
|
+
const content = `${JSON.stringify(readConfigDiagnostics().config, null, 2)}\n`;
|
|
127
|
+
if (path === "-") process.stdout.write(content);
|
|
128
|
+
else { writeFileSync(path, content, { encoding: "utf8", mode: 0o600 }); console.log(`Exported config to ${path}.`); }
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
if (action === "import") {
|
|
132
|
+
const path = args.shift();
|
|
133
|
+
const yes = takeFlag(args, "--yes");
|
|
134
|
+
if (!path) throw new CliUsageError("import path is required", USAGE);
|
|
135
|
+
if (!yes) throw new CliUsageError("import requires --yes", USAGE);
|
|
136
|
+
rejectArgs(args, USAGE);
|
|
137
|
+
saveConfig(validate(loadInput(path)));
|
|
138
|
+
printData({ ok: true, source: path }, wantsJson, [`Imported config from ${path}. Restart or run ocx sync if needed.`]);
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
throw new CliUsageError(`unknown config command ${action}`, USAGE);
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export const CONFIG_USAGE = USAGE;
|
package/src/cli/debug.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { findLiveProxy, probeHostname } from "../server/proxy-liveness";
|
|
|
2
2
|
import { DEBUG_ENV, type DebugSettingsView } from "../lib/debug-settings";
|
|
3
3
|
import { runningProxyUpdateHeaders } from "../oauth/login-cli";
|
|
4
4
|
|
|
5
|
-
type DebugScope = "provider" | "usage" | "injection";
|
|
5
|
+
type DebugScope = "provider" | "usage" | "injection" | "claude";
|
|
6
6
|
|
|
7
7
|
async function requireLiveProxy() {
|
|
8
8
|
const live = await findLiveProxy();
|
|
@@ -54,10 +54,14 @@ function printScopeStatus(scope: DebugScope, view: DebugSettingsView): void {
|
|
|
54
54
|
console.log(`Usage debug: ${view.usage ? "ON" : "off"}`);
|
|
55
55
|
console.log(` env=${view.env.usage ? "on" : "off"}, runtime=${view.runtimeOverride.usage === undefined ? "env/default" : view.runtimeOverride.usage ? "on" : "off"}`);
|
|
56
56
|
console.log(" Tail: ocx debug usage logs [-f] (via running proxy API)");
|
|
57
|
-
} else {
|
|
57
|
+
} else if (scope === "injection") {
|
|
58
58
|
console.log(`Injection debug: ${view.injection ? "ON" : "off"}`);
|
|
59
59
|
console.log(` env=${view.env.injection ? "on" : "off"}, runtime=${view.runtimeOverride.injection === undefined ? "env/default" : view.runtimeOverride.injection ? "on" : "off"}`);
|
|
60
60
|
console.log(" Lines appear on the proxy console when multi-agent guidance is injected.");
|
|
61
|
+
} else {
|
|
62
|
+
console.log(`Claude inbound debug: ${view.claude ? "ON" : "off"}`);
|
|
63
|
+
console.log(` env=${view.env.claude ? "on" : "off"}, runtime=${view.runtimeOverride.claude === undefined ? "env/default" : view.runtimeOverride.claude ? "on" : "off"}`);
|
|
64
|
+
console.log(" View: ocx observe claude-inbound");
|
|
61
65
|
}
|
|
62
66
|
}
|
|
63
67
|
|
|
@@ -142,7 +146,10 @@ async function handleScopeCommand(scope: DebugScope, actionArgv: string[]): Prom
|
|
|
142
146
|
|
|
143
147
|
if (action === "on" || action === "off") {
|
|
144
148
|
const enabled = action === "on";
|
|
145
|
-
const body = scope === "provider" ? { debug: enabled }
|
|
149
|
+
const body = scope === "provider" ? { debug: enabled }
|
|
150
|
+
: scope === "usage" ? { usage: enabled }
|
|
151
|
+
: scope === "injection" ? { injection: enabled }
|
|
152
|
+
: { claude: enabled };
|
|
146
153
|
printScopeStatus(scope, await putDebugSettings(body));
|
|
147
154
|
console.log(`\n${scope} debug is now ${enabled ? "enabled" : "disabled"}.`);
|
|
148
155
|
return;
|
|
@@ -161,8 +168,10 @@ async function handleScopeCommand(scope: DebugScope, actionArgv: string[]): Prom
|
|
|
161
168
|
}
|
|
162
169
|
|
|
163
170
|
if (action === "logs") {
|
|
164
|
-
if (scope === "injection") {
|
|
165
|
-
console.error(
|
|
171
|
+
if (scope === "injection" || scope === "claude") {
|
|
172
|
+
console.error(scope === "claude"
|
|
173
|
+
? "Use: ocx observe claude-inbound"
|
|
174
|
+
: "Injection debug has no buffered log stream; use: ocx observe injection");
|
|
166
175
|
process.exit(1);
|
|
167
176
|
}
|
|
168
177
|
const follow = actionArgv.slice(1).some(arg => arg === "-f" || arg === "--follow");
|
|
@@ -171,8 +180,8 @@ async function handleScopeCommand(scope: DebugScope, actionArgv: string[]): Prom
|
|
|
171
180
|
return;
|
|
172
181
|
}
|
|
173
182
|
|
|
174
|
-
console.error(scope === "injection"
|
|
175
|
-
?
|
|
183
|
+
console.error(scope === "injection" || scope === "claude"
|
|
184
|
+
? `Usage: ocx debug ${scope} on|off|status|reset`
|
|
176
185
|
: `Usage: ocx debug ${scope} on|off|status|reset|logs [-f]`);
|
|
177
186
|
process.exit(1);
|
|
178
187
|
}
|
|
@@ -183,17 +192,19 @@ function printTopLevelHelp(): void {
|
|
|
183
192
|
console.log(" ocx debug provider on|off|status|reset|logs [-f]");
|
|
184
193
|
console.log(" ocx debug usage on|off|status|reset|logs [-f]");
|
|
185
194
|
console.log(" ocx debug injection on|off|status|reset");
|
|
195
|
+
console.log(" ocx debug claude on|off|status|reset");
|
|
186
196
|
console.log("");
|
|
187
197
|
console.log("Env defaults on start:");
|
|
188
198
|
console.log(" provider → OCX_DEBUG=1 (legacy OCX_DEBUG_FRAMES still works)");
|
|
189
199
|
console.log(` usage → ${DEBUG_ENV.usage}=1`);
|
|
190
200
|
console.log(` injection→ ${DEBUG_ENV.injection}=1`);
|
|
201
|
+
console.log(` claude → ${DEBUG_ENV.claude}=1`);
|
|
191
202
|
}
|
|
192
203
|
|
|
193
204
|
export async function handleDebugCommand(argv: string[]): Promise<void> {
|
|
194
205
|
const sub = (argv[0] ?? "").trim().toLowerCase();
|
|
195
206
|
|
|
196
|
-
if (sub === "provider" || sub === "usage" || sub === "injection") {
|
|
207
|
+
if (sub === "provider" || sub === "usage" || sub === "injection" || sub === "claude") {
|
|
197
208
|
await handleScopeCommand(sub, argv.slice(1));
|
|
198
209
|
return;
|
|
199
210
|
}
|
|
@@ -205,6 +216,7 @@ export async function handleDebugCommand(argv: string[]): Promise<void> {
|
|
|
205
216
|
console.log(` provider → OCX_DEBUG = ${envDebugEnabled() ? "on" : "off"}`);
|
|
206
217
|
console.log(` usage → ${DEBUG_ENV.usage} = ${process.env[DEBUG_ENV.usage] === "1" ? "on" : "off"}`);
|
|
207
218
|
console.log(` injection→ ${DEBUG_ENV.injection} = ${process.env[DEBUG_ENV.injection] === "1" ? "on" : "off"}`);
|
|
219
|
+
console.log(` claude → ${DEBUG_ENV.claude} = ${process.env[DEBUG_ENV.claude] === "1" ? "on" : "off"}`);
|
|
208
220
|
console.log("");
|
|
209
221
|
}
|
|
210
222
|
printTopLevelHelp();
|
package/src/cli/doctor.ts
CHANGED
|
@@ -480,10 +480,14 @@ export type ServiceMemoryData = {
|
|
|
480
480
|
platform: string;
|
|
481
481
|
rss: number;
|
|
482
482
|
heapUsed: number;
|
|
483
|
+
external: number;
|
|
484
|
+
arrayBuffers: number;
|
|
485
|
+
observedBytes?: number;
|
|
486
|
+
observedMetric?: MemoryMetric;
|
|
483
487
|
jscHeap: { heapSize: number } | null;
|
|
484
488
|
streamMode: string;
|
|
485
489
|
eagerRelay: { useEagerRelay: boolean; reason: string } | null;
|
|
486
|
-
watchdog: { warnThresholdBytes: number; lastWarnAt: number | null } | null;
|
|
490
|
+
watchdog: { warnThresholdBytes: number; lastWarnAt: number | null; observedBytes?: number; observedMetric?: MemoryMetric } | null;
|
|
487
491
|
};
|
|
488
492
|
|
|
489
493
|
export type ServiceMemoryReport =
|
|
@@ -493,6 +497,19 @@ export type ServiceMemoryReport =
|
|
|
493
497
|
|
|
494
498
|
const SERVICE_MEMORY_TIMEOUT_MS = 2000;
|
|
495
499
|
const DEFAULT_MEMORY_THRESHOLD_BYTES = 4 * 1024 ** 3;
|
|
500
|
+
type MemoryMetric = "rss" | "external" | "arrayBuffers";
|
|
501
|
+
|
|
502
|
+
function observedMemory(data: { rss: number; external?: number; arrayBuffers?: number }): {
|
|
503
|
+
bytes: number;
|
|
504
|
+
metric: MemoryMetric;
|
|
505
|
+
} {
|
|
506
|
+
const values: Array<{ metric: MemoryMetric; bytes: number }> = [
|
|
507
|
+
{ metric: "rss", bytes: data.rss },
|
|
508
|
+
{ metric: "external", bytes: data.external ?? 0 },
|
|
509
|
+
{ metric: "arrayBuffers", bytes: data.arrayBuffers ?? 0 },
|
|
510
|
+
];
|
|
511
|
+
return values.reduce((best, next) => next.bytes > best.bytes ? next : best, values[0]);
|
|
512
|
+
}
|
|
496
513
|
|
|
497
514
|
export async function fetchServiceMemory(
|
|
498
515
|
host: string,
|
|
@@ -519,13 +536,26 @@ export async function fetchServiceMemory(
|
|
|
519
536
|
platform: typeof body.platform === "string" ? body.platform : "unknown",
|
|
520
537
|
rss: body.rss,
|
|
521
538
|
heapUsed: typeof body.heapUsed === "number" ? body.heapUsed : 0,
|
|
539
|
+
external: typeof body.external === "number" ? body.external : 0,
|
|
540
|
+
arrayBuffers: typeof body.arrayBuffers === "number" ? body.arrayBuffers : 0,
|
|
541
|
+
observedBytes: typeof body.observedBytes === "number" ? body.observedBytes : undefined,
|
|
542
|
+
observedMetric: body.observedMetric === "rss" || body.observedMetric === "external" || body.observedMetric === "arrayBuffers"
|
|
543
|
+
? body.observedMetric
|
|
544
|
+
: undefined,
|
|
522
545
|
jscHeap: body.jscHeap && typeof body.jscHeap.heapSize === "number" ? { heapSize: body.jscHeap.heapSize } : null,
|
|
523
546
|
streamMode: typeof body.streamMode === "string" ? body.streamMode : "auto",
|
|
524
547
|
eagerRelay: body.eagerRelay && typeof body.eagerRelay.reason === "string"
|
|
525
548
|
? { useEagerRelay: body.eagerRelay.useEagerRelay === true, reason: body.eagerRelay.reason }
|
|
526
549
|
: null,
|
|
527
550
|
watchdog: body.watchdog && typeof body.watchdog.warnThresholdBytes === "number"
|
|
528
|
-
? {
|
|
551
|
+
? {
|
|
552
|
+
warnThresholdBytes: body.watchdog.warnThresholdBytes,
|
|
553
|
+
lastWarnAt: body.watchdog.lastWarnAt ?? null,
|
|
554
|
+
observedBytes: typeof body.watchdog.observedBytes === "number" ? body.watchdog.observedBytes : undefined,
|
|
555
|
+
observedMetric: body.watchdog.observedMetric === "rss" || body.watchdog.observedMetric === "external" || body.watchdog.observedMetric === "arrayBuffers"
|
|
556
|
+
? body.watchdog.observedMetric
|
|
557
|
+
: undefined,
|
|
558
|
+
}
|
|
529
559
|
: null,
|
|
530
560
|
},
|
|
531
561
|
};
|
|
@@ -550,22 +580,29 @@ export function formatServiceMemoryLines(report: ServiceMemoryReport): string[]
|
|
|
550
580
|
}
|
|
551
581
|
const d = report.data;
|
|
552
582
|
lines.push(` ok service pid ${d.pid}: Bun ${d.bunVersion} on ${d.platform}`);
|
|
553
|
-
|
|
583
|
+
const observed = observedMemory(d);
|
|
584
|
+
const observedBytes = d.observedBytes ?? d.watchdog?.observedBytes ?? observed.bytes;
|
|
585
|
+
const observedMetric = d.observedMetric ?? d.watchdog?.observedMetric ?? observed.metric;
|
|
586
|
+
lines.push(` rss=${mb(d.rss)}, external=${mb(d.external)}, arrayBuffers=${mb(d.arrayBuffers)}, heapUsed=${mb(d.heapUsed)}${d.jscHeap ? `, jscHeap=${mb(d.jscHeap.heapSize)}` : ""}`);
|
|
587
|
+
lines.push(` observed=${mb(observedBytes)} (${observedMetric})`);
|
|
554
588
|
lines.push(` streamMode=${d.streamMode}${d.eagerRelay ? ` (eager relay: ${d.eagerRelay.useEagerRelay ? "on" : "off"}, ${d.eagerRelay.reason})` : ""}`);
|
|
555
589
|
if (d.watchdog) {
|
|
556
590
|
lines.push(` watchdog threshold=${mb(d.watchdog.warnThresholdBytes)}${d.watchdog.lastWarnAt ? `, last warn ${new Date(d.watchdog.lastWarnAt).toISOString()}` : ", no warnings"}`);
|
|
557
591
|
}
|
|
558
|
-
// Interpretation rule
|
|
559
|
-
// doctor and watchdog never disagree about
|
|
560
|
-
//
|
|
592
|
+
// Interpretation rule: reuse the watchdog threshold and the same max-of
|
|
593
|
+
// observed memory counters, so doctor and watchdog never disagree about
|
|
594
|
+
// "high". RSS/working-set can under-report committed retention on Windows, and
|
|
595
|
+
// Bun 1.3.14 heap counters are not standalone leak proof.
|
|
561
596
|
const threshold = d.watchdog?.warnThresholdBytes ?? DEFAULT_MEMORY_THRESHOLD_BYTES;
|
|
562
597
|
const jsShare = d.rss > 0 ? Math.max(d.heapUsed, d.jscHeap?.heapSize ?? 0) / d.rss : 0;
|
|
563
|
-
if (
|
|
598
|
+
if (observedBytes < threshold) {
|
|
564
599
|
lines.push(" memory usage looks normal");
|
|
600
|
+
} else if (observedMetric !== "rss") {
|
|
601
|
+
lines.push(` !! high observed memory via ${observedMetric}; Windows RSS/working-set counters may be blind. See docs: troubleshooting/windows-memory`);
|
|
565
602
|
} else if (jsShare < 0.25) {
|
|
566
603
|
lines.push(" !! high RSS with a small JS heap — native-side growth (Bun runtime buffers/handles). See docs: troubleshooting/windows-memory");
|
|
567
604
|
} else if (jsShare >= 0.5) {
|
|
568
|
-
lines.push(" !! high RSS
|
|
605
|
+
lines.push(" !! high RSS with large JS/JSC counters — possible JS-side retention; compare responseState/external samples before filing an app leak");
|
|
569
606
|
} else {
|
|
570
607
|
lines.push(" !! high RSS, indeterminate split — capture two doctor runs over time to see the trend");
|
|
571
608
|
}
|
package/src/cli/help.ts
CHANGED
|
@@ -12,6 +12,7 @@ type HelpEntry = {
|
|
|
12
12
|
|
|
13
13
|
const helpEntries: Record<string, HelpEntry> = {
|
|
14
14
|
init: { usage: "ocx init", summary: "Interactive setup for providers and Codex config injection." },
|
|
15
|
+
setup: { usage: "ocx setup", summary: "Interactive setup for providers and Codex config injection (alias of init)." },
|
|
15
16
|
start: { usage: "ocx start [--port <port>]", summary: "Start the proxy server and sync models to Codex." },
|
|
16
17
|
stop: { usage: "ocx stop", summary: "Stop the proxy and restore native Codex config." },
|
|
17
18
|
restore: {
|
|
@@ -64,8 +65,8 @@ const helpEntries: Record<string, HelpEntry> = {
|
|
|
64
65
|
status: { usage: "ocx status", summary: "Check proxy server status." },
|
|
65
66
|
doctor: { usage: "ocx doctor", summary: "Diagnose environment/network issues (paths, WSL /mnt, proxy env, ChatGPT reachability)." },
|
|
66
67
|
debug: {
|
|
67
|
-
usage: "ocx debug
|
|
68
|
-
summary: "Show or toggle runtime provider
|
|
68
|
+
usage: "ocx debug <provider|usage|injection|claude> <on|off|status|reset|logs [-f]>",
|
|
69
|
+
summary: "Show or toggle runtime provider, usage, injection, and Claude debug capture.",
|
|
69
70
|
details: [
|
|
70
71
|
"Provider: ocx debug provider on | off | status | reset | logs [-f]",
|
|
71
72
|
"Usage JSONL: ocx debug usage on | off | status | reset | logs [-f]",
|
|
@@ -80,16 +81,16 @@ const helpEntries: Record<string, HelpEntry> = {
|
|
|
80
81
|
summary: "Update opencodex. Preview installs stay on the preview tag unless overridden.",
|
|
81
82
|
},
|
|
82
83
|
provider: {
|
|
83
|
-
usage: "ocx provider <list|add|remove|show|set-default>",
|
|
84
|
+
usage: "ocx provider <list|add|edit|test|remove|show|set-default|selected|quota|presets|account-mode>",
|
|
84
85
|
summary: "Non-interactive provider management.",
|
|
85
86
|
details: [
|
|
86
|
-
"Subcommands: list, add
|
|
87
|
+
"Subcommands: list, add/edit/test/remove/show, set-default, selected, quota, presets, account-mode",
|
|
87
88
|
"Registry providers are auto-configured by name. Custom providers need --adapter and --base-url.",
|
|
88
89
|
"Run `ocx provider --help` for full usage and examples.",
|
|
89
90
|
],
|
|
90
91
|
},
|
|
91
92
|
account: {
|
|
92
|
-
usage: "ocx account <list|current|use|refresh|auto-switch|remove|add-key> ...",
|
|
93
|
+
usage: "ocx account <list|current|use|refresh|auto-switch|login|reauth|code|cancel|remove|add-key|reset-credits> ...",
|
|
93
94
|
summary: "List and switch provider accounts and API-key pools (GUI parity).",
|
|
94
95
|
details: [
|
|
95
96
|
"list [provider] Codex account pool, OAuth accounts and API keys (identifiers shown masked as the API returns them).",
|
|
@@ -99,11 +100,13 @@ const helpEntries: Record<string, HelpEntry> = {
|
|
|
99
100
|
"auto-switch <provider> <on|off|status|threshold N> Control the Codex pool threshold.",
|
|
100
101
|
"remove <provider> <id> --yes Remove a stored account or key after an existence check.",
|
|
101
102
|
"add-key <provider> [--label <label>] Add a key read only from piped stdin.",
|
|
103
|
+
"login/reauth/code/cancel Run browser or manual-code auth from a headless shell.",
|
|
104
|
+
"reset-credits <id|main> [--consume --yes] Inspect or consume Codex reset credits.",
|
|
102
105
|
"Codex pool switches apply to new sessions; running threads keep their account.",
|
|
103
106
|
],
|
|
104
107
|
},
|
|
105
108
|
models: {
|
|
106
|
-
usage: "ocx models
|
|
109
|
+
usage: "ocx models <list|live|add|edit|remove|enable|disable|provider|selected|context|shadow> ...",
|
|
107
110
|
summary: "List models and manage custom (manually registered) models.",
|
|
108
111
|
details: [
|
|
109
112
|
"List available models from static config with no subcommand (liveModels may add more at runtime).",
|
|
@@ -116,6 +119,47 @@ const helpEntries: Record<string, HelpEntry> = {
|
|
|
116
119
|
"Changes apply immediately to a running proxy (catalog sync).",
|
|
117
120
|
],
|
|
118
121
|
},
|
|
122
|
+
model: {
|
|
123
|
+
usage: "ocx model <subcommand>",
|
|
124
|
+
summary: "Alias of ocx models.",
|
|
125
|
+
},
|
|
126
|
+
combo: {
|
|
127
|
+
usage: "ocx combo <list|show|set|remove> ...",
|
|
128
|
+
summary: "Manage combo failover and round-robin virtual models.",
|
|
129
|
+
details: ["Alias hierarchy: ocx route combo ...", "Use --targets provider/model[:weight],provider/model[:weight]."],
|
|
130
|
+
},
|
|
131
|
+
route: {
|
|
132
|
+
usage: "ocx route combo <list|show|set|remove> ...",
|
|
133
|
+
summary: "Manage routing features; combo is currently the supported routing resource.",
|
|
134
|
+
},
|
|
135
|
+
agent: {
|
|
136
|
+
usage: "ocx agent <status|injection|effort|subagents|fallback|sidecar> ...",
|
|
137
|
+
summary: "Manage headless multi-agent, roster, effort, injection, and sidecar settings.",
|
|
138
|
+
},
|
|
139
|
+
observe: {
|
|
140
|
+
usage: "ocx observe <logs|usage|storage|memory|debug|claude-inbound|injection> ...",
|
|
141
|
+
summary: "Inspect proxy requests, usage, storage, memory, and debug data.",
|
|
142
|
+
},
|
|
143
|
+
logs: { usage: "ocx logs [filters] [--follow] [--json|--jsonl]", summary: "Alias of ocx observe logs." },
|
|
144
|
+
usage: { usage: "ocx usage [--range <7d|30d|all>] [--surface <all|codex|claude|grok>] [--json]", summary: "Alias of ocx observe usage." },
|
|
145
|
+
storage: { usage: "ocx storage [--json]", summary: "Alias of ocx observe storage." },
|
|
146
|
+
memory: { usage: "ocx memory [--json]", summary: "Alias of ocx observe memory." },
|
|
147
|
+
access: {
|
|
148
|
+
usage: "ocx access <key|endpoints|models|test> ...",
|
|
149
|
+
summary: "Manage OpenCodex admission API keys and inspect external endpoints.",
|
|
150
|
+
},
|
|
151
|
+
"api-key": { usage: "ocx api-key <list|create|remove> ...", summary: "Alias of ocx access key." },
|
|
152
|
+
grok: { usage: "ocx grok <status|exclude|include|set|clear|apply> ...", summary: "Manage and apply the Grok Build model fence." },
|
|
153
|
+
integration: { usage: "ocx integration <claude|grok> ...", summary: "Manage supported client integrations." },
|
|
154
|
+
system: {
|
|
155
|
+
usage: "ocx system <status|settings|startup|diagnostics|sync|update> ...",
|
|
156
|
+
summary: "Manage headless runtime settings, startup, sync, diagnostics, and updates.",
|
|
157
|
+
},
|
|
158
|
+
config: {
|
|
159
|
+
usage: "ocx config <show|get|set|unset|validate|export|import> ...",
|
|
160
|
+
summary: "Inspect and safely modify validated OpenCodex configuration.",
|
|
161
|
+
details: ["Secrets are masked by show/get. Import requires --yes and validates before writing."],
|
|
162
|
+
},
|
|
119
163
|
claude: {
|
|
120
164
|
usage: "ocx claude [claude args...]",
|
|
121
165
|
summary: "Launch Claude Code wired to the proxy (env injection + gateway model discovery).",
|
|
@@ -136,6 +180,8 @@ const helpEntries: Record<string, HelpEntry> = {
|
|
|
136
180
|
"Families: opus, fable, sonnet, haiku. New routes start in opus.",
|
|
137
181
|
"`none` is valid only when that family is empty.",
|
|
138
182
|
"Legacy apply flags remain supported: --static, --hybrid, --discovery-only.",
|
|
183
|
+
"",
|
|
184
|
+
"Claude Code settings: ocx claude config <status|set> ...",
|
|
139
185
|
],
|
|
140
186
|
},
|
|
141
187
|
restart: {
|
|
@@ -174,7 +220,7 @@ export function printUsage(): void {
|
|
|
174
220
|
console.log(`opencodex (ocx) — Universal provider proxy for Codex
|
|
175
221
|
|
|
176
222
|
Usage:
|
|
177
|
-
ocx
|
|
223
|
+
ocx setup Interactive setup (alias: init)
|
|
178
224
|
ocx start [--port <port>] Start the proxy server (auto-syncs models to Codex)
|
|
179
225
|
ocx stop Stop the proxy AND restore native Codex (plain codex works again)
|
|
180
226
|
ocx restore Restore native Codex without stopping (alias: eject)
|
|
@@ -190,18 +236,24 @@ Usage:
|
|
|
190
236
|
ocx sync-cache Refresh Codex's model cache from the active catalog
|
|
191
237
|
ocx status Check proxy server status
|
|
192
238
|
ocx doctor Diagnose environment/network issues (WSL, proxy, ChatGPT reachability)
|
|
193
|
-
ocx debug
|
|
194
|
-
|
|
195
|
-
ocx login <provider> OAuth login (xai) — opens browser, stores token in ~/.opencodex/auth.json
|
|
239
|
+
ocx debug <scope> provider/usage/injection/claude on|off|status|reset
|
|
240
|
+
ocx login <provider> OAuth or API-key provider login
|
|
196
241
|
ocx logout <provider> Remove a stored OAuth login
|
|
197
242
|
ocx gui Open the opencodex dashboard
|
|
198
243
|
ocx update [--tag <tag>] Update opencodex (keeps preview installs on @preview)
|
|
199
244
|
ocx restart Stop and restart the proxy
|
|
200
245
|
ocx v2 <sub> multi_agent_v2 surface (status|on|off|mode|threads)
|
|
201
246
|
ocx health [--json] Check proxy health (exit 0=healthy, 1=not)
|
|
202
|
-
ocx provider <sub>
|
|
203
|
-
ocx account <sub> Accounts/
|
|
204
|
-
ocx models <sub>
|
|
247
|
+
ocx provider <sub> Providers, connectivity, quota, and selected models
|
|
248
|
+
ocx account <sub> Accounts, login/reauth, key pools, and quota controls
|
|
249
|
+
ocx models <sub> Live/custom models, visibility, context, and shadow calls
|
|
250
|
+
ocx combo <sub> Combo failover/round-robin routing
|
|
251
|
+
ocx agent <sub> Subagents, injection, effort caps, and sidecars
|
|
252
|
+
ocx observe <sub> Logs, usage, storage, memory, and debug data
|
|
253
|
+
ocx access <sub> External API keys and endpoint information
|
|
254
|
+
ocx grok <sub> Grok Build model selection and apply
|
|
255
|
+
ocx system <sub> Runtime settings, startup, sync, and updates
|
|
256
|
+
ocx config <sub> Validated configuration show/get/set/import/export
|
|
205
257
|
ocx claude [args...] Launch Claude Code wired to the proxy (model discovery on)
|
|
206
258
|
ocx claude desktop [sub] Manage and apply Claude Desktop's four-family profile
|
|
207
259
|
ocx help [command] Show help
|
package/src/cli/index.ts
CHANGED
|
@@ -94,6 +94,21 @@ async function waitForProxy(timeoutMs = 8_000): Promise<LiveProxy | null> {
|
|
|
94
94
|
return null;
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
+
/**
|
|
98
|
+
* A Grok fence sync that throws is best-effort by design — it must never block startup.
|
|
99
|
+
* Reporting nothing, however, is what lets a STALE fence survive: `~/.grok/config.toml`
|
|
100
|
+
* keeps naming whatever port the last successful sync wrote, and once that listener is
|
|
101
|
+
* gone every grok turn retries against a refused connection while our own log stays
|
|
102
|
+
* silent (2026-07-27 field report: 8 entries pinned to a dead 127.0.0.1:4179).
|
|
103
|
+
* So say what failed and name the single command that repairs it.
|
|
104
|
+
*/
|
|
105
|
+
function grokSyncFailureMessage(err: unknown): string {
|
|
106
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
107
|
+
return `Grok Build config sync failed: ${detail}. `
|
|
108
|
+
+ "~/.grok/config.toml may still point at a previous proxy port — "
|
|
109
|
+
+ "run 'ocx ensure' (or apply from the dashboard's Grok page) to repoint it.";
|
|
110
|
+
}
|
|
111
|
+
|
|
97
112
|
/** Argv for detached `start`, optionally hard-pinning the listen port. */
|
|
98
113
|
function startArgv(port?: number): string[] {
|
|
99
114
|
const args = [process.argv[1], "start"];
|
|
@@ -273,7 +288,7 @@ async function handleStart(options: { block?: boolean } = {}) {
|
|
|
273
288
|
// Auto-install .zshrc hook (idempotent — skips if already present).
|
|
274
289
|
installShellHook();
|
|
275
290
|
|
|
276
|
-
await maybeShowStarPrompt(); // once-only [
|
|
291
|
+
await maybeShowStarPrompt(); // once-only [y/N] GitHub-star prompt on first interactive start
|
|
277
292
|
await syncModelsToCodex(port).catch(() => {});
|
|
278
293
|
if (!currentExternalCodexModelProvider() && !shouldInjectApiAuthHeader(config) && config.syncResumeHistory !== false) {
|
|
279
294
|
historyGuardian = startHistoryMigrationGuardian();
|
|
@@ -299,7 +314,14 @@ async function handleStart(options: { block?: boolean } = {}) {
|
|
|
299
314
|
const r = await syncGrokConfig(port, config, config.hostname ? { hostname: config.hostname } : {});
|
|
300
315
|
if (r.changed) console.log(" + Grok Build config updated (~/.grok/config.toml)");
|
|
301
316
|
else if (!r.ok) console.error(`⚠️ ${r.message}`);
|
|
302
|
-
} catch {
|
|
317
|
+
} catch (err) {
|
|
318
|
+
// Best-effort: grok integration must never block startup. But swallowing the error
|
|
319
|
+
// silently is how a stale fence survives unnoticed — ~/.grok/config.toml keeps
|
|
320
|
+
// pointing at whatever port the LAST successful sync wrote, and if that listener is
|
|
321
|
+
// gone every grok turn retries against a refused connection with nothing in our log
|
|
322
|
+
// to explain it. Name the failure and the one command that repairs it.
|
|
323
|
+
console.error(`⚠️ ${grokSyncFailureMessage(err)}`);
|
|
324
|
+
}
|
|
303
325
|
if (options.block ?? true) {
|
|
304
326
|
setInterval(() => {}, 60_000);
|
|
305
327
|
await new Promise<void>(() => {});
|
|
@@ -327,7 +349,7 @@ async function handleEnsure() {
|
|
|
327
349
|
const g = await syncGrokConfig(live.port, config, live.hostname ? { hostname: live.hostname } : {});
|
|
328
350
|
if (g.changed) console.log(" + Grok Build config updated (~/.grok/config.toml)");
|
|
329
351
|
else if (!g.ok) console.error(`⚠️ ${g.message}`);
|
|
330
|
-
} catch {
|
|
352
|
+
} catch (err) { console.error(`⚠️ ${grokSyncFailureMessage(err)}`); }
|
|
331
353
|
console.log(`✅ Proxy running on port ${live.port}`);
|
|
332
354
|
return;
|
|
333
355
|
}
|
|
@@ -354,7 +376,7 @@ async function handleEnsure() {
|
|
|
354
376
|
const g = await syncGrokConfig(port, config, config.hostname ? { hostname: config.hostname } : {});
|
|
355
377
|
if (g.changed) console.log(" + Grok Build config updated (~/.grok/config.toml)");
|
|
356
378
|
else if (!g.ok) console.error(`⚠️ ${g.message}`);
|
|
357
|
-
} catch {
|
|
379
|
+
} catch (err) { console.error(`⚠️ ${grokSyncFailureMessage(err)}`); }
|
|
358
380
|
// Always sync the LIVE port: after a fallback-port start, config.port still names the
|
|
359
381
|
// busy preferred port — syncing that would point Codex at a dead listener.
|
|
360
382
|
await syncModelsToCodex(port).catch(e => {
|
|
@@ -666,7 +688,8 @@ function handleRecoverHistory() {
|
|
|
666
688
|
}
|
|
667
689
|
|
|
668
690
|
switch (command) {
|
|
669
|
-
case "init":
|
|
691
|
+
case "init":
|
|
692
|
+
case "setup": {
|
|
670
693
|
const { runInit } = await import("./init");
|
|
671
694
|
await runInit();
|
|
672
695
|
break;
|
|
@@ -889,9 +912,82 @@ switch (command) {
|
|
|
889
912
|
process.exitCode = await cmdAccount(args.slice(1));
|
|
890
913
|
break;
|
|
891
914
|
}
|
|
892
|
-
case "models":
|
|
915
|
+
case "models":
|
|
916
|
+
case "model": {
|
|
893
917
|
const { handleModels } = await import("./models");
|
|
894
|
-
handleModels(args.slice(1));
|
|
918
|
+
await handleModels(args.slice(1));
|
|
919
|
+
break;
|
|
920
|
+
}
|
|
921
|
+
case "combo": {
|
|
922
|
+
const { handleComboCommand } = await import("./combo");
|
|
923
|
+
process.exitCode = await handleComboCommand(args.slice(1));
|
|
924
|
+
break;
|
|
925
|
+
}
|
|
926
|
+
case "route": {
|
|
927
|
+
if (args[1] !== "combo") {
|
|
928
|
+
console.error("Usage: ocx route combo <subcommand>");
|
|
929
|
+
process.exitCode = 2;
|
|
930
|
+
break;
|
|
931
|
+
}
|
|
932
|
+
const { handleComboCommand } = await import("./combo");
|
|
933
|
+
process.exitCode = await handleComboCommand(args.slice(2));
|
|
934
|
+
break;
|
|
935
|
+
}
|
|
936
|
+
case "agent": {
|
|
937
|
+
const { handleAgentCommand } = await import("./agent");
|
|
938
|
+
process.exitCode = await handleAgentCommand(args.slice(1));
|
|
939
|
+
break;
|
|
940
|
+
}
|
|
941
|
+
case "observe": {
|
|
942
|
+
const { handleObserveCommand } = await import("./observe");
|
|
943
|
+
process.exitCode = await handleObserveCommand(args.slice(1));
|
|
944
|
+
break;
|
|
945
|
+
}
|
|
946
|
+
case "logs":
|
|
947
|
+
case "usage":
|
|
948
|
+
case "storage":
|
|
949
|
+
case "memory": {
|
|
950
|
+
const { handleObserveCommand } = await import("./observe");
|
|
951
|
+
process.exitCode = await handleObserveCommand([command, ...args.slice(1)]);
|
|
952
|
+
break;
|
|
953
|
+
}
|
|
954
|
+
case "access": {
|
|
955
|
+
const { handleAccessCommand } = await import("./access");
|
|
956
|
+
process.exitCode = await handleAccessCommand(args.slice(1));
|
|
957
|
+
break;
|
|
958
|
+
}
|
|
959
|
+
case "api-key": {
|
|
960
|
+
const { handleAccessCommand } = await import("./access");
|
|
961
|
+
process.exitCode = await handleAccessCommand(["key", ...args.slice(1)]);
|
|
962
|
+
break;
|
|
963
|
+
}
|
|
964
|
+
case "grok": {
|
|
965
|
+
const { handleGrokCommand } = await import("./integrations");
|
|
966
|
+
process.exitCode = await handleGrokCommand(args.slice(1));
|
|
967
|
+
break;
|
|
968
|
+
}
|
|
969
|
+
case "integration": {
|
|
970
|
+
const integration = args[1];
|
|
971
|
+
if (integration === "grok") {
|
|
972
|
+
const { handleGrokCommand } = await import("./integrations");
|
|
973
|
+
process.exitCode = await handleGrokCommand(args.slice(2));
|
|
974
|
+
} else if (integration === "claude") {
|
|
975
|
+
const { handleClaudeConfigCommand } = await import("./integrations");
|
|
976
|
+
process.exitCode = await handleClaudeConfigCommand(args.slice(2));
|
|
977
|
+
} else {
|
|
978
|
+
console.error("Usage: ocx integration <claude|grok> <subcommand>");
|
|
979
|
+
process.exitCode = 2;
|
|
980
|
+
}
|
|
981
|
+
break;
|
|
982
|
+
}
|
|
983
|
+
case "system": {
|
|
984
|
+
const { handleSystemCommand } = await import("./system-command");
|
|
985
|
+
process.exitCode = await handleSystemCommand(args.slice(1));
|
|
986
|
+
break;
|
|
987
|
+
}
|
|
988
|
+
case "config": {
|
|
989
|
+
const { handleConfigCommand } = await import("./config-command");
|
|
990
|
+
process.exitCode = await handleConfigCommand(args.slice(1));
|
|
895
991
|
break;
|
|
896
992
|
}
|
|
897
993
|
case "claude": {
|
|
@@ -903,6 +999,11 @@ switch (command) {
|
|
|
903
999
|
if (exitCode !== 0) process.exit(exitCode);
|
|
904
1000
|
break;
|
|
905
1001
|
}
|
|
1002
|
+
if (args[1] === "config") {
|
|
1003
|
+
const { handleClaudeConfigCommand } = await import("./integrations");
|
|
1004
|
+
process.exitCode = await handleClaudeConfigCommand(args.slice(2));
|
|
1005
|
+
break;
|
|
1006
|
+
}
|
|
906
1007
|
process.exit(await cmdClaude(args.slice(1)));
|
|
907
1008
|
}
|
|
908
1009
|
case "help":
|