@iislee/opencodex 2.31.0 → 2.35.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 +3 -3
- package/bin/ocx.mjs +99 -70
- package/gui/dist/assets/index-Be1sDpWw.css +1 -0
- package/gui/dist/assets/index-D4OKgUV4.js +114 -0
- package/gui/dist/index.html +2 -2
- package/package.json +3 -1
- package/src/adapters/anthropic.ts +104 -15
- package/src/adapters/base.ts +29 -1
- package/src/adapters/command-code.ts +42 -6
- package/src/adapters/cursor/call-id.ts +44 -0
- package/src/adapters/cursor/catalog.ts +541 -0
- package/src/adapters/cursor/checkpoint-store.ts +15 -10
- package/src/adapters/cursor/cursor-errors.ts +15 -0
- package/src/adapters/cursor/discovery.ts +69 -18
- package/src/adapters/cursor/effort-map.ts +79 -1
- package/src/adapters/cursor/envelope-echo.ts +290 -0
- package/src/adapters/cursor/live-models.ts +7 -2
- package/src/adapters/cursor/live-transport.ts +14 -0
- package/src/adapters/cursor/message-mapper.ts +4 -1
- package/src/adapters/cursor/native-exec-desktop.ts +23 -0
- package/src/adapters/cursor/native-exec-fs.ts +2 -2
- package/src/adapters/cursor/native-exec-network.ts +1 -1
- package/src/adapters/cursor/native-exec-shell.ts +5 -2
- package/src/adapters/cursor/native-exec.ts +20 -0
- package/src/adapters/cursor/protobuf-request.ts +68 -14
- package/src/adapters/cursor/request-builder.ts +40 -10
- package/src/adapters/cursor/tool-definitions.ts +14 -4
- package/src/adapters/cursor/tool-result-normalize.ts +28 -5
- package/src/adapters/cursor/types.ts +20 -0
- package/src/adapters/cursor.ts +209 -38
- package/src/adapters/exec-tool-result-normalize.ts +99 -0
- package/src/adapters/google-antigravity-replay.ts +173 -25
- package/src/adapters/google-antigravity-wire.ts +5 -0
- package/src/adapters/google-errors.ts +44 -12
- package/src/adapters/google.ts +136 -24
- package/src/adapters/image.ts +1 -1
- package/src/adapters/kiro-constants.ts +27 -0
- package/src/adapters/kiro-tools.ts +55 -16
- package/src/adapters/kiro.ts +182 -23
- package/src/adapters/openai-chat.ts +300 -242
- package/src/adapters/openai-responses.ts +353 -27
- package/src/adapters/run-turn-queue.ts +36 -1
- package/src/adapters/tool-catalog-nudge.ts +4 -3
- package/src/adapters/xai-tool-schema.ts +436 -0
- package/src/adapters/xai-web-search.ts +17 -16
- package/src/bridge.ts +90 -37
- package/src/chat/inbound.ts +14 -1
- package/src/chat/outbound.ts +15 -7
- package/src/claude/agents-inject.ts +8 -1
- package/src/claude/context-windows.ts +16 -9
- package/src/claude/outbound.ts +24 -11
- package/src/cli/access.ts +46 -3
- package/src/cli/account-api.ts +116 -19
- package/src/cli/account-extended.ts +262 -29
- package/src/cli/account-main.ts +12 -12
- package/src/cli/account.ts +69 -15
- package/src/cli/agent.ts +8 -1
- package/src/cli/alias.ts +66 -0
- package/src/cli/capabilities-command.ts +94 -0
- package/src/cli/capabilities.ts +496 -0
- package/src/cli/claude-desktop.ts +31 -11
- package/src/cli/claude.ts +26 -1
- package/src/cli/dispatch.ts +256 -28
- package/src/cli/doctor.ts +126 -14
- package/src/cli/help.ts +17 -6
- package/src/cli/index.ts +39 -9
- package/src/cli/init.ts +1 -0
- package/src/cli/inspect.ts +230 -0
- package/src/cli/models-runtime.ts +95 -0
- package/src/cli/models.ts +26 -10
- package/src/cli/observe.ts +31 -8
- package/src/cli/provider-runtime.ts +16 -2
- package/src/cli/provider.ts +2 -1
- package/src/cli/registry.ts +49 -10
- package/src/cli/runtime-api.ts +51 -7
- package/src/cli/status.ts +41 -1
- package/src/cli/storage.ts +234 -0
- package/src/cli/system-command.ts +16 -0
- package/src/cli/system-restart-client.ts +1 -1
- package/src/cli/usage-report.ts +184 -0
- package/src/cli/v2.ts +34 -10
- package/src/cli/version-skew.ts +46 -0
- package/src/clients/config-export.ts +85 -3
- package/src/clients/effective-status.ts +30 -2
- package/src/clients/probes/cline.ts +81 -0
- package/src/codex/account-label.ts +21 -0
- package/src/codex/account-pause.ts +2 -1
- package/src/codex/account-priority.ts +3 -2
- package/src/codex/app-server-processes.ts +83 -7
- package/src/codex/auth-api.ts +50 -10
- package/src/codex/auth-context.ts +21 -18
- package/src/codex/autostart-health.ts +16 -0
- package/src/codex/catalog/aggregation.ts +18 -0
- package/src/codex/catalog/effort.ts +18 -3
- package/src/codex/catalog/metadata.ts +27 -1
- package/src/codex/catalog/native-models.ts +5 -2
- package/src/codex/catalog/parsing.ts +72 -27
- package/src/codex/catalog/provider-fetch.ts +187 -31
- package/src/codex/catalog/sync.ts +131 -5
- package/src/codex/catalog.ts +1 -1
- package/src/codex/codex-write-lock.ts +3 -1
- package/src/codex/convergence-types.ts +1 -1
- package/src/codex/convergence.ts +27 -2
- package/src/codex/desired-state.ts +2 -2
- package/src/codex/desktop-app-restart.ts +355 -0
- package/src/codex/history-job.ts +32 -3
- package/src/codex/history-manifest.ts +112 -0
- package/src/codex/history-migration-guardian.ts +5 -5
- package/src/codex/history-provider.ts +825 -247
- package/src/codex/history-worker.ts +8 -5
- package/src/codex/inject-coordination.ts +83 -0
- package/src/codex/inject.ts +63 -22
- package/src/codex/injected-marker.ts +1 -1
- package/src/codex/internal/history-writer.ts +4 -3
- package/src/codex/log-guard/inspect.ts +22 -4
- package/src/codex/model-entitlements.ts +9 -2
- package/src/codex/native-profile-startup.ts +157 -27
- package/src/codex/native-residue.ts +26 -33
- package/src/codex/prompt-layers.ts +371 -25
- package/src/codex/prompt-text-probe.ts +238 -0
- package/src/codex/quota.ts +91 -10
- package/src/codex/routing.ts +9 -0
- package/src/codex/shim.ts +56 -3
- package/src/codex/subagent-model-fallback.ts +104 -25
- package/src/codex/transition-state.ts +116 -8
- package/src/combos/failover.ts +27 -0
- package/src/combos/types.ts +10 -0
- package/src/compatibility/index.ts +26 -0
- package/src/compatibility/manifest.ts +253 -0
- package/src/compatibility/openai-responses.ts +121 -0
- package/src/config/atomic-write.ts +219 -0
- package/src/config/paths.ts +40 -0
- package/src/config/process-state.ts +309 -0
- package/src/config/provider-validation.ts +177 -0
- package/src/config/rebase-provenance.ts +68 -0
- package/src/config.ts +206 -824
- package/src/generated/compatibility-version.json +373 -213
- package/src/images/loop.ts +5 -4
- package/src/images/plan.ts +5 -4
- package/src/integrations/ownership-policy.ts +160 -0
- package/src/integrations/ownership.ts +46 -2
- package/src/integrations/registry.ts +7 -0
- package/src/integrations/state.ts +79 -7
- package/src/integrations/writer.ts +26 -2
- package/src/lab/conformance/fixtures/protocol-v1-cases.json +1 -1
- package/src/lab/fabric/producer-child.ts +1 -1
- package/src/lib/admin-secrets.ts +24 -0
- package/src/lib/bounded-body.ts +14 -2
- package/src/lib/config-ownership.ts +20 -0
- package/src/lib/errors.ts +36 -3
- package/src/lib/package-tree-integrity.ts +101 -0
- package/src/lib/process-control.ts +2 -1
- package/src/lib/service-secrets.ts +15 -0
- package/src/lib/tool-argument-integers.ts +56 -5
- package/src/oauth/callback-server.ts +22 -2
- package/src/oauth/cline.ts +203 -0
- package/src/oauth/generic-account-failover.ts +231 -0
- package/src/oauth/health.ts +1 -1
- package/src/oauth/index.ts +65 -6
- package/src/oauth/key-providers.ts +10 -1
- package/src/oauth/kimi.ts +9 -1
- package/src/oauth/kiro.ts +45 -0
- package/src/oauth/local-token-detect.ts +2 -0
- package/src/oauth/open-browser-choice.ts +26 -0
- package/src/oauth/store.ts +14 -5
- package/src/oauth/types.ts +15 -0
- package/src/providers/auto-compact-budget.ts +65 -0
- package/src/providers/command-code-efforts.ts +38 -6
- package/src/providers/context-cap.ts +4 -3
- package/src/providers/default-aliases.ts +65 -0
- package/src/providers/derive.ts +28 -0
- package/src/providers/fastwire.ts +7 -1
- package/src/providers/label.ts +34 -1
- package/src/providers/model-presets.ts +119 -0
- package/src/providers/new-model-policy.ts +146 -0
- package/src/providers/provider-id-rewrite.ts +2 -1
- package/src/providers/quota.ts +65 -7
- package/src/providers/registry.ts +184 -69
- package/src/providers/slug-codec.ts +52 -0
- package/src/providers/xai-transport.ts +21 -0
- package/src/reasoning-effort.ts +19 -2
- package/src/responses/apply-patch-envelope.ts +63 -0
- package/src/responses/code-mode-helper-compat.ts +50 -0
- package/src/responses/custom-tool-compat.ts +157 -39
- package/src/responses/namespace-tool-compat.ts +84 -4
- package/src/responses/parser.ts +7 -2
- package/src/responses/reasoning-replay-cache.ts +81 -3
- package/src/responses/schema.ts +5 -1
- package/src/responses/state.ts +94 -4
- package/src/responses/thought-signature-replay.ts +17 -0
- package/src/responses/turn-termination.ts +107 -0
- package/src/router.ts +40 -2
- package/src/routing/analytics.ts +1 -0
- package/src/routing/quota.ts +10 -0
- package/src/server/auth-cors.ts +27 -7
- package/src/server/chat-completions.ts +24 -16
- package/src/server/chat-native-sse.ts +3 -3
- package/src/server/chat-native.ts +30 -11
- package/src/server/claude-messages.ts +1 -1
- package/src/server/index.ts +179 -32
- package/src/server/lifecycle.ts +52 -1
- package/src/server/local-management-read-client.ts +1 -1
- package/src/server/local-provider-reload-client.ts +1 -1
- package/src/server/management/agent-settings-routes.ts +45 -23
- package/src/server/management/codex-prompt-routes.ts +570 -0
- package/src/server/management/combo-routes.ts +2 -1
- package/src/server/management/config-routes.ts +59 -12
- package/src/server/management/context.ts +10 -1
- package/src/server/management/logs-usage-routes.ts +30 -24
- package/src/server/management/model-routes.ts +274 -4
- package/src/server/management/model-rows.ts +4 -0
- package/src/server/management/native-integration-routes.ts +2 -1
- package/src/server/management/oauth-account-routes.ts +10 -4
- package/src/server/management/provider-capability-config.ts +1 -1
- package/src/server/management/provider-routes.ts +81 -11
- package/src/server/management/route-registry.ts +332 -0
- package/src/server/management/routing-profile-routes.ts +2 -2
- package/src/server/management/system-restart.ts +1 -1
- package/src/server/management-api.ts +18 -16
- package/src/server/port-reclaim.ts +1 -1
- package/src/server/proxy-liveness.ts +29 -5
- package/src/server/relay-eager.ts +147 -20
- package/src/server/relay.ts +251 -19
- package/src/server/request-log-conversation.ts +51 -0
- package/src/server/request-log.ts +70 -16
- package/src/server/responses/codex-auth-error.ts +55 -0
- package/src/server/responses/collaboration.ts +37 -3
- package/src/server/responses/combo-stream-preflight.ts +178 -0
- package/src/server/responses/compact.ts +6 -21
- package/src/server/responses/core.ts +810 -208
- package/src/server/responses/empty-completion-guard.ts +63 -6
- package/src/server/responses/fetch-helpers.ts +17 -102
- package/src/server/responses/input-admission.ts +3 -1
- package/src/server/responses/passthrough-error.ts +33 -9
- package/src/server/responses/responses-field-backfill.ts +105 -13
- package/src/server/responses/ws-upstream.ts +75 -2
- package/src/server/responses-custom-tool-repair.ts +87 -6
- package/src/server/responses-terminal-repair.ts +25 -4
- package/src/server/responses-undeclared-tool-guard.ts +241 -18
- package/src/server/sse-frame-buffer.ts +31 -4
- package/src/server/ws-bridge.ts +14 -2
- package/src/service.ts +42 -4
- package/src/storage/policy-job.ts +14 -4
- package/src/storage/policy.ts +88 -23
- package/src/types/config.ts +66 -0
- package/src/types/provider.ts +69 -0
- package/src/types/request.ts +17 -4
- package/src/types/tools.ts +116 -11
- package/src/types.ts +3 -1
- package/src/update/index.ts +5 -4
- package/src/update/job.ts +3 -1
- package/src/update/transactional-install.mjs +8 -1
- package/src/usage/expected-prices.ts +43 -24
- package/src/usage/log.ts +63 -12
- package/src/usage/summary.ts +211 -8
- package/src/vision/describe.ts +18 -13
- package/src/web-search/exa-executor.ts +40 -9
- package/src/web-search/executor.ts +10 -3
- package/src/web-search/loop.ts +5 -4
- package/gui/dist/assets/index-BF38heuV.js +0 -104
- package/gui/dist/assets/index-DMiI18Kv.css +0 -1
package/src/cli/doctor.ts
CHANGED
|
@@ -10,11 +10,14 @@
|
|
|
10
10
|
import { accessSync, constants, existsSync, readFileSync } from "node:fs";
|
|
11
11
|
import { homedir } from "node:os";
|
|
12
12
|
import { dirname, join } from "node:path";
|
|
13
|
-
import { getConfigDir, getConfigPath, readConfigDiagnostics,
|
|
13
|
+
import { getConfigDir, getConfigPath, readConfigDiagnostics, resolveEnvValue } from "../config";
|
|
14
|
+
import { readPid } from "../config/process-state";
|
|
14
15
|
import { findLiveProxy, type LiveProxy } from "../server/proxy-liveness";
|
|
15
16
|
import { BUN_RUNTIME_SOURCES } from "../lib/bun-runtime";
|
|
16
17
|
import type { BunRuntimeSource } from "../lib/bun-runtime";
|
|
17
18
|
import { maskAccountId } from "../lib/privacy";
|
|
19
|
+
import { tokenCollidesWithAdmin } from "../lib/admin-secrets";
|
|
20
|
+
import { readInstalledServiceToken } from "../lib/service-secrets";
|
|
18
21
|
import { PROXY_ENV_KEYS, proxyEnvPresent } from "../lib/proxy-env";
|
|
19
22
|
import { LOCAL_MANAGEMENT_READ_PATHS } from "../lib/local-management-capability";
|
|
20
23
|
import { readCodexTokens } from "../codex/auth-collision";
|
|
@@ -41,7 +44,7 @@ import {
|
|
|
41
44
|
resolveEffectiveUserIdentity,
|
|
42
45
|
} from "../codex/user-identity";
|
|
43
46
|
import { collectProjectCodexConfigWarnings, formatProjectCodexConfigWarningsForDoctor } from "../codex/project-config-warnings";
|
|
44
|
-
import { collectStartupHealth, startupHealthSummary } from "../codex/autostart-health";
|
|
47
|
+
import { collectStartupHealth, formatStartupRoutingDetail, startupHealthSummary } from "../codex/autostart-health";
|
|
45
48
|
import {
|
|
46
49
|
displayCodexRuntimePath,
|
|
47
50
|
loadLastEffortClamp,
|
|
@@ -57,7 +60,35 @@ import {
|
|
|
57
60
|
} from "../server/local-management-read-client";
|
|
58
61
|
export { resolveCodexHomeDir } from "../codex/home";
|
|
59
62
|
|
|
60
|
-
|
|
63
|
+
/**
|
|
64
|
+
* `FAIL` exists for a condition that makes the surface unusable rather than degraded.
|
|
65
|
+
* A review of the #2696 work pointed out that reporting a fully fenced management plane
|
|
66
|
+
* — every `/api/*` returning 503 — at the same level as a directory-permission note
|
|
67
|
+
* misleads the reader about severity.
|
|
68
|
+
*
|
|
69
|
+
* Doctor's own exit code still belongs to the uniform contract in wp3b (devlog 025);
|
|
70
|
+
* this type only fixes what the operator is told.
|
|
71
|
+
*/
|
|
72
|
+
export type OAuthDoctorCheck = { level: "OK" | "WARN" | "FAIL"; message: string };
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Whether any FAIL-level condition was seen during this `runDoctor` pass.
|
|
76
|
+
*
|
|
77
|
+
* Module-scoped and reset at the top of `runDoctor` rather than threaded through, because
|
|
78
|
+
* `runDoctor` reports by direct `console.log` across a dozen sections and has no checks
|
|
79
|
+
* collection to inspect. Reset matters for the test suite, which calls `runDoctor` several
|
|
80
|
+
* times in one process; a sticky flag would make the second call fail because the first did.
|
|
81
|
+
*/
|
|
82
|
+
let doctorSawFailure = false;
|
|
83
|
+
|
|
84
|
+
function recordDoctorFailure(): void {
|
|
85
|
+
doctorSawFailure = true;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/** True when the last `runDoctor` pass saw a FAIL-level condition. */
|
|
89
|
+
export function doctorFailed(): boolean {
|
|
90
|
+
return doctorSawFailure;
|
|
91
|
+
}
|
|
61
92
|
|
|
62
93
|
function pathIsWritable(path: string): boolean {
|
|
63
94
|
try {
|
|
@@ -136,6 +167,50 @@ function describeDoctorHealth(entry: OAuthHealthEntry): string {
|
|
|
136
167
|
}
|
|
137
168
|
}
|
|
138
169
|
|
|
170
|
+
/**
|
|
171
|
+
* Detect the management/data-plane credential collision behind #2696.
|
|
172
|
+
*
|
|
173
|
+
* The service exports the service token file as `OPENCODEX_API_AUTH_TOKEN` before
|
|
174
|
+
* starting the proxy. When that value is the admin token, the server treats the
|
|
175
|
+
* management credential as a data-plane admission secret and fences the ENTIRE
|
|
176
|
+
* management plane closed at boot: every `/api/*` returns 503, including on a loopback
|
|
177
|
+
* install that never needed a data-plane secret.
|
|
178
|
+
*
|
|
179
|
+
* `assertNotAdminToken` in src/service.ts now refuses to create this state, but an
|
|
180
|
+
* install made before that guard existed is already broken on disk, and the symptom
|
|
181
|
+
* (every management command failing) points nowhere. This is the check that names it.
|
|
182
|
+
*
|
|
183
|
+
* Observe-only, like the rest of doctor: it compares shapes and never prints, logs, or
|
|
184
|
+
* returns a credential value.
|
|
185
|
+
*/
|
|
186
|
+
export function dataPlaneCredentialCollisionCheck(
|
|
187
|
+
env: NodeJS.ProcessEnv = process.env,
|
|
188
|
+
installedServiceToken: string | null = readInstalledServiceToken(),
|
|
189
|
+
): OAuthDoctorCheck {
|
|
190
|
+
const dataPlane = env.OPENCODEX_API_AUTH_TOKEN?.trim() || installedServiceToken?.trim() || "";
|
|
191
|
+
if (!dataPlane) {
|
|
192
|
+
return { level: "OK", message: "No data-plane token is set, so it cannot collide with the management token." };
|
|
193
|
+
}
|
|
194
|
+
// Same comparison as assertNotAdminToken: minted prefix or configuredAdminToken
|
|
195
|
+
// (env or admin-api-token file). The file token is the one the service wrapper
|
|
196
|
+
// actually exports; inspecting only the doctor process env reported OK on every
|
|
197
|
+
// already-broken install (#2696).
|
|
198
|
+
if (!tokenCollidesWithAdmin(dataPlane, env)) {
|
|
199
|
+
return { level: "OK", message: "Data-plane and management credentials are distinct." };
|
|
200
|
+
}
|
|
201
|
+
return {
|
|
202
|
+
// Not a degradation: while this holds, every /api/* returns 503 and no ocx
|
|
203
|
+
// management command can work at all.
|
|
204
|
+
level: "FAIL",
|
|
205
|
+
message:
|
|
206
|
+
"The data-plane secret (OPENCODEX_API_AUTH_TOKEN or the service token file) holds the "
|
|
207
|
+
+ "management (admin) token, so the proxy fences the whole management API closed and "
|
|
208
|
+
+ "every ocx management command fails with 503. "
|
|
209
|
+
+ "Action: unset OPENCODEX_API_AUTH_TOKEN, replace the service token file with a distinct "
|
|
210
|
+
+ "data-plane key, then re-run `ocx service install` and restart the proxy",
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
|
|
139
214
|
/**
|
|
140
215
|
* OAuth reliability checks for `ocx doctor`. Observe-only: never mutates
|
|
141
216
|
* credentials, locks, or networking. Every WARN includes a recovery Action.
|
|
@@ -146,6 +221,8 @@ export async function collectOAuthDoctorChecks(
|
|
|
146
221
|
): Promise<OAuthDoctorCheck[]> {
|
|
147
222
|
const checks: OAuthDoctorCheck[] = [];
|
|
148
223
|
|
|
224
|
+
checks.push(dataPlaneCredentialCollisionCheck());
|
|
225
|
+
|
|
149
226
|
if (isOAuthCredentialStorageWritable()) {
|
|
150
227
|
checks.push({ level: "OK", message: "OAuth credential storage directory is writable for atomic auth.json updates." });
|
|
151
228
|
} else {
|
|
@@ -934,6 +1011,9 @@ export async function runDoctor(args: string[] = []): Promise<void> {
|
|
|
934
1011
|
}
|
|
935
1012
|
|
|
936
1013
|
console.log("opencodex doctor\n");
|
|
1014
|
+
// Reset per pass: the suite drives runDoctor several times in one process, and a sticky
|
|
1015
|
+
// flag would fail the second call because the first saw a problem.
|
|
1016
|
+
doctorSawFailure = false;
|
|
937
1017
|
|
|
938
1018
|
// Ordering note: the memory/runtime section renders after "Running proxy
|
|
939
1019
|
// process proxy env" below; helpers live above runDoctor for testability.
|
|
@@ -982,7 +1062,7 @@ export async function runDoctor(args: string[] = []): Promise<void> {
|
|
|
982
1062
|
const startup = collectStartupHealth(doctorConfig);
|
|
983
1063
|
console.log("\nCodex restart safety");
|
|
984
1064
|
console.log(` ${startup.rebootSafe ? "ok " : "!! "} ${startupHealthSummary(startup)}`);
|
|
985
|
-
console.log(`
|
|
1065
|
+
console.log(` ${formatStartupRoutingDetail(startup)}`);
|
|
986
1066
|
|
|
987
1067
|
console.log("\nCodex runtime selection");
|
|
988
1068
|
{
|
|
@@ -1019,6 +1099,20 @@ export async function runDoctor(args: string[] = []): Promise<void> {
|
|
|
1019
1099
|
configFn: () => ({ port: doctorConfig.port, hostname: doctorConfig.hostname }),
|
|
1020
1100
|
});
|
|
1021
1101
|
|
|
1102
|
+
// Mirrors `ocx status` through the same comparison rather than a second implementation:
|
|
1103
|
+
// two diagnostics disagreeing about whether an install is stale is worse than one (#2701).
|
|
1104
|
+
// No extra probe -- findLiveProxy already carried the version back.
|
|
1105
|
+
{
|
|
1106
|
+
const { packageVersion } = await import("./help");
|
|
1107
|
+
const { computeVersionSkew } = await import("./version-skew");
|
|
1108
|
+
const skew = computeVersionSkew(packageVersion(), live?.version);
|
|
1109
|
+
if (skew.skewed && skew.warning) {
|
|
1110
|
+
console.log(`!! ${skew.warning}`);
|
|
1111
|
+
} else if (skew.proxyVersion !== null) {
|
|
1112
|
+
console.log(`ok ocx ${skew.cliVersion} matches the running proxy`);
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
|
|
1022
1116
|
const currentProxyEnv = collectProxyEnv();
|
|
1023
1117
|
const configuredProxy = collectConfiguredProxy();
|
|
1024
1118
|
const runningProxyEnv = collectRunningProxyEnv({
|
|
@@ -1072,10 +1166,10 @@ export async function runDoctor(args: string[] = []): Promise<void> {
|
|
|
1072
1166
|
console.log(` ${probe.ok ? "ok " : "-- "} ${WHAM_USAGE_URL}`);
|
|
1073
1167
|
console.log(` ${detail}, ${probe.durationMs}ms, ${probe.authenticated ? "authenticated" : "unauthenticated"}`);
|
|
1074
1168
|
|
|
1075
|
-
// Design B upgrade visibility:
|
|
1076
|
-
//
|
|
1077
|
-
//
|
|
1078
|
-
console.log("\nCodex history
|
|
1169
|
+
// Design B upgrade visibility: only the backup manifest authorizes restoring provider
|
|
1170
|
+
// metadata. Bare routed rows have unknown provenance and remain unchanged. This read-only
|
|
1171
|
+
// probe reports manifest work and database readability; it never mutates.
|
|
1172
|
+
console.log("\nCodex history metadata restore");
|
|
1079
1173
|
// The history failure messages point here; make the visit worthwhile by
|
|
1080
1174
|
// probing the coordinator namespace the locks live in. The probe exercises
|
|
1081
1175
|
// identity, runtime-root, and permission checks without taking any lock or
|
|
@@ -1096,11 +1190,17 @@ export async function runDoctor(args: string[] = []): Promise<void> {
|
|
|
1096
1190
|
for (const line of formatCoordinatorDoctorLines(inspectCodexCoordinator())) console.log(line);
|
|
1097
1191
|
const pending = countPendingOpencodexHistory();
|
|
1098
1192
|
if (pending.failed) {
|
|
1099
|
-
|
|
1193
|
+
if (pending.failureReason === "busy") {
|
|
1194
|
+
console.log(" -- history database, backup manifest, or rollout file is busy — exact metadata restore is pending");
|
|
1195
|
+
} else if (pending.failureReason === "permission") {
|
|
1196
|
+
console.log(" -- state DB or backup manifest access was denied — restore state unknown");
|
|
1197
|
+
} else {
|
|
1198
|
+
console.log(" -- backup manifest or restore target failed integrity checks — manual review required");
|
|
1199
|
+
}
|
|
1100
1200
|
} else if (pending.pendingRows === 0 && pending.backupEntries === 0) {
|
|
1101
|
-
console.log(" ok no
|
|
1201
|
+
console.log(" ok no manifest-backed provider metadata pending; untracked routed history is unchanged");
|
|
1102
1202
|
} else {
|
|
1103
|
-
console.log(` -- ${pending.
|
|
1203
|
+
console.log(` -- ${pending.backupEntries} backup manifest entr${pending.backupEntries === 1 ? "y" : "ies"} pending exact metadata restore`);
|
|
1104
1204
|
}
|
|
1105
1205
|
|
|
1106
1206
|
console.log("\nProject Codex configs");
|
|
@@ -1141,6 +1241,12 @@ export async function runDoctor(args: string[] = []): Promise<void> {
|
|
|
1141
1241
|
console.log("\nOAuth reliability");
|
|
1142
1242
|
for (const check of await collectOAuthDoctorChecks()) {
|
|
1143
1243
|
console.log(` [${check.level}] ${check.message}`);
|
|
1244
|
+
// A diagnostic that always exits 0 cannot gate anything, which defeats the point of
|
|
1245
|
+
// running it from a script (#2697's sibling defect). FAIL is the level reserved for a
|
|
1246
|
+
// surface that is unusable rather than degraded, so it -- and only it -- fails the
|
|
1247
|
+
// command. WARN stays exit 0 on purpose: warning on a degraded-but-working install
|
|
1248
|
+
// must not break a pipeline that is legitimately green.
|
|
1249
|
+
if (check.level === "FAIL") recordDoctorFailure();
|
|
1144
1250
|
}
|
|
1145
1251
|
|
|
1146
1252
|
// #857: a running Codex app-server can keep an older in-memory catalog than
|
|
@@ -1148,7 +1254,7 @@ export async function runDoctor(args: string[] = []): Promise<void> {
|
|
|
1148
1254
|
const { collectCodexAppServerCatalogState } = await import("../codex/app-server-processes");
|
|
1149
1255
|
const catalogState = collectCodexAppServerCatalogState();
|
|
1150
1256
|
if (catalogState.state === "stale") {
|
|
1151
|
-
console.log(` [WARN] Codex app-server (PID(s): ${catalogState.processes.map(p => p.pid).join(", ")}) started before the on-disk catalog changed; its in-memory model list disagrees with ocx. Action: restart Codex (or run \`ocx sync --restart-codex\`)`);
|
|
1257
|
+
console.log(` [WARN] Codex app-server (PID(s): ${catalogState.processes.map(p => p.pid).join(", ")}) started before the on-disk catalog changed; its in-memory model list disagrees with ocx. Action: restart Codex (or run \`ocx sync --restart-codex\`; on Windows the desktop app may need \`ocx sync --restart-desktop-app\`)`);
|
|
1152
1258
|
} else if (catalogState.state === "unknown") {
|
|
1153
1259
|
console.log(" [WARN] Could not verify whether the running Codex app-server's model catalog is current (start time or catalog unreadable). Action: if the model list looks stale, restart Codex");
|
|
1154
1260
|
} else if (catalogState.state === "fresh") {
|
|
@@ -1185,8 +1291,14 @@ export async function runDoctor(args: string[] = []): Promise<void> {
|
|
|
1185
1291
|
}
|
|
1186
1292
|
}
|
|
1187
1293
|
}
|
|
1188
|
-
if (pending.failed
|
|
1189
|
-
hints.push("
|
|
1294
|
+
if (pending.failed && pending.failureReason === "busy") {
|
|
1295
|
+
hints.push("Backed-up history metadata is pending or its state is unreadable. The running proxy retries exact restoration automatically; to force it now, close the Codex app and run 'ocx sync'. Untracked routed history is not relabeled.");
|
|
1296
|
+
} else if (pending.failed && pending.failureReason === "permission") {
|
|
1297
|
+
hints.push("Backed-up history metadata could not be inspected because access was denied. Fix access to the reported Codex history paths, then run 'ocx sync'; repeated retries do not repair permissions.");
|
|
1298
|
+
} else if (pending.failed) {
|
|
1299
|
+
hints.push("The history manifest or its target is invalid or changed. Preserve both, inspect the manifest/database/rollout identity, and do not repeatedly run 'ocx sync' until the mismatch is understood. Untracked routed history is not relabeled.");
|
|
1300
|
+
} else if (pending.backupEntries > 0) {
|
|
1301
|
+
hints.push("Backed-up history metadata is pending. The running proxy retries exact restoration automatically; to force it now, close the Codex app and run 'ocx sync'. Untracked routed history is not relabeled.");
|
|
1190
1302
|
}
|
|
1191
1303
|
if (dual.dualInstall && !dual.effectiveIsWindowsMount) {
|
|
1192
1304
|
hints.push(`Codex is installed on BOTH WSL and Windows. Each side keeps its own ~/.codex (logins, config, catalog are separate); ocx here manages the Linux one. To share a single home, set CODEX_HOME=${dual.windowsCodexHomes[0] ?? `${dual.automountRoot}/c/Users/<you>/.codex`} in WSL (drvfs file locking is less reliable).`);
|
package/src/cli/help.ts
CHANGED
|
@@ -5,7 +5,14 @@ import { findCommand } from "./registry";
|
|
|
5
5
|
|
|
6
6
|
const repoRoot = dirname(fileURLToPath(new URL("../../package.json", import.meta.url)));
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
/**
|
|
9
|
+
* Version of the `ocx` bundle this process is running from.
|
|
10
|
+
*
|
|
11
|
+
* Exported so `status`/`doctor` can compare it against the version the live proxy reports,
|
|
12
|
+
* which is how a stale `ocx` earlier on PATH becomes visible (#2701). Returns `"unknown"`
|
|
13
|
+
* rather than throwing; callers must treat that as "cannot compare", not as a mismatch.
|
|
14
|
+
*/
|
|
15
|
+
export function packageVersion(): string {
|
|
9
16
|
const raw = readFileSync(join(repoRoot, "package.json"), "utf8");
|
|
10
17
|
const parsed = JSON.parse(raw) as { version?: unknown };
|
|
11
18
|
return typeof parsed.version === "string" ? parsed.version : "unknown";
|
|
@@ -24,8 +31,8 @@ Usage:
|
|
|
24
31
|
ocx stop Stop the proxy AND restore native Codex (plain codex works again)
|
|
25
32
|
ocx restore Restore native Codex without stopping (alias: eject)
|
|
26
33
|
ocx restore back Re-point codex at the running proxy (undo restore)
|
|
27
|
-
ocx recover-history --legacy-openai
|
|
28
|
-
|
|
34
|
+
ocx recover-history --legacy-openai --yes
|
|
35
|
+
Force all user-message opencodex rows to OpenAI (legacy recovery)
|
|
29
36
|
ocx uninstall Remove service/shim/config and restore native Codex (alias: remove)
|
|
30
37
|
ocx service [sub] Run as a background service (default: install/update/start)
|
|
31
38
|
ocx codex-shim <sub> Auto-start proxy when \`codex\` launches (install|status|uninstall|remove)
|
|
@@ -49,21 +56,25 @@ Usage:
|
|
|
49
56
|
ocx restart Stop and restart the proxy
|
|
50
57
|
ocx v2 <sub> multi_agent_v2 surface (status|on|off|mode|keep-native-v1|threads|mode-hint)
|
|
51
58
|
ocx health [--json] Check proxy health (exit 0=healthy, 1=not)
|
|
59
|
+
ocx capabilities [--json] List declared capabilities and the API routes they drive
|
|
52
60
|
ocx ready [--json] [--wait [--timeout <s>]] Check post-sync readiness (exit 0 only when ready)
|
|
53
61
|
ocx provider <sub> Providers, connectivity, quota, and selected models
|
|
54
62
|
ocx account <sub> Accounts, login/reauth, key pools, and quota controls
|
|
55
63
|
ocx models <sub> Live/custom models, visibility, context, and shadow calls
|
|
64
|
+
ocx alias <sub> Short names for providers and models (list, set, rm, defaults)
|
|
56
65
|
ocx combo <sub> Combo failover/round-robin routing
|
|
57
66
|
ocx agent <sub> Subagents, injection, effort caps, and sidecars
|
|
58
67
|
ocx observe <sub> Logs, usage, storage, memory, and debug data
|
|
68
|
+
ocx inspect <sub> Effective config, catalog, analytics, pacing, client-config
|
|
59
69
|
ocx route <sub> Routing features (combo, policy)
|
|
60
70
|
ocx logs [filters] Alias of ocx observe logs
|
|
61
|
-
ocx usage [--range <7d|30d|all>]
|
|
62
|
-
|
|
71
|
+
ocx usage [--range <today|1d|7d|30d|all>] [--provider <name>] [--model <id>]
|
|
72
|
+
Token and estimated-cost report (alias of ocx observe usage)
|
|
73
|
+
ocx storage <sub> Storage report, cleanup, trash, and the cleanup policy
|
|
63
74
|
ocx memory [--json] Alias of ocx observe memory
|
|
64
75
|
ocx api-key <sub> Alias of ocx access key
|
|
65
76
|
ocx access <sub> External API keys and endpoint information
|
|
66
|
-
ocx export --client <id> Print a client config wired to the running proxy (
|
|
77
|
+
ocx export --client <id> Print a client config wired to the running proxy (12 clients)
|
|
67
78
|
ocx integration client <sub> Enable, disable, inspect or roll back a client integration
|
|
68
79
|
ocx grok <sub> Grok Build model selection and apply
|
|
69
80
|
ocx system <sub> Runtime settings, startup, sync, and updates
|
package/src/cli/index.ts
CHANGED
|
@@ -12,6 +12,9 @@ import {
|
|
|
12
12
|
codexAutoStartEnabled,
|
|
13
13
|
getConfigDir,
|
|
14
14
|
loadConfig,
|
|
15
|
+
saveConfig,
|
|
16
|
+
} from "../config";
|
|
17
|
+
import {
|
|
15
18
|
readPid,
|
|
16
19
|
readPidFileValue,
|
|
17
20
|
readRuntimePort,
|
|
@@ -19,11 +22,11 @@ import {
|
|
|
19
22
|
removePidIfValueIs,
|
|
20
23
|
removeRuntimePort,
|
|
21
24
|
removeRuntimePortIfPidIs,
|
|
22
|
-
saveConfig,
|
|
23
25
|
writePid,
|
|
24
26
|
writeRuntimePort,
|
|
25
|
-
} from "../config";
|
|
26
|
-
import { collectStatus } from "./status";
|
|
27
|
+
} from "../config/process-state";
|
|
28
|
+
import { collectStatus, unusedProxyWarningLines } from "./status";
|
|
29
|
+
import { takeFlag } from "./runtime-api";
|
|
27
30
|
|
|
28
31
|
import {
|
|
29
32
|
discoverStableProxyForRestart,
|
|
@@ -43,8 +46,8 @@ import { runReady, type ReadyArgs } from "./ready";
|
|
|
43
46
|
import { runCli } from "./root";
|
|
44
47
|
import { ProxyOwnershipRefusedError, stopProxy } from "../lib/process-control";
|
|
45
48
|
import { loadServiceTokenFromFile } from "../lib/service-secrets";
|
|
46
|
-
import { diagnoseService, isServiceOwnershipError, serviceCommand, serviceEnvironmentOwnedHere, serviceStartableFromTray, serviceStatusSummary, stopServiceIfInstalled, uninstallServiceIfInstalled } from "../service";
|
|
47
|
-
import { startupHealthSummary } from "../codex/autostart-health";
|
|
49
|
+
import { assertNotAdminToken, diagnoseService, isServiceOwnershipError, serviceCommand, serviceEnvironmentOwnedHere, serviceStartableFromTray, serviceStatusSummary, stopServiceIfInstalled, uninstallServiceIfInstalled } from "../service";
|
|
50
|
+
import { formatStartupRoutingDetail, startupHealthSummary } from "../codex/autostart-health";
|
|
48
51
|
import { drainAndShutdown, isRecyclingForExit, startServer } from "../server";
|
|
49
52
|
import { injectSystemEnv, reconcileShellHook, revertSystemEnv, uninstallShellHook } from "../server/system-env";
|
|
50
53
|
import { buildDesktop3pRegistry } from "../claude/desktop-3p";
|
|
@@ -221,6 +224,11 @@ async function handleStart(options: { block?: boolean } = {}) {
|
|
|
221
224
|
// auth path reads OPENCODEX_API_AUTH_TOKEN from the environment.
|
|
222
225
|
const serviceToken = loadServiceTokenFromFile(process.env);
|
|
223
226
|
if (serviceToken) process.env.OPENCODEX_API_AUTH_TOKEN = serviceToken;
|
|
227
|
+
// The service wrapper (and WinSW via OCX_API_TOKEN_FILE) can still export a colliding
|
|
228
|
+
// token that install now refuses to write. Refuse it here too, before bind, so an
|
|
229
|
+
// already-broken file cannot fence /api/* closed at boot (#2696).
|
|
230
|
+
const present = process.env.OPENCODEX_API_AUTH_TOKEN?.trim();
|
|
231
|
+
if (present) assertNotAdminToken(present);
|
|
224
232
|
const requestedPort = parsePortOption();
|
|
225
233
|
const owner = await findProxyOwnerBeforeJournalRecovery();
|
|
226
234
|
if (owner.live) {
|
|
@@ -828,8 +836,12 @@ async function handleUninstall() {
|
|
|
828
836
|
|
|
829
837
|
async function handleStatus() {
|
|
830
838
|
const statusArgs = args.slice(1);
|
|
831
|
-
|
|
832
|
-
|
|
839
|
+
// Order-independent: the previous form only honoured `--json` as the LONE argument, so
|
|
840
|
+
// `ocx status --json --anything` silently printed human output to a caller that asked
|
|
841
|
+
// for JSON. Take the flag out of argv, then reject whatever is left over -- which keeps
|
|
842
|
+
// the strict unknown-argument behaviour rather than trading one defect for another.
|
|
843
|
+
const wantsJson = takeFlag(statusArgs, "--json");
|
|
844
|
+
if (statusArgs.length > 0) {
|
|
833
845
|
console.error("Usage: ocx status [--json]");
|
|
834
846
|
process.exit(1);
|
|
835
847
|
}
|
|
@@ -846,6 +858,18 @@ async function handleStatus() {
|
|
|
846
858
|
console.log(`❌ Proxy: ${status.proxyLabel}`);
|
|
847
859
|
}
|
|
848
860
|
console.log(` Health: ${status.healthLabel}`);
|
|
861
|
+
// Printed here, not only in --json: a stale ocx on PATH is exactly the situation where
|
|
862
|
+
// the operator is reading human output and wondering why the CLI disagrees with the
|
|
863
|
+
// dashboard. Adding the JSON field alone would satisfy a test and help nobody (#2701).
|
|
864
|
+
if (status.json.versionSkew.warning) {
|
|
865
|
+
console.log(` ⚠️ ${status.json.versionSkew.warning}`);
|
|
866
|
+
}
|
|
867
|
+
for (const line of unusedProxyWarningLines({
|
|
868
|
+
proxyUp: Boolean(status.json.proxy.pid || status.json.proxy.health.ok),
|
|
869
|
+
routingKind: status.json.startup.routingKind,
|
|
870
|
+
})) {
|
|
871
|
+
console.log(` ${line}`);
|
|
872
|
+
}
|
|
849
873
|
if (!(status.json.proxy.pid || status.json.proxy.health.ok)) {
|
|
850
874
|
console.log(" ↳ Not running — Codex/Claude requests will fail with connection errors.");
|
|
851
875
|
// The service summary a few lines below already tells a registered-but-not-serving
|
|
@@ -865,6 +889,7 @@ async function handleStatus() {
|
|
|
865
889
|
console.log(` Default provider: ${status.json.defaultProvider}`);
|
|
866
890
|
console.log(` Codex autostart: ${status.json.codexAutostart ? "enabled" : "disabled"}`);
|
|
867
891
|
console.log(` Restart safety: ${startupHealthSummary(status.json.startup)}`);
|
|
892
|
+
console.log(` ${formatStartupRoutingDetail(status.json.startup)}`);
|
|
868
893
|
console.log(` Service: ${status.json.service.summary}`);
|
|
869
894
|
console.log(` ${status.json.codexShim.summary}`);
|
|
870
895
|
console.log(` Codex runtime: ${status.json.codexRuntime.path}`);
|
|
@@ -905,8 +930,13 @@ async function handleStatus() {
|
|
|
905
930
|
|
|
906
931
|
async function handleRecoverHistory() {
|
|
907
932
|
if (args[1] !== "--legacy-openai") {
|
|
908
|
-
console.error("Usage: ocx recover-history --legacy-openai");
|
|
909
|
-
console.error("
|
|
933
|
+
console.error("Usage: ocx recover-history --legacy-openai --yes");
|
|
934
|
+
console.error("This force-relabels every user-message opencodex row to OpenAI, including legitimate dedicated-provider history. Back up first and use it only for pre-backup legacy recovery.");
|
|
935
|
+
process.exit(1);
|
|
936
|
+
}
|
|
937
|
+
console.error("WARNING: this force-relabels every user-message opencodex row to OpenAI, normalizes exec to cli, and includes legitimate dedicated-provider history.");
|
|
938
|
+
if (args.length !== 3 || args[2] !== "--yes") {
|
|
939
|
+
console.error("Re-run with explicit confirmation: ocx recover-history --legacy-openai --yes");
|
|
910
940
|
process.exit(1);
|
|
911
941
|
}
|
|
912
942
|
// Manifest-independent legacy ejection, serialized like every other history
|
package/src/cli/init.ts
CHANGED
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `ocx system-surface` — the remaining routes that had no CLI caller at all (wp7).
|
|
3
|
+
*
|
|
4
|
+
* These are grouped by what an operator is trying to do, not by which server module owns them:
|
|
5
|
+
* inspecting effective configuration, reading the generated client-config snippet, toggling the
|
|
6
|
+
* native client integrations, checking request pacing and routing analytics, and reading the
|
|
7
|
+
* Codex system prompt.
|
|
8
|
+
*
|
|
9
|
+
* One route here is deliberately READ-ONLY forever. `POST /api/github/star` spends the operator's
|
|
10
|
+
* GitHub identity, and no CLI flag can carry that consent — the server requires a real dashboard
|
|
11
|
+
* session for it. So `star` reads status and says what it cannot do, rather than offering a
|
|
12
|
+
* `--yes` that would be a lie.
|
|
13
|
+
*/
|
|
14
|
+
import {
|
|
15
|
+
CliUsageError,
|
|
16
|
+
printData,
|
|
17
|
+
rejectArgs,
|
|
18
|
+
runCliAction,
|
|
19
|
+
runtimeRequest,
|
|
20
|
+
summaryLines,
|
|
21
|
+
takeFlag,
|
|
22
|
+
takeOption,
|
|
23
|
+
type RuntimeApiDeps,
|
|
24
|
+
} from "./runtime-api";
|
|
25
|
+
|
|
26
|
+
const USAGE = `Usage:
|
|
27
|
+
ocx inspect config [--json]
|
|
28
|
+
ocx inspect catalog [--json]
|
|
29
|
+
ocx inspect routing-analytics [--json]
|
|
30
|
+
ocx inspect pacing [--name <provider>] [--json]
|
|
31
|
+
ocx inspect key-providers [--json]
|
|
32
|
+
ocx inspect codex-prompt [--text] [--json]
|
|
33
|
+
ocx inspect client-config --client <id> [--json]
|
|
34
|
+
ocx inspect star [--json]
|
|
35
|
+
ocx inspect windows-tray [--json]
|
|
36
|
+
ocx integration native [list] [--json]
|
|
37
|
+
ocx integration native <claude|claude-desktop|codex|grok> <on|off> [--json]
|
|
38
|
+
ocx agent request-user-input [on|off] [--json]`;
|
|
39
|
+
|
|
40
|
+
/** A read that takes no arguments beyond `--json`. */
|
|
41
|
+
async function read(path: string, argv: string[], deps: RuntimeApiDeps): Promise<void> {
|
|
42
|
+
const args = [...argv];
|
|
43
|
+
const wantsJson = takeFlag(args, "--json");
|
|
44
|
+
rejectArgs(args, USAGE);
|
|
45
|
+
const result = await runtimeRequest(path, {}, deps);
|
|
46
|
+
printData(result, wantsJson, summaryLines(result));
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const NATIVE_CLIENTS = ["claude", "claude-desktop", "codex", "grok"] as const;
|
|
50
|
+
|
|
51
|
+
interface NativeClientRow {
|
|
52
|
+
clientId?: string;
|
|
53
|
+
state?: string;
|
|
54
|
+
installed?: boolean;
|
|
55
|
+
desiredEnabled?: boolean;
|
|
56
|
+
configPath?: string;
|
|
57
|
+
disableBlocked?: unknown;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* The shared depth-1 flattener renders an array as "N item(s)", so a bare
|
|
62
|
+
* `integration native list` printed `clients: 4 item(s)` -- the per-client state the operator
|
|
63
|
+
* asked for was in the payload and discarded before the terminal. Same defect class wp4 fixed
|
|
64
|
+
* for the account and key tables.
|
|
65
|
+
*/
|
|
66
|
+
function nativeLines(payload: unknown): string[] {
|
|
67
|
+
const clients = (payload as { clients?: NativeClientRow[] } | null)?.clients;
|
|
68
|
+
if (!Array.isArray(clients) || clients.length === 0) return ["No native client integrations reported."];
|
|
69
|
+
const lines = ["CLIENT STATE INSTALLED DESIRED CONFIG"];
|
|
70
|
+
for (const row of clients) {
|
|
71
|
+
lines.push([
|
|
72
|
+
(row.clientId ?? "?").padEnd(16),
|
|
73
|
+
(row.state ?? "?").padEnd(10),
|
|
74
|
+
(row.installed === true ? "yes" : row.installed === false ? "no" : "?").padEnd(10),
|
|
75
|
+
(row.desiredEnabled === true ? "on" : row.desiredEnabled === false ? "off" : "?").padEnd(8),
|
|
76
|
+
row.configPath ?? "",
|
|
77
|
+
].join(" ").trimEnd());
|
|
78
|
+
// A blocked disable is the reason a toggle did not take effect, so it is never silent.
|
|
79
|
+
if (row.disableBlocked !== null && row.disableBlocked !== undefined) {
|
|
80
|
+
lines.push(` disable blocked: ${typeof row.disableBlocked === "string" ? row.disableBlocked : JSON.stringify(row.disableBlocked)}`);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return lines;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
async function nativeIntegration(argv: string[], deps: RuntimeApiDeps): Promise<void> {
|
|
87
|
+
const action = argv[0] && !argv[0].startsWith("-") ? argv[0] : "list";
|
|
88
|
+
const rest = argv[0] && !argv[0].startsWith("-") ? argv.slice(1) : argv;
|
|
89
|
+
|
|
90
|
+
if (action === "list") {
|
|
91
|
+
const args = [...rest];
|
|
92
|
+
const wantsJson = takeFlag(args, "--json");
|
|
93
|
+
rejectArgs(args, USAGE);
|
|
94
|
+
const result = await runtimeRequest("/api/native-integrations", {}, deps);
|
|
95
|
+
printData(result, wantsJson, nativeLines(result));
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (!(NATIVE_CLIENTS as readonly string[]).includes(action)) {
|
|
100
|
+
throw new CliUsageError(`unknown native client ${action}; expected one of ${NATIVE_CLIENTS.join(", ")}`, USAGE);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const args = [...rest];
|
|
104
|
+
const wantsJson = takeFlag(args, "--json");
|
|
105
|
+
const state = args.shift();
|
|
106
|
+
rejectArgs(args, USAGE);
|
|
107
|
+
if (state !== "on" && state !== "off") {
|
|
108
|
+
throw new CliUsageError(`expected on or off after ${action}`, USAGE);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Toggling rewrites the client's own config file, which is why each client has its own route
|
|
112
|
+
// rather than one route with a client parameter.
|
|
113
|
+
const result = await runtimeRequest(`/api/native-integrations/${action}`, {
|
|
114
|
+
method: "PUT",
|
|
115
|
+
headers: { "content-type": "application/json" },
|
|
116
|
+
body: JSON.stringify({ enabled: state === "on" }),
|
|
117
|
+
}, deps);
|
|
118
|
+
printData(result, wantsJson, summaryLines(result));
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Exported unwrapped so `ocx agent request-user-input` can call it INSIDE its own
|
|
123
|
+
* `runCliAction`. Wrapping an already-wrapped handler reports one failure twice: the inner
|
|
124
|
+
* wrapper prints the error and returns a code, and the outer one prints again on the rethrow.
|
|
125
|
+
*/
|
|
126
|
+
export async function requestUserInputAction(argv: string[], deps: RuntimeApiDeps): Promise<void> {
|
|
127
|
+
const args = [...argv];
|
|
128
|
+
const wantsJson = takeFlag(args, "--json");
|
|
129
|
+
const state = args.shift();
|
|
130
|
+
rejectArgs(args, USAGE);
|
|
131
|
+
|
|
132
|
+
const path = "/api/codex-auth/features/default-mode-request-user-input";
|
|
133
|
+
// No argument means show. A read must not write the value it is reporting.
|
|
134
|
+
if (state === undefined) {
|
|
135
|
+
const result = await runtimeRequest(path, {}, deps);
|
|
136
|
+
printData(result, wantsJson, summaryLines(result));
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
if (state !== "on" && state !== "off") throw new CliUsageError("expected on or off", USAGE);
|
|
140
|
+
|
|
141
|
+
const result = await runtimeRequest(path, {
|
|
142
|
+
method: "PUT",
|
|
143
|
+
headers: { "content-type": "application/json" },
|
|
144
|
+
body: JSON.stringify({ enabled: state === "on" }),
|
|
145
|
+
}, deps);
|
|
146
|
+
printData(result, wantsJson, summaryLines(result));
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
async function clientConfig(argv: string[], deps: RuntimeApiDeps): Promise<void> {
|
|
150
|
+
const args = [...argv];
|
|
151
|
+
const wantsJson = takeFlag(args, "--json");
|
|
152
|
+
const client = takeOption(args, "--client");
|
|
153
|
+
rejectArgs(args, USAGE);
|
|
154
|
+
if (!client) throw new CliUsageError("--client is required", USAGE);
|
|
155
|
+
// The valid client list is not duplicated here: the route answers 400 naming every accepted id,
|
|
156
|
+
// which is more useful than a local list that can drift out of date.
|
|
157
|
+
const result = await runtimeRequest(`/api/client-config?client=${encodeURIComponent(client)}`, {}, deps);
|
|
158
|
+
printData(result, wantsJson, summaryLines(result));
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
async function pacing(argv: string[], deps: RuntimeApiDeps): Promise<void> {
|
|
162
|
+
const args = [...argv];
|
|
163
|
+
const wantsJson = takeFlag(args, "--json");
|
|
164
|
+
const name = takeOption(args, "--name");
|
|
165
|
+
rejectArgs(args, USAGE);
|
|
166
|
+
const suffix = name ? `?name=${encodeURIComponent(name)}` : "";
|
|
167
|
+
const result = await runtimeRequest(`/api/provider-request-pacing${suffix}`, {}, deps);
|
|
168
|
+
printData(result, wantsJson, summaryLines(result));
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
async function codexPrompt(argv: string[], deps: RuntimeApiDeps): Promise<void> {
|
|
172
|
+
const args = [...argv];
|
|
173
|
+
const wantsJson = takeFlag(args, "--json");
|
|
174
|
+
const asText = takeFlag(args, "--text");
|
|
175
|
+
rejectArgs(args, USAGE);
|
|
176
|
+
if (asText && wantsJson) throw new CliUsageError("--text and --json cannot be combined", USAGE);
|
|
177
|
+
if (asText) {
|
|
178
|
+
// The /text variant answers the prompt body itself, so it is printed verbatim rather than
|
|
179
|
+
// flattened through the summary renderer.
|
|
180
|
+
const result = await runtimeRequest<unknown>("/api/codex-prompt/text", {}, deps);
|
|
181
|
+
console.log(typeof result === "string" ? result : JSON.stringify(result, null, 2));
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
const result = await runtimeRequest("/api/codex-prompt", {}, deps);
|
|
185
|
+
printData(result, wantsJson, summaryLines(result));
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
async function star(argv: string[], deps: RuntimeApiDeps): Promise<void> {
|
|
189
|
+
const args = [...argv];
|
|
190
|
+
const wantsJson = takeFlag(args, "--json");
|
|
191
|
+
rejectArgs(args, USAGE);
|
|
192
|
+
const result = await runtimeRequest<Record<string, unknown>>("/api/github/star", {}, deps);
|
|
193
|
+
if (wantsJson) {
|
|
194
|
+
printData(result, true);
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
printData(result, false, summaryLines(result));
|
|
198
|
+
// Said plainly, because the natural next question is "then star it", and the answer is that no
|
|
199
|
+
// CLI invocation can: the POST requires a dashboard session precisely so an agent cannot spend
|
|
200
|
+
// the operator's identity on their behalf.
|
|
201
|
+
console.log("Starring is not available from the CLI: it uses your GitHub identity, so only you can do it from the dashboard.");
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
export async function handleInspectCommand(argv: string[], deps: RuntimeApiDeps = {}): Promise<number> {
|
|
205
|
+
return runCliAction(async () => {
|
|
206
|
+
const hasSub = argv[0] !== undefined && !argv[0].startsWith("-");
|
|
207
|
+
const sub = hasSub ? argv[0]! : "config";
|
|
208
|
+
const rest = hasSub ? argv.slice(1) : argv;
|
|
209
|
+
if (sub === "config") await read("/api/config", rest, deps);
|
|
210
|
+
else if (sub === "catalog") await read("/api/catalog", rest, deps);
|
|
211
|
+
else if (sub === "routing-analytics") await read("/api/routing-analytics", rest, deps);
|
|
212
|
+
else if (sub === "key-providers") await read("/api/key-providers", rest, deps);
|
|
213
|
+
else if (sub === "windows-tray") await read("/api/windows-tray", rest, deps);
|
|
214
|
+
else if (sub === "pacing") await pacing(rest, deps);
|
|
215
|
+
else if (sub === "client-config") await clientConfig(rest, deps);
|
|
216
|
+
else if (sub === "codex-prompt") await codexPrompt(rest, deps);
|
|
217
|
+
else if (sub === "star") await star(rest, deps);
|
|
218
|
+
else throw new CliUsageError(`unknown inspect command ${sub}`, USAGE);
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
export async function handleIntegrationCommand(argv: string[], deps: RuntimeApiDeps = {}): Promise<number> {
|
|
223
|
+
return runCliAction(async () => {
|
|
224
|
+
const [sub, ...rest] = argv;
|
|
225
|
+
if (sub === "native" || sub === undefined) await nativeIntegration(sub === undefined ? [] : rest, deps);
|
|
226
|
+
else throw new CliUsageError(`unknown integration command ${sub}`, USAGE);
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
export const INSPECT_USAGE = USAGE;
|