@iislee/opencodex 2.11.0 → 2.35.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS_INSTALL.md +109 -0
- package/README.md +117 -22
- package/bin/ocx.mjs +245 -88
- package/bin/package-main.mjs +1 -1
- package/gui/dist/assets/index-Be1sDpWw.css +1 -0
- package/gui/dist/assets/index-D4OKgUV4.js +114 -0
- package/gui/dist/index.html +2 -2
- package/gui/dist/provider-icons/alibaba-color.svg +1 -1
- package/gui/dist/provider-icons/antigravity-color.svg +1 -1
- package/gui/dist/provider-icons/claude-color.svg +1 -1
- package/gui/dist/provider-icons/cline-color.svg +16 -0
- package/gui/dist/provider-icons/cloudflare-ai-gateway-color.svg +1 -1
- package/gui/dist/provider-icons/commandcode-color.svg +1 -0
- package/gui/dist/provider-icons/copilot-color.svg +1 -1
- package/gui/dist/provider-icons/cursor-color.svg +1 -1
- package/gui/dist/provider-icons/deepseek-color.svg +1 -1
- package/gui/dist/provider-icons/firepass-color.svg +1 -1
- package/gui/dist/provider-icons/fireworks-color.svg +1 -1
- package/gui/dist/provider-icons/gemini-color.svg +1 -1
- package/gui/dist/provider-icons/github-copilot-color.svg +1 -1
- package/gui/dist/provider-icons/gitlab-duo-color.svg +1 -1
- package/gui/dist/provider-icons/grok.svg +1 -1
- package/gui/dist/provider-icons/groq-color.svg +1 -1
- package/gui/dist/provider-icons/huggingface-color.svg +1 -1
- package/gui/dist/provider-icons/kimi-color.svg +1 -1
- package/gui/dist/provider-icons/kiro-color.svg +2 -2
- package/gui/dist/provider-icons/lm-studio-color.svg +1 -1
- package/gui/dist/provider-icons/mistral-color.svg +1 -1
- package/gui/dist/provider-icons/moonshot-color.svg +1 -1
- package/gui/dist/provider-icons/nvidia-color.svg +1 -1
- package/gui/dist/provider-icons/ollama-color.svg +1 -1
- package/gui/dist/provider-icons/openai.svg +1 -1
- package/gui/dist/provider-icons/opencode.svg +2 -1
- package/gui/dist/provider-icons/openrouter-color.svg +1 -1
- package/gui/dist/provider-icons/pi.svg +2 -2
- package/gui/dist/provider-icons/qianfan-color.svg +1 -1
- package/gui/dist/provider-icons/qwen-portal-color.svg +1 -1
- package/gui/dist/provider-icons/vercel-ai-gateway-color.svg +1 -1
- package/gui/dist/provider-icons/vllm-color.svg +1 -1
- package/gui/dist/provider-icons/xiaomi-color.svg +1 -1
- package/package.json +21 -10
- package/src/adapters/anthropic-output-schema.ts +137 -0
- package/src/adapters/anthropic.ts +475 -62
- package/src/adapters/base.ts +82 -7
- package/src/adapters/client-fingerprint.ts +18 -12
- package/src/adapters/cline-pass-deepseek-v4-tool-replay.ts +69 -0
- package/src/adapters/command-code.ts +637 -0
- package/src/adapters/cursor/call-id.ts +44 -0
- package/src/adapters/cursor/catalog.ts +541 -0
- package/src/adapters/cursor/checkpoint-store.ts +308 -0
- package/src/adapters/cursor/cursor-errors.ts +144 -6
- package/src/adapters/cursor/discovery.ts +122 -14
- package/src/adapters/cursor/effort-map.ts +106 -4
- package/src/adapters/cursor/envelope-echo.ts +290 -0
- package/src/adapters/cursor/framing.ts +39 -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/live-models.ts +180 -59
- package/src/adapters/cursor/live-transport.ts +623 -170
- package/src/adapters/cursor/message-mapper.ts +4 -1
- package/src/adapters/cursor/native-exec-common.ts +23 -2
- package/src/adapters/cursor/native-exec-desktop.ts +23 -0
- package/src/adapters/cursor/native-exec-fs.ts +10 -7
- package/src/adapters/cursor/native-exec-network.ts +1 -1
- package/src/adapters/cursor/native-exec-shell.ts +5 -3
- package/src/adapters/cursor/native-exec.ts +121 -14
- package/src/adapters/cursor/protobuf-events.ts +829 -11
- package/src/adapters/cursor/protobuf-request.ts +447 -75
- package/src/adapters/cursor/request-builder.ts +271 -35
- package/src/adapters/cursor/tool-definitions.ts +252 -12
- package/src/adapters/cursor/tool-result-normalize.ts +115 -0
- package/src/adapters/cursor/transport.ts +22 -0
- package/src/adapters/cursor/types.ts +48 -1
- package/src/adapters/cursor.ts +336 -39
- package/src/adapters/exec-tool-result-normalize.ts +99 -0
- package/src/adapters/google-antigravity-replay.ts +713 -41
- package/src/adapters/google-antigravity-wire.ts +48 -10
- package/src/adapters/google-errors.ts +44 -12
- package/src/adapters/google-http.ts +38 -10
- package/src/adapters/google-tool-schema.ts +80 -15
- package/src/adapters/google-truncation.ts +11 -0
- package/src/adapters/google.ts +742 -86
- package/src/adapters/identity.ts +39 -6
- package/src/adapters/image.ts +1 -1
- package/src/adapters/kiro-constants.ts +27 -0
- package/src/adapters/kiro-errors.ts +11 -0
- package/src/adapters/kiro-events.ts +19 -1
- package/src/adapters/kiro-thinking.ts +18 -2
- package/src/adapters/kiro-tools.ts +65 -17
- package/src/adapters/kiro.ts +270 -73
- package/src/adapters/mimo-free.ts +17 -0
- package/src/adapters/openai-chat-url.ts +11 -0
- package/src/adapters/openai-chat.ts +1377 -344
- package/src/adapters/openai-responses-url.ts +16 -0
- package/src/adapters/openai-responses.ts +1164 -64
- package/src/adapters/registry.ts +175 -0
- package/src/adapters/responses-tool-schema.ts +67 -0
- package/src/adapters/run-turn-queue.ts +36 -1
- package/src/adapters/tool-call-id.ts +119 -0
- package/src/adapters/tool-catalog-nudge.ts +105 -21
- package/src/adapters/xai-tool-schema.ts +436 -0
- package/src/adapters/xai-web-search.ts +186 -0
- package/src/bridge.ts +512 -92
- package/src/chat/inbound.ts +57 -20
- package/src/chat/outbound.ts +97 -33
- package/src/claude/agents-inject.ts +40 -10
- package/src/claude/context-windows.ts +37 -14
- package/src/claude/desktop-3p.ts +243 -9
- package/src/claude/gateway-cache.ts +41 -4
- package/src/claude/inbound.ts +72 -3
- package/src/claude/model-info.ts +38 -15
- package/src/claude/outbound.ts +88 -21
- package/src/cli/access.ts +46 -3
- package/src/cli/account-api.ts +150 -19
- package/src/cli/account-auth.ts +33 -6
- package/src/cli/account-catalog-refresh.ts +14 -0
- package/src/cli/account-extended.ts +648 -28
- package/src/cli/account-main.ts +317 -0
- package/src/cli/account.ts +97 -19
- package/src/cli/agent.ts +53 -2
- package/src/cli/alias.ts +66 -0
- package/src/cli/capabilities-command.ts +94 -0
- package/src/cli/capabilities.ts +496 -0
- package/src/cli/claude-agent-startup-sync.ts +73 -0
- package/src/cli/claude-desktop.ts +60 -15
- package/src/cli/claude.ts +129 -35
- package/src/cli/codex-log-guard-doctor.ts +103 -0
- package/src/cli/codex-shim-autorestore.ts +2 -0
- package/src/cli/codex-shim-readiness.ts +76 -0
- package/src/cli/combo.ts +8 -0
- package/src/cli/config-command.ts +74 -10
- package/src/cli/dispatch.ts +821 -0
- package/src/cli/doctor.ts +440 -56
- package/src/cli/ensure-desired-integrations.ts +152 -0
- package/src/cli/export-command.ts +46 -20
- package/src/cli/help.ts +34 -274
- package/src/cli/index.ts +417 -528
- package/src/cli/init.ts +5 -17
- package/src/cli/inspect.ts +230 -0
- package/src/cli/integrations.ts +120 -2
- 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 +130 -2
- package/src/cli/models.ts +118 -16
- package/src/cli/observe.ts +123 -11
- package/src/cli/opencode.ts +4 -2
- package/src/cli/provider-runtime.ts +34 -3
- package/src/cli/provider.ts +25 -3
- package/src/cli/ready.ts +301 -0
- package/src/cli/registry.ts +476 -0
- package/src/cli/root.ts +86 -0
- package/src/cli/route-policy.ts +92 -0
- package/src/cli/runtime-api.ts +57 -10
- package/src/cli/star-prompt.ts +71 -15
- package/src/cli/status.ts +51 -4
- package/src/cli/storage.ts +234 -0
- package/src/cli/system-command.ts +16 -0
- package/src/cli/system-restart-client.ts +146 -0
- package/src/cli/tray-proxy.ts +153 -6
- package/src/cli/usage-report.ts +184 -0
- package/src/cli/v2.ts +137 -18
- package/src/cli/version-skew.ts +46 -0
- package/src/cli.ts +1 -1
- package/src/clients/config-export.ts +1442 -23
- package/src/clients/effective-status.ts +30 -2
- package/src/clients/probes/cline.ts +81 -0
- package/src/codex/account-label.ts +35 -1
- package/src/codex/account-lifecycle.ts +130 -13
- package/src/codex/account-namespaces.ts +49 -3
- package/src/codex/account-pause.ts +2 -1
- package/src/codex/account-priority.ts +84 -0
- package/src/codex/account-store.ts +29 -2
- package/src/codex/account-usability.ts +25 -2
- package/src/codex/admission.ts +256 -0
- package/src/codex/affinity-debug.ts +162 -0
- package/src/codex/app-server-processes.ts +570 -107
- package/src/codex/app-server-restart-service.ts +232 -0
- package/src/codex/auth-api.ts +893 -246
- package/src/codex/auth-collision.ts +5 -3
- package/src/codex/auth-context.ts +348 -32
- package/src/codex/autostart-health.ts +24 -1
- package/src/codex/catalog/account-models.ts +67 -0
- package/src/codex/catalog/aggregation.ts +86 -10
- package/src/codex/catalog/bundled.ts +331 -33
- package/src/codex/catalog/effort.ts +138 -32
- package/src/codex/catalog/filesystem-evidence.ts +302 -0
- package/src/codex/catalog/kinds.ts +2 -0
- package/src/codex/catalog/metadata.ts +555 -45
- package/src/codex/catalog/native-models.ts +75 -0
- package/src/codex/catalog/parsing.ts +269 -30
- package/src/codex/catalog/provider-fetch.ts +1626 -144
- package/src/codex/catalog/sync.ts +1576 -187
- 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 +6 -3
- package/src/codex/codex-write-lock.ts +386 -0
- package/src/codex/convergence-types.ts +614 -0
- package/src/codex/convergence.ts +676 -0
- package/src/codex/coordinator-doctor.ts +332 -0
- package/src/codex/custom-model-catalog-migration.ts +176 -0
- package/src/codex/desired-state.ts +230 -0
- package/src/codex/desktop-app-restart.ts +355 -0
- package/src/codex/features.ts +636 -39
- package/src/codex/generation.ts +202 -0
- package/src/codex/history-job.ts +436 -0
- package/src/codex/history-lock.ts +242 -0
- package/src/codex/history-manifest.ts +112 -0
- package/src/codex/history-migration-guardian.ts +30 -24
- package/src/codex/history-provider.ts +1016 -235
- package/src/codex/history-transition.ts +105 -0
- package/src/codex/history-worker.ts +223 -0
- package/src/codex/inject-coordination.ts +373 -0
- package/src/codex/inject.ts +1114 -152
- package/src/codex/injected-marker.ts +37 -3
- package/src/codex/integration-record.ts +266 -0
- package/src/codex/internal/catalog-writer.ts +203 -0
- package/src/codex/internal/history-writer.ts +81 -0
- package/src/codex/journal.ts +66 -4
- package/src/codex/log-guard/inspect.ts +524 -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 +24 -0
- package/src/codex/main-account.ts +29 -1
- package/src/codex/management-convergence.ts +167 -0
- package/src/codex/model-cache.ts +56 -10
- package/src/codex/model-entitlements.ts +360 -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 +622 -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 +675 -0
- package/src/codex/paths.ts +80 -1
- package/src/codex/plan-from-token.ts +140 -0
- package/src/codex/plan.ts +40 -0
- package/src/codex/plugins-doctor.ts +1 -1
- package/src/codex/pool-rotation.ts +74 -4
- package/src/codex/project-config-warnings.ts +20 -6
- package/src/codex/prompt-journal.ts +352 -0
- package/src/codex/prompt-layers.ts +1313 -0
- package/src/codex/prompt-lock.ts +143 -0
- package/src/codex/prompt-text-probe.ts +238 -0
- package/src/codex/quota-rejection.ts +298 -0
- package/src/codex/quota.ts +264 -21
- package/src/codex/refresh.ts +11 -2
- package/src/codex/reset-credit-recovery.ts +1044 -0
- package/src/codex/routing.ts +514 -94
- package/src/codex/runtime.ts +159 -38
- package/src/codex/shim.ts +1065 -31
- package/src/codex/subagent-model-fallback.ts +437 -43
- package/src/codex/sync.ts +191 -2
- package/src/codex/transition-state.ts +720 -0
- package/src/codex/upstream-host-health.ts +368 -0
- package/src/codex/user-identity.ts +557 -0
- package/src/codex/warmup.ts +187 -81
- package/src/codex/write-coordination.ts +114 -0
- package/src/combos/failover.ts +47 -0
- package/src/combos/index.ts +4 -0
- package/src/combos/request.ts +32 -0
- package/src/combos/types.ts +91 -9
- package/src/compatibility/index.ts +26 -0
- package/src/compatibility/manifest.ts +253 -0
- package/src/compatibility/openai-responses.ts +121 -0
- package/src/config/atomic-write.ts +219 -0
- package/src/config/paths.ts +40 -0
- package/src/config/process-state.ts +309 -0
- package/src/config/provider-name.ts +24 -0
- package/src/config/provider-validation.ts +177 -0
- package/src/config/rebase-provenance.ts +68 -0
- package/src/config.ts +1713 -709
- package/src/generated/compatibility-version.json +3324 -0
- package/src/generated/{jawcode-model-metadata.ts → model-metadata.ts} +19 -17
- package/src/grok/inject.ts +16 -5
- package/src/grok/inspect.ts +45 -0
- package/src/grok/sync.ts +2 -2
- package/src/images/loop.ts +157 -33
- package/src/images/plan.ts +24 -13
- 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-policy.ts +160 -0
- package/src/integrations/ownership.ts +155 -0
- package/src/integrations/registry.ts +166 -0
- package/src/integrations/serialize.ts +314 -0
- package/src/integrations/state.ts +433 -0
- package/src/integrations/store.ts +103 -0
- package/src/integrations/writer-lock.ts +98 -0
- package/src/integrations/writer.ts +715 -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/admin-secrets.ts +24 -0
- package/src/lib/app-owned-memory-stores.ts +22 -0
- package/src/lib/bounded-body.ts +167 -11
- package/src/lib/bun-runtime.ts +125 -12
- package/src/lib/bun-stream-caps.ts +13 -9
- package/src/lib/codex-restart-contract.ts +120 -0
- package/src/lib/config-ownership.ts +26 -2
- package/src/lib/destination-policy.ts +65 -1
- package/src/lib/errors.ts +80 -5
- package/src/lib/fabric-task-execution-authority.ts +7 -0
- package/src/lib/fabric-task-host.ts +29 -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/optional-shutdown-hooks.ts +57 -0
- package/src/lib/package-tree-integrity.ts +101 -0
- package/src/lib/pinned-http.ts +145 -26
- package/src/lib/process-control.ts +6 -2
- package/src/lib/provider-outbound.ts +49 -9
- package/src/lib/redact.ts +419 -3
- package/src/lib/self-launch-argv.ts +15 -0
- package/src/lib/server-resource-ownership.ts +71 -0
- package/src/lib/service-secrets.ts +15 -0
- package/src/lib/shadow-call.ts +35 -4
- package/src/lib/sse-decoder.ts +41 -0
- package/src/lib/state-store-registrations.ts +10 -2
- package/src/lib/system-restart-contract.ts +73 -0
- package/src/lib/token-estimate.ts +19 -2
- package/src/lib/tool-argument-integers.ts +253 -0
- package/src/lib/translator-budget.ts +44 -0
- package/src/lib/upstream-http-version.ts +57 -0
- package/src/lib/upstream-reachability.ts +95 -0
- package/src/lib/upstream-retry.ts +156 -3
- package/src/lib/windows-atomic-replace.ts +155 -0
- package/src/lib/windows-elevation.ts +70 -2
- package/src/lib/windows-secret-acl.ts +409 -69
- 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 +33 -5
- 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.ts +12 -1
- package/src/oauth/callback-server.ts +30 -4
- package/src/oauth/chatgpt.ts +12 -1
- package/src/oauth/cline.ts +203 -0
- package/src/oauth/command-code.ts +239 -0
- package/src/oauth/cursor.ts +46 -5
- package/src/oauth/generic-account-failover.ts +231 -0
- package/src/oauth/google-antigravity.ts +35 -3
- package/src/oauth/health.ts +20 -12
- package/src/oauth/index.ts +462 -71
- package/src/oauth/key-providers.ts +25 -0
- package/src/oauth/kimi.ts +25 -3
- package/src/oauth/kiro.ts +95 -6
- package/src/oauth/local-token-detect.ts +13 -2
- package/src/oauth/log.ts +3 -1
- package/src/oauth/login-cli.ts +88 -28
- package/src/oauth/nous.ts +798 -0
- package/src/oauth/open-browser-choice.ts +26 -0
- package/src/oauth/store.ts +133 -26
- package/src/oauth/token-guardian.ts +9 -3
- package/src/oauth/types.ts +15 -0
- package/src/pi/models.ts +2 -2
- package/src/providers/alibaba-region-migration.ts +1 -1
- package/src/providers/antigravity-models.ts +521 -31
- package/src/providers/auto-compact-budget.ts +65 -0
- package/src/providers/base-url-choices.ts +10 -0
- package/src/providers/codex-capacity.ts +292 -0
- package/src/providers/command-code-efforts.ts +176 -0
- package/src/providers/context-cap.ts +26 -8
- package/src/providers/cursor-pool.ts +72 -0
- package/src/providers/default-aliases.ts +65 -0
- package/src/providers/derive.ts +281 -6
- package/src/providers/fastwire.ts +507 -0
- package/src/providers/free-directory.ts +10 -7
- package/src/providers/google-vertex-location.ts +14 -0
- package/src/providers/key-failover.ts +71 -3
- package/src/providers/label.ts +35 -2
- package/src/providers/model-discovery-limits.ts +16 -0
- package/src/providers/model-discovery.ts +115 -22
- package/src/providers/model-presets.ts +119 -0
- package/src/providers/model-rename-migration.ts +255 -0
- package/src/providers/model-rename-startup.ts +28 -0
- package/src/providers/new-model-policy.ts +146 -0
- package/src/providers/openai-sidecar.ts +72 -4
- package/src/providers/openai-tier-startup.ts +31 -2
- package/src/providers/openai-tiers.ts +119 -4
- package/src/providers/openai-virtual-models.ts +1 -0
- package/src/providers/opencode-zen-rate-limit.ts +102 -0
- package/src/providers/provider-id-rewrite.ts +30 -0
- package/src/providers/quota.ts +1379 -40
- package/src/providers/registry.ts +1545 -112
- package/src/providers/request-pacing.ts +310 -0
- package/src/providers/service-tier.ts +277 -0
- package/src/providers/slug-codec.ts +94 -6
- 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 +32 -4
- package/src/reasoning-effort.ts +68 -3
- package/src/responses/apply-patch-envelope.ts +63 -0
- package/src/responses/code-mode-helper-compat.ts +50 -0
- package/src/responses/compaction.ts +26 -1
- package/src/responses/custom-tool-compat.ts +384 -0
- package/src/responses/hosted-tool-policy.ts +9 -0
- package/src/responses/namespace-tool-compat.ts +435 -0
- package/src/responses/parser.ts +225 -38
- package/src/responses/provider-continuation.ts +98 -0
- package/src/responses/provider-opaque-metadata.ts +73 -0
- package/src/responses/reasoning-envelope.ts +9 -1
- package/src/responses/reasoning-replay-cache.ts +504 -0
- package/src/responses/schema.ts +12 -2
- package/src/responses/spill-store.ts +75 -10
- package/src/responses/state.ts +659 -31
- package/src/responses/thought-signature-replay.ts +364 -0
- package/src/responses/tool-search-compat.ts +301 -0
- package/src/responses/truncated-stop-reason.ts +60 -0
- package/src/responses/turn-termination.ts +107 -0
- package/src/router.ts +405 -31
- package/src/routing/analytics.ts +379 -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 +155 -0
- package/src/routing/request-evidence.ts +45 -0
- package/src/routing/trace.ts +776 -0
- package/src/server/adapter-resolve.ts +2 -29
- package/src/server/auth-cors.ts +289 -48
- package/src/server/background-lifecycle.ts +182 -0
- package/src/server/chat-completions.ts +154 -72
- package/src/server/chat-native-sse.ts +331 -0
- package/src/server/chat-native.ts +445 -0
- package/src/server/claude-messages.ts +160 -44
- package/src/server/direct-local-http.ts +347 -0
- package/src/server/effort-policy.ts +18 -0
- package/src/server/github-copilot-responses-repair.ts +338 -0
- package/src/server/gui-static.ts +39 -10
- package/src/server/images.ts +94 -12
- package/src/server/index.ts +1021 -190
- package/src/server/lifecycle.ts +336 -14
- package/src/server/live.ts +136 -17
- 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 +433 -116
- package/src/server/management/api-key-usage.ts +31 -5
- package/src/server/management/body.ts +6 -0
- package/src/server/management/codex-prompt-routes.ts +570 -0
- package/src/server/management/combo-routes.ts +64 -25
- package/src/server/management/config-routes.ts +511 -51
- package/src/server/management/context.ts +89 -2
- 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 +127 -52
- package/src/server/management/model-routes.ts +461 -133
- package/src/server/management/model-rows.ts +167 -0
- package/src/server/management/native-integration-routes.ts +770 -0
- package/src/server/management/oauth-account-routes.ts +90 -8
- package/src/server/management/provider-capability-config.ts +48 -0
- package/src/server/management/provider-routes.ts +836 -159
- package/src/server/management/request-history-routes.ts +191 -0
- package/src/server/management/route-registry.ts +332 -0
- package/src/server/management/routing-analytics-routes.ts +74 -0
- package/src/server/management/routing-profile-routes.ts +380 -0
- package/src/server/management/shared.ts +27 -11
- package/src/server/management/sidebar-routes.ts +47 -31
- 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 +289 -33
- package/src/server/management/system-routes.ts +77 -0
- package/src/server/management/usage-summary-cache.ts +9 -1
- 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 +126 -23
- package/src/server/management-auth.ts +220 -5
- package/src/server/passive-route-linker.ts +66 -0
- package/src/server/port-reclaim.ts +1 -1
- package/src/server/ports.ts +41 -1
- package/src/server/proxy-liveness.ts +161 -10
- package/src/server/readiness.ts +99 -0
- package/src/server/relay-eager.ts +221 -54
- package/src/server/relay.ts +479 -87
- package/src/server/request-decompress.ts +113 -6
- package/src/server/request-log-conversation.ts +51 -0
- package/src/server/request-log.ts +298 -31
- 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/codex-auth-error.ts +55 -0
- package/src/server/responses/collaboration.ts +239 -36
- package/src/server/responses/combo-stream-preflight.ts +178 -0
- package/src/server/responses/compact.ts +444 -72
- package/src/server/responses/core.ts +3579 -436
- package/src/server/responses/empty-completion-guard.ts +333 -0
- package/src/server/responses/encrypted-payload.ts +62 -39
- package/src/server/responses/fetch-helpers.ts +89 -99
- package/src/server/responses/input-admission.ts +187 -0
- package/src/server/responses/pacing-overload.ts +13 -0
- package/src/server/responses/passthrough-error.ts +33 -9
- package/src/server/responses/policy-fallback.ts +178 -0
- package/src/server/responses/responses-field-backfill.ts +343 -0
- package/src/server/responses/terminal-guard.ts +26 -5
- package/src/server/responses/upstream-error.ts +5 -0
- package/src/server/responses/ws-upstream.ts +381 -0
- package/src/server/responses-custom-tool-repair.ts +363 -0
- package/src/server/responses-item-id-repair.ts +54 -6
- 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 +363 -0
- package/src/server/responses-tool-search-repair.ts +267 -0
- package/src/server/responses-undeclared-tool-guard.ts +376 -0
- package/src/server/responses.ts +18 -2
- package/src/server/search.ts +78 -13
- package/src/server/sse-frame-buffer.ts +319 -0
- package/src/server/sse-payload-rewrite.ts +110 -22
- package/src/server/startup-action-control.ts +8 -1
- package/src/server/startup-health-cache.ts +19 -1
- package/src/server/system-env.ts +80 -9
- package/src/server/ws-bridge.ts +53 -40
- package/src/service-manager-probe.ts +892 -0
- package/src/service.ts +1153 -94
- package/src/sidecar/auth.ts +92 -0
- package/src/sidecar/candidates.ts +83 -0
- package/src/storage/cleanup.ts +2 -2
- package/src/storage/policy-job.ts +14 -4
- package/src/storage/policy.ts +88 -23
- package/src/storage/scanner.ts +1 -1
- package/src/storage/worker-lifecycle.ts +14 -14
- package/src/tray/windows-tray.ps1 +83 -9
- package/src/tray/windows.ts +43 -16
- package/src/types/accounts.ts +37 -0
- package/src/types/config.ts +911 -0
- package/src/types/provider.ts +614 -0
- package/src/types/request.ts +397 -0
- package/src/types/tools.ts +236 -0
- package/src/types/wire.ts +80 -0
- package/src/types.ts +107 -1237
- package/src/update/index.ts +37 -23
- package/src/update/job.ts +445 -68
- package/src/update/notify.ts +12 -6
- package/src/update/npm-cache-preflight.d.mts +47 -0
- package/src/update/npm-cache-preflight.mjs +201 -0
- package/src/update/transactional-install.d.mts +22 -0
- package/src/update/transactional-install.mjs +266 -0
- package/src/usage/cost.ts +0 -0
- package/src/usage/expected-prices.ts +287 -16
- package/src/usage/log.ts +666 -50
- package/src/usage/summary.ts +388 -17
- 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 +10 -6
- package/src/vision/backends.ts +97 -0
- package/src/vision/describe.ts +27 -16
- package/src/vision/eligibility.ts +250 -0
- package/src/vision/index.ts +238 -24
- 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 +13 -7
- package/src/web-search/backends.ts +108 -0
- package/src/web-search/exa-executor.ts +119 -0
- package/src/web-search/executor.ts +21 -6
- package/src/web-search/gemini-executor.ts +141 -0
- package/src/web-search/index.ts +150 -15
- package/src/web-search/loop.ts +284 -54
- package/src/web-search/parse.ts +125 -30
- package/src/web-search/sources.ts +60 -0
- package/src/web-search/xai-executor.ts +219 -0
- package/gui/dist/assets/index-DTpMHS4F.js +0 -67
- package/gui/dist/assets/index-ZNVDE3C7.css +0 -1
- package/gui/dist/provider-icons/antigravity.svg +0 -1
- package/gui/dist/provider-icons/claude.svg +0 -1
- package/gui/dist/provider-icons/copilot.svg +0 -1
- package/gui/dist/provider-icons/cursor.svg +0 -2
- package/gui/dist/provider-icons/gemini.svg +0 -1
- package/gui/dist/provider-icons/grok-color.svg +0 -1
- package/gui/dist/provider-icons/kiro.svg +0 -14
- package/src/cli/internal-dispatch.ts +0 -20
|
@@ -1,11 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { createAzureAdapter } from "../adapters/azure";
|
|
3
|
-
import { createCursorAdapter } from "../adapters/cursor";
|
|
4
|
-
import { createGoogleAdapter } from "../adapters/google";
|
|
5
|
-
import { createKiroAdapter } from "../adapters/kiro";
|
|
6
|
-
import { createMimoFreeAdapter } from "../adapters/mimo-free";
|
|
7
|
-
import { createOpenAIChatAdapter } from "../adapters/openai-chat";
|
|
8
|
-
import { createResponsesPassthroughAdapter } from "../adapters/openai-responses";
|
|
1
|
+
import { createRegisteredAdapter } from "../adapters/registry";
|
|
9
2
|
import type { OcxProviderConfig } from "../types";
|
|
10
3
|
import { isWirePinnedModel, MODEL_ADAPTER_OVERRIDE_ALLOWED, pinnedWireAdapter } from "../types";
|
|
11
4
|
import { isCanonicalOpenAiForwardProvider } from "../providers/openai-tiers";
|
|
@@ -56,25 +49,5 @@ export function resolveWireProtocolOverride(
|
|
|
56
49
|
|
|
57
50
|
/** Build the provider adapter for a resolved provider config. */
|
|
58
51
|
export function resolveAdapter(providerConfig: OcxProviderConfig, cacheRetention?: "none" | "short" | "long") {
|
|
59
|
-
|
|
60
|
-
case "openai-chat":
|
|
61
|
-
return createOpenAIChatAdapter(providerConfig);
|
|
62
|
-
case "anthropic":
|
|
63
|
-
return createAnthropicAdapter(providerConfig, cacheRetention);
|
|
64
|
-
case "openai-responses":
|
|
65
|
-
return createResponsesPassthroughAdapter(providerConfig);
|
|
66
|
-
case "google":
|
|
67
|
-
return createGoogleAdapter(providerConfig);
|
|
68
|
-
case "kiro":
|
|
69
|
-
return createKiroAdapter(providerConfig);
|
|
70
|
-
case "azure":
|
|
71
|
-
case "azure-openai":
|
|
72
|
-
return createAzureAdapter(providerConfig);
|
|
73
|
-
case "cursor":
|
|
74
|
-
return createCursorAdapter(providerConfig);
|
|
75
|
-
case "mimo-free":
|
|
76
|
-
return createMimoFreeAdapter(providerConfig);
|
|
77
|
-
default:
|
|
78
|
-
throw new Error(`Unknown adapter: ${providerConfig.adapter}`);
|
|
79
|
-
}
|
|
52
|
+
return createRegisteredAdapter(providerConfig, { cacheRetention });
|
|
80
53
|
}
|
package/src/server/auth-cors.ts
CHANGED
|
@@ -1,21 +1,34 @@
|
|
|
1
1
|
import { timingSafeEqual } from "node:crypto";
|
|
2
2
|
import { formatErrorResponse } from "../bridge";
|
|
3
|
+
import {
|
|
4
|
+
codexAutoStartEnabled,
|
|
5
|
+
modelPreferHostedToolsConfigError,
|
|
6
|
+
providerModelCostsConfigError,
|
|
7
|
+
requestPacingConfigError,
|
|
8
|
+
retryOn429PolicyConfigError,
|
|
9
|
+
sanitizeModelCostsForDisplay,
|
|
10
|
+
} from "../config";
|
|
3
11
|
import {
|
|
4
12
|
apiKeyTransportConfigError,
|
|
5
13
|
booleanRecordConfigError,
|
|
6
14
|
modelAdapterRecordConfigError,
|
|
7
|
-
|
|
15
|
+
nonBlankStringArrayConfigError,
|
|
8
16
|
positiveIntegerConfigError,
|
|
9
17
|
positiveIntegerRecordConfigError,
|
|
10
18
|
providerBaseUrlConfigError,
|
|
11
19
|
providerHeadersConfigError,
|
|
12
20
|
reasoningSummaryDeliveryRecordConfigError,
|
|
13
|
-
|
|
21
|
+
upstreamHttpVersionConfigError,
|
|
22
|
+
} from "../config/provider-validation";
|
|
14
23
|
import { providerDestinationConfigError } from "../lib/destination-policy";
|
|
15
|
-
import {
|
|
24
|
+
import { redactSecretString } from "../lib/redact";
|
|
25
|
+
import { effectiveGoogleMode, getProviderRegistryEntry, providerCodexAccountMode, providerMatchesRegistryTransport, registryEntryForProviderDestination } from "../providers/registry";
|
|
16
26
|
import { providerConfigSeed } from "../providers/derive";
|
|
17
27
|
import type { OcxConfig, OcxProviderConfig } from "../types";
|
|
18
28
|
import { openRouterRoutingConfigError } from "../providers/openrouter-routing";
|
|
29
|
+
import { modelAutoCompactTokenLimitsConfigError } from "../providers/auto-compact-budget";
|
|
30
|
+
import { googleVertexLocationConfigError } from "../providers/google-vertex-location";
|
|
31
|
+
import { xaiResponsesOptInState } from "../providers/xai-responses-opt-in";
|
|
19
32
|
|
|
20
33
|
let _corsOrigin = "http://localhost:10100";
|
|
21
34
|
export function setCorsOrigin(port: number): void { _corsOrigin = `http://localhost:${port}`; }
|
|
@@ -69,7 +82,7 @@ export function isSameOriginAsRequest(req: Request, origin: string): boolean {
|
|
|
69
82
|
}
|
|
70
83
|
}
|
|
71
84
|
|
|
72
|
-
export function isAllowedRequestOrigin(req: Request, config:
|
|
85
|
+
export function isAllowedRequestOrigin(req: Request, config: RequestPolicyView): boolean {
|
|
73
86
|
const origin = req.headers.get("Origin");
|
|
74
87
|
if (!isApiAuthRequired(config)) {
|
|
75
88
|
if (!isLoopbackRequestHost(req.headers.get("Host"))) return false;
|
|
@@ -78,17 +91,30 @@ export function isAllowedRequestOrigin(req: Request, config: OcxConfig): boolean
|
|
|
78
91
|
return !origin || isLoopbackOriginValue(origin) || isSameOriginAsRequest(req, origin) || isExtraAllowedOrigin(origin, config);
|
|
79
92
|
}
|
|
80
93
|
|
|
81
|
-
function isExtraAllowedOrigin(origin: string, cfg:
|
|
94
|
+
function isExtraAllowedOrigin(origin: string, cfg: RequestPolicyView): boolean {
|
|
82
95
|
if (!cfg.corsAllowOrigins?.length) return false;
|
|
96
|
+
const parsedOrigin = comparableOrigin(origin);
|
|
83
97
|
return cfg.corsAllowOrigins.some(allowed => {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
}
|
|
98
|
+
const parsedAllowed = comparableOrigin(allowed);
|
|
99
|
+
return parsedOrigin !== null && parsedAllowed !== null
|
|
100
|
+
? parsedAllowed === parsedOrigin
|
|
101
|
+
: allowed === origin;
|
|
89
102
|
});
|
|
90
103
|
}
|
|
91
104
|
|
|
105
|
+
function comparableOrigin(value: string): string | null {
|
|
106
|
+
try {
|
|
107
|
+
const parsed = new URL(value);
|
|
108
|
+
if (parsed.origin !== "null") return parsed.origin;
|
|
109
|
+
// WHATWG URL exposes authority-based custom schemes (for example browser
|
|
110
|
+
// extensions) as opaque `null` origins. Compare their scheme + authority so
|
|
111
|
+
// one allowlisted extension cannot admit every other opaque origin.
|
|
112
|
+
return parsed.host ? `${parsed.protocol}//${parsed.host}` : null;
|
|
113
|
+
} catch {
|
|
114
|
+
return null;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
92
118
|
export function managementRequestOrigin(req: Request, config: OcxConfig): string | null {
|
|
93
119
|
const host = req.headers.get("Host");
|
|
94
120
|
const parsedHost = parseHttpHost(host);
|
|
@@ -119,17 +145,53 @@ export function browserSecurityHeaders(): Record<string, string> {
|
|
|
119
145
|
};
|
|
120
146
|
}
|
|
121
147
|
|
|
122
|
-
|
|
148
|
+
/**
|
|
149
|
+
* Baseline data-plane request headers. ChatGPT-Account-Id is required for browser/Electron
|
|
150
|
+
* ChatGPT & Codex App voice preflights (direct forward auth matches the bearer to this account
|
|
151
|
+
* id). The OpenAI-Alpha .. X-OAI-Attestation block covers GPT-Live voice protocol headers
|
|
152
|
+
* relayed by the /v1/live call-create path.
|
|
153
|
+
*/
|
|
154
|
+
const STATIC_ALLOWED_REQUEST_HEADERS =
|
|
155
|
+
"Content-Type, Authorization, X-OpenCodex-API-Key, X-Api-Key, Anthropic-Version, Anthropic-Beta, ChatGPT-Account-Id, OpenAI-Alpha, X-Session-Id, Session-Id, Thread-Id, Originator, X-OAI-Attestation";
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* A fixed allow-list cannot enumerate vendor telemetry headers: the OpenAI and Anthropic
|
|
159
|
+
* browser SDKs send `X-Stainless-*` describing runtime and retry state, and the browser blocks
|
|
160
|
+
* the real request when the preflight omits even one of them (#1773).
|
|
161
|
+
*
|
|
162
|
+
* Echo what an already-allowed origin asked for, and fall back to the static list otherwise.
|
|
163
|
+
* The echo is deliberately gated on the origin check that ran first: this widens which headers
|
|
164
|
+
* an admitted caller may send, never which origins are admitted, and it grants nothing to an
|
|
165
|
+
* origin that would have been rejected anyway. Authentication is unchanged — the preflight
|
|
166
|
+
* itself carries no credential and produces no auth or account-pool side effect.
|
|
167
|
+
*/
|
|
168
|
+
function allowedRequestHeaders(req?: Request): string {
|
|
169
|
+
const requested = req?.headers.get("Access-Control-Request-Headers")?.trim();
|
|
170
|
+
if (!requested) return STATIC_ALLOWED_REQUEST_HEADERS;
|
|
171
|
+
const seen = new Set(STATIC_ALLOWED_REQUEST_HEADERS.split(",").map(h => h.trim().toLowerCase()));
|
|
172
|
+
const extra: string[] = [];
|
|
173
|
+
for (const raw of requested.split(",")) {
|
|
174
|
+
const name = raw.trim();
|
|
175
|
+
// Header names are case-insensitive on the wire, so normalize before de-duplicating;
|
|
176
|
+
// echo the caller's spelling for the ones we add.
|
|
177
|
+
if (!name || seen.has(name.toLowerCase())) continue;
|
|
178
|
+
seen.add(name.toLowerCase());
|
|
179
|
+
extra.push(name);
|
|
180
|
+
}
|
|
181
|
+
return extra.length === 0 ? STATIC_ALLOWED_REQUEST_HEADERS : `${STATIC_ALLOWED_REQUEST_HEADERS}, ${extra.join(", ")}`;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export function corsHeaders(req?: Request, config?: RequestPolicyView): Record<string, string> {
|
|
123
185
|
const origin = req?.headers.get("Origin");
|
|
124
|
-
const
|
|
186
|
+
const originAllowed = Boolean(origin && req && config && isAllowedRequestOrigin(req, config));
|
|
187
|
+
const allowOrigin = originAllowed && origin ? origin : _corsOrigin;
|
|
125
188
|
return {
|
|
126
189
|
"Access-Control-Allow-Origin": allowOrigin,
|
|
127
190
|
"Access-Control-Allow-Methods": "GET, POST, PUT, PATCH, DELETE, OPTIONS",
|
|
128
|
-
|
|
129
|
-
//
|
|
130
|
-
//
|
|
131
|
-
"
|
|
132
|
-
"Vary": "Origin",
|
|
191
|
+
"Access-Control-Allow-Headers": allowedRequestHeaders(originAllowed ? req : undefined),
|
|
192
|
+
// A response that varies by the request's headers must say so, or a shared cache can
|
|
193
|
+
// replay one client's allow-list to a client that asked for different headers.
|
|
194
|
+
"Vary": "Origin, Access-Control-Request-Headers",
|
|
133
195
|
...browserSecurityHeaders(),
|
|
134
196
|
};
|
|
135
197
|
}
|
|
@@ -143,7 +205,7 @@ export function managementCorsHeaders(req?: Request, config?: OcxConfig): Record
|
|
|
143
205
|
return headers;
|
|
144
206
|
}
|
|
145
207
|
|
|
146
|
-
export function withCors(response: Response, req: Request, config:
|
|
208
|
+
export function withCors(response: Response, req: Request, config: RequestPolicyView): Response {
|
|
147
209
|
const headers = new Headers(response.headers);
|
|
148
210
|
for (const [name, value] of Object.entries(corsHeaders(req, config))) {
|
|
149
211
|
headers.set(name, value);
|
|
@@ -167,14 +229,18 @@ export function withManagementCors(response: Response, req: Request, config: Ocx
|
|
|
167
229
|
});
|
|
168
230
|
}
|
|
169
231
|
|
|
170
|
-
export function jsonResponse(data: unknown, status = 200, req?: Request, config?:
|
|
232
|
+
export function jsonResponse(data: unknown, status = 200, req?: Request, config?: RequestPolicyView): Response {
|
|
171
233
|
return new Response(JSON.stringify(data), {
|
|
172
234
|
status,
|
|
173
235
|
headers: { "Content-Type": "application/json", ...corsHeaders(req, config) },
|
|
174
236
|
});
|
|
175
237
|
}
|
|
176
238
|
|
|
177
|
-
|
|
239
|
+
// The parameter is vestigial — the token has always come from the environment — but callers
|
|
240
|
+
// pass a config, so keep accepting one. Typed as `unknown` rather than `OcxConfig` so a narrow
|
|
241
|
+
// policy view can reach it too (#1102); widening to OcxConfig here would force every caller in
|
|
242
|
+
// the admission path back to the full config.
|
|
243
|
+
export function configuredApiAuthToken(_config?: unknown): string | undefined {
|
|
178
244
|
const token = process.env.OPENCODEX_API_AUTH_TOKEN?.trim();
|
|
179
245
|
return token || undefined;
|
|
180
246
|
}
|
|
@@ -191,10 +257,37 @@ export function isLoopbackHostname(hostname: string | undefined): boolean {
|
|
|
191
257
|
return normalized === "" || normalized === "localhost" || normalized === "127.0.0.1" || normalized === "::1" || normalized === "[::1]";
|
|
192
258
|
}
|
|
193
259
|
|
|
194
|
-
export function isApiAuthRequired(config: OcxConfig): boolean {
|
|
260
|
+
export function isApiAuthRequired(config: Pick<OcxConfig, "hostname">): boolean {
|
|
195
261
|
return !isLoopbackHostname(config.hostname);
|
|
196
262
|
}
|
|
197
263
|
|
|
264
|
+
/**
|
|
265
|
+
* The slice of config that decides admission and CORS, and nothing else (#1102).
|
|
266
|
+
*
|
|
267
|
+
* The unauthenticated loopback listener shares this process with the public one: same routing,
|
|
268
|
+
* same account pool, same drain. The only thing it must see differently is its own bind
|
|
269
|
+
* address, because `isApiAuthRequired` reads `hostname` and the shared config says "0.0.0.0".
|
|
270
|
+
*
|
|
271
|
+
* Two ways to express that were rejected. Passing the whole config with `hostname` rewritten
|
|
272
|
+
* and holding it for the listener's lifetime would go stale the moment the management API
|
|
273
|
+
* changes a setting. Adding an `allowUnauthenticated` parameter to the resolvers would create a
|
|
274
|
+
* callable admission bypass that the PUBLIC listener could also reach — the switch would exist
|
|
275
|
+
* on the wrong side of the boundary.
|
|
276
|
+
*
|
|
277
|
+
* So this type is deliberately narrow: it cannot masquerade as a business config, and a policy
|
|
278
|
+
* view that leaks into a routing path fails to typecheck rather than silently taking effect.
|
|
279
|
+
*/
|
|
280
|
+
export type RequestPolicyView = Pick<OcxConfig, "hostname" | "corsAllowOrigins" | "apiKeys">;
|
|
281
|
+
|
|
282
|
+
/** Derive the per-request policy view for a listener. Cheap enough to build per request. */
|
|
283
|
+
export function requestPolicyView(config: OcxConfig, bindHostname: string): RequestPolicyView {
|
|
284
|
+
return {
|
|
285
|
+
hostname: bindHostname,
|
|
286
|
+
...(config.corsAllowOrigins ? { corsAllowOrigins: config.corsAllowOrigins } : {}),
|
|
287
|
+
...(config.apiKeys ? { apiKeys: config.apiKeys } : {}),
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
|
|
198
291
|
export function assertServerAuthConfig(config: OcxConfig): void {
|
|
199
292
|
const hasConfiguredDataCredential = !!configuredApiAuthToken(config)
|
|
200
293
|
|| (config.apiKeys ?? []).some(entry => !!entry.key.trim());
|
|
@@ -222,10 +315,20 @@ function secretEquals(actual: string, expected: string | undefined): boolean {
|
|
|
222
315
|
* point at, and a sentinel string in the id would collide with a hand-edited
|
|
223
316
|
* entry that happens to be named `loopback`.
|
|
224
317
|
*/
|
|
318
|
+
/**
|
|
319
|
+
* HOW an admission credential was presented.
|
|
320
|
+
*
|
|
321
|
+
* The credential IDENTITY (which key matched) and its PRESENTATION (which header carried it)
|
|
322
|
+
* are different facts, and #1686 needs both: a proxy secret arriving as a bearer on the
|
|
323
|
+
* Responses transport is admissible, but only if the upstream credential is then guaranteed to
|
|
324
|
+
* be substituted. Collapsing the two is what made that flow unexpressible.
|
|
325
|
+
*/
|
|
326
|
+
export type DataPlaneAdmissionSource = "loopback" | "dedicated" | "bearer" | "x-api-key";
|
|
327
|
+
|
|
225
328
|
export type DataPlaneAdmission =
|
|
226
|
-
| { kind: "configured"; keyId: string }
|
|
227
|
-
| { kind: "environment" }
|
|
228
|
-
| { kind: "loopback" };
|
|
329
|
+
| { kind: "configured"; keyId: string; source: DataPlaneAdmissionSource }
|
|
330
|
+
| { kind: "environment"; source: DataPlaneAdmissionSource }
|
|
331
|
+
| { kind: "loopback"; source: "loopback" };
|
|
229
332
|
|
|
230
333
|
/**
|
|
231
334
|
* Which admission secret `token` is, or null when it is none of them.
|
|
@@ -236,12 +339,16 @@ export type DataPlaneAdmission =
|
|
|
236
339
|
* discarded, which is what makes per-key attribution possible without touching
|
|
237
340
|
* the admission decision itself.
|
|
238
341
|
*/
|
|
239
|
-
export function resolveDataPlaneAdmissionSecret(
|
|
342
|
+
export function resolveDataPlaneAdmissionSecret(
|
|
343
|
+
token: string,
|
|
344
|
+
config: Pick<OcxConfig, "apiKeys">,
|
|
345
|
+
source: DataPlaneAdmissionSource = "dedicated",
|
|
346
|
+
): DataPlaneAdmission | null {
|
|
240
347
|
const actual = token.trim();
|
|
241
348
|
if (!actual) return null;
|
|
242
|
-
if (secretEquals(actual, configuredApiAuthToken(config))) return { kind: "environment" };
|
|
349
|
+
if (secretEquals(actual, configuredApiAuthToken(config))) return { kind: "environment", source };
|
|
243
350
|
for (const k of config.apiKeys ?? []) {
|
|
244
|
-
if (secretEquals(actual, k.key)) return { kind: "configured", keyId: k.id };
|
|
351
|
+
if (secretEquals(actual, k.key)) return { kind: "configured", keyId: k.id, source };
|
|
245
352
|
}
|
|
246
353
|
return null;
|
|
247
354
|
}
|
|
@@ -288,8 +395,12 @@ export interface ApiAuthMatrixRow {
|
|
|
288
395
|
* against every cell rather than reading the table back to itself.
|
|
289
396
|
*/
|
|
290
397
|
export const AUTH_MATRIX: readonly ApiAuthMatrixRow[] = [
|
|
291
|
-
|
|
292
|
-
|
|
398
|
+
// #1686: a bearer that is one of OUR admission secrets is now accepted here. It is safe
|
|
399
|
+
// because materializeCodexUpstreamAuth substitutes the stored main credential rather than
|
|
400
|
+
// forwarding it; a bearer that is NOT our secret stays unadmitted and remains Codex Direct
|
|
401
|
+
// passthrough, so the two bearer domains still never mix. `x-api-key` is still rejected.
|
|
402
|
+
{ endpoint: "/v1/responses", bearer: "accepted", dedicated: "accepted", xApiKey: "rejected" },
|
|
403
|
+
{ endpoint: "/v1/chat/completions", bearer: "accepted", dedicated: "accepted", xApiKey: "rejected" },
|
|
293
404
|
{ endpoint: "/v1/messages", bearer: "accepted", dedicated: "accepted", xApiKey: "accepted" },
|
|
294
405
|
{ endpoint: "/v1/models", bearer: "accepted", dedicated: "accepted", xApiKey: "accepted" },
|
|
295
406
|
];
|
|
@@ -324,22 +435,24 @@ export function validateForwardAdmissionCredential(headers: Headers, config: Ocx
|
|
|
324
435
|
* Resolving form of `hasValidApiAuth`: identical header precedence, identical
|
|
325
436
|
* decision, but it names the admission instead of collapsing it to a boolean.
|
|
326
437
|
*/
|
|
327
|
-
export function resolveApiAuth(req: Request, config:
|
|
438
|
+
export function resolveApiAuth(req: Request, config: RequestPolicyView): DataPlaneAdmission | null {
|
|
328
439
|
// A loopback bind never reads a token at all, so there is no key to name.
|
|
329
|
-
if (!isApiAuthRequired(config)) return { kind: "loopback" };
|
|
330
|
-
const
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
440
|
+
if (!isApiAuthRequired(config)) return { kind: "loopback", source: "loopback" };
|
|
441
|
+
const dedicated = req.headers.get("x-opencodex-api-key")?.trim();
|
|
442
|
+
if (dedicated) return resolveDataPlaneAdmissionSecret(dedicated, config, "dedicated");
|
|
443
|
+
const bearer = req.headers.get("authorization")?.replace(/^Bearer\s+/i, "").trim();
|
|
444
|
+
if (bearer) return resolveDataPlaneAdmissionSecret(bearer, config, "bearer");
|
|
445
|
+
// Anthropic-SDK clients (Claude Code with ANTHROPIC_API_KEY) authenticate via x-api-key.
|
|
446
|
+
const apiKey = req.headers.get("x-api-key")?.trim();
|
|
447
|
+
if (apiKey) return resolveDataPlaneAdmissionSecret(apiKey, config, "x-api-key");
|
|
448
|
+
return null;
|
|
336
449
|
}
|
|
337
450
|
|
|
338
|
-
export function hasValidApiAuth(req: Request, config:
|
|
451
|
+
export function hasValidApiAuth(req: Request, config: RequestPolicyView): boolean {
|
|
339
452
|
return resolveApiAuth(req, config) !== null;
|
|
340
453
|
}
|
|
341
454
|
|
|
342
|
-
export function requireApiAuth(req: Request, config:
|
|
455
|
+
export function requireApiAuth(req: Request, config: RequestPolicyView, _kind: "data-plane"): Response | null {
|
|
343
456
|
if (hasValidApiAuth(req, config)) return null;
|
|
344
457
|
return formatErrorResponse(401, "authentication_error", "opencodex API key required");
|
|
345
458
|
}
|
|
@@ -349,16 +462,25 @@ export function requireApiAuth(req: Request, config: OcxConfig, _kind: "data-pla
|
|
|
349
462
|
* Codex Direct. Remote binds must use the dedicated proxy header so the two bearer
|
|
350
463
|
* domains can never be confused.
|
|
351
464
|
*/
|
|
352
|
-
export function resolveResponsesApiAuth(req: Request, config:
|
|
353
|
-
if (!isApiAuthRequired(config)) return { kind: "loopback" };
|
|
354
|
-
//
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
465
|
+
export function resolveResponsesApiAuth(req: Request, config: RequestPolicyView): DataPlaneAdmission | null {
|
|
466
|
+
if (!isApiAuthRequired(config)) return { kind: "loopback", source: "loopback" };
|
|
467
|
+
// The dedicated header still WINS, because it is unambiguous.
|
|
468
|
+
const dedicated = req.headers.get("x-opencodex-api-key")?.trim();
|
|
469
|
+
if (dedicated) return resolveDataPlaneAdmissionSecret(dedicated, config, "dedicated");
|
|
470
|
+
// #1686: a bearer may also be one of OUR admission secrets. Rejecting it outright meant a
|
|
471
|
+
// Codex client configured with `env_key` could not reach Direct at all. Admitting it is only
|
|
472
|
+
// safe because the upstream credential is then SUBSTITUTED rather than forwarded -- see
|
|
473
|
+
// materializeCodexUpstreamAuth. A bearer that is NOT our secret stays unadmitted here and
|
|
474
|
+
// remains Codex Direct passthrough, so the two bearer domains still never mix.
|
|
475
|
+
const bearer = req.headers.get("authorization")?.replace(/^Bearer\s+/i, "").trim();
|
|
476
|
+
if (bearer) return resolveDataPlaneAdmissionSecret(bearer, config, "bearer");
|
|
477
|
+
// `x-api-key` is deliberately NOT accepted on this transport.
|
|
478
|
+
return null;
|
|
359
479
|
}
|
|
360
480
|
|
|
361
|
-
|
|
481
|
+
|
|
482
|
+
|
|
483
|
+
export function requireResponsesApiAuth(req: Request, config: RequestPolicyView): Response | null {
|
|
362
484
|
if (resolveResponsesApiAuth(req, config)) return null;
|
|
363
485
|
return formatErrorResponse(401, "authentication_error", "opencodex API key required");
|
|
364
486
|
}
|
|
@@ -375,6 +497,42 @@ function sameCanonicalProviderSeed(actual: Record<string, unknown>, expected: Oc
|
|
|
375
497
|
return actualKeys.every(key => JSON.stringify(actual[key]) === JSON.stringify((expected as unknown as Record<string, unknown>)[key]));
|
|
376
498
|
}
|
|
377
499
|
|
|
500
|
+
function positiveWindowValue(value: unknown): boolean {
|
|
501
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value > 0;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
/**
|
|
505
|
+
* Shape-check the two context overlays before the canonical seed comparison drops them.
|
|
506
|
+
*
|
|
507
|
+
* `null` means "clear this" and is normalized by the PATCH field mask, but this validator
|
|
508
|
+
* also runs for POST and reload, where a whole provider object lands on disk verbatim. A
|
|
509
|
+
* `null` surviving there would be a value no reader expects, so full objects must carry a
|
|
510
|
+
* real number or omit the field.
|
|
511
|
+
*/
|
|
512
|
+
function nativeContextOverlayError(raw: Record<string, unknown>): string | null {
|
|
513
|
+
if (Object.hasOwn(raw, "contextWindow") && !positiveWindowValue(raw.contextWindow)) {
|
|
514
|
+
return "provider openai contextWindow must be a positive safe integer";
|
|
515
|
+
}
|
|
516
|
+
if (Object.hasOwn(raw, "modelContextWindows")) {
|
|
517
|
+
const windows = raw.modelContextWindows;
|
|
518
|
+
if (typeof windows !== "object" || windows === null || Array.isArray(windows)) {
|
|
519
|
+
return "provider openai modelContextWindows must be a plain object";
|
|
520
|
+
}
|
|
521
|
+
for (const [model, value] of Object.entries(windows as Record<string, unknown>)) {
|
|
522
|
+
if (model.trim() === "") return "provider openai modelContextWindows keys must be nonblank model ids";
|
|
523
|
+
if (!positiveWindowValue(value)) {
|
|
524
|
+
return "provider openai modelContextWindows values must be positive safe integers";
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
return null;
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
/**
|
|
532
|
+
* Validate a provider object arriving at the management write boundary. Returns an error
|
|
533
|
+
* string, or null when the provider may be persisted. Caller-controlled names/fields are
|
|
534
|
+
* redacted and JSON-escaped so secrets never reach the response.
|
|
535
|
+
*/
|
|
378
536
|
export function providerManagementConfigError(name: unknown, provider: unknown): string | null {
|
|
379
537
|
if (typeof name !== "string" || !provider || typeof provider !== "object" || Array.isArray(provider)) {
|
|
380
538
|
return "provider must be a plain object";
|
|
@@ -392,7 +550,25 @@ export function providerManagementConfigError(name: unknown, provider: unknown):
|
|
|
392
550
|
return "provider openai codexAccountMode must be pool or direct";
|
|
393
551
|
}
|
|
394
552
|
if (seed) seed.codexAccountMode = raw.codexAccountMode;
|
|
395
|
-
const
|
|
553
|
+
const canonicalCandidate = { ...raw };
|
|
554
|
+
delete canonicalCandidate.responsesSnapshotRepair;
|
|
555
|
+
// modelCosts is a user-owned display overlay, not part of the canonical
|
|
556
|
+
// forward seed; it is validated separately below (providerModelCostsConfigError).
|
|
557
|
+
delete canonicalCandidate.modelCosts;
|
|
558
|
+
// requestPacing is a user-owned transport overlay, not part of the canonical seed.
|
|
559
|
+
delete canonicalCandidate.requestPacing;
|
|
560
|
+
// Context windows are the same kind of user-owned overlay as requestPacing: the operator
|
|
561
|
+
// narrowing what their own native rows advertise. They can only ever LOWER the measured
|
|
562
|
+
// window (see nativeOpenAiContextWindow), so admitting them cannot widen what the proxy
|
|
563
|
+
// claims. Validated first — this function also guards POST/reload, where nothing
|
|
564
|
+
// normalizes the shape afterwards, so a bad value would reach disk.
|
|
565
|
+
const contextOverlayError = nativeContextOverlayError(raw);
|
|
566
|
+
if (contextOverlayError) return contextOverlayError;
|
|
567
|
+
delete canonicalCandidate.contextWindow;
|
|
568
|
+
delete canonicalCandidate.modelContextWindows;
|
|
569
|
+
// User-owned soft compaction policy; it does not alter the canonical transport seed.
|
|
570
|
+
delete canonicalCandidate.modelAutoCompactTokenLimits;
|
|
571
|
+
const canonical = seed && sameCanonicalProviderSeed(canonicalCandidate, seed);
|
|
396
572
|
if (!canonical) {
|
|
397
573
|
return `provider ${name} must equal the canonical built-in provider seed`;
|
|
398
574
|
}
|
|
@@ -402,14 +578,45 @@ export function providerManagementConfigError(name: unknown, provider: unknown):
|
|
|
402
578
|
const typed = provider as unknown as OcxProviderConfig;
|
|
403
579
|
const baseUrlError = providerBaseUrlConfigError(typed.baseUrl);
|
|
404
580
|
if (baseUrlError) return `provider ${name} ${baseUrlError}`;
|
|
581
|
+
if (effectiveGoogleMode(name, typed) === "vertex" && typed.location !== undefined) {
|
|
582
|
+
const locationError = googleVertexLocationConfigError(typed.location);
|
|
583
|
+
if (locationError) return `provider ${name} ${locationError}`;
|
|
584
|
+
}
|
|
405
585
|
const destinationError = providerDestinationConfigError(name, typed);
|
|
406
586
|
if (destinationError) return `provider ${name} ${destinationError}`;
|
|
407
587
|
const headersError = providerHeadersConfigError(typed.headers);
|
|
408
588
|
if (headersError) return `provider ${name} ${headersError}`;
|
|
589
|
+
const retryOn429Error = retryOn429PolicyConfigError(raw.retryOn429);
|
|
590
|
+
if (retryOn429Error) {
|
|
591
|
+
// The provider name is caller-controlled and can be token-shaped; redact and JSON-escape
|
|
592
|
+
// it before it reaches the management API response.
|
|
593
|
+
return `provider ${JSON.stringify(redactSecretString(name))} ${retryOn429Error}`;
|
|
594
|
+
}
|
|
595
|
+
const requestPacingError = requestPacingConfigError(raw.requestPacing);
|
|
596
|
+
if (requestPacingError) {
|
|
597
|
+
return `provider ${JSON.stringify(redactSecretString(name))} ${requestPacingError}`;
|
|
598
|
+
}
|
|
599
|
+
const upstreamHttpVersionError = upstreamHttpVersionConfigError(raw.upstreamHttpVersion);
|
|
600
|
+
if (upstreamHttpVersionError) {
|
|
601
|
+
return `provider ${JSON.stringify(redactSecretString(name))} ${upstreamHttpVersionError}`;
|
|
602
|
+
}
|
|
603
|
+
const modelCostsError = providerModelCostsConfigError(raw.modelCosts);
|
|
604
|
+
if (modelCostsError) {
|
|
605
|
+
// The provider name is caller-controlled and can be token-shaped; redact and JSON-escape
|
|
606
|
+
// it before it reaches the management API response (same rule as retryOn429 above).
|
|
607
|
+
return `provider ${JSON.stringify(redactSecretString(name))} ${modelCostsError}`;
|
|
608
|
+
}
|
|
409
609
|
const apiKeyTransportError = apiKeyTransportConfigError(typed);
|
|
410
610
|
if (apiKeyTransportError) return `provider ${name} ${apiKeyTransportError}`;
|
|
411
611
|
const maxInputError = positiveIntegerRecordConfigError(raw.modelMaxInputTokens, "modelMaxInputTokens");
|
|
412
612
|
if (maxInputError) return `provider ${name} ${maxInputError}`;
|
|
613
|
+
const autoCompactError = modelAutoCompactTokenLimitsConfigError(
|
|
614
|
+
raw.modelAutoCompactTokenLimits,
|
|
615
|
+
{ requireNativeIds: name === "openai" },
|
|
616
|
+
);
|
|
617
|
+
if (autoCompactError) {
|
|
618
|
+
return `provider ${JSON.stringify(redactSecretString(name))} ${autoCompactError}`;
|
|
619
|
+
}
|
|
413
620
|
const reasoningSummariesError = booleanRecordConfigError(raw.modelSupportsReasoningSummaries, "modelSupportsReasoningSummaries");
|
|
414
621
|
if (reasoningSummariesError) return `provider ${name} ${reasoningSummariesError}`;
|
|
415
622
|
const reasoningSummaryDeliveryError = reasoningSummaryDeliveryRecordConfigError(
|
|
@@ -419,10 +626,25 @@ export function providerManagementConfigError(name: unknown, provider: unknown):
|
|
|
419
626
|
if (reasoningSummaryDeliveryError) return `provider ${name} ${reasoningSummaryDeliveryError}`;
|
|
420
627
|
const modelAdaptersError = modelAdapterRecordConfigError(raw.modelAdapters, "modelAdapters", name, typed);
|
|
421
628
|
if (modelAdaptersError) return `provider ${name} ${modelAdaptersError}`;
|
|
629
|
+
const preferHostedToolsError = modelPreferHostedToolsConfigError(
|
|
630
|
+
raw.modelPreferHostedTools,
|
|
631
|
+
"modelPreferHostedTools",
|
|
632
|
+
name,
|
|
633
|
+
typed,
|
|
634
|
+
);
|
|
635
|
+
if (preferHostedToolsError) return `provider ${name} ${preferHostedToolsError}`;
|
|
636
|
+
if (raw.responsesSnapshotRepair !== undefined && typeof raw.responsesSnapshotRepair !== "boolean") {
|
|
637
|
+
return `provider ${name} responsesSnapshotRepair must be a boolean`;
|
|
638
|
+
}
|
|
422
639
|
const defaultMaxOutputError = positiveIntegerConfigError(raw.defaultMaxOutputTokens, "defaultMaxOutputTokens");
|
|
423
640
|
if (defaultMaxOutputError) return `provider ${name} ${defaultMaxOutputError}`;
|
|
424
641
|
const maxOutputError = positiveIntegerRecordConfigError(raw.modelMaxOutputTokens, "modelMaxOutputTokens");
|
|
425
642
|
if (maxOutputError) return `provider ${name} ${maxOutputError}`;
|
|
643
|
+
const structuredOutputOptOutError = nonBlankStringArrayConfigError(
|
|
644
|
+
raw.noStructuredOutputModels,
|
|
645
|
+
"noStructuredOutputModels",
|
|
646
|
+
);
|
|
647
|
+
if (structuredOutputOptOutError) return `provider ${name} ${structuredOutputOptOutError}`;
|
|
426
648
|
const openRouterError = openRouterRoutingConfigError(typed);
|
|
427
649
|
if (openRouterError) return `provider ${name} ${openRouterError}`;
|
|
428
650
|
if (typed.authMode === "local") {
|
|
@@ -469,6 +691,7 @@ export function copyIfDefined<K extends keyof OcxProviderConfig>(
|
|
|
469
691
|
if (value !== undefined) out[key as string] = value as unknown;
|
|
470
692
|
}
|
|
471
693
|
|
|
694
|
+
/** Public dashboard DTO for config.json: provider entries with secrets stripped and documented fields exposed (including `modelCosts`). */
|
|
472
695
|
export function safeConfigDTO(config: OcxConfig): unknown {
|
|
473
696
|
const providers: Record<string, Record<string, unknown>> = {};
|
|
474
697
|
for (const [name, provider] of Object.entries(config.providers)) {
|
|
@@ -478,8 +701,14 @@ export function safeConfigDTO(config: OcxConfig): unknown {
|
|
|
478
701
|
hasApiKey: !!provider.apiKey,
|
|
479
702
|
hasHeaders: !!provider.headers && Object.keys(provider.headers).length > 0,
|
|
480
703
|
};
|
|
704
|
+
if (name === "xai") {
|
|
705
|
+
dto.xaiResponsesOptInState = xaiResponsesOptInState(provider);
|
|
706
|
+
}
|
|
481
707
|
for (const key of [
|
|
482
708
|
"defaultModel",
|
|
709
|
+
"alias",
|
|
710
|
+
"modelAliases",
|
|
711
|
+
"defaultAliases",
|
|
483
712
|
"disabled",
|
|
484
713
|
"allowPrivateNetwork",
|
|
485
714
|
"authMode",
|
|
@@ -487,26 +716,34 @@ export function safeConfigDTO(config: OcxConfig): unknown {
|
|
|
487
716
|
"keyOptional",
|
|
488
717
|
"freeTier",
|
|
489
718
|
"liveModels",
|
|
719
|
+
"requestPacing",
|
|
490
720
|
"models",
|
|
491
721
|
"contextWindow",
|
|
492
722
|
"modelContextWindows",
|
|
723
|
+
"modelAutoCompactTokenLimits",
|
|
493
724
|
"defaultMaxOutputTokens",
|
|
494
725
|
"modelMaxOutputTokens",
|
|
495
726
|
"openRouterRouting",
|
|
496
727
|
"modelOpenRouterRouting",
|
|
497
728
|
"reasoningEfforts",
|
|
498
729
|
"modelReasoningEfforts",
|
|
730
|
+
"reasoningWireFormat",
|
|
499
731
|
"noVisionModels",
|
|
500
732
|
"noReasoningModels",
|
|
501
733
|
"noTemperatureModels",
|
|
502
734
|
"noTopPModels",
|
|
503
735
|
"noPenaltyModels",
|
|
736
|
+
"noStructuredOutputModels",
|
|
737
|
+
"upstreamHttpVersion",
|
|
504
738
|
"autoToolChoiceOnlyModels",
|
|
505
739
|
"preserveReasoningContentModels",
|
|
740
|
+
"requiresReasoningPlaceholderModels",
|
|
506
741
|
"escapeBuiltinToolNames",
|
|
507
742
|
] as const) {
|
|
508
743
|
copyIfDefined(dto, provider, key);
|
|
509
744
|
}
|
|
745
|
+
const modelCosts = sanitizeModelCostsForDisplay(provider.modelCosts);
|
|
746
|
+
if (modelCosts) dto.modelCosts = modelCosts;
|
|
510
747
|
// Resolve the note by DESTINATION, not by name. A preset saved under a custom name is
|
|
511
748
|
// still pointed at the same vendor route, and a usage restriction the user needs to see
|
|
512
749
|
// must not disappear because the row was renamed. Prefer the same-name entry so an
|
|
@@ -523,8 +760,12 @@ export function safeConfigDTO(config: OcxConfig): unknown {
|
|
|
523
760
|
port: config.port,
|
|
524
761
|
hostname: config.hostname ?? "127.0.0.1",
|
|
525
762
|
defaultProvider: config.defaultProvider,
|
|
763
|
+
defaultModelAliases: config.defaultModelAliases,
|
|
526
764
|
codexAutoStart: codexAutoStartEnabled(config),
|
|
527
765
|
websockets: config.websockets,
|
|
766
|
+
// The GUI's browser-open toggle reads and writes this; absent means the
|
|
767
|
+
// historical auto-open behavior.
|
|
768
|
+
oauthOpenBrowser: config.oauthOpenBrowser !== false,
|
|
528
769
|
providers,
|
|
529
770
|
};
|
|
530
771
|
}
|