@bitkyc08/opencodex 2.42.0 → 2.43.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/AGENTS_INSTALL.md +2 -2
- package/README.md +31 -0
- package/bin/ocx.mjs +10 -1
- package/gui/dist/assets/index-DS1NE4Jn.css +1 -0
- package/gui/dist/assets/index-Djowl68T.js +112 -0
- package/gui/dist/index.html +2 -2
- package/package.json +1 -1
- package/src/adapters/anthropic-image-codec.ts +304 -0
- package/src/adapters/anthropic-image-normalize.ts +8 -298
- package/src/adapters/anthropic.ts +30 -7
- package/src/adapters/command-code.ts +7 -5
- package/src/adapters/cursor/desktop-executor-contract.ts +15 -0
- package/src/adapters/cursor/images.ts +36 -6
- package/src/adapters/cursor/live-transport.ts +7 -2
- package/src/adapters/cursor/native-exec-desktop.ts +2 -15
- package/src/adapters/cursor/protobuf-request.ts +13 -3
- package/src/adapters/cursor/tool-definitions.ts +5 -670
- package/src/adapters/cursor/tool-guidance.ts +236 -0
- package/src/adapters/cursor/tool-naming.ts +252 -0
- package/src/adapters/cursor/tool-schemas.ts +195 -0
- package/src/adapters/cursor/types.ts +6 -3
- package/src/adapters/exec-tool-result-normalize.ts +1 -1
- package/src/adapters/google-errors.ts +9 -1
- package/src/adapters/google.ts +1 -0
- package/src/adapters/kiro-calibration.ts +181 -0
- package/src/adapters/kiro.ts +135 -3
- package/src/adapters/openai-responses.ts +216 -26
- package/src/adapters/responses-code-mode.ts +59 -0
- package/src/adapters/tool-catalog-nudge.ts +1 -1
- package/src/adapters/xai-schema-analysis.ts +86 -0
- package/src/adapters/xai-tool-schema.ts +2 -87
- package/src/adapters/xai-web-search.ts +1 -1
- package/src/bridge.ts +22 -10
- package/src/chat/inbound.ts +11 -3
- package/src/claude/inbound-content-options.ts +60 -0
- package/src/claude/inbound-model-options.ts +142 -0
- package/src/claude/inbound-records.ts +7 -0
- package/src/claude/inbound.ts +10 -202
- package/src/claude/model-info.ts +45 -0
- package/src/cli/account-auth.ts +21 -6
- package/src/cli/capabilities.ts +13 -2
- package/src/cli/claude.ts +232 -39
- package/src/cli/config-command.ts +9 -1
- package/src/cli/dispatch.ts +5 -1
- package/src/cli/doctor.ts +10 -0
- package/src/cli/effort.ts +372 -0
- package/src/cli/export-command.ts +3 -9
- package/src/cli/help.ts +1 -0
- package/src/cli/index.ts +13 -0
- package/src/cli/init.ts +4 -0
- package/src/cli/model-selection-guidance.ts +30 -0
- package/src/cli/models-runtime.ts +3 -2
- package/src/cli/models.ts +8 -3
- package/src/cli/opencode.ts +4 -1
- package/src/cli/provider-runtime.ts +65 -0
- package/src/cli/provider.ts +8 -0
- package/src/cli/registry.ts +16 -2
- package/src/cli/runtime-api.ts +3 -1
- package/src/cli/star-prompt.ts +22 -6
- package/src/cli/status-probes.ts +168 -0
- package/src/cli/status.ts +5 -168
- package/src/clients/config-export/constants.ts +69 -0
- package/src/clients/config-export/contracts.ts +154 -0
- package/src/clients/config-export/dsh.ts +132 -0
- package/src/clients/config-export/fast-models.ts +29 -0
- package/src/clients/config-export/mcode.ts +83 -0
- package/src/clients/config-export/model-metadata.ts +108 -0
- package/src/clients/config-export/omp.ts +104 -0
- package/src/clients/config-export/zcode.ts +92 -0
- package/src/clients/config-export.ts +18 -710
- package/src/codex/account-lifecycle.ts +20 -3
- package/src/codex/account-usability.ts +2 -0
- package/src/codex/auth-api.ts +170 -23
- package/src/codex/auth-context.ts +328 -24
- package/src/codex/catalog/effort.ts +30 -4
- package/src/codex/catalog/metadata.ts +39 -9
- package/src/codex/catalog/native-models.ts +53 -12
- package/src/codex/catalog/parsing.ts +119 -5
- package/src/codex/catalog/provider-fetch.ts +150 -20
- package/src/codex/catalog/reserve.ts +52 -0
- package/src/codex/catalog/sync.ts +89 -16
- package/src/codex/convergence-types.ts +1 -0
- package/src/codex/convergence.ts +2 -0
- package/src/codex/data/upstream-models.json +1 -1
- package/src/codex/forward-transport-headers.ts +25 -0
- package/src/codex/inject.ts +3 -28
- package/src/codex/legacy-config-keys.ts +68 -0
- package/src/codex/log-guard/inspect-schema.ts +137 -0
- package/src/codex/log-guard/inspect.ts +2 -134
- package/src/codex/loopback-target.ts +54 -0
- package/src/codex/main-account-cache.ts +63 -1
- package/src/codex/main-account-hard-lock.ts +52 -0
- package/src/codex/main-account.ts +3 -1
- package/src/codex/management-convergence.ts +3 -0
- package/src/codex/model-entitlements.ts +54 -4
- package/src/codex/project-config-warnings.ts +92 -2
- package/src/codex/prompt-layers/encoding.ts +80 -0
- package/src/codex/prompt-layers/paths.ts +54 -0
- package/src/codex/prompt-layers/revision.ts +55 -0
- package/src/codex/prompt-layers/toml-edit.ts +163 -0
- package/src/codex/prompt-layers/toml-read.ts +181 -0
- package/src/codex/prompt-layers.ts +14 -520
- package/src/codex/quota-auto-refresh-state.ts +16 -0
- package/src/codex/quota-auto-refresh.ts +219 -0
- package/src/codex/quota-types.ts +51 -0
- package/src/codex/quota.ts +252 -93
- package/src/codex/reserve-availability.ts +177 -0
- package/src/codex/routing.ts +28 -9
- package/src/codex/shim.ts +53 -11
- package/src/codex/subagent-model-fallback.ts +23 -3
- package/src/combos/failover.ts +125 -7
- package/src/combos/identifiers.ts +89 -0
- package/src/combos/index.ts +4 -0
- package/src/combos/resolve.ts +80 -9
- package/src/combos/types.ts +20 -93
- package/src/config/subagent-models.ts +24 -0
- package/src/config.ts +156 -13
- package/src/generated/compatibility-version.json +454 -166
- package/src/generated/model-metadata.ts +1 -1
- package/src/integrations/journal.ts +65 -4
- package/src/integrations/store.ts +5 -0
- package/src/lab/events/limits.ts +4 -0
- package/src/lib/destination-policy.ts +31 -2
- package/src/lib/errors.ts +39 -0
- package/src/lib/provider-outbound.ts +69 -3
- package/src/lib/proxy-env.ts +22 -0
- package/src/lib/redact-folding.ts +176 -0
- package/src/lib/redact.ts +2 -175
- package/src/lib/state-store-sweeper.ts +20 -6
- package/src/lib/token-estimate.ts +94 -27
- package/src/lib/windows-user-principal.ts +53 -5
- package/src/oauth/anthropic-routing.ts +99 -3
- package/src/oauth/generic-account-failover.ts +36 -13
- package/src/oauth/index.ts +127 -26
- package/src/oauth/login-cli.ts +5 -0
- package/src/oauth/meta-muse.ts +117 -15
- package/src/oauth/pool-settings-capability.ts +15 -4
- package/src/providers/api-keys.ts +8 -10
- package/src/providers/default-aliases.ts +39 -0
- package/src/providers/derive.ts +10 -2
- package/src/providers/fastwire.ts +36 -7
- package/src/providers/initial-model-selection-runtime.ts +90 -0
- package/src/providers/initial-model-selection.ts +120 -0
- package/src/providers/key-failover.ts +134 -54
- package/src/providers/key-store.ts +11 -1
- package/src/providers/label.ts +1 -1
- package/src/providers/model-discovery.ts +76 -0
- package/src/providers/model-rename-startup.ts +72 -8
- package/src/providers/openai-sidecar.ts +17 -5
- package/src/providers/openai-tiers-destination.ts +102 -0
- package/src/providers/openai-tiers.ts +2 -99
- package/src/providers/opencode-go-transport.ts +41 -0
- package/src/providers/quota-key-accounts.ts +141 -0
- package/src/providers/quota-types.ts +9 -0
- package/src/providers/quota.ts +535 -104
- package/src/providers/registry.ts +60 -17
- package/src/providers/xai-responses-opt-in.ts +31 -5
- package/src/quota/reset-activation.ts +81 -0
- package/src/quota/reset-detector.ts +305 -0
- package/src/quota/reset-notify-config.ts +162 -0
- package/src/quota/reset-observer.ts +125 -0
- package/src/quota/reset-poller.ts +160 -0
- package/src/quota/reset-seen-store.ts +385 -0
- package/src/quota/reset-sinks.ts +199 -0
- package/src/quota/window-mapping.ts +106 -0
- package/src/responses/apply-patch-envelope.ts +46 -0
- package/src/responses/code-mode-helper-compat.ts +39 -1
- package/src/responses/custom-tool-compat.ts +10 -4
- package/src/responses/hosted-tool-policy.ts +12 -4
- package/src/responses/parser-content.ts +133 -0
- package/src/responses/parser-text-format.ts +24 -0
- package/src/responses/parser-tools.ts +188 -0
- package/src/responses/parser.ts +3 -326
- package/src/responses/state.ts +124 -28
- package/src/router.ts +48 -13
- package/src/routing/analytics.ts +1 -0
- package/src/routing/capability.ts +17 -4
- package/src/server/auth-cors.ts +7 -1
- package/src/server/background-lifecycle.ts +23 -1
- package/src/server/chat-completions.ts +25 -3
- package/src/server/claude-messages.ts +62 -5
- package/src/server/effort-row.ts +1 -1
- package/src/server/fast-row.ts +295 -0
- package/src/server/gui-static.ts +30 -4
- package/src/server/index.ts +107 -21
- package/src/server/management/agent-settings-routes.ts +2 -2
- package/src/server/management/combo-routes.ts +37 -9
- package/src/server/management/config-routes.ts +93 -2
- package/src/server/management/integration-routes.ts +108 -0
- package/src/server/management/model-routes.ts +13 -3
- package/src/server/management/model-rows.ts +20 -1
- package/src/server/management/native-integration-routes.ts +4 -1
- package/src/server/management/oauth-account-routes.ts +38 -10
- package/src/server/management/provider-routes.ts +34 -3
- package/src/server/management/quota-reset-routes.ts +57 -0
- package/src/server/management/route-registry.ts +7 -4
- package/src/server/management/shared.ts +19 -5
- package/src/server/management/system-routes.ts +3 -2
- package/src/server/management-api.ts +14 -2
- package/src/server/ports.ts +12 -2
- package/src/server/relay-eager.ts +38 -23
- package/src/server/relay.ts +4 -0
- package/src/server/request-log.ts +6 -0
- package/src/server/responses/codex-ws-correlation.ts +65 -0
- package/src/server/responses/codex-ws-exchange.ts +261 -0
- package/src/server/responses/codex-ws-metadata.ts +134 -0
- package/src/server/responses/codex-ws-pool.ts +162 -0
- package/src/server/responses/codex-ws-request.ts +87 -0
- package/src/server/responses/codex-ws-session.ts +93 -0
- package/src/server/responses/codex-ws-wire.ts +144 -0
- package/src/server/responses/collaboration.ts +41 -1
- package/src/server/responses/compact.ts +105 -12
- package/src/server/responses/core.ts +476 -57
- package/src/server/responses/fetch-helpers.ts +10 -3
- package/src/server/responses/input-admission.ts +16 -9
- package/src/server/responses/responses-field-backfill.ts +1 -1
- package/src/server/responses/ws-upstream.ts +34 -318
- package/src/server/responses-custom-tool-repair.ts +20 -4
- package/src/server/responses-undeclared-tool-guard.ts +100 -8
- package/src/server/safe-response-headers.ts +23 -0
- package/src/server/search.ts +9 -0
- package/src/server/subagent-models-startup.ts +27 -0
- package/src/server/system-env-shell.ts +238 -0
- package/src/server/system-env.ts +7 -234
- package/src/server/ws-bridge.ts +3 -25
- package/src/server/xai-responses-startup.ts +21 -0
- package/src/service-manager-probe.ts +1 -1
- package/src/service.ts +55 -16
- package/src/types/config.ts +108 -12
- package/src/types/provider.ts +36 -7
- package/src/types/tools.ts +26 -1
- package/src/types.ts +2 -0
- package/src/update/notify.ts +8 -2
- package/src/usage/cost.ts +38 -28
- package/src/usage/expected-prices.ts +34 -15
- package/src/usage/log.ts +2 -0
- package/src/usage/model-identity.ts +26 -0
- package/src/usage/summary.ts +15 -1
- package/src/vision/describe.ts +6 -0
- package/src/vision/image-rewrite.ts +108 -0
- package/src/vision/index.ts +19 -306
- package/src/vision/plan.ts +205 -0
- package/src/web-search/executor.ts +6 -0
- package/src/web-search/index.ts +8 -1
- package/gui/dist/assets/index-BU1tE0sr.js +0 -112
- package/gui/dist/assets/index-DL9-iS6J.css +0 -1
package/src/cli/account-auth.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { writeSync } from "node:fs";
|
|
2
|
+
import { modelSelectionGuidance, modelSelectionNextSteps } from "./model-selection-guidance";
|
|
2
3
|
import { warnIfCodexCatalogRefreshPending } from "./account-catalog-refresh";
|
|
4
|
+
import { isCodexResetCreditOperationId } from "../codex/reset-credit-recovery";
|
|
3
5
|
import {
|
|
4
6
|
CliUsageError,
|
|
5
7
|
printData,
|
|
@@ -34,7 +36,7 @@ const USAGE = `Usage:
|
|
|
34
36
|
ocx account login <provider> [--id <account-id>] [--reauth] [--device] [--code -] [--no-wait] [--json]
|
|
35
37
|
ocx account code <provider> [--flow <flow-id>] [--json] (reads the code from stdin)
|
|
36
38
|
ocx account cancel <provider> [--flow <flow-id>] [--json]
|
|
37
|
-
ocx account reset-credits <account-id|main> [--consume --yes] [--json]
|
|
39
|
+
ocx account reset-credits <account-id|main> [--consume --yes [--operation-id <uuid>]] [--json]
|
|
38
40
|
|
|
39
41
|
--device runs the OpenAI device-code login instead of the browser callback: use
|
|
40
42
|
it when the proxy has no browser or nothing can reach localhost:1455, such as a
|
|
@@ -136,7 +138,7 @@ async function login(argv: string[], deps: RuntimeApiDeps): Promise<void> {
|
|
|
136
138
|
}, deps);
|
|
137
139
|
}
|
|
138
140
|
if (noWait) {
|
|
139
|
-
|
|
141
|
+
printData({ ...start, modelSelection: modelSelectionNextSteps("openai", true) }, wantsJson, modelSelectionGuidance("openai", true));
|
|
140
142
|
return;
|
|
141
143
|
}
|
|
142
144
|
if (!start.flowId) throw new CliUsageError("login did not return a flow id");
|
|
@@ -152,7 +154,7 @@ async function login(argv: string[], deps: RuntimeApiDeps): Promise<void> {
|
|
|
152
154
|
{}, deps,
|
|
153
155
|
);
|
|
154
156
|
if (state.status === "done") {
|
|
155
|
-
printData(state, wantsJson, [`Logged in${state.email ? ` as ${String(state.email)}` : ""}
|
|
157
|
+
printData({ ...state, modelSelection: modelSelectionNextSteps("openai") }, wantsJson, [`Logged in${state.email ? ` as ${String(state.email)}` : ""}.`, ...modelSelectionGuidance("openai")]);
|
|
156
158
|
if (!wantsJson) warnIfCodexCatalogRefreshPending(state);
|
|
157
159
|
return;
|
|
158
160
|
}
|
|
@@ -183,7 +185,7 @@ async function login(argv: string[], deps: RuntimeApiDeps): Promise<void> {
|
|
|
183
185
|
}, deps);
|
|
184
186
|
}
|
|
185
187
|
if (noWait) {
|
|
186
|
-
|
|
188
|
+
printData({ ...start, modelSelection: modelSelectionNextSteps(provider, true) }, wantsJson, modelSelectionGuidance(provider, true));
|
|
187
189
|
return;
|
|
188
190
|
}
|
|
189
191
|
for (let attempt = 0; attempt < 100; attempt++) {
|
|
@@ -191,7 +193,7 @@ async function login(argv: string[], deps: RuntimeApiDeps): Promise<void> {
|
|
|
191
193
|
const state = await runtimeRequest<Record<string, unknown>>(`/api/oauth/status?provider=${encodeURIComponent(provider)}`, {}, deps);
|
|
192
194
|
if (state.error) throw new CliUsageError(String(state.error));
|
|
193
195
|
if (state.loggedIn === true) {
|
|
194
|
-
printData(state, wantsJson, [`Logged in to ${provider}
|
|
196
|
+
printData({ ...state, modelSelection: modelSelectionNextSteps(provider) }, wantsJson, [`Logged in to ${provider}.`, ...modelSelectionGuidance(provider)]);
|
|
195
197
|
return;
|
|
196
198
|
}
|
|
197
199
|
}
|
|
@@ -252,12 +254,25 @@ async function resetCredits(argv: string[], deps: RuntimeApiDeps): Promise<void>
|
|
|
252
254
|
const wantsJson = takeFlag(args, "--json");
|
|
253
255
|
const consume = takeFlag(args, "--consume");
|
|
254
256
|
const yes = takeFlag(args, "--yes");
|
|
257
|
+
// Before rejectArgs: takeOption splices its two tokens out of `args`.
|
|
258
|
+
const operationId = takeOption(args, "--operation-id");
|
|
255
259
|
if (!rawId) throw new CliUsageError("account id is required", USAGE);
|
|
256
260
|
if (consume && !yes) throw new CliUsageError("consuming a reset credit requires --yes", USAGE);
|
|
261
|
+
if (operationId !== undefined && !consume) {
|
|
262
|
+
throw new CliUsageError("--operation-id requires --consume", USAGE);
|
|
263
|
+
}
|
|
264
|
+
if (operationId !== undefined && !isCodexResetCreditOperationId(operationId)) {
|
|
265
|
+
throw new CliUsageError("--operation-id must be a UUIDv4", USAGE);
|
|
266
|
+
}
|
|
257
267
|
rejectArgs(args, USAGE);
|
|
258
268
|
const accountId = rawId === "main" ? "__main__" : rawId;
|
|
259
269
|
const result = consume
|
|
260
|
-
? await runtimeRequest("/api/codex-auth/reset-credits/consume", {
|
|
270
|
+
? await runtimeRequest("/api/codex-auth/reset-credits/consume", {
|
|
271
|
+
method: "POST",
|
|
272
|
+
// Spread, not `operationId: undefined`: the server distinguishes an absent
|
|
273
|
+
// key (legacy random id) from a caller who asked for a stable identity.
|
|
274
|
+
body: JSON.stringify({ accountId, ...(operationId === undefined ? {} : { operationId }) }),
|
|
275
|
+
}, deps)
|
|
261
276
|
: await runtimeRequest(`/api/codex-auth/reset-credits?accountId=${encodeURIComponent(accountId)}`, {}, deps);
|
|
262
277
|
printData(result, wantsJson);
|
|
263
278
|
}
|
package/src/cli/capabilities.ts
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* top-level `const USAGE`, evaluated at import time, so a cycle back into this table
|
|
12
12
|
* would resolve to `undefined` under ESM rather than throwing -- silently emptying the
|
|
13
13
|
* usage text that `rejectArgs` hands to `CliUsageError`, in the exact error-reporting
|
|
14
|
-
* surface the CLI-operability issues are about. `tests/cli-capabilities.test.ts` asserts
|
|
14
|
+
* surface the CLI-operability issues are about. `tests/cli/cli-capabilities.test.ts` asserts
|
|
15
15
|
* the absence of those imports and that every rendered usage string is non-empty, so the
|
|
16
16
|
* failure mode is loud instead of degraded.
|
|
17
17
|
*
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
* `HEAD_CAPABILITIES`. They exit in the CLI head (`root.ts`) before dispatch and have no
|
|
20
20
|
* runner key, so listing them as ordinary capabilities would break the registry parity
|
|
21
21
|
* assertion that every canonical entry is a direct runner. `help` is excluded from
|
|
22
|
-
* `CLI_COMMANDS` deliberately -- `tests/cli-registry.test.ts` documents it as a
|
|
22
|
+
* `CLI_COMMANDS` deliberately -- `tests/cli/cli-registry.test.ts` documents it as a
|
|
23
23
|
* head-handled pseudo-case -- and that decision is preserved here rather than reversed.
|
|
24
24
|
*/
|
|
25
25
|
|
|
@@ -153,6 +153,17 @@ export const CAPABILITIES: readonly Capability[] = [
|
|
|
153
153
|
json: "envelope",
|
|
154
154
|
details: ["Reads local config; drives no management API route."],
|
|
155
155
|
},
|
|
156
|
+
{
|
|
157
|
+
command: ["provider", "resets"],
|
|
158
|
+
summary: "Recently detected quota resets and whether reset notifications are enabled.",
|
|
159
|
+
routes: [{ method: "GET", path: "/api/quota-resets" }],
|
|
160
|
+
flags: [
|
|
161
|
+
{ name: "--json", value: "boolean", summary: "Emit reset events as JSON." },
|
|
162
|
+
{ name: "--limit", value: "number", summary: "Limit returned events; defaults to 20, capped at 100." },
|
|
163
|
+
],
|
|
164
|
+
mutates: false,
|
|
165
|
+
json: "payload",
|
|
166
|
+
},
|
|
156
167
|
{
|
|
157
168
|
command: ["provider", "keychain"],
|
|
158
169
|
summary: "Move a provider's API key into the OS keychain, restore it, or report where it lives.",
|
package/src/cli/claude.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `ocx claude [claude args...]` — launch Claude Code
|
|
2
|
+
* `ocx claude [claude args...]` — launch Claude Code through the local proxy,
|
|
3
|
+
* or natively when Claude routing is explicitly disabled.
|
|
3
4
|
*
|
|
4
5
|
* Mirrors `ccr code` UX (devlog/260711_claude_inbound/020, 003 E1/E2/E5/G1):
|
|
5
6
|
* ensures the proxy is running, injects the Anthropic env slots, then execs the
|
|
@@ -9,8 +10,9 @@
|
|
|
9
10
|
import { spawn } from "node:child_process";
|
|
10
11
|
import { loadConfig } from "../config";
|
|
11
12
|
import { injectClaudeAgentDefs } from "../claude/agents-inject";
|
|
13
|
+
import { CLAUDE_ALIAS_PREFIX_V1, CLAUDE_ALIAS_PREFIX_V2 } from "../claude/alias";
|
|
12
14
|
import { effectiveModelEnv, resolveAutoContext } from "../claude/context-windows";
|
|
13
|
-
import { refreshGatewayModelCacheFromProxy } from "../claude/gateway-cache";
|
|
15
|
+
import { claudeConfigDir, refreshGatewayModelCacheFromProxy } from "../claude/gateway-cache";
|
|
14
16
|
import { commandInvocation } from "../lib/win-exec";
|
|
15
17
|
import { isProxyAdmissionSecret } from "../server/auth-cors";
|
|
16
18
|
import { findLiveProxy } from "../server/proxy-liveness";
|
|
@@ -21,10 +23,11 @@ import { resolveClaudeAuthMode } from "../claude/auth-mode";
|
|
|
21
23
|
import { withProcessRuntimeProvenance } from "../lib/bun-runtime";
|
|
22
24
|
import { selfLaunchArgv } from "../lib/self-launch-argv";
|
|
23
25
|
import { ANTHROPIC_PARENT_ENV_SLOTS, trustedNodeLauncherContext, type AnthropicParentEnvSlot } from "./launcher-context";
|
|
24
|
-
import { readClientConnectionState } from "../client/state";
|
|
25
|
-
import { readServiceApiTokenState } from "../lib/service-secrets";
|
|
26
|
+
import { readClientConnectionState, type ClientConnectionState } from "../client/state";
|
|
27
|
+
import { readServiceApiTokenState, type ServiceApiTokenState } from "../lib/service-secrets";
|
|
26
28
|
import { DEFAULT_CATALOG_PATH } from "../codex/paths";
|
|
27
29
|
import { readFileSync } from "node:fs";
|
|
30
|
+
import { join } from "node:path";
|
|
28
31
|
import { aliasForNative, aliasForRoute } from "../claude/alias";
|
|
29
32
|
import { desktop3pAlias } from "../claude/desktop-3p";
|
|
30
33
|
|
|
@@ -49,6 +52,48 @@ export type ClaudeEnvDeps = {
|
|
|
49
52
|
allowRootSkipPermissions?: boolean;
|
|
50
53
|
};
|
|
51
54
|
|
|
55
|
+
function deleteUntrustedAnthropicSlots(env: ClaudeLaunchEnv, deps: ClaudeEnvDeps): void {
|
|
56
|
+
const explicitSlots = deps.preBunAnthropicSlots;
|
|
57
|
+
const trustedSlots = explicitSlots === undefined
|
|
58
|
+
? trustedNodeLauncherContext()?.anthropicEnvSlots ?? []
|
|
59
|
+
: explicitSlots ?? [];
|
|
60
|
+
const exported = new Set<AnthropicParentEnvSlot>(trustedSlots);
|
|
61
|
+
for (const name of ANTHROPIC_PARENT_ENV_SLOTS) {
|
|
62
|
+
const value = env[name];
|
|
63
|
+
if (value !== undefined && value !== "" && !exported.has(name)) delete env[name];
|
|
64
|
+
}
|
|
65
|
+
delete env.OCX_PRE_BUN_ANTHROPIC_ENV;
|
|
66
|
+
delete env.OCX_NODE_LAUNCH_CONTEXT;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Read Claude Code's own persisted `/model` picker default.
|
|
71
|
+
*
|
|
72
|
+
* An absent `settings.json` is the ordinary fresh-install case and stays silent. A
|
|
73
|
+
* present-but-unparseable one is not: swallowing it would drop the "saved model requires
|
|
74
|
+
* the proxy" warning exactly when the file is broken, so the native session would start
|
|
75
|
+
* on a model the user never chose with no explanation. Name the file, never its contents.
|
|
76
|
+
*/
|
|
77
|
+
export function readPickerDefaultModel(configDir: string): string | null {
|
|
78
|
+
const file = join(configDir, "settings.json");
|
|
79
|
+
let raw: string;
|
|
80
|
+
try {
|
|
81
|
+
raw = readFileSync(file, "utf8");
|
|
82
|
+
} catch (err) { // no-excuse-ok: catch -- an absent picker settings file is the default install state.
|
|
83
|
+
if ((err as NodeJS.ErrnoException).code !== "ENOENT") {
|
|
84
|
+
console.warn(`⚠ Could not read Claude Code settings at ${file}; the saved model check is skipped this run.`);
|
|
85
|
+
}
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
try {
|
|
89
|
+
const parsed = JSON.parse(raw) as Record<string, unknown>;
|
|
90
|
+
return typeof parsed.model === "string" && parsed.model.trim() !== "" ? parsed.model.trim() : null;
|
|
91
|
+
} catch { // no-excuse-ok: catch -- a corrupt picker file must warn, not abort the launch.
|
|
92
|
+
console.warn(`⚠ Claude Code settings at ${file} are not valid JSON; the saved model check is skipped this run.`);
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
52
97
|
function isClaudeLoopbackHostname(hostname: string): boolean {
|
|
53
98
|
const normalized = hostname.toLowerCase().replace(/\.$/, "");
|
|
54
99
|
return normalized === "localhost"
|
|
@@ -128,18 +173,7 @@ export function buildClaudeEnv(
|
|
|
128
173
|
// Direct `bun src/cli/index.ts` therefore loses ambient Anthropic values. That is a
|
|
129
174
|
// real cost to a documented entry point, and the escape hatch is the launcher: run
|
|
130
175
|
// through `ocx` (the published bin) and genuine shell exports are preserved by proof.
|
|
131
|
-
|
|
132
|
-
const trustedSlots = explicitSlots === undefined
|
|
133
|
-
? trustedNodeLauncherContext()?.anthropicEnvSlots ?? []
|
|
134
|
-
: explicitSlots ?? [];
|
|
135
|
-
const exported = new Set<AnthropicParentEnvSlot>(trustedSlots);
|
|
136
|
-
for (const name of ANTHROPIC_PARENT_ENV_SLOTS) {
|
|
137
|
-
const value = env[name];
|
|
138
|
-
if (value !== undefined && value !== "" && !exported.has(name)) delete env[name];
|
|
139
|
-
}
|
|
140
|
-
// Never forward old or current provenance seams to Claude Code.
|
|
141
|
-
delete env.OCX_PRE_BUN_ANTHROPIC_ENV;
|
|
142
|
-
delete env.OCX_NODE_LAUNCH_CONTEXT;
|
|
176
|
+
deleteUntrustedAnthropicSlots(env, deps);
|
|
143
177
|
const setDefault = (name: string, value: string | undefined) => {
|
|
144
178
|
if (value === undefined || value.length === 0) return;
|
|
145
179
|
if (env[name] !== undefined && env[name] !== "") return; // user wins
|
|
@@ -302,7 +336,12 @@ export function buildClaudeEnv(
|
|
|
302
336
|
* daemon registers every selector form — audit R3#1). 3s bound + management auth header.
|
|
303
337
|
* (no [1m] marking, conservative).
|
|
304
338
|
*/
|
|
305
|
-
export
|
|
339
|
+
export interface ClaudeCodeLiveState {
|
|
340
|
+
contextWindows: Record<string, number>;
|
|
341
|
+
enabled?: boolean;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
export async function fetchClaudeCodeState(config: OcxConfig, port: number, timeoutMs = 3_000): Promise<ClaudeCodeLiveState> {
|
|
306
345
|
try {
|
|
307
346
|
const headers = new Headers();
|
|
308
347
|
const token = configuredAdminToken();
|
|
@@ -311,15 +350,22 @@ export async function fetchClaudeContextWindows(config: OcxConfig, port: number,
|
|
|
311
350
|
headers,
|
|
312
351
|
signal: AbortSignal.timeout(timeoutMs),
|
|
313
352
|
});
|
|
314
|
-
if (!res.ok) return {};
|
|
315
|
-
const body = await res.json() as { contextWindows?: Record<string, number
|
|
316
|
-
return
|
|
353
|
+
if (!res.ok) return { contextWindows: {} };
|
|
354
|
+
const body = await res.json() as { contextWindows?: Record<string, number>; enabled?: boolean };
|
|
355
|
+
return {
|
|
356
|
+
contextWindows: body.contextWindows && typeof body.contextWindows === "object" ? body.contextWindows : {},
|
|
357
|
+
...(typeof body.enabled === "boolean" ? { enabled: body.enabled } : {}),
|
|
358
|
+
};
|
|
317
359
|
} catch {
|
|
318
360
|
console.error("⚠ 모델 컨텍스트 정보를 불러오지 못했습니다 — 1M 자동 표시는 이번 실행에서 생략됩니다.");
|
|
319
|
-
return {};
|
|
361
|
+
return { contextWindows: {} };
|
|
320
362
|
}
|
|
321
363
|
}
|
|
322
364
|
|
|
365
|
+
export async function fetchClaudeContextWindows(config: OcxConfig, port: number, timeoutMs = 3_000): Promise<Record<string, number>> {
|
|
366
|
+
return (await fetchClaudeCodeState(config, port, timeoutMs)).contextWindows;
|
|
367
|
+
}
|
|
368
|
+
|
|
323
369
|
export function readConnectedClaudeContextWindows(path = DEFAULT_CATALOG_PATH): Record<string, number> {
|
|
324
370
|
try {
|
|
325
371
|
const parsed = JSON.parse(readFileSync(path, "utf8")) as { models?: unknown };
|
|
@@ -384,6 +430,139 @@ export async function ensureProxyForClaude(deps: ClaudeProxyEnsureDeps = {}): Pr
|
|
|
384
430
|
return null;
|
|
385
431
|
}
|
|
386
432
|
|
|
433
|
+
export const CLAUDE_NATIVE_ROUTING_OFF =
|
|
434
|
+
"ℹ️ Claude Code routing is disabled in OpenCodex. Launching Claude Code natively. Enable Claude routing to use the proxy again.";
|
|
435
|
+
|
|
436
|
+
export const CLAUDE_NATIVE_LIVE_DISABLED =
|
|
437
|
+
"ℹ️ The running OpenCodex proxy has Claude Code routing disabled. Launching Claude Code natively. Restart the service after enabling routing.";
|
|
438
|
+
|
|
439
|
+
export type ClaudeLaunchPlan =
|
|
440
|
+
| { kind: "routed" }
|
|
441
|
+
| { kind: "native"; notice: string };
|
|
442
|
+
|
|
443
|
+
export function claudeLaunchPlan(
|
|
444
|
+
configuredEnabled: boolean,
|
|
445
|
+
liveEnabled: boolean | undefined,
|
|
446
|
+
): ClaudeLaunchPlan {
|
|
447
|
+
if (!configuredEnabled) return { kind: "native", notice: CLAUDE_NATIVE_ROUTING_OFF };
|
|
448
|
+
if (liveEnabled === false) return { kind: "native", notice: CLAUDE_NATIVE_LIVE_DISABLED };
|
|
449
|
+
return { kind: "routed" };
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
export type ClaudeLaunchPreflight =
|
|
453
|
+
| { kind: "continue" }
|
|
454
|
+
| { kind: "native"; notice: string }
|
|
455
|
+
| { kind: "error"; message: string };
|
|
456
|
+
|
|
457
|
+
/** Validate connected-client ownership before any native fallback can run. */
|
|
458
|
+
export function claudeLaunchPreflight(
|
|
459
|
+
configuredEnabled: boolean,
|
|
460
|
+
clientState: ClientConnectionState,
|
|
461
|
+
tokenState?: ServiceApiTokenState,
|
|
462
|
+
): ClaudeLaunchPreflight {
|
|
463
|
+
if (clientState.kind === "invalid" || clientState.kind === "mismatched") {
|
|
464
|
+
return { kind: "error", message: `Client state is ${clientState.kind}: ${clientState.reason}` };
|
|
465
|
+
}
|
|
466
|
+
if (clientState.kind === "connected") {
|
|
467
|
+
if (!clientState.value.selectedClients.includes("claude")) {
|
|
468
|
+
return { kind: "error", message: "Claude is not selected for this remote hub connection." };
|
|
469
|
+
}
|
|
470
|
+
if (tokenState?.kind !== "present" || tokenState.fingerprint !== clientState.value.tokenFingerprint) {
|
|
471
|
+
return {
|
|
472
|
+
kind: "error",
|
|
473
|
+
message: tokenState?.kind === "absent"
|
|
474
|
+
? "Connected service token is missing."
|
|
475
|
+
: "Connected service token ownership changed.",
|
|
476
|
+
};
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
return configuredEnabled
|
|
480
|
+
? { kind: "continue" }
|
|
481
|
+
: { kind: "native", notice: CLAUDE_NATIVE_ROUTING_OFF };
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
const NATIVE_STRIPPED_LEVERS = [
|
|
485
|
+
"CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY",
|
|
486
|
+
"CLAUDE_CODE_PROVIDER_MANAGED_BY_HOST",
|
|
487
|
+
"CLAUDE_CODE_MAX_CONTEXT_TOKENS",
|
|
488
|
+
"CLAUDE_CODE_AUTO_COMPACT_WINDOW",
|
|
489
|
+
"CLAUDE_CODE_ALWAYS_ENABLE_EFFORT",
|
|
490
|
+
"DISABLE_COMPACT",
|
|
491
|
+
] as const;
|
|
492
|
+
|
|
493
|
+
const MODEL_ENV_SLOT_NAMES = [
|
|
494
|
+
"ANTHROPIC_MODEL",
|
|
495
|
+
"ANTHROPIC_DEFAULT_OPUS_MODEL",
|
|
496
|
+
"ANTHROPIC_DEFAULT_SONNET_MODEL",
|
|
497
|
+
"ANTHROPIC_DEFAULT_FABLE_MODEL",
|
|
498
|
+
"ANTHROPIC_DEFAULT_HAIKU_MODEL",
|
|
499
|
+
"ANTHROPIC_SMALL_FAST_MODEL",
|
|
500
|
+
] as const;
|
|
501
|
+
|
|
502
|
+
const DESKTOP_3P_ALIAS = /^claude-opus-4(?:-8)?-[a-z][0-9a-z]{2}$/;
|
|
503
|
+
|
|
504
|
+
export function isProxyOnlyModelId(value: string, providerNames: readonly string[] = []): boolean {
|
|
505
|
+
const id = value.trim().replace(/\[1m\]$/, "");
|
|
506
|
+
if (!id) return false;
|
|
507
|
+
if (id.startsWith(CLAUDE_ALIAS_PREFIX_V1) || id.startsWith(CLAUDE_ALIAS_PREFIX_V2) || DESKTOP_3P_ALIAS.test(id)) {
|
|
508
|
+
return true;
|
|
509
|
+
}
|
|
510
|
+
const slash = id.indexOf("/");
|
|
511
|
+
return slash > 0 && providerNames.includes(id.slice(0, slash));
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
export function buildNativeClaudeEnv(
|
|
515
|
+
config: OcxConfig,
|
|
516
|
+
base: ClaudeLaunchEnv,
|
|
517
|
+
deps: ClaudeEnvDeps = {},
|
|
518
|
+
): ClaudeLaunchEnv {
|
|
519
|
+
const env: ClaudeLaunchEnv = { ...base };
|
|
520
|
+
deleteUntrustedAnthropicSlots(env, deps);
|
|
521
|
+
|
|
522
|
+
const admissionSlots = ["ANTHROPIC_AUTH_TOKEN", "ANTHROPIC_API_KEY"] as const;
|
|
523
|
+
const hasOwnedAdmission = admissionSlots.some(name => {
|
|
524
|
+
const value = env[name]?.trim();
|
|
525
|
+
return Boolean(value && (value === PROXY_MARKER || isProxyAdmissionSecret(value, config)));
|
|
526
|
+
});
|
|
527
|
+
const baseUrl = env.ANTHROPIC_BASE_URL;
|
|
528
|
+
if (hasOwnedAdmission && targetsLocalClaudeProxy(baseUrl, config.port)) {
|
|
529
|
+
delete env.ANTHROPIC_BASE_URL;
|
|
530
|
+
}
|
|
531
|
+
for (const name of admissionSlots) {
|
|
532
|
+
const value = env[name]?.trim();
|
|
533
|
+
if (value && (value === PROXY_MARKER || isProxyAdmissionSecret(value, config))) delete env[name];
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
for (const name of NATIVE_STRIPPED_LEVERS) delete env[name];
|
|
537
|
+
const providerNames = Object.keys(config.providers);
|
|
538
|
+
for (const name of MODEL_ENV_SLOT_NAMES) {
|
|
539
|
+
const value = env[name];
|
|
540
|
+
if (value && isProxyOnlyModelId(value, providerNames)) delete env[name];
|
|
541
|
+
}
|
|
542
|
+
if (deps.allowRootSkipPermissions === true && !env.IS_SANDBOX) env.IS_SANDBOX = "1";
|
|
543
|
+
return env;
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
export function nativeModelOverride(
|
|
547
|
+
pickedModel: string | null,
|
|
548
|
+
configuredModel: string | undefined,
|
|
549
|
+
args: readonly string[],
|
|
550
|
+
providerNames: readonly string[] = [],
|
|
551
|
+
): { flag?: string[]; warning?: string } {
|
|
552
|
+
if (!pickedModel || !isProxyOnlyModelId(pickedModel, providerNames)) return {};
|
|
553
|
+
if (args.some(arg => arg === "--model" || arg.startsWith("--model="))) return {};
|
|
554
|
+
const fallback = configuredModel?.trim();
|
|
555
|
+
if (fallback && !isProxyOnlyModelId(fallback, providerNames)) {
|
|
556
|
+
return {
|
|
557
|
+
flag: ["--model", fallback],
|
|
558
|
+
warning: `ℹ️ The saved model (${pickedModel}) requires the proxy. This native session will use ${fallback}.`,
|
|
559
|
+
};
|
|
560
|
+
}
|
|
561
|
+
return {
|
|
562
|
+
warning: `⚠ The saved model (${pickedModel}) requires the proxy. Use \`--model <Anthropic model>\` or select a native model in this session.`,
|
|
563
|
+
};
|
|
564
|
+
}
|
|
565
|
+
|
|
387
566
|
const CLAUDE_INSTALL_HINT = "❌ `claude` CLI not found. Install it first: npm install -g @anthropic-ai/claude-code";
|
|
388
567
|
|
|
389
568
|
/**
|
|
@@ -417,28 +596,19 @@ export function rootSkipPermissionsNotice(env: ClaudeLaunchEnv): string {
|
|
|
417
596
|
|
|
418
597
|
export async function cmdClaude(args: string[]): Promise<number> {
|
|
419
598
|
const config = loadConfig();
|
|
420
|
-
if (config.claudeCode?.enabled === false) {
|
|
421
|
-
console.error("Claude inbound is disabled (config.claudeCode.enabled=false — flip the Claude ON toggle in the GUI or edit config).");
|
|
422
|
-
return 1;
|
|
423
|
-
}
|
|
424
599
|
const clientState = readClientConnectionState();
|
|
425
|
-
|
|
426
|
-
|
|
600
|
+
const tokenState = clientState.kind === "connected" ? readServiceApiTokenState() : undefined;
|
|
601
|
+
const preflight = claudeLaunchPreflight(config.claudeCode?.enabled !== false, clientState, tokenState);
|
|
602
|
+
if (preflight.kind === "error") {
|
|
603
|
+
console.error(preflight.message);
|
|
427
604
|
return 1;
|
|
428
605
|
}
|
|
606
|
+
if (preflight.kind === "native") return launchNativeClaude(config, args, preflight.notice);
|
|
429
607
|
let route: number | ClaudeRoutingTarget;
|
|
430
608
|
let contextWindows: Record<string, number>;
|
|
431
609
|
if (clientState.kind === "connected") {
|
|
432
|
-
if (
|
|
433
|
-
|
|
434
|
-
return 1;
|
|
435
|
-
}
|
|
436
|
-
const token = readServiceApiTokenState();
|
|
437
|
-
if (token.kind !== "present" || token.fingerprint !== clientState.value.tokenFingerprint) {
|
|
438
|
-
console.error(token.kind === "absent" ? "Connected service token is missing." : "Connected service token ownership changed.");
|
|
439
|
-
return 1;
|
|
440
|
-
}
|
|
441
|
-
route = { baseUrl: clientState.value.serverUrl, admissionToken: token.token };
|
|
610
|
+
if (tokenState?.kind !== "present") return 1;
|
|
611
|
+
route = { baseUrl: clientState.value.serverUrl, admissionToken: tokenState.token };
|
|
442
612
|
contextWindows = readConnectedClaudeContextWindows();
|
|
443
613
|
} else {
|
|
444
614
|
const port = await ensureProxyForClaude();
|
|
@@ -446,8 +616,11 @@ export async function cmdClaude(args: string[]): Promise<number> {
|
|
|
446
616
|
console.error("❌ Proxy did not become healthy after starting.");
|
|
447
617
|
return 1;
|
|
448
618
|
}
|
|
619
|
+
const liveState = await fetchClaudeCodeState(config, port);
|
|
620
|
+
const plan = claudeLaunchPlan(true, liveState.enabled);
|
|
621
|
+
if (plan.kind === "native") return launchNativeClaude(config, args, plan.notice);
|
|
449
622
|
route = port;
|
|
450
|
-
contextWindows =
|
|
623
|
+
contextWindows = liveState.contextWindows;
|
|
451
624
|
}
|
|
452
625
|
const allowRootSkipPermissions = shouldAllowRootSkipPermissions(args);
|
|
453
626
|
const env = buildClaudeEnv(config, route, process.env, contextWindows, { allowRootSkipPermissions });
|
|
@@ -479,7 +652,27 @@ export async function cmdClaude(args: string[]): Promise<number> {
|
|
|
479
652
|
console.error(`⚠ Claude agent definitions could not be synced: ${message}`);
|
|
480
653
|
}
|
|
481
654
|
}
|
|
482
|
-
return
|
|
655
|
+
return spawnClaude(args, env);
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
async function launchNativeClaude(config: OcxConfig, args: string[], notice: string): Promise<number> {
|
|
659
|
+
console.error(notice);
|
|
660
|
+
const providerNames = Object.keys(config.providers);
|
|
661
|
+
const override = nativeModelOverride(
|
|
662
|
+
readPickerDefaultModel(claudeConfigDir()),
|
|
663
|
+
config.claudeCode?.model,
|
|
664
|
+
args,
|
|
665
|
+
providerNames,
|
|
666
|
+
);
|
|
667
|
+
if (override.warning) console.error(override.warning);
|
|
668
|
+
const allowRootSkipPermissions = shouldAllowRootSkipPermissions(args);
|
|
669
|
+
const env = buildNativeClaudeEnv(config, process.env, { allowRootSkipPermissions });
|
|
670
|
+
if (allowRootSkipPermissions) console.error(rootSkipPermissionsNotice(env));
|
|
671
|
+
return spawnClaude([...(override.flag ?? []), ...args], env);
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
function spawnClaude(args: string[], env: ClaudeLaunchEnv): Promise<number> {
|
|
675
|
+
return new Promise<number>(resolve => {
|
|
483
676
|
const inv = commandInvocation("claude", args);
|
|
484
677
|
const child = spawn(inv.file, inv.args, { stdio: "inherit", env: env as NodeJS.ProcessEnv, ...inv.options });
|
|
485
678
|
child.on("error", (err: NodeJS.ErrnoException) => {
|
|
@@ -15,7 +15,15 @@ const USAGE = `Usage:
|
|
|
15
15
|
ocx config export <path|->
|
|
16
16
|
ocx config import <path|-> --yes [--json]`;
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
/**
|
|
19
|
+
* Keys whose VALUE is a credential and must never be printed or exported.
|
|
20
|
+
*
|
|
21
|
+
* `webhookUrl` is here because for Slack and Discord the URL itself is the authorization:
|
|
22
|
+
* anyone holding it can post to the channel. It looks like configuration rather than a secret,
|
|
23
|
+
* which is exactly why it needs to be named explicitly — none of the other patterns match it,
|
|
24
|
+
* so `ocx config show` printed it and `config export` wrote it to disk in the clear.
|
|
25
|
+
*/
|
|
26
|
+
const SECRET_KEYS = /^(apiKey|key|accessToken|refreshToken|idToken|token|password|clientSecret|webhookUrl)$/i;
|
|
19
27
|
const BLOCKED_SEGMENTS = new Set(["__proto__", "prototype", "constructor"]);
|
|
20
28
|
|
|
21
29
|
function redact(value: unknown, key = ""): unknown {
|
package/src/cli/dispatch.ts
CHANGED
|
@@ -349,7 +349,7 @@ const commandRunners: Record<string, CommandRunner> = {
|
|
|
349
349
|
: "Remote hub catalog synchronized.");
|
|
350
350
|
await handleConnectedSyncCatalogWrite(result, restartCodex, restartDesktopApp);
|
|
351
351
|
// `process.exitCode` rather than a literal 0, for the same reason every other
|
|
352
|
-
// runner does it (tests/cli-transport-honesty.test.ts): the catalog-write helper
|
|
352
|
+
// runner does it (tests/cli/cli-transport-honesty.test.ts): the catalog-write helper
|
|
353
353
|
// drives app-server restarts, and one of those recording a failure must not be
|
|
354
354
|
// erased by the value this runner returns. It reads 0 on the ordinary path. Node
|
|
355
355
|
// types it as `number | string`; only a numeric code means anything here.
|
|
@@ -698,6 +698,10 @@ const commandRunners: Record<string, CommandRunner> = {
|
|
|
698
698
|
return await handleRoutePolicyCommand(deps.args.slice(2));
|
|
699
699
|
}
|
|
700
700
|
},
|
|
701
|
+
effort: async deps => {
|
|
702
|
+
const { handleEffortCommand } = await import("./effort");
|
|
703
|
+
return await handleEffortCommand(deps.args.slice(1), { findLiveProxy: deps.findLiveProxy });
|
|
704
|
+
},
|
|
701
705
|
agent: async deps => {
|
|
702
706
|
const { handleAgentCommand } = await import("./agent");
|
|
703
707
|
return await handleAgentCommand(deps.args.slice(1));
|
package/src/cli/doctor.ts
CHANGED
|
@@ -46,6 +46,10 @@ import {
|
|
|
46
46
|
resolveEffectiveUserIdentity,
|
|
47
47
|
} from "../codex/user-identity";
|
|
48
48
|
import { collectProjectCodexConfigWarnings, formatProjectCodexConfigWarningsForDoctor } from "../codex/project-config-warnings";
|
|
49
|
+
import {
|
|
50
|
+
collectLegacyCodexConfigKeyDiagnostics,
|
|
51
|
+
formatLegacyCodexConfigKeyDiagnosticsForDoctor,
|
|
52
|
+
} from "../codex/legacy-config-keys";
|
|
49
53
|
import { collectStartupHealth, formatStartupRoutingDetail, startupHealthSummary } from "../codex/autostart-health";
|
|
50
54
|
import {
|
|
51
55
|
displayCodexRuntimePath,
|
|
@@ -1270,6 +1274,12 @@ export async function runDoctor(args: string[] = []): Promise<void> {
|
|
|
1270
1274
|
}
|
|
1271
1275
|
}
|
|
1272
1276
|
|
|
1277
|
+
console.log("\nCodex config compatibility");
|
|
1278
|
+
const legacyKeyResult = collectLegacyCodexConfigKeyDiagnostics();
|
|
1279
|
+
for (const line of formatLegacyCodexConfigKeyDiagnosticsForDoctor(legacyKeyResult)) {
|
|
1280
|
+
console.log(line);
|
|
1281
|
+
}
|
|
1282
|
+
|
|
1273
1283
|
console.log("\nCodex agent role files");
|
|
1274
1284
|
const tomlFallbackRoles = scanCodexAgentRolesWithTomlModelFallback(resolveCodexHomeDirImpl());
|
|
1275
1285
|
if (tomlFallbackRoles.length === 0) {
|