@yansigit/opencodex 2.31.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 +109 -0
- package/LICENSE +21 -0
- package/README.md +303 -0
- package/assets/architecture.png +0 -0
- package/assets/banner.png +0 -0
- package/assets/claude-code-models.gif +0 -0
- package/assets/codex-app-picker.png +0 -0
- package/bin/ocx.mjs +587 -0
- package/bin/package-main.mjs +9 -0
- package/gui/dist/assets/index-BNJ7r4Gd.js +102 -0
- package/gui/dist/assets/index-CGoDO3uO.css +1 -0
- package/gui/dist/favicon.png +0 -0
- package/gui/dist/icons.svg +24 -0
- package/gui/dist/index.html +25 -0
- package/gui/dist/logo.png +0 -0
- package/gui/dist/provider-icons/alibaba-color.svg +1 -0
- package/gui/dist/provider-icons/antigravity-color.svg +1 -0
- package/gui/dist/provider-icons/claude-color.svg +1 -0
- package/gui/dist/provider-icons/cline-color.svg +16 -0
- package/gui/dist/provider-icons/cloudflare-ai-gateway-color.svg +1 -0
- package/gui/dist/provider-icons/commandcode-color.svg +1 -0
- package/gui/dist/provider-icons/copilot-color.svg +1 -0
- package/gui/dist/provider-icons/cursor-color.svg +2 -0
- package/gui/dist/provider-icons/deepseek-color.svg +1 -0
- package/gui/dist/provider-icons/discord.svg +1 -0
- package/gui/dist/provider-icons/firepass-color.svg +1 -0
- package/gui/dist/provider-icons/fireworks-color.svg +1 -0
- package/gui/dist/provider-icons/gemini-color.svg +1 -0
- package/gui/dist/provider-icons/github-copilot-color.svg +1 -0
- package/gui/dist/provider-icons/gitlab-duo-color.svg +1 -0
- package/gui/dist/provider-icons/grok.svg +1 -0
- package/gui/dist/provider-icons/groq-color.svg +1 -0
- package/gui/dist/provider-icons/huggingface-color.svg +1 -0
- package/gui/dist/provider-icons/kimi-color.svg +1 -0
- package/gui/dist/provider-icons/kiro-color.svg +15 -0
- package/gui/dist/provider-icons/lm-studio-color.svg +1 -0
- package/gui/dist/provider-icons/mistral-color.svg +1 -0
- package/gui/dist/provider-icons/moonshot-color.svg +1 -0
- package/gui/dist/provider-icons/nvidia-color.svg +1 -0
- package/gui/dist/provider-icons/ollama-color.svg +1 -0
- package/gui/dist/provider-icons/openai.svg +1 -0
- package/gui/dist/provider-icons/opencode.svg +2 -0
- package/gui/dist/provider-icons/openrouter-color.svg +1 -0
- package/gui/dist/provider-icons/pi.svg +21 -0
- package/gui/dist/provider-icons/qianfan-color.svg +1 -0
- package/gui/dist/provider-icons/qwen-portal-color.svg +1 -0
- package/gui/dist/provider-icons/telegram.svg +1 -0
- package/gui/dist/provider-icons/vercel-ai-gateway-color.svg +1 -0
- package/gui/dist/provider-icons/vllm-color.svg +1 -0
- package/gui/dist/provider-icons/xiaomi-color.svg +1 -0
- package/package.json +108 -0
- package/src/AGENTS.md +28 -0
- package/src/adapters/anthropic-image-guard.ts +251 -0
- package/src/adapters/anthropic-image-normalize.ts +518 -0
- package/src/adapters/anthropic-output-schema.ts +137 -0
- package/src/adapters/anthropic.ts +1327 -0
- package/src/adapters/azure.ts +36 -0
- package/src/adapters/base.ts +121 -0
- package/src/adapters/client-fingerprint.ts +65 -0
- package/src/adapters/cline-pass-deepseek-v4-tool-replay.ts +69 -0
- package/src/adapters/command-code.ts +601 -0
- package/src/adapters/cursor/arg-codec.ts +38 -0
- package/src/adapters/cursor/arg-normalize.ts +104 -0
- package/src/adapters/cursor/checkpoint-store.ts +303 -0
- package/src/adapters/cursor/cursor-errors.ts +288 -0
- package/src/adapters/cursor/discovery.ts +333 -0
- package/src/adapters/cursor/effort-map.ts +151 -0
- package/src/adapters/cursor/exec-policy.ts +88 -0
- package/src/adapters/cursor/framing.ts +250 -0
- package/src/adapters/cursor/gen/agent_pb.ts +15274 -0
- package/src/adapters/cursor/h2-pool.ts +123 -0
- package/src/adapters/cursor/http1-bidi.ts +361 -0
- package/src/adapters/cursor/images.ts +704 -0
- package/src/adapters/cursor/kv-store.ts +52 -0
- package/src/adapters/cursor/live-models.ts +269 -0
- package/src/adapters/cursor/live-smoke-gate.ts +41 -0
- package/src/adapters/cursor/live-transport.ts +1653 -0
- package/src/adapters/cursor/mcp-config.ts +42 -0
- package/src/adapters/cursor/mcp-manager.ts +333 -0
- package/src/adapters/cursor/message-mapper.ts +49 -0
- package/src/adapters/cursor/native-exec-common.ts +76 -0
- package/src/adapters/cursor/native-exec-desktop.ts +184 -0
- package/src/adapters/cursor/native-exec-fs.ts +332 -0
- package/src/adapters/cursor/native-exec-mcp.ts +153 -0
- package/src/adapters/cursor/native-exec-network.ts +43 -0
- package/src/adapters/cursor/native-exec-shell.ts +547 -0
- package/src/adapters/cursor/native-exec-tools.ts +118 -0
- package/src/adapters/cursor/native-exec.ts +663 -0
- package/src/adapters/cursor/protobuf-events.ts +1381 -0
- package/src/adapters/cursor/protobuf-request.ts +1032 -0
- package/src/adapters/cursor/request-builder.ts +461 -0
- package/src/adapters/cursor/thread-continuity.ts +67 -0
- package/src/adapters/cursor/tool-definitions.ts +735 -0
- package/src/adapters/cursor/tool-result-normalize.ts +92 -0
- package/src/adapters/cursor/transport-retry.ts +132 -0
- package/src/adapters/cursor/transport.ts +79 -0
- package/src/adapters/cursor/types.ts +79 -0
- package/src/adapters/cursor.ts +322 -0
- package/src/adapters/google-antigravity-hosts.ts +48 -0
- package/src/adapters/google-antigravity-replay.ts +827 -0
- package/src/adapters/google-antigravity-tools.ts +105 -0
- package/src/adapters/google-antigravity-wire.ts +141 -0
- package/src/adapters/google-errors.ts +92 -0
- package/src/adapters/google-http.ts +460 -0
- package/src/adapters/google-tool-schema.ts +238 -0
- package/src/adapters/google-truncation.ts +24 -0
- package/src/adapters/google-wire-compiler.ts +232 -0
- package/src/adapters/google.ts +1377 -0
- package/src/adapters/identity.ts +77 -0
- package/src/adapters/image.ts +23 -0
- package/src/adapters/kiro-constants.ts +16 -0
- package/src/adapters/kiro-errors.ts +208 -0
- package/src/adapters/kiro-events.ts +197 -0
- package/src/adapters/kiro-images.ts +129 -0
- package/src/adapters/kiro-retry.ts +312 -0
- package/src/adapters/kiro-thinking.ts +112 -0
- package/src/adapters/kiro-tool-fallback.ts +36 -0
- package/src/adapters/kiro-tools.ts +224 -0
- package/src/adapters/kiro-truncation.ts +33 -0
- package/src/adapters/kiro-wire.ts +129 -0
- package/src/adapters/kiro.ts +1936 -0
- package/src/adapters/mimo-free.ts +280 -0
- package/src/adapters/openai-chat-url.ts +11 -0
- package/src/adapters/openai-chat.ts +1980 -0
- package/src/adapters/openai-responses-url.ts +16 -0
- package/src/adapters/openai-responses.ts +1911 -0
- package/src/adapters/registry.ts +175 -0
- package/src/adapters/responses-tool-schema.ts +67 -0
- package/src/adapters/run-turn-queue.ts +114 -0
- package/src/adapters/tool-call-id.ts +119 -0
- package/src/adapters/tool-catalog-nudge.ts +154 -0
- package/src/adapters/upstream-http-error.ts +48 -0
- package/src/adapters/xai-web-search.ts +185 -0
- package/src/bridge.ts +1986 -0
- package/src/chat/inbound.ts +319 -0
- package/src/chat/outbound.ts +821 -0
- package/src/claude/agents-inject.ts +266 -0
- package/src/claude/alias.ts +149 -0
- package/src/claude/auth-detect.ts +229 -0
- package/src/claude/auth-mode-migration.ts +32 -0
- package/src/claude/auth-mode.ts +62 -0
- package/src/claude/context-windows.ts +205 -0
- package/src/claude/desktop-3p-guard.ts +35 -0
- package/src/claude/desktop-3p-paths.ts +84 -0
- package/src/claude/desktop-3p.ts +615 -0
- package/src/claude/desktop-health.ts +26 -0
- package/src/claude/desktop-profile.ts +263 -0
- package/src/claude/gateway-cache.ts +107 -0
- package/src/claude/inbound-debug.ts +163 -0
- package/src/claude/inbound.ts +578 -0
- package/src/claude/model-info.ts +174 -0
- package/src/claude/outbound.ts +926 -0
- package/src/cli/access.ts +108 -0
- package/src/cli/account-api.ts +302 -0
- package/src/cli/account-auth.ts +250 -0
- package/src/cli/account-catalog-refresh.ts +14 -0
- package/src/cli/account-extended.ts +737 -0
- package/src/cli/account-main.ts +317 -0
- package/src/cli/account.ts +299 -0
- package/src/cli/agent-driven.ts +70 -0
- package/src/cli/agent.ts +290 -0
- package/src/cli/catalog-prewarm.ts +27 -0
- package/src/cli/claude-agent-startup-sync.ts +73 -0
- package/src/cli/claude-desktop.ts +213 -0
- package/src/cli/claude.ts +355 -0
- package/src/cli/codex-log-guard-doctor.ts +103 -0
- package/src/cli/codex-shim-autorestore.ts +47 -0
- package/src/cli/codex-shim-readiness.ts +76 -0
- package/src/cli/combo.ts +127 -0
- package/src/cli/config-command.ts +209 -0
- package/src/cli/debug.ts +228 -0
- package/src/cli/dispatch.ts +585 -0
- package/src/cli/doctor.ts +1202 -0
- package/src/cli/ensure-desired-integrations.ts +152 -0
- package/src/cli/export-command.ts +213 -0
- package/src/cli/help.ts +101 -0
- package/src/cli/index.ts +973 -0
- package/src/cli/init.ts +211 -0
- package/src/cli/integrations.ts +260 -0
- package/src/cli/interactive-confirm.ts +133 -0
- package/src/cli/lab.ts +607 -0
- package/src/cli/launcher-context.ts +77 -0
- package/src/cli/minimax.ts +497 -0
- package/src/cli/models-runtime.ts +245 -0
- package/src/cli/models.ts +422 -0
- package/src/cli/observe.ts +206 -0
- package/src/cli/opencode.ts +588 -0
- package/src/cli/provider-replit.ts +232 -0
- package/src/cli/provider-runtime.ts +179 -0
- package/src/cli/provider.ts +492 -0
- package/src/cli/ready.ts +301 -0
- package/src/cli/registry.ts +422 -0
- package/src/cli/replit-gateway-key-input.ts +138 -0
- package/src/cli/root.ts +86 -0
- package/src/cli/route-policy.ts +92 -0
- package/src/cli/runtime-api.ts +328 -0
- package/src/cli/star-prompt.ts +211 -0
- package/src/cli/status-oauth.ts +78 -0
- package/src/cli/status.ts +328 -0
- package/src/cli/system-command.ts +112 -0
- package/src/cli/system-restart-client.ts +146 -0
- package/src/cli/tray-proxy.ts +199 -0
- package/src/cli/v2.ts +268 -0
- package/src/cli.ts +10 -0
- package/src/clients/config-export.ts +1704 -0
- package/src/codex/account-id.ts +34 -0
- package/src/codex/account-label.ts +47 -0
- package/src/codex/account-lifecycle.ts +172 -0
- package/src/codex/account-namespace-match.ts +63 -0
- package/src/codex/account-namespaces.ts +195 -0
- package/src/codex/account-pause.ts +20 -0
- package/src/codex/account-priority.ts +83 -0
- package/src/codex/account-runtime-state.ts +31 -0
- package/src/codex/account-store.ts +544 -0
- package/src/codex/account-usability.ts +43 -0
- package/src/codex/admission.ts +256 -0
- package/src/codex/affinity-debug.ts +162 -0
- package/src/codex/agent-roles-sync.ts +225 -0
- package/src/codex/agent-roles.ts +238 -0
- package/src/codex/app-server-processes.ts +1143 -0
- package/src/codex/app-server-restart-service.ts +232 -0
- package/src/codex/auth-api.ts +2147 -0
- package/src/codex/auth-collision.ts +109 -0
- package/src/codex/auth-context.ts +665 -0
- package/src/codex/autostart-health.ts +156 -0
- package/src/codex/catalog/account-models.ts +67 -0
- package/src/codex/catalog/aggregation.ts +436 -0
- package/src/codex/catalog/bundled.ts +549 -0
- package/src/codex/catalog/effort.ts +446 -0
- package/src/codex/catalog/filesystem-evidence.ts +302 -0
- package/src/codex/catalog/kinds.ts +2 -0
- package/src/codex/catalog/metadata.ts +664 -0
- package/src/codex/catalog/native-models.ts +72 -0
- package/src/codex/catalog/parsing.ts +650 -0
- package/src/codex/catalog/provider-fetch.ts +2064 -0
- package/src/codex/catalog/sync.ts +1883 -0
- package/src/codex/catalog-admission.ts +199 -0
- package/src/codex/catalog-refresh-status.ts +105 -0
- package/src/codex/catalog-write-serialization.ts +242 -0
- package/src/codex/catalog.ts +14 -0
- package/src/codex/codex-write-lock.ts +384 -0
- package/src/codex/convergence-types.ts +614 -0
- package/src/codex/convergence.ts +651 -0
- package/src/codex/coordinator-doctor.ts +332 -0
- package/src/codex/custom-model-catalog-migration.ts +176 -0
- package/src/codex/data/upstream-models.json +830 -0
- package/src/codex/desired-state.ts +230 -0
- package/src/codex/exec-invocation.ts +22 -0
- package/src/codex/features.ts +1566 -0
- package/src/codex/generation.ts +202 -0
- package/src/codex/history-job.ts +407 -0
- package/src/codex/history-lock.ts +242 -0
- package/src/codex/history-migration-guardian.ts +108 -0
- package/src/codex/history-provider.ts +979 -0
- package/src/codex/history-transition.ts +105 -0
- package/src/codex/history-worker.ts +220 -0
- package/src/codex/home.ts +206 -0
- package/src/codex/inject-coordination.ts +290 -0
- package/src/codex/inject.ts +1733 -0
- package/src/codex/injected-marker.ts +106 -0
- package/src/codex/integration-record.ts +266 -0
- package/src/codex/internal/catalog-writer.ts +203 -0
- package/src/codex/internal/history-writer.ts +80 -0
- package/src/codex/journal.ts +225 -0
- package/src/codex/log-guard/inspect.ts +506 -0
- package/src/codex/log-guard/lock.ts +150 -0
- package/src/codex/log-guard/maintenance.ts +403 -0
- package/src/codex/log-guard/path-safety.ts +88 -0
- package/src/codex/log-guard/policy.ts +44 -0
- package/src/codex/log-guard/processes.ts +205 -0
- package/src/codex/log-guard/protection.ts +489 -0
- package/src/codex/log-guard/sqlite-errors.ts +9 -0
- package/src/codex/main-account-cache.ts +56 -0
- package/src/codex/main-account.ts +68 -0
- package/src/codex/management-convergence.ts +167 -0
- package/src/codex/model-cache.ts +273 -0
- package/src/codex/model-entitlements.ts +353 -0
- package/src/codex/native-main-admission.ts +47 -0
- package/src/codex/native-main-auth-temp.ts +187 -0
- package/src/codex/native-main-claim.ts +178 -0
- package/src/codex/native-main-lock-file.ts +162 -0
- package/src/codex/native-main-owner.ts +329 -0
- package/src/codex/native-profile-api.ts +247 -0
- package/src/codex/native-profile-manager.ts +1531 -0
- package/src/codex/native-profile-processes.ts +121 -0
- package/src/codex/native-profile-recovery.ts +99 -0
- package/src/codex/native-profile-stage-store.ts +387 -0
- package/src/codex/native-profile-startup.ts +492 -0
- package/src/codex/native-profile-store.ts +855 -0
- package/src/codex/native-profile-types.ts +120 -0
- package/src/codex/native-residue.ts +682 -0
- package/src/codex/paths.ts +144 -0
- package/src/codex/plan-from-token.ts +140 -0
- package/src/codex/plan.ts +40 -0
- package/src/codex/plugins-doctor.ts +242 -0
- package/src/codex/pool-rotation.ts +295 -0
- package/src/codex/project-config-warnings.ts +425 -0
- package/src/codex/prompt-journal.ts +352 -0
- package/src/codex/prompt-layers.ts +967 -0
- package/src/codex/prompt-lock.ts +143 -0
- package/src/codex/quota-rejection.ts +298 -0
- package/src/codex/quota.ts +573 -0
- package/src/codex/refresh.ts +62 -0
- package/src/codex/reset-credit-recovery.ts +1044 -0
- package/src/codex/routing.ts +1888 -0
- package/src/codex/runtime.ts +659 -0
- package/src/codex/shim.ts +2170 -0
- package/src/codex/subagent-defaults.ts +550 -0
- package/src/codex/subagent-model-fallback.ts +784 -0
- package/src/codex/sync.ts +319 -0
- package/src/codex/transition-state.ts +612 -0
- package/src/codex/upstream-host-health.ts +368 -0
- package/src/codex/user-identity.ts +557 -0
- package/src/codex/warmup.ts +298 -0
- package/src/codex/websocket-registry.ts +100 -0
- package/src/codex/write-coordination.ts +114 -0
- package/src/combos/failover.ts +160 -0
- package/src/combos/index.ts +45 -0
- package/src/combos/request.ts +94 -0
- package/src/combos/resolve.ts +232 -0
- package/src/combos/types.ts +398 -0
- package/src/config/provider-name.ts +24 -0
- package/src/config.ts +4041 -0
- package/src/fork/register.ts +3 -0
- package/src/generated/compatibility-version.json +3116 -0
- package/src/generated/model-metadata.ts +106 -0
- package/src/github/star-state.ts +203 -0
- package/src/grok/inject.ts +530 -0
- package/src/grok/inspect.ts +45 -0
- package/src/grok/status.ts +121 -0
- package/src/grok/sync.ts +66 -0
- package/src/images/artifacts.ts +516 -0
- package/src/images/fulfill-video.ts +163 -0
- package/src/images/fulfill.ts +149 -0
- package/src/images/index.ts +4 -0
- package/src/images/loop.ts +955 -0
- package/src/images/plan.ts +143 -0
- package/src/images/synthetic-tool.ts +133 -0
- package/src/images/types.ts +41 -0
- package/src/images/xai-client.ts +141 -0
- package/src/images/xai-video-client.ts +163 -0
- package/src/index.ts +22 -0
- package/src/integrations/config-io.ts +269 -0
- package/src/integrations/journal.ts +315 -0
- package/src/integrations/merge.ts +135 -0
- package/src/integrations/mutation-flight.ts +71 -0
- package/src/integrations/native/ownership-preflight.ts +202 -0
- package/src/integrations/omp-yaml-source.ts +358 -0
- package/src/integrations/owned-refresh.ts +74 -0
- package/src/integrations/ownership.ts +111 -0
- package/src/integrations/registry.ts +159 -0
- package/src/integrations/serialize.ts +314 -0
- package/src/integrations/state.ts +361 -0
- package/src/integrations/store.ts +103 -0
- package/src/integrations/writer-lock.ts +98 -0
- package/src/integrations/writer.ts +691 -0
- package/src/lab/artifacts/sanitize.ts +586 -0
- package/src/lab/artifacts/secure-fs.ts +475 -0
- package/src/lab/artifacts/store.ts +310 -0
- package/src/lab/automation/budgets.ts +78 -0
- package/src/lab/automation/config-persistence.ts +256 -0
- package/src/lab/automation/constants.ts +39 -0
- package/src/lab/automation/cooldown.ts +103 -0
- package/src/lab/automation/dispatch.ts +211 -0
- package/src/lab/automation/index.ts +13 -0
- package/src/lab/automation/orchestrator.ts +499 -0
- package/src/lab/automation/persistence.ts +512 -0
- package/src/lab/automation/planner.ts +371 -0
- package/src/lab/automation/policy.ts +136 -0
- package/src/lab/automation/queue.ts +191 -0
- package/src/lab/automation/recovery.ts +24 -0
- package/src/lab/automation/route-context.ts +21 -0
- package/src/lab/automation/run-key.ts +44 -0
- package/src/lab/automation/runs-query.ts +34 -0
- package/src/lab/automation/types.ts +160 -0
- package/src/lab/conformance/assertion.ts +325 -0
- package/src/lab/conformance/digest.ts +22 -0
- package/src/lab/conformance/executor.ts +741 -0
- package/src/lab/conformance/fixture-provider.ts +27 -0
- package/src/lab/conformance/fixtures/live-v1-cases.json +175 -0
- package/src/lab/conformance/fixtures/protocol-v1-cases.json +461 -0
- package/src/lab/conformance/harness-budget.ts +47 -0
- package/src/lab/conformance/index.ts +5 -0
- package/src/lab/conformance/jcs.ts +64 -0
- package/src/lab/conformance/json-pointer.ts +39 -0
- package/src/lab/conformance/manifest.ts +180 -0
- package/src/lab/conformance/mcp-stub.ts +179 -0
- package/src/lab/conformance/negative-controls.ts +164 -0
- package/src/lab/conformance/observation.ts +355 -0
- package/src/lab/conformance/runner.ts +68 -0
- package/src/lab/conformance/sse-normalize.ts +59 -0
- package/src/lab/conformance/suite-manifest.ts +78 -0
- package/src/lab/conformance/types.ts +214 -0
- package/src/lab/constants.ts +126 -0
- package/src/lab/digest.ts +64 -0
- package/src/lab/events/errors.ts +9 -0
- package/src/lab/events/limits.ts +117 -0
- package/src/lab/events/types.ts +229 -0
- package/src/lab/events/validate.ts +781 -0
- package/src/lab/fabric/constants.ts +40 -0
- package/src/lab/fabric/executor.ts +492 -0
- package/src/lab/fabric/index.ts +80 -0
- package/src/lab/fabric/manifest.ts +222 -0
- package/src/lab/fabric/observe.ts +489 -0
- package/src/lab/fabric/patch.ts +79 -0
- package/src/lab/fabric/producer-child.ts +139 -0
- package/src/lab/fabric/producer-isolate.ts +276 -0
- package/src/lab/fabric/producer-protocol.ts +61 -0
- package/src/lab/fabric/scratch.ts +439 -0
- package/src/lab/fabric/subject.ts +106 -0
- package/src/lab/fabric/types.ts +134 -0
- package/src/lab/fabric/verifier.ts +98 -0
- package/src/lab/index.ts +54 -0
- package/src/lab/ledger/artifact-refs.ts +127 -0
- package/src/lab/ledger/invalidation.ts +136 -0
- package/src/lab/ledger/purge.ts +310 -0
- package/src/lab/ledger/store.ts +532 -0
- package/src/lab/live/credential-lease.ts +53 -0
- package/src/lab/live/destination.ts +155 -0
- package/src/lab/live/executor.ts +336 -0
- package/src/lab/live/inert-tools.ts +56 -0
- package/src/lab/live/manifest.ts +85 -0
- package/src/lab/live/mcp-loopback.ts +57 -0
- package/src/lab/live/runner.ts +19 -0
- package/src/lab/live/sandbox.ts +61 -0
- package/src/lab/live/suite-manifest.ts +41 -0
- package/src/lab/live/transport.ts +118 -0
- package/src/lab/live/types.ts +197 -0
- package/src/lab/observe/from-conformance.ts +301 -0
- package/src/lab/observe/from-live.ts +117 -0
- package/src/lab/paths.ts +153 -0
- package/src/lab/projection/rebuild.ts +495 -0
- package/src/lab/projection/schema.ts +135 -0
- package/src/lab/projection/verdicts.ts +474 -0
- package/src/lab/projection/verification.ts +412 -0
- package/src/lab/public/bundle.ts +217 -0
- package/src/lab/public/community-authority.ts +175 -0
- package/src/lab/public/community-files.ts +29 -0
- package/src/lab/public/community.ts +479 -0
- package/src/lab/public/file-safety.ts +155 -0
- package/src/lab/public/ids.ts +26 -0
- package/src/lab/public/index.ts +16 -0
- package/src/lab/public/mutation-lock.ts +424 -0
- package/src/lab/public/operator.ts +353 -0
- package/src/lab/public/origin-purge.ts +79 -0
- package/src/lab/public/origin.ts +203 -0
- package/src/lab/public/privacy.ts +143 -0
- package/src/lab/public/private-file.ts +261 -0
- package/src/lab/public/project.ts +124 -0
- package/src/lab/public/purge-test-fault.ts +21 -0
- package/src/lab/public/purge.ts +223 -0
- package/src/lab/public/registry.ts +44 -0
- package/src/lab/public/revocation.ts +252 -0
- package/src/lab/public/signature.ts +243 -0
- package/src/lab/public/storage.ts +105 -0
- package/src/lab/public/strict-json.ts +206 -0
- package/src/lab/public/time.ts +26 -0
- package/src/lab/public/types.ts +172 -0
- package/src/lab/public/validate.ts +391 -0
- package/src/lab/query/catalog.ts +101 -0
- package/src/lab/query/connection.ts +107 -0
- package/src/lab/query/constants.ts +4 -0
- package/src/lab/query/cursor.ts +132 -0
- package/src/lab/query/dto-map.ts +277 -0
- package/src/lab/query/errors.ts +22 -0
- package/src/lab/query/freshness.ts +53 -0
- package/src/lab/query/index.ts +45 -0
- package/src/lab/query/latest-observation.ts +59 -0
- package/src/lab/query/passive-production.ts +159 -0
- package/src/lab/query/queries.ts +444 -0
- package/src/lab/query/types.ts +266 -0
- package/src/lab/subject/behavior-fingerprint.ts +77 -0
- package/src/lab/subject/installation-salt.ts +112 -0
- package/src/lab/subject/protocol-subject.ts +80 -0
- package/src/lab/subject/route-subject.ts +74 -0
- package/src/lib/abort.ts +146 -0
- package/src/lib/admin-secrets.ts +25 -0
- package/src/lib/admission.ts +83 -0
- package/src/lib/app-owned-memory-stores.ts +195 -0
- package/src/lib/app-owned-memory.ts +265 -0
- package/src/lib/bounded-body.ts +346 -0
- package/src/lib/bun-binary-validator.d.mts +3 -0
- package/src/lib/bun-binary-validator.mjs +18 -0
- package/src/lib/bun-runtime.ts +184 -0
- package/src/lib/bun-stream-caps.ts +130 -0
- package/src/lib/codex-restart-contract.ts +120 -0
- package/src/lib/config-ownership.ts +364 -0
- package/src/lib/crash-guard.ts +344 -0
- package/src/lib/debug-log-buffer.ts +83 -0
- package/src/lib/debug-settings.ts +108 -0
- package/src/lib/debug.ts +31 -0
- package/src/lib/destination-policy.ts +380 -0
- package/src/lib/errors.ts +406 -0
- package/src/lib/eventstream-decoder.ts +253 -0
- package/src/lib/fabric-task-execution-authority.ts +7 -0
- package/src/lib/fabric-task-host.ts +29 -0
- package/src/lib/gcp-adc.ts +341 -0
- package/src/lib/injection-debug-log.ts +58 -0
- package/src/lib/lab-activation.ts +223 -0
- package/src/lib/lab-live-execution-authority.ts +13 -0
- package/src/lib/lab-live-host.ts +30 -0
- package/src/lib/lab-live-pinned-sender.ts +56 -0
- package/src/lib/lab-live-route-production.ts +130 -0
- package/src/lib/lab-passive-linker-registration.ts +26 -0
- package/src/lib/local-management-attestation.ts +51 -0
- package/src/lib/local-management-capability.ts +100 -0
- package/src/lib/local-provider-reload-contract.ts +100 -0
- package/src/lib/open-url.ts +25 -0
- package/src/lib/optional-shutdown-hooks.ts +57 -0
- package/src/lib/pinned-http.ts +270 -0
- package/src/lib/privacy.ts +20 -0
- package/src/lib/process-control.ts +168 -0
- package/src/lib/provider-outbound.ts +210 -0
- package/src/lib/provider-url.ts +14 -0
- package/src/lib/proxy-env.ts +18 -0
- package/src/lib/redact.ts +521 -0
- package/src/lib/retry-after.ts +55 -0
- package/src/lib/self-launch-argv.ts +15 -0
- package/src/lib/server-resource-ownership.ts +71 -0
- package/src/lib/service-secrets.ts +25 -0
- package/src/lib/shadow-call.ts +61 -0
- package/src/lib/sidecar-tracker.ts +52 -0
- package/src/lib/sse-decoder.ts +364 -0
- package/src/lib/state-store-registrations.ts +119 -0
- package/src/lib/state-store-sweeper.ts +184 -0
- package/src/lib/system-restart-contract.ts +73 -0
- package/src/lib/test-home-guard.ts +90 -0
- package/src/lib/token-estimate.ts +86 -0
- package/src/lib/tool-argument-integers.ts +202 -0
- package/src/lib/translator-budget.ts +400 -0
- package/src/lib/upstream-http-version.ts +57 -0
- package/src/lib/upstream-reachability.ts +95 -0
- package/src/lib/upstream-retry.ts +392 -0
- package/src/lib/win-exec.ts +115 -0
- package/src/lib/win-paths.ts +68 -0
- package/src/lib/windows-atomic-replace.ts +156 -0
- package/src/lib/windows-elevation.ts +773 -0
- package/src/lib/windows-secret-acl.ts +854 -0
- package/src/lib/windows-service-wrappers.ts +72 -0
- package/src/lib/windows-text.ts +106 -0
- package/src/lib/windows-user-principal.ts +341 -0
- package/src/lib/winsw.ts +403 -0
- package/src/oauth/account-import/google-antigravity-adapter.ts +74 -0
- package/src/oauth/account-import/index.ts +15 -0
- package/src/oauth/account-import/parser.ts +83 -0
- package/src/oauth/account-import/registry.ts +18 -0
- package/src/oauth/account-import/service.ts +75 -0
- package/src/oauth/account-import/types.ts +91 -0
- package/src/oauth/anthropic-routing.ts +594 -0
- package/src/oauth/anthropic.ts +188 -0
- package/src/oauth/antigravity-routing.ts +151 -0
- package/src/oauth/callback-server.ts +300 -0
- package/src/oauth/chatgpt.ts +161 -0
- package/src/oauth/command-code.ts +239 -0
- package/src/oauth/cursor.ts +252 -0
- package/src/oauth/github-copilot.ts +428 -0
- package/src/oauth/google-antigravity.ts +262 -0
- package/src/oauth/health.ts +407 -0
- package/src/oauth/index.ts +1504 -0
- package/src/oauth/key-providers.ts +124 -0
- package/src/oauth/kimi.ts +227 -0
- package/src/oauth/kiro-credentials.ts +726 -0
- package/src/oauth/kiro.ts +621 -0
- package/src/oauth/local-token-detect.ts +130 -0
- package/src/oauth/log.ts +50 -0
- package/src/oauth/login-cli.ts +223 -0
- package/src/oauth/nous.ts +798 -0
- package/src/oauth/pkce.ts +15 -0
- package/src/oauth/store.ts +728 -0
- package/src/oauth/token-guardian.ts +309 -0
- package/src/oauth/types.ts +62 -0
- package/src/oauth/xai.ts +241 -0
- package/src/providers/alibaba-region-backup.ts +75 -0
- package/src/providers/alibaba-region-migration.ts +156 -0
- package/src/providers/alibaba-region-startup.ts +36 -0
- package/src/providers/antigravity-models.ts +695 -0
- package/src/providers/antigravity-quota.ts +216 -0
- package/src/providers/api-keys.ts +140 -0
- package/src/providers/base-url-choices.ts +74 -0
- package/src/providers/codex-capacity.ts +292 -0
- package/src/providers/command-code-efforts.ts +144 -0
- package/src/providers/context-cap.ts +82 -0
- package/src/providers/cursor-pool.ts +72 -0
- package/src/providers/derive.ts +586 -0
- package/src/providers/fastwire.ts +501 -0
- package/src/providers/free-directory.ts +187 -0
- package/src/providers/github-copilot-transport.ts +56 -0
- package/src/providers/google-vertex-location.ts +14 -0
- package/src/providers/key-failover.ts +271 -0
- package/src/providers/kiro-models.ts +67 -0
- package/src/providers/label.ts +19 -0
- package/src/providers/model-discovery-limits.ts +16 -0
- package/src/providers/model-discovery.ts +449 -0
- package/src/providers/model-rename-migration.ts +255 -0
- package/src/providers/model-rename-startup.ts +28 -0
- package/src/providers/openai-sidecar.ts +243 -0
- package/src/providers/openai-tier-startup.ts +56 -0
- package/src/providers/openai-tiers.ts +423 -0
- package/src/providers/openai-virtual-models.ts +83 -0
- package/src/providers/opencode-zen-rate-limit.ts +102 -0
- package/src/providers/openrouter-routing.ts +102 -0
- package/src/providers/provider-id-rewrite.ts +185 -0
- package/src/providers/quota.ts +2345 -0
- package/src/providers/registry.ts +2918 -0
- package/src/providers/replit/constants.ts +27 -0
- package/src/providers/replit/derive.ts +85 -0
- package/src/providers/replit/headers.ts +28 -0
- package/src/providers/replit/origin.ts +55 -0
- package/src/providers/replit/pair-install-response.ts +72 -0
- package/src/providers/replit/probe.ts +199 -0
- package/src/providers/replit/setup.ts +350 -0
- package/src/providers/request-pacing.ts +310 -0
- package/src/providers/service-tier.ts +277 -0
- package/src/providers/slug-codec.ts +103 -0
- package/src/providers/static-model-discovery.ts +86 -0
- package/src/providers/xai-responses-opt-in.ts +15 -0
- package/src/providers/xai-transport.ts +148 -0
- package/src/reasoning-effort.ts +183 -0
- package/src/responses/compaction.ts +142 -0
- package/src/responses/custom-tool-compat.ts +266 -0
- package/src/responses/hosted-tool-policy.ts +9 -0
- package/src/responses/namespace-tool-compat.ts +355 -0
- package/src/responses/parser.ts +838 -0
- package/src/responses/provider-continuation.ts +98 -0
- package/src/responses/provider-opaque-metadata.ts +73 -0
- package/src/responses/reasoning-envelope.ts +60 -0
- package/src/responses/reasoning-replay-cache.ts +426 -0
- package/src/responses/schema.ts +165 -0
- package/src/responses/spill-store.ts +459 -0
- package/src/responses/state.ts +1433 -0
- package/src/responses/thought-signature-replay.ts +347 -0
- package/src/responses/tool-groups.ts +19 -0
- package/src/responses/tool-search-compat.ts +301 -0
- package/src/responses/truncated-stop-reason.ts +60 -0
- package/src/router.ts +761 -0
- package/src/routing/analytics.ts +378 -0
- package/src/routing/capability.ts +244 -0
- package/src/routing/compatibility/assemble.ts +73 -0
- package/src/routing/compatibility/behavior.ts +278 -0
- package/src/routing/compatibility/catalog.ts +99 -0
- package/src/routing/compatibility/endpoint.ts +52 -0
- package/src/routing/compatibility/lab-evidence-provider.ts +130 -0
- package/src/routing/compatibility/policy.ts +181 -0
- package/src/routing/compatibility/provider-slot.ts +56 -0
- package/src/routing/compatibility/reader.ts +110 -0
- package/src/routing/compatibility/subject.ts +191 -0
- package/src/routing/compatibility/types.ts +64 -0
- package/src/routing/compatibility/version.ts +104 -0
- package/src/routing/cost.ts +77 -0
- package/src/routing/evaluator.ts +495 -0
- package/src/routing/health.ts +412 -0
- package/src/routing/history/cursor.ts +43 -0
- package/src/routing/history/indexer.ts +605 -0
- package/src/routing/history/schema.ts +72 -0
- package/src/routing/profile-namespace.ts +15 -0
- package/src/routing/profile.ts +547 -0
- package/src/routing/quota.ts +145 -0
- package/src/routing/request-evidence.ts +45 -0
- package/src/routing/trace.ts +776 -0
- package/src/server/adapter-resolve.ts +53 -0
- package/src/server/auth-cors.ts +751 -0
- package/src/server/background-lifecycle.ts +182 -0
- package/src/server/chat-completions.ts +442 -0
- package/src/server/chat-native-sse.ts +331 -0
- package/src/server/chat-native.ts +426 -0
- package/src/server/claude-messages.ts +1030 -0
- package/src/server/direct-local-http.ts +347 -0
- package/src/server/effort-policy.ts +190 -0
- package/src/server/github-copilot-responses-repair.ts +338 -0
- package/src/server/gui-static.ts +152 -0
- package/src/server/image-retry.ts +42 -0
- package/src/server/images.ts +568 -0
- package/src/server/index.ts +1813 -0
- package/src/server/lifecycle.ts +498 -0
- package/src/server/live.ts +717 -0
- package/src/server/local-management-read-client.ts +90 -0
- package/src/server/local-provider-reload-client.ts +137 -0
- package/src/server/management/agent-settings-routes.ts +1433 -0
- package/src/server/management/api-access.ts +141 -0
- package/src/server/management/api-key-usage.ts +193 -0
- package/src/server/management/body.ts +41 -0
- package/src/server/management/combo-routes.ts +263 -0
- package/src/server/management/config-routes.ts +835 -0
- package/src/server/management/context.ts +113 -0
- package/src/server/management/integration-routes.ts +498 -0
- package/src/server/management/lab-automation-routes.ts +206 -0
- package/src/server/management/lab-routes.ts +563 -0
- package/src/server/management/logs-usage-routes.ts +586 -0
- package/src/server/management/model-routes.ts +560 -0
- package/src/server/management/model-rows.ts +163 -0
- package/src/server/management/native-integration-routes.ts +769 -0
- package/src/server/management/oauth-account-routes.ts +637 -0
- package/src/server/management/provider-capability-config.ts +48 -0
- package/src/server/management/provider-routes.ts +1033 -0
- package/src/server/management/replit-provider-routes.ts +86 -0
- package/src/server/management/request-history-routes.ts +191 -0
- package/src/server/management/routing-analytics-routes.ts +74 -0
- package/src/server/management/routing-profile-routes.ts +385 -0
- package/src/server/management/shared.ts +286 -0
- package/src/server/management/sidebar-routes.ts +106 -0
- package/src/server/management/storage-log-guard-routes.ts +186 -0
- package/src/server/management/sync-response.ts +69 -0
- package/src/server/management/system-restart.ts +435 -0
- package/src/server/management/system-routes.ts +194 -0
- package/src/server/management/usage-summary-cache.ts +94 -0
- package/src/server/management/vision-sidecar-options.ts +167 -0
- package/src/server/management/web-search-sidecar-options.ts +120 -0
- package/src/server/management-api.ts +314 -0
- package/src/server/management-auth.ts +482 -0
- package/src/server/memory-watchdog.ts +156 -0
- package/src/server/passive-route-linker.ts +66 -0
- package/src/server/port-reclaim.ts +307 -0
- package/src/server/ports.ts +156 -0
- package/src/server/proxy-liveness.ts +328 -0
- package/src/server/readiness.ts +99 -0
- package/src/server/relay-eager.ts +353 -0
- package/src/server/relay.ts +1209 -0
- package/src/server/request-decompress.ts +239 -0
- package/src/server/request-log-conversation.ts +168 -0
- package/src/server/request-log.ts +1259 -0
- package/src/server/responses/agent-task-recovery-cache.ts +143 -0
- package/src/server/responses/agent-task-recovery.ts +465 -0
- package/src/server/responses/collaboration.ts +551 -0
- package/src/server/responses/compact.ts +771 -0
- package/src/server/responses/core.ts +5389 -0
- package/src/server/responses/empty-completion-guard.ts +276 -0
- package/src/server/responses/encrypted-payload.ts +331 -0
- package/src/server/responses/fetch-helpers.ts +232 -0
- package/src/server/responses/input-admission.ts +185 -0
- package/src/server/responses/pacing-overload.ts +13 -0
- package/src/server/responses/passthrough-error.ts +78 -0
- package/src/server/responses/policy-fallback.ts +178 -0
- package/src/server/responses/responses-field-backfill.ts +251 -0
- package/src/server/responses/terminal-guard.ts +251 -0
- package/src/server/responses/upstream-error.ts +53 -0
- package/src/server/responses/ws-upstream.ts +308 -0
- package/src/server/responses-custom-tool-repair.ts +282 -0
- package/src/server/responses-image-gen-repair.ts +132 -0
- package/src/server/responses-item-id-repair.ts +272 -0
- package/src/server/responses-json-events.ts +90 -0
- package/src/server/responses-model-rewrite.ts +29 -0
- package/src/server/responses-reasoning-summary-rewrite.ts +178 -0
- package/src/server/responses-snapshot-repair.ts +621 -0
- package/src/server/responses-terminal-repair.ts +342 -0
- package/src/server/responses-tool-search-repair.ts +267 -0
- package/src/server/responses-undeclared-tool-guard.ts +153 -0
- package/src/server/responses.ts +25 -0
- package/src/server/search.ts +201 -0
- package/src/server/sse-frame-buffer.ts +292 -0
- package/src/server/sse-payload-rewrite.ts +263 -0
- package/src/server/startup-action-control.ts +315 -0
- package/src/server/startup-health-cache.ts +131 -0
- package/src/server/system-env.ts +484 -0
- package/src/server/windows-tcp-drop.ts +184 -0
- package/src/server/windows-tray-control.ts +41 -0
- package/src/server/ws-bridge.ts +472 -0
- package/src/service-manager-probe.ts +892 -0
- package/src/service.ts +3575 -0
- package/src/sidecar/auth.ts +92 -0
- package/src/sidecar/candidates.ts +83 -0
- package/src/stall-timeout.ts +20 -0
- package/src/storage/cleanup-job.ts +57 -0
- package/src/storage/cleanup.ts +3085 -0
- package/src/storage/policy-job.ts +457 -0
- package/src/storage/policy-scheduler.ts +40 -0
- package/src/storage/policy-worker.ts +59 -0
- package/src/storage/policy.ts +527 -0
- package/src/storage/restore-job.ts +299 -0
- package/src/storage/restore-worker.ts +58 -0
- package/src/storage/scanner.ts +238 -0
- package/src/storage/storage-mutation-coordinator.ts +139 -0
- package/src/storage/worker-lifecycle.ts +215 -0
- package/src/tray/assets/opencodex-tray-offline.ico +0 -0
- package/src/tray/assets/opencodex-tray-online.ico +0 -0
- package/src/tray/assets/opencodex-tray-warning.ico +0 -0
- package/src/tray/assets/opencodex-tray.png +0 -0
- package/src/tray/windows-tray.ps1 +364 -0
- package/src/tray/windows.ts +757 -0
- package/src/types/accounts.ts +37 -0
- package/src/types/config.ts +876 -0
- package/src/types/provider.ts +545 -0
- package/src/types/request.ts +384 -0
- package/src/types/tools.ts +131 -0
- package/src/types/wire.ts +80 -0
- package/src/types.ts +106 -0
- package/src/update/badge.ts +72 -0
- package/src/update/index.ts +415 -0
- package/src/update/job.ts +1887 -0
- package/src/update/notify.ts +263 -0
- package/src/update/npm-cache-preflight.d.mts +47 -0
- package/src/update/npm-cache-preflight.mjs +201 -0
- package/src/update/npm-invocation.d.mts +23 -0
- package/src/update/npm-invocation.mjs +94 -0
- package/src/update/transactional-install.d.mts +22 -0
- package/src/update/transactional-install.mjs +259 -0
- package/src/update/tray-update-plan.d.mts +18 -0
- package/src/update/tray-update-plan.mjs +38 -0
- package/src/usage/cost.ts +625 -0
- package/src/usage/debug.ts +97 -0
- package/src/usage/expected-prices.ts +416 -0
- package/src/usage/log.ts +1223 -0
- package/src/usage/summary.ts +753 -0
- package/src/usage/totals.ts +14 -0
- package/src/usage/user-cost-overlay-reconciler.ts +313 -0
- package/src/usage/user-cost-overlays.ts +314 -0
- package/src/vision/anthropic-describe.ts +189 -0
- package/src/vision/backends.ts +97 -0
- package/src/vision/describe.ts +131 -0
- package/src/vision/eligibility.ts +250 -0
- package/src/vision/index.ts +681 -0
- package/src/vision/reasoning.ts +55 -0
- package/src/vision/routed-describe.ts +175 -0
- package/src/vision/timeout-bounds.ts +9 -0
- package/src/web-search/anthropic-executor.ts +195 -0
- package/src/web-search/backends.ts +108 -0
- package/src/web-search/exa-executor.ts +88 -0
- package/src/web-search/executor.ts +113 -0
- package/src/web-search/format-result.ts +89 -0
- package/src/web-search/gemini-executor.ts +141 -0
- package/src/web-search/index.ts +331 -0
- package/src/web-search/loop.ts +896 -0
- package/src/web-search/parse.ts +315 -0
- package/src/web-search/progress-stream.ts +342 -0
- package/src/web-search/sources.ts +60 -0
- package/src/web-search/synthetic-tool.ts +47 -0
- package/src/web-search/xai-executor.ts +219 -0
|
@@ -0,0 +1,560 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Codex parses a catalog entry's `input_modalities` as a closed enum, and one out-of-enum
|
|
6
|
+
* value makes it reject the ENTIRE catalog file — plugins, apps and MCP servers all stop
|
|
7
|
+
* loading over one model's metadata (#759).
|
|
8
|
+
*
|
|
9
|
+
* The catalog writer normalizes on the way out, but a rejected value stored here would still
|
|
10
|
+
* be handed back to the GUI and CLI as if it were real, and the offline `ocx models add` path
|
|
11
|
+
* already refuses it. Validate at ingress so all three paths agree.
|
|
12
|
+
*/
|
|
13
|
+
const ALLOWED_INPUT_MODALITIES = new Set(["text", "image", "audio"]);
|
|
14
|
+
|
|
15
|
+
function readInputModalities(raw: unknown): { values?: string[]; error?: string } {
|
|
16
|
+
if (raw === undefined) return {};
|
|
17
|
+
if (!Array.isArray(raw)) return { error: "inputModalities must be an array" };
|
|
18
|
+
// Reject non-strings rather than filtering them out. Dropping them silently accepted a
|
|
19
|
+
// malformed POST and, worse, let a PUT of `[42]` clear the stored modalities while
|
|
20
|
+
// answering 200 — the opposite of the contract this validator exists to state. An empty
|
|
21
|
+
// array stays valid: that is how `ocx models edit --modalities -` clears the field.
|
|
22
|
+
const rejected: string[] = [];
|
|
23
|
+
for (const value of raw) {
|
|
24
|
+
if (typeof value !== "string") return { error: "inputModalities must contain only strings" };
|
|
25
|
+
if (!ALLOWED_INPUT_MODALITIES.has(value)) rejected.push(value);
|
|
26
|
+
}
|
|
27
|
+
if (rejected.length > 0) {
|
|
28
|
+
return { error: `unsupported input modality: ${rejected.join(", ")} (allowed: text, image, audio)` };
|
|
29
|
+
}
|
|
30
|
+
return { values: raw as string[] };
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Custom-row reasoning ladder. Labels are validated against the Codex ladder (low..ultra)
|
|
35
|
+
* exactly like provider `modelReasoningEfforts` values; unknown labels would otherwise
|
|
36
|
+
* surface in a catalog the upstream never accepts. An empty array is meaningful (explicit
|
|
37
|
+
* "no reasoning" hides the effort control) and must be preserved, not cleared.
|
|
38
|
+
*/
|
|
39
|
+
function readReasoningEfforts(raw: unknown): { values?: string[]; error?: string } {
|
|
40
|
+
if (raw === undefined) return {};
|
|
41
|
+
if (!Array.isArray(raw)) return { error: "reasoningEfforts must be an array" };
|
|
42
|
+
const rejected: string[] = [];
|
|
43
|
+
const values: string[] = [];
|
|
44
|
+
for (const value of raw) {
|
|
45
|
+
if (typeof value !== "string") return { error: "reasoningEfforts must contain only strings" };
|
|
46
|
+
if (!isDeclaredReasoningEffort(value)) { rejected.push(value); continue; }
|
|
47
|
+
if (!values.includes(value)) values.push(value);
|
|
48
|
+
}
|
|
49
|
+
if (rejected.length > 0) {
|
|
50
|
+
return { error: `unsupported reasoning effort: ${rejected.join(", ")} (allowed: none, minimal, low, medium, high, xhigh, max, ultra)` };
|
|
51
|
+
}
|
|
52
|
+
// Canonical order: the catalog writes supported_reasoning_levels in input order and the
|
|
53
|
+
// fallback default picks the first entry, so a caller-chosen order must not leak through.
|
|
54
|
+
return { values: canonicalizeReasoningEfforts(values) };
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** Default effort must be a ladder member that the declared ladder actually includes. */
|
|
58
|
+
function readDefaultReasoningEffort(raw: unknown, efforts: string[] | undefined): { value?: string; error?: string } {
|
|
59
|
+
if (raw === undefined) return {};
|
|
60
|
+
if (raw === null) return { value: undefined };
|
|
61
|
+
if (typeof raw !== "string" || !isDeclaredReasoningEffort(raw)) {
|
|
62
|
+
return { error: "defaultReasoningEffort must be one of: none, minimal, low, medium, high, xhigh, max, ultra" };
|
|
63
|
+
}
|
|
64
|
+
if (efforts === undefined || efforts.length === 0) {
|
|
65
|
+
return { error: "defaultReasoningEffort requires a non-empty reasoningEfforts ladder" };
|
|
66
|
+
}
|
|
67
|
+
if (!efforts.includes(raw)) {
|
|
68
|
+
return { error: `defaultReasoningEffort "${raw}" is not in the declared reasoningEfforts ladder` };
|
|
69
|
+
}
|
|
70
|
+
return { value: raw };
|
|
71
|
+
}
|
|
72
|
+
import type { CatalogModel } from "../../codex/catalog";
|
|
73
|
+
import { accountBoundNativeOpenAiSlugsBySelector, catalogModelSlug, configuredNativeAliasSlugs, disabledNativeSlugs, invalidateCodexModelsCache, nativeModelRows, shouldIncludeAccountBoundNativeOpenAi, uniqueCatalogModelsForPublicList } from "../../codex/catalog";
|
|
74
|
+
import { CatalogGatherBusyError } from "../../codex/catalog/provider-fetch";
|
|
75
|
+
import { getProviderLiveModelCount } from "../../codex/model-cache";
|
|
76
|
+
import {
|
|
77
|
+
DEFAULT_SUBAGENT_MODELS,
|
|
78
|
+
codexAutoStartEnabled,
|
|
79
|
+
hasOwnProvider,
|
|
80
|
+
isValidProviderName,
|
|
81
|
+
multiAgentGuidanceEnabled,
|
|
82
|
+
providerBaseUrlConfigError,
|
|
83
|
+
providerHeadersConfigError,
|
|
84
|
+
saveConfigPreservingClaudeCode,
|
|
85
|
+
} from "../../config";
|
|
86
|
+
import {
|
|
87
|
+
clearLoginState,
|
|
88
|
+
getLoginStatus,
|
|
89
|
+
isPublicOAuthProvider,
|
|
90
|
+
listOAuthProviders,
|
|
91
|
+
startLoginFlow,
|
|
92
|
+
submitManualLoginCode,
|
|
93
|
+
upsertOAuthProvider,
|
|
94
|
+
} from "../../oauth";
|
|
95
|
+
import { removeCredential } from "../../oauth/store";
|
|
96
|
+
import { providerDestinationResolvedError } from "../../lib/destination-policy";
|
|
97
|
+
import { enrichProviderFromCatalog, listKeyLoginProviders } from "../../oauth/key-providers";
|
|
98
|
+
import { deriveProviderPresets } from "../../providers/derive";
|
|
99
|
+
import { providerCodexAccountMode } from "../../providers/registry";
|
|
100
|
+
import { encodedModelIdCollides, routedSlug, slugEquals } from "../../providers/slug-codec";
|
|
101
|
+
import { knownModelIdsForProvider } from "../../router";
|
|
102
|
+
import { COMBO_NAMESPACE, comboDisabledModelSelectors, comboModelId, preservesPhysicalComboProvider } from "../../combos";
|
|
103
|
+
import { clearProviderQuotaCache, fetchProviderQuotaReports } from "../../providers/quota";
|
|
104
|
+
import { isCanonicalOpenAiForwardProvider } from "../../providers/openai-tiers";
|
|
105
|
+
import { clearThreadAccountMap } from "../../codex/routing";
|
|
106
|
+
import { primeCodexPoolQuotas } from "../../codex/auth-api";
|
|
107
|
+
import { DEFAULT_PROVIDER_CONTEXT_CAP, globalContextCapValue, providerContextCap, providerContextCaps, setAllProviderContextCaps, setGlobalContextCapValue, setProviderContextCap } from "../../providers/context-cap";
|
|
108
|
+
import { resolveCodexHomeDir } from "../../codex/home";
|
|
109
|
+
import { readUsageEntries } from "../../usage/log";
|
|
110
|
+
import { getUsageDebugLogEntries } from "../../usage/debug";
|
|
111
|
+
import { parseRange, parseUsageSurface, summarizeUsage } from "../../usage/summary";
|
|
112
|
+
import { stripCodexRuntimeProviderFields } from "../../codex/auth-context";
|
|
113
|
+
import { getProviderRegistryEntry } from "../../providers/registry";
|
|
114
|
+
import { getDebugLogEntries } from "../../lib/debug-log-buffer";
|
|
115
|
+
import { canonicalizeReasoningEfforts, isDeclaredReasoningEffort } from "../../reasoning-effort";
|
|
116
|
+
import { getInjectionDebugLogEntries } from "../../lib/injection-debug-log";
|
|
117
|
+
import {
|
|
118
|
+
clearDebugSettings,
|
|
119
|
+
clearDebugSetting,
|
|
120
|
+
getDebugSettings,
|
|
121
|
+
setDebugSettings,
|
|
122
|
+
type DebugFlag,
|
|
123
|
+
} from "../../lib/debug-settings";
|
|
124
|
+
import type { OcxClaudeCodeConfig, OcxConfig, OcxCustomModel, OcxProviderConfig } from "../../types";
|
|
125
|
+
import { drainAndShutdown } from "../lifecycle";
|
|
126
|
+
import { filterRequestLogs, getRequestLogEntries, type RequestLogEntry } from "../request-log";
|
|
127
|
+
import { estimateComboCost, estimateRequestCost, normalizeCostTokens, tokensPerSecond } from "../../usage/cost";
|
|
128
|
+
import type { PersistedUsageAttempt } from "../../usage/log";
|
|
129
|
+
import { isAllowedRequestOrigin, jsonResponse, providerManagementConfigError, publicProviderBaseUrl, safeConfigDTO, corsHeaders } from "../auth-cors";
|
|
130
|
+
import { applySystemEnvToggle } from "../system-env";
|
|
131
|
+
import {
|
|
132
|
+
ClientPathError,
|
|
133
|
+
EXPORT_CLIENTS,
|
|
134
|
+
EXPORT_CLIENT_IDS,
|
|
135
|
+
OPENCODE_PROVIDER_ID,
|
|
136
|
+
buildClientConfigText,
|
|
137
|
+
isExportClientId,
|
|
138
|
+
opencodeProxyBaseUrl,
|
|
139
|
+
} from "../../clients/config-export";
|
|
140
|
+
import type {
|
|
141
|
+
ExportClientId,
|
|
142
|
+
ExportModel,
|
|
143
|
+
OpencodeGeneratedConfig,
|
|
144
|
+
PiGeneratedConfig,
|
|
145
|
+
} from "../../clients/config-export";
|
|
146
|
+
|
|
147
|
+
import { isPlainRecord, parseDebugLogQuery, tokPerSecondResult, unavailableCostReason, costResult, requestLogDto, stripRegistryOnlyStaticHeaders, fetchAllModels } from "./shared";
|
|
148
|
+
import type { MetricUnavailableReason, TokPerSecondResult, CostEstimateReason, CostResult, MetricSource } from "./shared";
|
|
149
|
+
import type { ManagementContext } from "./context";
|
|
150
|
+
import { listManagementModelRows, loadExportModels } from "./model-rows";
|
|
151
|
+
import { readManagementJsonBody, rethrowManagementBodyTooLarge } from "./body";
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Counts read back off the SERIALIZED document rather than recomputed from the input rows.
|
|
155
|
+
* `modelsWithoutLimits` drives a GUI line claiming "these models ship without limits", so it
|
|
156
|
+
* has to describe the bytes the user actually receives — a parallel reimplementation of the
|
|
157
|
+
* core's "authoritative context window" rule would be free to drift from it silently.
|
|
158
|
+
*/
|
|
159
|
+
function summarizeExportedModels(client: ExportClientId, document: unknown): { modelCount: number; modelsWithoutLimits: number } {
|
|
160
|
+
// Each client counts its own document shape. The previous branch assumed
|
|
161
|
+
// "anything that is not OpenCode must be Pi", which silently misread the
|
|
162
|
+
// moment a third client existed.
|
|
163
|
+
return EXPORT_CLIENTS[client].summarize(document);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
export async function handleModelRoutes(ctx: ManagementContext): Promise<Response | null> {
|
|
167
|
+
const { req, url, config, deps, convergeCodexCatalog, syncClaudeAgentDefsBestEffort } = ctx;
|
|
168
|
+
// A handler persists the exact config object passed in. Production defaults to
|
|
169
|
+
// the real store; tests that pass an in-memory fixture inject a no-op/spy. Do not
|
|
170
|
+
// bypass this seam with a dynamic config import — doing so replaced a user's
|
|
171
|
+
// ~/.opencodex/config.json with the `existing-uuid` test fixture.
|
|
172
|
+
const persistConfig = deps.saveConfigPreservingClaudeCode ?? saveConfigPreservingClaudeCode;
|
|
173
|
+
|
|
174
|
+
if (url.pathname === "/api/catalog" && req.method === "GET") {
|
|
175
|
+
const { readCatalog, readCodexCatalogPath } = await import("../../codex/catalog");
|
|
176
|
+
const catalog = readCatalog(readCodexCatalogPath());
|
|
177
|
+
if (!catalog) return jsonResponse({ error: "catalog not found" }, 404, req, config);
|
|
178
|
+
const headers: Record<string, string> = {
|
|
179
|
+
"Content-Type": "application/json",
|
|
180
|
+
...corsHeaders(req, config),
|
|
181
|
+
};
|
|
182
|
+
const { loadPersistedCodexRuntime } = await import("../../codex/runtime");
|
|
183
|
+
const version = loadPersistedCodexRuntime()?.selectedVersion;
|
|
184
|
+
if (version) headers["x-opencodex-codex-version"] = version;
|
|
185
|
+
return new Response(JSON.stringify(catalog), { status: 200, headers });
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
if (url.pathname === "/api/models" && req.method === "GET") {
|
|
189
|
+
return jsonResponse(await listManagementModelRows(config));
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Client config document for OpenCode / Pi, built from the SAME function `ocx export`
|
|
194
|
+
* calls, so the bytes a user downloads here and the bytes they pipe from the CLI cannot
|
|
195
|
+
* disagree. Read-only: this route never writes the user's client config.
|
|
196
|
+
*/
|
|
197
|
+
if (url.pathname === "/api/client-config" && req.method === "GET") {
|
|
198
|
+
const requested = url.searchParams.get("client")?.trim() ?? "";
|
|
199
|
+
if (!isExportClientId(requested)) {
|
|
200
|
+
return jsonResponse(
|
|
201
|
+
{ error: `client must be one of: ${EXPORT_CLIENT_IDS.join(", ")}` },
|
|
202
|
+
400,
|
|
203
|
+
req,
|
|
204
|
+
config,
|
|
205
|
+
);
|
|
206
|
+
}
|
|
207
|
+
const spec = EXPORT_CLIENTS[requested];
|
|
208
|
+
// Resolved before the catalog load on purpose. A refused override is a
|
|
209
|
+
// property of the request, not of the catalog: validating it afterwards
|
|
210
|
+
// let a busy or failing catalog answer 503 first, so a user with a
|
|
211
|
+
// relative override never saw the message that says how to fix it — and
|
|
212
|
+
// the route did the enumeration work anyway for input it was going to
|
|
213
|
+
// reject.
|
|
214
|
+
let destination: string;
|
|
215
|
+
try {
|
|
216
|
+
destination = spec.destination(process.env);
|
|
217
|
+
} catch (error) {
|
|
218
|
+
// A client's own environment override can name a path the resolver
|
|
219
|
+
// refuses — a relative value, which this process and the client would
|
|
220
|
+
// resolve against different working directories. That is a
|
|
221
|
+
// user-correctable configuration error, not a server fault, so it leaves
|
|
222
|
+
// this boundary as a bounded 400 instead of escaping handleManagementAPI
|
|
223
|
+
// as a generic 500 and stripping the message that says how to fix it.
|
|
224
|
+
// `integrations/state.ts` and `integrations/writer.ts` already catch the
|
|
225
|
+
// same error on their paths; this route was the one that did not.
|
|
226
|
+
if (!(error instanceof ClientPathError)) throw error;
|
|
227
|
+
return jsonResponse({ error: error.message }, 400, req, config);
|
|
228
|
+
}
|
|
229
|
+
let models: ExportModel[];
|
|
230
|
+
try {
|
|
231
|
+
// The ONE loader every export surface uses. It carries the visibility
|
|
232
|
+
// filter with it, so this route and the integration routes cannot end up
|
|
233
|
+
// telling a client about different models.
|
|
234
|
+
models = await loadExportModels(config);
|
|
235
|
+
} catch (error) {
|
|
236
|
+
// A partial or empty `models` block reads as a valid config while offering nothing,
|
|
237
|
+
// so a catalog failure is surfaced as unavailable rather than serialized. The
|
|
238
|
+
// catalog-busy error keeps its own 503 from handleManagementAPI.
|
|
239
|
+
if (error instanceof CatalogGatherBusyError) throw error;
|
|
240
|
+
return jsonResponse(
|
|
241
|
+
{ error: `model catalog unavailable: ${error instanceof Error ? error.message : String(error)}` },
|
|
242
|
+
503,
|
|
243
|
+
req,
|
|
244
|
+
config,
|
|
245
|
+
);
|
|
246
|
+
}
|
|
247
|
+
const built = buildClientConfigText(requested, {
|
|
248
|
+
baseUrl: opencodeProxyBaseUrl(Number(url.port) || config.port, config.hostname),
|
|
249
|
+
models,
|
|
250
|
+
config,
|
|
251
|
+
});
|
|
252
|
+
const document = built.document;
|
|
253
|
+
return jsonResponse({
|
|
254
|
+
client: spec.id,
|
|
255
|
+
filename: spec.filename,
|
|
256
|
+
destination,
|
|
257
|
+
apiKeyEnv: spec.apiKeyEnv,
|
|
258
|
+
exportHint: spec.exportHint,
|
|
259
|
+
// The client's own format and the exact bytes for it. The GUI previously
|
|
260
|
+
// re-serialized `config` as JSON, which is wrong for four of the six
|
|
261
|
+
// clients; `mediaType` also drives the download blob.
|
|
262
|
+
format: built.format,
|
|
263
|
+
mediaType: built.mediaType,
|
|
264
|
+
text: built.text,
|
|
265
|
+
...summarizeExportedModels(requested, document),
|
|
266
|
+
config: document,
|
|
267
|
+
}, 200, req, config);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// Enable/disable models: which routed models Codex sees. PUT hides them from the catalog +
|
|
271
|
+
// /v1/models and invalidates Codex's 5-min models cache so it applies on the next turn.
|
|
272
|
+
if (url.pathname === "/api/disabled-models" && req.method === "PUT") {
|
|
273
|
+
let body: { models?: unknown };
|
|
274
|
+
try { body = await readManagementJsonBody(req); } catch (error) { rethrowManagementBodyTooLarge(error); return jsonResponse({ error: "invalid JSON body" }, 400); }
|
|
275
|
+
const disabled = Array.isArray(body.models) ? body.models.filter((m): m is string => typeof m === "string") : [];
|
|
276
|
+
config.disabledModels = disabled;
|
|
277
|
+
persistConfig(config);
|
|
278
|
+
const catalogRefresh = await convergeCodexCatalog();
|
|
279
|
+
return jsonResponse({ ok: true, disabled, catalogRefresh });
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// One user-facing visibility switch spans two persisted filters: a provider allowlist and the
|
|
283
|
+
// shared blocklist. Keep the update atomic so an interrupted request cannot expose a half-applied
|
|
284
|
+
// state. Native rows only use the blocklist; routed/custom rows also join a non-empty allowlist.
|
|
285
|
+
if (url.pathname === "/api/model-visibility" && req.method === "PUT") {
|
|
286
|
+
let parsedBody: unknown;
|
|
287
|
+
try { parsedBody = await readManagementJsonBody(req); } catch (error) { rethrowManagementBodyTooLarge(error); return jsonResponse({ error: "invalid JSON body" }, 400); }
|
|
288
|
+
if (!isPlainRecord(parsedBody)) return jsonResponse({ error: "invalid model visibility request" }, 400);
|
|
289
|
+
const body = parsedBody;
|
|
290
|
+
const scope = body.scope === "models" || body.scope === "provider" ? body.scope : null;
|
|
291
|
+
const provider = typeof body.provider === "string" ? body.provider.trim() : "";
|
|
292
|
+
if (!scope || !provider || !isValidProviderName(provider) || typeof body.enabled !== "boolean" || !Array.isArray(body.targets)) {
|
|
293
|
+
return jsonResponse({ error: "invalid model visibility request" }, 400);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
const providerConfig = hasOwnProvider(config.providers, provider) ? config.providers[provider] : undefined;
|
|
297
|
+
const isVirtualComboNamespace = provider === COMBO_NAMESPACE && !preservesPhysicalComboProvider(config);
|
|
298
|
+
if (!providerConfig && provider !== "openai" && !isVirtualComboNamespace) {
|
|
299
|
+
return jsonResponse({ error: "unknown model visibility provider" }, 400);
|
|
300
|
+
}
|
|
301
|
+
const accountNativeQualified = shouldIncludeAccountBoundNativeOpenAi(config)
|
|
302
|
+
? [...accountBoundNativeOpenAiSlugsBySelector(config).entries()].flatMap(([selector, slugs]) =>
|
|
303
|
+
slugs.filter(slug => !nativeModelRows(config).some(row => row.slug === slug)).map(slug => `${selector}/${slug}`))
|
|
304
|
+
: [];
|
|
305
|
+
const supportedNative = new Set([
|
|
306
|
+
...nativeModelRows(config).map(row => row.slug),
|
|
307
|
+
...accountNativeQualified,
|
|
308
|
+
]);
|
|
309
|
+
const targets: Array<{ id: string; native: boolean }> = [];
|
|
310
|
+
const seen = new Set<string>();
|
|
311
|
+
for (const value of body.targets) {
|
|
312
|
+
if (!isPlainRecord(value) || typeof value.id !== "string" || (value.native !== undefined && typeof value.native !== "boolean")) {
|
|
313
|
+
return jsonResponse({ error: "invalid model visibility target" }, 400);
|
|
314
|
+
}
|
|
315
|
+
const id = value.id.trim();
|
|
316
|
+
const native = value.native === true;
|
|
317
|
+
if (!id || (provider === "openai") !== native || (native && !supportedNative.has(id))) {
|
|
318
|
+
return jsonResponse({ error: "invalid model visibility target" }, 400);
|
|
319
|
+
}
|
|
320
|
+
const key = `${native ? "native" : "routed"}:${id}`;
|
|
321
|
+
if (!seen.has(key)) {
|
|
322
|
+
seen.add(key);
|
|
323
|
+
targets.push({ id, native });
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
if (targets.length === 0) return jsonResponse({ error: "model visibility targets required" }, 400);
|
|
327
|
+
|
|
328
|
+
const knownComboSelectors = new Set(
|
|
329
|
+
Object.entries(config.combos ?? {}).flatMap(([id, combo]) => (
|
|
330
|
+
comboDisabledModelSelectors(id, combo)
|
|
331
|
+
)),
|
|
332
|
+
);
|
|
333
|
+
const targetComboSelectors = new Map<string, Set<string>>();
|
|
334
|
+
if (isVirtualComboNamespace) {
|
|
335
|
+
for (const target of targets) {
|
|
336
|
+
const combo = config.combos && Object.hasOwn(config.combos, target.id) ? config.combos[target.id] : undefined;
|
|
337
|
+
if (!combo) return jsonResponse({ error: "invalid model visibility target" }, 400);
|
|
338
|
+
targetComboSelectors.set(target.id, new Set(comboDisabledModelSelectors(target.id, combo)));
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
const matchesTarget = (stored: string, target: { id: string; native: boolean }) => target.native
|
|
342
|
+
? stored === target.id
|
|
343
|
+
: isVirtualComboNamespace
|
|
344
|
+
? targetComboSelectors.get(target.id)!.has(stored)
|
|
345
|
+
: slugEquals(stored, provider, target.id);
|
|
346
|
+
|
|
347
|
+
let disabled = [...new Set(config.disabledModels ?? [])];
|
|
348
|
+
if (body.enabled) {
|
|
349
|
+
if (scope === "provider") {
|
|
350
|
+
if (providerConfig && !isVirtualComboNamespace) delete providerConfig.selectedModels;
|
|
351
|
+
if (isVirtualComboNamespace) {
|
|
352
|
+
disabled = disabled.filter(stored => !knownComboSelectors.has(stored));
|
|
353
|
+
} else {
|
|
354
|
+
const nativeIds = provider === "openai"
|
|
355
|
+
? disabledNativeSlugs({ disabledModels: disabled })
|
|
356
|
+
: new Set<string>();
|
|
357
|
+
const accountNativeIds = provider === "openai" ? new Set(accountNativeQualified) : new Set<string>();
|
|
358
|
+
const nativeAliasSlugs = provider === "openai"
|
|
359
|
+
? configuredNativeAliasSlugs(config)
|
|
360
|
+
: new Set<string>();
|
|
361
|
+
disabled = disabled.filter(stored => (
|
|
362
|
+
knownComboSelectors.has(stored)
|
|
363
|
+
|| nativeAliasSlugs.has(stored)
|
|
364
|
+
|| (!stored.startsWith(`${provider}/`) && !nativeIds.has(stored) && !accountNativeIds.has(stored))
|
|
365
|
+
));
|
|
366
|
+
}
|
|
367
|
+
} else {
|
|
368
|
+
if (!isVirtualComboNamespace && providerConfig?.selectedModels && providerConfig.selectedModels.length > 0) {
|
|
369
|
+
const additions = targets.filter(target => !target.native).map(target => target.id);
|
|
370
|
+
providerConfig.selectedModels = [...new Set([...providerConfig.selectedModels, ...additions])];
|
|
371
|
+
}
|
|
372
|
+
disabled = disabled.filter(stored => !targets.some(target => matchesTarget(stored, target)));
|
|
373
|
+
}
|
|
374
|
+
} else {
|
|
375
|
+
for (const target of targets) {
|
|
376
|
+
const canonical = target.native
|
|
377
|
+
? target.id
|
|
378
|
+
: isVirtualComboNamespace
|
|
379
|
+
? comboModelId(target.id)
|
|
380
|
+
: routedSlug(provider, target.id);
|
|
381
|
+
const alreadyDisabled = disabled.some(stored => matchesTarget(stored, target));
|
|
382
|
+
if (!alreadyDisabled) disabled.push(canonical);
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
config.disabledModels = disabled;
|
|
387
|
+
persistConfig(config);
|
|
388
|
+
const catalogRefresh = await convergeCodexCatalog();
|
|
389
|
+
return jsonResponse({ ok: true, scope, provider, enabled: body.enabled, disabled, catalogRefresh });
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
if (url.pathname === "/api/custom-models" && req.method === "GET") {
|
|
393
|
+
return jsonResponse(config.customModels ?? []);
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
if (url.pathname === "/api/custom-models" && req.method === "POST") {
|
|
397
|
+
let body: { provider?: unknown; modelId?: unknown; displayName?: unknown; contextWindow?: unknown; inputModalities?: unknown; reasoningEfforts?: unknown; defaultReasoningEffort?: unknown };
|
|
398
|
+
try { body = await readManagementJsonBody(req); } catch (error) { rethrowManagementBodyTooLarge(error); return jsonResponse({ error: "invalid JSON body" }, 400); }
|
|
399
|
+
const provider = typeof body.provider === "string" ? body.provider.trim() : "";
|
|
400
|
+
const modelId = typeof body.modelId === "string" ? body.modelId.trim() : "";
|
|
401
|
+
if (!provider || !modelId) return jsonResponse({ error: "provider and modelId are required" }, 400);
|
|
402
|
+
if (!isValidProviderName(provider)) return jsonResponse({ error: "invalid provider name" }, 400);
|
|
403
|
+
if (!hasOwnProvider(config.providers, provider)) return jsonResponse({ error: "provider not configured" }, 404);
|
|
404
|
+
const displayName = typeof body.displayName === "string" && body.displayName.trim() ? body.displayName.trim() : undefined;
|
|
405
|
+
if (displayName?.includes("/")) return jsonResponse({ error: "displayName must not contain /" }, 400);
|
|
406
|
+
const contextWindow = typeof body.contextWindow === "number" && body.contextWindow > 0 ? Math.floor(body.contextWindow) : undefined;
|
|
407
|
+
const modalities = readInputModalities(body.inputModalities);
|
|
408
|
+
if (modalities.error) return jsonResponse({ error: modalities.error }, 400);
|
|
409
|
+
const inputModalities = modalities.values;
|
|
410
|
+
const reasoning = readReasoningEfforts(body.reasoningEfforts);
|
|
411
|
+
if (reasoning.error) return jsonResponse({ error: reasoning.error }, 400);
|
|
412
|
+
const defaultEffort = readDefaultReasoningEffort(body.defaultReasoningEffort, reasoning.values);
|
|
413
|
+
if (defaultEffort.error) return jsonResponse({ error: defaultEffort.error }, 400);
|
|
414
|
+
const existing = config.customModels ?? [];
|
|
415
|
+
const newSlug = routedSlug(provider, modelId);
|
|
416
|
+
if (existing.some(cm => routedSlug(cm.provider, cm.modelId) === newSlug)) {
|
|
417
|
+
return jsonResponse({ error: "duplicate model" }, 409);
|
|
418
|
+
}
|
|
419
|
+
const known = knownModelIdsForProvider(provider, config.providers[provider], config);
|
|
420
|
+
if (encodedModelIdCollides(modelId, known)) {
|
|
421
|
+
return jsonResponse({ error: "ambiguous model id" }, 409);
|
|
422
|
+
}
|
|
423
|
+
const entry: OcxCustomModel = {
|
|
424
|
+
id: randomUUID(),
|
|
425
|
+
provider,
|
|
426
|
+
modelId,
|
|
427
|
+
...(displayName ? { displayName } : {}),
|
|
428
|
+
...(contextWindow ? { contextWindow } : {}),
|
|
429
|
+
...(inputModalities && inputModalities.length > 0 ? { inputModalities } : {}),
|
|
430
|
+
...(reasoning.values !== undefined ? { reasoningEfforts: reasoning.values } : {}),
|
|
431
|
+
...(defaultEffort.value ? { defaultReasoningEffort: defaultEffort.value } : {}),
|
|
432
|
+
addedAt: new Date().toISOString(),
|
|
433
|
+
};
|
|
434
|
+
config.customModels = [...existing, entry];
|
|
435
|
+
persistConfig(config);
|
|
436
|
+
const catalogRefresh = await convergeCodexCatalog();
|
|
437
|
+
return jsonResponse({ ...entry, catalogRefresh }, 201);
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
const customPutMatch = url.pathname.match(/^\/api\/custom-models\/([^/]+)$/);
|
|
441
|
+
if (customPutMatch && req.method === "PUT") {
|
|
442
|
+
let id: string;
|
|
443
|
+
try { id = decodeURIComponent(customPutMatch[1]); } catch { return jsonResponse({ error: "invalid id encoding" }, 400); }
|
|
444
|
+
let body: { displayName?: unknown; contextWindow?: unknown; inputModalities?: unknown; modelId?: unknown; reasoningEfforts?: unknown; defaultReasoningEffort?: unknown };
|
|
445
|
+
try { body = await readManagementJsonBody(req); } catch (error) { rethrowManagementBodyTooLarge(error); return jsonResponse({ error: "invalid JSON body" }, 400); }
|
|
446
|
+
const list = config.customModels ?? [];
|
|
447
|
+
const idx = list.findIndex(cm => cm.id === id);
|
|
448
|
+
if (idx === -1) return jsonResponse({ error: "not found" }, 404);
|
|
449
|
+
const cm = { ...list[idx] };
|
|
450
|
+
if (typeof body.modelId === "string" && body.modelId.trim()) {
|
|
451
|
+
cm.modelId = body.modelId.trim();
|
|
452
|
+
}
|
|
453
|
+
if (body.displayName !== undefined) {
|
|
454
|
+
const dn = typeof body.displayName === "string" ? body.displayName.trim() : "";
|
|
455
|
+
if (dn.includes("/")) return jsonResponse({ error: "displayName must not contain /" }, 400);
|
|
456
|
+
cm.displayName = dn || undefined;
|
|
457
|
+
}
|
|
458
|
+
if (body.contextWindow !== undefined) {
|
|
459
|
+
cm.contextWindow = typeof body.contextWindow === "number" && body.contextWindow > 0 ? Math.floor(body.contextWindow) : undefined;
|
|
460
|
+
}
|
|
461
|
+
if (body.inputModalities !== undefined) {
|
|
462
|
+
const edited = readInputModalities(body.inputModalities);
|
|
463
|
+
if (edited.error) return jsonResponse({ error: edited.error }, 400);
|
|
464
|
+
cm.inputModalities = edited.values && edited.values.length > 0 ? edited.values : undefined;
|
|
465
|
+
}
|
|
466
|
+
// `null` clears the stored ladder back to "inherit from the provider row"; `[]` stays
|
|
467
|
+
// stored as an explicit "no reasoning" override. The default effort rides along and is
|
|
468
|
+
// validated against the ladder the row ends up with.
|
|
469
|
+
if (body.reasoningEfforts !== undefined) {
|
|
470
|
+
if (body.reasoningEfforts === null) {
|
|
471
|
+
cm.reasoningEfforts = undefined;
|
|
472
|
+
} else {
|
|
473
|
+
const edited = readReasoningEfforts(body.reasoningEfforts);
|
|
474
|
+
if (edited.error) return jsonResponse({ error: edited.error }, 400);
|
|
475
|
+
cm.reasoningEfforts = edited.values;
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
if (body.defaultReasoningEffort !== undefined) {
|
|
479
|
+
const edited = readDefaultReasoningEffort(body.defaultReasoningEffort, cm.reasoningEfforts);
|
|
480
|
+
if (edited.error) return jsonResponse({ error: edited.error }, 400);
|
|
481
|
+
cm.defaultReasoningEffort = edited.value;
|
|
482
|
+
}
|
|
483
|
+
// Mirror of the POST invariant: a default only survives as a member of the final ladder.
|
|
484
|
+
// Without this, a ladder shrink/clear on a row that was created with a default leaves a
|
|
485
|
+
// stale default that re-applies itself onto the inherited ladder in the generated catalog
|
|
486
|
+
// (the GUI toggle-off path sends only reasoningEfforts, never the default).
|
|
487
|
+
if (cm.defaultReasoningEffort !== undefined) {
|
|
488
|
+
const ladder = cm.reasoningEfforts;
|
|
489
|
+
if (!ladder || ladder.length === 0 || !ladder.includes(cm.defaultReasoningEffort)) {
|
|
490
|
+
cm.defaultReasoningEffort = undefined;
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
const updatedSlug = routedSlug(cm.provider, cm.modelId);
|
|
494
|
+
if (list.some((other, i) => i !== idx && routedSlug(other.provider, other.modelId) === updatedSlug)) {
|
|
495
|
+
return jsonResponse({ error: "duplicate model" }, 409);
|
|
496
|
+
}
|
|
497
|
+
const known = knownModelIdsForProvider(cm.provider, config.providers[cm.provider], {
|
|
498
|
+
customModels: list.filter((_, i) => i !== idx),
|
|
499
|
+
});
|
|
500
|
+
if (encodedModelIdCollides(cm.modelId, known)) {
|
|
501
|
+
return jsonResponse({ error: "ambiguous model id" }, 409);
|
|
502
|
+
}
|
|
503
|
+
list[idx] = cm;
|
|
504
|
+
config.customModels = list;
|
|
505
|
+
persistConfig(config);
|
|
506
|
+
const catalogRefresh = await convergeCodexCatalog();
|
|
507
|
+
return jsonResponse({ ...cm, catalogRefresh });
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
const customDelMatch = url.pathname.match(/^\/api\/custom-models\/([^/]+)$/);
|
|
511
|
+
if (customDelMatch && req.method === "DELETE") {
|
|
512
|
+
let id: string;
|
|
513
|
+
try { id = decodeURIComponent(customDelMatch[1]); } catch { return jsonResponse({ error: "invalid id encoding" }, 400); }
|
|
514
|
+
const list = config.customModels ?? [];
|
|
515
|
+
const idx = list.findIndex(cm => cm.id === id);
|
|
516
|
+
if (idx === -1) return jsonResponse({ error: "not found" }, 404);
|
|
517
|
+
list.splice(idx, 1);
|
|
518
|
+
config.customModels = list.length > 0 ? list : undefined;
|
|
519
|
+
persistConfig(config);
|
|
520
|
+
const catalogRefresh = await convergeCodexCatalog();
|
|
521
|
+
return jsonResponse({ ok: true, catalogRefresh });
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
// Per-provider catalog allowlist (issue #52): when a provider has a non-empty selectedModels list,
|
|
525
|
+
// only those ids ship to Codex's catalog / /v1/models. GET returns the CURRENT selection plus the
|
|
526
|
+
// FULL available set per provider (unfiltered — the picker needs everything to choose from).
|
|
527
|
+
if (url.pathname === "/api/selected-models" && req.method === "GET") {
|
|
528
|
+
const models = await fetchAllModels(config);
|
|
529
|
+
const available: Record<string, string[]> = {};
|
|
530
|
+
for (const m of models) (available[m.provider] ??= []).push(m.id);
|
|
531
|
+
const selected: Record<string, string[]> = {};
|
|
532
|
+
// Live-catalog provenance. The GUI cannot infer this by subtracting known custom ids: an id
|
|
533
|
+
// that is both custom and discovered would make a real live catalog look custom-only.
|
|
534
|
+
const liveModelCounts: Record<string, number> = {};
|
|
535
|
+
for (const [name, prov] of Object.entries(config.providers)) {
|
|
536
|
+
if (Array.isArray(prov.selectedModels) && prov.selectedModels.length > 0) selected[name] = [...prov.selectedModels];
|
|
537
|
+
const liveCount = getProviderLiveModelCount(name);
|
|
538
|
+
if (liveCount !== undefined) liveModelCounts[name] = liveCount;
|
|
539
|
+
}
|
|
540
|
+
return jsonResponse({ selected, available, liveModelCounts });
|
|
541
|
+
}
|
|
542
|
+
if (url.pathname === "/api/selected-models" && req.method === "PUT") {
|
|
543
|
+
let body: { provider?: unknown; models?: unknown };
|
|
544
|
+
try { body = await readManagementJsonBody(req); } catch (error) { rethrowManagementBodyTooLarge(error); return jsonResponse({ error: "invalid JSON body" }, 400); }
|
|
545
|
+
const provider = typeof body.provider === "string" ? body.provider : "";
|
|
546
|
+
if (!provider || !hasOwnProvider(config.providers, provider)) {
|
|
547
|
+
return jsonResponse({ error: "unknown provider" }, provider ? 404 : 400);
|
|
548
|
+
}
|
|
549
|
+
const models = Array.isArray(body.models)
|
|
550
|
+
? [...new Set(body.models.filter((m): m is string => typeof m === "string"))]
|
|
551
|
+
: [];
|
|
552
|
+
// Empty list clears the allowlist (provider reverts to exposing all models).
|
|
553
|
+
if (models.length > 0) config.providers[provider].selectedModels = models;
|
|
554
|
+
else delete config.providers[provider].selectedModels;
|
|
555
|
+
persistConfig(config);
|
|
556
|
+
const catalogRefresh = await convergeCodexCatalog();
|
|
557
|
+
return jsonResponse({ ok: true, provider, selected: models, catalogRefresh });
|
|
558
|
+
}
|
|
559
|
+
return null;
|
|
560
|
+
}
|