@iislee/opencodex 2.11.0 → 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/README.md +114 -19
- package/bin/ocx.mjs +164 -36
- package/bin/package-main.mjs +1 -1
- package/gui/dist/assets/index-BF38heuV.js +104 -0
- package/gui/dist/assets/index-DMiI18Kv.css +1 -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 +19 -10
- package/src/adapters/anthropic-output-schema.ts +137 -0
- package/src/adapters/anthropic.ts +376 -52
- package/src/adapters/base.ts +54 -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 +601 -0
- package/src/adapters/cursor/checkpoint-store.ts +303 -0
- package/src/adapters/cursor/cursor-errors.ts +129 -6
- package/src/adapters/cursor/discovery.ts +61 -4
- package/src/adapters/cursor/effort-map.ts +27 -3
- 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 +174 -58
- package/src/adapters/cursor/live-transport.ts +609 -170
- package/src/adapters/cursor/native-exec-common.ts +23 -2
- 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 +0 -1
- package/src/adapters/cursor/native-exec.ts +101 -14
- package/src/adapters/cursor/protobuf-events.ts +829 -11
- package/src/adapters/cursor/protobuf-request.ts +383 -65
- package/src/adapters/cursor/request-builder.ts +237 -31
- package/src/adapters/cursor/tool-definitions.ts +242 -12
- package/src/adapters/cursor/tool-result-normalize.ts +92 -0
- package/src/adapters/cursor/transport.ts +22 -0
- package/src/adapters/cursor/types.ts +28 -1
- package/src/adapters/cursor.ts +133 -7
- package/src/adapters/google-antigravity-replay.ts +558 -34
- package/src/adapters/google-antigravity-wire.ts +43 -10
- 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 +618 -74
- package/src/adapters/identity.ts +39 -6
- 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 +10 -1
- package/src/adapters/kiro.ts +92 -54
- package/src/adapters/mimo-free.ts +17 -0
- package/src/adapters/openai-chat-url.ts +11 -0
- package/src/adapters/openai-chat.ts +1311 -336
- package/src/adapters/openai-responses-url.ts +16 -0
- package/src/adapters/openai-responses.ts +830 -56
- package/src/adapters/registry.ts +175 -0
- package/src/adapters/responses-tool-schema.ts +67 -0
- package/src/adapters/tool-call-id.ts +119 -0
- package/src/adapters/tool-catalog-nudge.ts +104 -21
- package/src/adapters/xai-web-search.ts +185 -0
- package/src/bridge.ts +428 -61
- package/src/chat/inbound.ts +43 -19
- package/src/chat/outbound.ts +82 -26
- package/src/claude/agents-inject.ts +32 -9
- package/src/claude/context-windows.ts +21 -5
- 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 +70 -16
- package/src/cli/account-api.ts +35 -1
- package/src/cli/account-auth.ts +33 -6
- package/src/cli/account-catalog-refresh.ts +14 -0
- package/src/cli/account-extended.ts +389 -2
- package/src/cli/account-main.ts +317 -0
- package/src/cli/account.ts +30 -6
- package/src/cli/agent.ts +45 -1
- package/src/cli/claude-agent-startup-sync.ts +73 -0
- package/src/cli/claude-desktop.ts +29 -4
- package/src/cli/claude.ts +103 -34
- 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 +593 -0
- package/src/cli/doctor.ts +315 -43
- package/src/cli/ensure-desired-integrations.ts +152 -0
- package/src/cli/export-command.ts +46 -20
- package/src/cli/help.ts +20 -271
- package/src/cli/index.ts +378 -519
- package/src/cli/init.ts +4 -17
- 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 +35 -2
- package/src/cli/models.ts +100 -14
- package/src/cli/observe.ts +92 -3
- package/src/cli/opencode.ts +4 -2
- package/src/cli/provider-runtime.ts +18 -1
- package/src/cli/provider.ts +24 -3
- package/src/cli/ready.ts +301 -0
- package/src/cli/registry.ts +437 -0
- package/src/cli/root.ts +86 -0
- package/src/cli/route-policy.ts +92 -0
- package/src/cli/runtime-api.ts +6 -3
- package/src/cli/star-prompt.ts +71 -15
- package/src/cli/status.ts +10 -3
- package/src/cli/system-restart-client.ts +146 -0
- package/src/cli/tray-proxy.ts +153 -6
- package/src/cli/v2.ts +105 -10
- package/src/cli.ts +1 -1
- package/src/clients/config-export.ts +1358 -21
- package/src/codex/account-label.ts +14 -1
- package/src/codex/account-lifecycle.ts +130 -13
- package/src/codex/account-namespaces.ts +49 -3
- package/src/codex/account-priority.ts +83 -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 +493 -106
- package/src/codex/app-server-restart-service.ts +232 -0
- package/src/codex/auth-api.ts +849 -242
- package/src/codex/auth-collision.ts +5 -3
- package/src/codex/auth-context.ts +345 -32
- package/src/codex/autostart-health.ts +8 -1
- package/src/codex/catalog/account-models.ts +67 -0
- package/src/codex/catalog/aggregation.ts +68 -10
- package/src/codex/catalog/bundled.ts +331 -33
- package/src/codex/catalog/effort.ts +121 -30
- package/src/codex/catalog/filesystem-evidence.ts +302 -0
- package/src/codex/catalog/kinds.ts +2 -0
- package/src/codex/catalog/metadata.ts +529 -45
- package/src/codex/catalog/native-models.ts +72 -0
- package/src/codex/catalog/parsing.ts +224 -30
- package/src/codex/catalog/provider-fetch.ts +1460 -134
- package/src/codex/catalog/sync.ts +1449 -186
- 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 +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/desired-state.ts +230 -0
- package/src/codex/features.ts +636 -39
- 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 +26 -20
- package/src/codex/history-provider.ts +231 -28
- package/src/codex/history-transition.ts +105 -0
- package/src/codex/history-worker.ts +220 -0
- package/src/codex/inject-coordination.ts +290 -0
- package/src/codex/inject.ts +1073 -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 +80 -0
- package/src/codex/journal.ts +66 -4
- 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 +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 +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 +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 +967 -0
- package/src/codex/prompt-lock.ts +143 -0
- package/src/codex/quota-rejection.ts +298 -0
- package/src/codex/quota.ts +175 -13
- package/src/codex/refresh.ts +11 -2
- package/src/codex/reset-credit-recovery.ts +1044 -0
- package/src/codex/routing.ts +505 -94
- package/src/codex/runtime.ts +159 -38
- package/src/codex/shim.ts +1009 -28
- package/src/codex/subagent-model-fallback.ts +350 -35
- package/src/codex/sync.ts +191 -2
- 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 +187 -81
- package/src/codex/write-coordination.ts +114 -0
- package/src/combos/failover.ts +20 -0
- package/src/combos/index.ts +4 -0
- package/src/combos/request.ts +32 -0
- package/src/combos/types.ts +81 -9
- package/src/config/provider-name.ts +24 -0
- package/src/config.ts +1762 -140
- package/src/generated/compatibility-version.json +3164 -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 +152 -29
- package/src/images/plan.ts +23 -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.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/app-owned-memory-stores.ts +22 -0
- package/src/lib/bounded-body.ts +153 -9
- 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 +6 -2
- package/src/lib/destination-policy.ts +65 -1
- package/src/lib/errors.ts +44 -2
- 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/pinned-http.ts +145 -26
- package/src/lib/process-control.ts +4 -1
- 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/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 +202 -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 +8 -2
- package/src/oauth/chatgpt.ts +12 -1
- package/src/oauth/command-code.ts +239 -0
- package/src/oauth/cursor.ts +46 -5
- package/src/oauth/google-antigravity.ts +35 -3
- package/src/oauth/health.ts +20 -12
- package/src/oauth/index.ts +398 -66
- package/src/oauth/key-providers.ts +16 -0
- package/src/oauth/kimi.ts +16 -2
- package/src/oauth/kiro.ts +50 -6
- package/src/oauth/local-token-detect.ts +11 -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/store.ts +119 -21
- package/src/oauth/token-guardian.ts +9 -3
- 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/base-url-choices.ts +10 -0
- package/src/providers/codex-capacity.ts +292 -0
- package/src/providers/command-code-efforts.ts +144 -0
- package/src/providers/context-cap.ts +22 -5
- package/src/providers/cursor-pool.ts +72 -0
- package/src/providers/derive.ts +253 -6
- package/src/providers/fastwire.ts +501 -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 +1 -1
- package/src/providers/model-discovery-limits.ts +16 -0
- package/src/providers/model-discovery.ts +115 -22
- package/src/providers/model-rename-migration.ts +255 -0
- package/src/providers/model-rename-startup.ts +28 -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 +29 -0
- package/src/providers/quota.ts +1319 -38
- package/src/providers/registry.ts +1429 -111
- package/src/providers/request-pacing.ts +310 -0
- package/src/providers/service-tier.ts +277 -0
- package/src/providers/slug-codec.ts +42 -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 +11 -4
- package/src/reasoning-effort.ts +49 -1
- package/src/responses/compaction.ts +26 -1
- 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 +220 -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 +426 -0
- package/src/responses/schema.ts +7 -1
- package/src/responses/spill-store.ts +75 -10
- package/src/responses/state.ts +565 -27
- package/src/responses/thought-signature-replay.ts +347 -0
- package/src/responses/tool-search-compat.ts +301 -0
- package/src/responses/truncated-stop-reason.ts +60 -0
- package/src/router.ts +366 -30
- 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 +2 -29
- package/src/server/auth-cors.ts +267 -46
- package/src/server/background-lifecycle.ts +182 -0
- package/src/server/chat-completions.ts +130 -56
- package/src/server/chat-native-sse.ts +331 -0
- package/src/server/chat-native.ts +426 -0
- package/src/server/claude-messages.ts +159 -43
- 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 +865 -181
- package/src/server/lifecycle.ts +284 -13
- 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 +398 -103
- package/src/server/management/api-key-usage.ts +31 -5
- package/src/server/management/body.ts +6 -0
- package/src/server/management/combo-routes.ts +62 -24
- package/src/server/management/config-routes.ts +464 -51
- package/src/server/management/context.ts +80 -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 +101 -32
- package/src/server/management/model-routes.ts +189 -131
- 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 +80 -4
- package/src/server/management/provider-capability-config.ts +48 -0
- package/src/server/management/provider-routes.ts +764 -157
- 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 +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 +288 -32
- 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 +115 -14
- package/src/server/management-auth.ts +220 -5
- package/src/server/passive-route-linker.ts +66 -0
- package/src/server/ports.ts +41 -1
- package/src/server/proxy-liveness.ts +132 -5
- package/src/server/readiness.ts +99 -0
- package/src/server/relay-eager.ts +82 -42
- package/src/server/relay.ts +236 -76
- package/src/server/request-decompress.ts +113 -6
- package/src/server/request-log.ts +235 -22
- 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 +204 -35
- package/src/server/responses/compact.ts +442 -55
- package/src/server/responses/core.ts +2872 -331
- package/src/server/responses/empty-completion-guard.ts +276 -0
- package/src/server/responses/encrypted-payload.ts +62 -39
- package/src/server/responses/fetch-helpers.ts +79 -4
- package/src/server/responses/input-admission.ts +185 -0
- package/src/server/responses/pacing-overload.ts +13 -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 +26 -5
- package/src/server/responses/upstream-error.ts +5 -0
- package/src/server/responses/ws-upstream.ts +308 -0
- package/src/server/responses-custom-tool-repair.ts +282 -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 +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 +18 -2
- package/src/server/search.ts +78 -13
- package/src/server/sse-frame-buffer.ts +292 -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 +39 -38
- package/src/service-manager-probe.ts +892 -0
- package/src/service.ts +1111 -90
- package/src/sidecar/auth.ts +92 -0
- package/src/sidecar/candidates.ts +83 -0
- package/src/storage/cleanup.ts +2 -2
- 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 +845 -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 +104 -1236
- package/src/update/index.ts +32 -19
- package/src/update/job.ts +442 -67
- 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 +259 -0
- package/src/usage/cost.ts +0 -0
- package/src/usage/expected-prices.ts +268 -16
- package/src/usage/log.ts +606 -41
- package/src/usage/summary.ts +177 -9
- 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 +9 -3
- 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 +88 -0
- package/src/web-search/executor.ts +11 -3
- package/src/web-search/gemini-executor.ts +141 -0
- package/src/web-search/index.ts +150 -15
- package/src/web-search/loop.ts +279 -50
- 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
package/src/oauth/index.ts
CHANGED
|
@@ -1,19 +1,23 @@
|
|
|
1
1
|
import type { KiroOAuthMetadata, OAuthController, OAuthCredentials } from "./types";
|
|
2
2
|
import { parseCallbackInput } from "./callback-server";
|
|
3
3
|
import type { OcxConfig, OcxProviderConfig, RefreshPolicy } from "../types";
|
|
4
|
+
import { maskEmail } from "../lib/privacy";
|
|
4
5
|
import { loadConfig, resolveEnvValue, saveConfig } from "../config";
|
|
5
6
|
import { KiroTokenRefreshError, environmentKiroRoutingMetadata, loginKiro, refreshKiroToken, settleKiroLoginTransaction } from "./kiro";
|
|
6
|
-
import { getAccountCredential, getAccountSet, removeAccount, saveAccountCredential, saveCredential, setActiveAccount, getCredential, credentialGeneration, createOAuthRefreshIntentLock, mergeAccountCredential, markAccountNeedsReauthIfGeneration, readOAuthRefreshIntent, writeOAuthRefreshIntent, markOAuthRefreshIntentStaleOwner, clearOAuthRefreshIntent, OAuthMutationBusyError } from "./store";
|
|
7
|
+
import { getAccountCredential, getAccountSet, removeAccount, saveAccountCredential, saveCredential, setActiveAccount, getCredential, credentialGeneration, createOAuthRefreshIntentLock, mergeAccountCredential, markAccountNeedsReauthIfGeneration, readOAuthRefreshIntent, writeOAuthRefreshIntent, markOAuthRefreshIntentStaleOwner, clearOAuthRefreshIntent, normalizeAuthStoreBuffer, OAuthMutationBusyError } from "./store";
|
|
7
8
|
import { loginXai, refreshXaiToken, XAI_LOCAL_CLI_DETACH_WARNING, XaiTokenRequestError } from "./xai";
|
|
8
9
|
import { ANTHROPIC_OAUTH_BETA, AnthropicTokenError, loginAnthropic, refreshAnthropicToken } from "./anthropic";
|
|
9
10
|
import { loginKimi, refreshKimiToken } from "./kimi";
|
|
11
|
+
import { loginNous, NousTokenError, refreshNousToken, clearNousRefreshIntent, RefreshIntentIOError } from "./nous";
|
|
10
12
|
import { loginChatGPT, refreshChatGPTToken } from "./chatgpt";
|
|
11
13
|
import { loginAntigravity, refreshAntigravityToken } from "./google-antigravity";
|
|
12
14
|
import { loginCursor, refreshCursorToken } from "./cursor";
|
|
13
15
|
import { loginGithubCopilot, refreshGithubCopilotToken, validateCopilotApiBaseUrl } from "./github-copilot";
|
|
16
|
+
import { loginCommandCode, refreshCommandCodeToken } from "./command-code";
|
|
17
|
+
import { ANTIGRAVITY_REQUEST_UA } from "../adapters/google-antigravity-wire";
|
|
14
18
|
import { deriveOAuthDefaultModel, deriveOAuthProviderConfig } from "../providers/derive";
|
|
15
19
|
import { apiKeyPoolEntryId, sanitizeApiKeyValue } from "../providers/api-keys";
|
|
16
|
-
import { effectiveGoogleMode, getProviderRegistryEntry, providerMatchesRegistryTransport } from "../providers/registry";
|
|
20
|
+
import { effectiveGoogleMode, getProviderRegistryEntry, mergeRegistryStaticHeaders, providerMatchesRegistryTransport } from "../providers/registry";
|
|
17
21
|
import { resolveProviderModelDiscoveryUrl } from "../providers/model-discovery";
|
|
18
22
|
import { resolveProviderTransport } from "../providers/xai-transport";
|
|
19
23
|
import { detectClaudeCodeToken, detectGrokCliToken, hasComparableGrokIdentity, isSameGrokIdentity, shouldAdoptGrokGeneration } from "./local-token-detect";
|
|
@@ -51,10 +55,26 @@ export interface OAuthAccessSnapshot {
|
|
|
51
55
|
accountId: string;
|
|
52
56
|
generation: string;
|
|
53
57
|
accessToken: string;
|
|
58
|
+
/** Cloud Code Assist project selected during Antigravity login. */
|
|
59
|
+
projectId?: string;
|
|
54
60
|
/** Safe request-routing subset; refresh-only Kiro client secrets never leave the credential store. */
|
|
55
61
|
kiro?: Pick<KiroOAuthMetadata, "profileArn" | "apiRegion" | "ssoRegion">;
|
|
56
62
|
}
|
|
57
63
|
|
|
64
|
+
export interface ObservedOAuthAccessSnapshot extends OAuthAccessSnapshot {
|
|
65
|
+
/** Allowlisted provider API origin consumed by GitHub Copilot model discovery. */
|
|
66
|
+
apiBaseUrl?: string;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export type OAuthActiveTokenObservation =
|
|
70
|
+
| { readonly kind: "available"; readonly snapshot: ObservedOAuthAccessSnapshot }
|
|
71
|
+
| { readonly kind: "missing" }
|
|
72
|
+
| { readonly kind: "malformed" }
|
|
73
|
+
| { readonly kind: "needs-reauth" }
|
|
74
|
+
| { readonly kind: "expired" }
|
|
75
|
+
| { readonly kind: "near-expiry" }
|
|
76
|
+
| { readonly kind: "unsupported" };
|
|
77
|
+
|
|
58
78
|
const MAX_OAUTH_TOKEN_REFRESH_FLIGHTS = 32;
|
|
59
79
|
const OAUTH_TOKEN_REFRESH_FLIGHT_STALE_MS = 120_000;
|
|
60
80
|
interface OAuthRefreshFlightEvidence { flightId: string; dispatched: boolean }
|
|
@@ -145,6 +165,14 @@ function oauthDefaultModel(id: string): string {
|
|
|
145
165
|
}
|
|
146
166
|
|
|
147
167
|
export const OAUTH_PROVIDERS: Record<string, OAuthProviderDef> = {
|
|
168
|
+
"command-code": {
|
|
169
|
+
// Add-account/reauth must not reimport the current local CLI credential.
|
|
170
|
+
login: (ctrl, opts) => loginCommandCode(ctrl, { importLocal: opts?.forceLogin ? "off" : "fallback" }),
|
|
171
|
+
refresh: refreshCommandCodeToken,
|
|
172
|
+
providerConfig: oauthConfig("command-code"),
|
|
173
|
+
defaultModel: oauthDefaultModel("command-code"),
|
|
174
|
+
defaultRefreshPolicy: "disabled",
|
|
175
|
+
},
|
|
148
176
|
xai: {
|
|
149
177
|
// forceLogin skips the local grok-cli import so a SECOND account can be chosen in the browser.
|
|
150
178
|
login: (ctrl, opts) => loginXai(ctrl, { importLocal: opts?.forceLogin ? "off" : "fallback" }),
|
|
@@ -167,6 +195,22 @@ export const OAUTH_PROVIDERS: Record<string, OAuthProviderDef> = {
|
|
|
167
195
|
providerConfig: oauthConfig("kimi"),
|
|
168
196
|
defaultModel: oauthDefaultModel("kimi"),
|
|
169
197
|
},
|
|
198
|
+
nous: {
|
|
199
|
+
// Nous Portal device-grant login (RFC 8628) against portal.nousresearch.com.
|
|
200
|
+
// The access token is the per-request inference JWT (scope inference:invoke).
|
|
201
|
+
// Refresh tokens are single-use and rotated server-side on every refresh:
|
|
202
|
+
// keep background refresh lazy-only (the default) so concurrent refreshes
|
|
203
|
+
// cannot trip the Portal's token-reuse revocation.
|
|
204
|
+
login: (ctrl) => loginNous(ctrl),
|
|
205
|
+
refresh: (rt, signal) => refreshNousToken(rt, signal),
|
|
206
|
+
providerConfig: oauthConfig("nous"),
|
|
207
|
+
defaultModel: oauthDefaultModel("nous"),
|
|
208
|
+
// Single-use rotating refresh tokens must never be background-refreshed
|
|
209
|
+
// proactively: concurrent refreshes would trip the Portal's reuse
|
|
210
|
+
// revocation. Anchor the lazy-only default explicitly so it cannot be
|
|
211
|
+
// silently overridden to proactive.
|
|
212
|
+
defaultRefreshPolicy: "lazy-only",
|
|
213
|
+
},
|
|
170
214
|
kiro: {
|
|
171
215
|
login: (ctrl, opts) => loginKiro(ctrl, { forceLogin: opts?.forceLogin }),
|
|
172
216
|
refresh: (rt, signal, credential) => refreshKiroToken(rt, signal, credential),
|
|
@@ -180,7 +224,7 @@ export const OAUTH_PROVIDERS: Record<string, OAuthProviderDef> = {
|
|
|
180
224
|
defaultModel: oauthDefaultModel("google-antigravity"),
|
|
181
225
|
},
|
|
182
226
|
cursor: {
|
|
183
|
-
login: (ctrl) => loginCursor(ctrl),
|
|
227
|
+
login: (ctrl, opts) => loginCursor(ctrl, undefined, { forceLogin: opts?.forceLogin }),
|
|
184
228
|
refresh: refreshCursorToken,
|
|
185
229
|
providerConfig: oauthConfig("cursor"),
|
|
186
230
|
defaultModel: oauthDefaultModel("cursor"),
|
|
@@ -248,12 +292,63 @@ export class UnsupportedOAuthProviderError extends Error {
|
|
|
248
292
|
}
|
|
249
293
|
|
|
250
294
|
export class OAuthLoginRequiredError extends Error {
|
|
295
|
+
readonly provider: string;
|
|
296
|
+
|
|
251
297
|
constructor(provider: string) {
|
|
252
298
|
super(`Not logged in to ${provider}. Run: ocx login ${provider}`);
|
|
253
299
|
this.name = "OAuthLoginRequiredError";
|
|
300
|
+
this.provider = provider;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
export class OAuthProviderPublicationError extends Error {
|
|
305
|
+
constructor() {
|
|
306
|
+
super("OAuth credential was saved, but the provider entry was not written. Resolve the account namespace collision, then retry login.");
|
|
307
|
+
this.name = "OAuthProviderPublicationError";
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
export class OAuthReauthIdentityMismatchError extends Error {
|
|
312
|
+
constructor() {
|
|
313
|
+
super("Signed-in account does not match the selected account. Sign in with the same account.");
|
|
314
|
+
this.name = "OAuthReauthIdentityMismatchError";
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
export class OAuthReauthIdentityUnverifiedError extends Error {
|
|
319
|
+
constructor() {
|
|
320
|
+
super("Could not verify signed-in account identity for reauth.");
|
|
321
|
+
this.name = "OAuthReauthIdentityUnverifiedError";
|
|
254
322
|
}
|
|
255
323
|
}
|
|
256
324
|
|
|
325
|
+
class OAuthLoginSupersededError extends Error {
|
|
326
|
+
constructor() {
|
|
327
|
+
super("OAuth login was superseded before credential persistence");
|
|
328
|
+
this.name = "OAuthLoginSupersededError";
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/** Project arbitrary OAuth failures onto the small, stable public error vocabulary. */
|
|
333
|
+
export function publicOAuthAuthenticationErrorMessage(error: unknown): string {
|
|
334
|
+
if (error instanceof OAuthMutationBusyError) {
|
|
335
|
+
return error.message === "OAuth mutation queue wait timed out"
|
|
336
|
+
? "OAuth mutation queue wait timed out"
|
|
337
|
+
: "OAuth mutation queue is busy";
|
|
338
|
+
}
|
|
339
|
+
if (
|
|
340
|
+
(error instanceof OAuthLoginRequiredError && isOAuthProvider(error.provider))
|
|
341
|
+
|| error instanceof OAuthProviderPublicationError
|
|
342
|
+
// Reauth identity outcomes carry fixed, account-free remediation text. Dropping them to the
|
|
343
|
+
// generic message hides WHICH failure the user must fix (sign in with the selected account).
|
|
344
|
+
|| error instanceof OAuthReauthIdentityMismatchError
|
|
345
|
+
|| error instanceof OAuthReauthIdentityUnverifiedError
|
|
346
|
+
|| error instanceof OAuthTokenRefreshBusyError
|
|
347
|
+
|| error instanceof OAuthTokenRefreshStaleError
|
|
348
|
+
) return error.message;
|
|
349
|
+
return "OAuth authentication failed. Check the OpenCodex account status and retry.";
|
|
350
|
+
}
|
|
351
|
+
|
|
257
352
|
function accessSnapshot(provider: string, accountId: string, cred: OAuthCredentials): OAuthAccessSnapshot {
|
|
258
353
|
const storedKiroRouting = {
|
|
259
354
|
...(cred.kiro?.profileArn ? { profileArn: cred.kiro.profileArn } : {}),
|
|
@@ -265,6 +360,7 @@ function accessSnapshot(provider: string, accountId: string, cred: OAuthCredenti
|
|
|
265
360
|
accountId,
|
|
266
361
|
generation: credentialGeneration(cred),
|
|
267
362
|
accessToken: cred.access,
|
|
363
|
+
...(cred.projectId ? { projectId: cred.projectId } : {}),
|
|
268
364
|
// Stored account metadata remains authoritative. Metadata-less legacy/environment credentials
|
|
269
365
|
// may use explicit environment routing, but never borrow the currently signed-in local CLI account.
|
|
270
366
|
...(provider === "kiro"
|
|
@@ -277,6 +373,38 @@ function accessSnapshot(provider: string, accountId: string, cred: OAuthCredenti
|
|
|
277
373
|
};
|
|
278
374
|
}
|
|
279
375
|
|
|
376
|
+
/**
|
|
377
|
+
* Observe the active OAuth token from an auth-store buffer supplied by its filesystem owner.
|
|
378
|
+
* Missing, malformed, reauth-required, and expiring credentials are typed no-token outcomes;
|
|
379
|
+
* this path never refreshes, locks, hardens, backs up, or persists credentials.
|
|
380
|
+
*/
|
|
381
|
+
export function observeActiveOAuthAccessToken(
|
|
382
|
+
provider: string,
|
|
383
|
+
authStoreBuffer: Uint8Array | null,
|
|
384
|
+
now = Date.now(),
|
|
385
|
+
): OAuthActiveTokenObservation {
|
|
386
|
+
const authStore = normalizeAuthStoreBuffer(authStoreBuffer);
|
|
387
|
+
if (authStore.kind === "absent") return { kind: "missing" };
|
|
388
|
+
if (authStore.kind === "malformed") return { kind: "malformed" };
|
|
389
|
+
if (!isOAuthProvider(provider)) return { kind: "unsupported" };
|
|
390
|
+
|
|
391
|
+
const accountSet = authStore.store[provider];
|
|
392
|
+
const account = accountSet?.accounts.find(candidate => candidate.id === accountSet.activeAccountId);
|
|
393
|
+
if (!account) return { kind: "missing" };
|
|
394
|
+
if (account.needsReauth) return { kind: "needs-reauth" };
|
|
395
|
+
if (account.credential.expires <= now) return { kind: "expired" };
|
|
396
|
+
if (account.credential.expires <= now + REFRESH_SKEW_MS) return { kind: "near-expiry" };
|
|
397
|
+
|
|
398
|
+
const apiBaseUrl = validateCopilotApiBaseUrl(account.credential.apiBaseUrl);
|
|
399
|
+
return {
|
|
400
|
+
kind: "available",
|
|
401
|
+
snapshot: {
|
|
402
|
+
...accessSnapshot(provider, account.id, account.credential),
|
|
403
|
+
...(apiBaseUrl ? { apiBaseUrl } : {}),
|
|
404
|
+
},
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
|
|
280
408
|
async function resolveAccessSnapshotForAccount(
|
|
281
409
|
provider: string,
|
|
282
410
|
accountId: string,
|
|
@@ -376,6 +504,11 @@ function terminal(error:unknown):boolean{
|
|
|
376
504
|
if(error instanceof XaiTokenRequestError)return ["invalid_grant","refresh_token_reused","revoked_token"].includes(error.oauthError??"");
|
|
377
505
|
if(error instanceof AnthropicTokenError)return (error.httpStatus===400||error.httpStatus===401)&&["invalid_grant","refresh_token_reused","revoked","revoked_token","refresh_token_revoked"].includes(error.oauthError??"");
|
|
378
506
|
if(error instanceof KiroTokenRefreshError)return (error.httpStatus===400||error.httpStatus===401)&&error.oauthError!==undefined;
|
|
507
|
+
if(error instanceof NousTokenError)return error.terminal===true||["invalid_grant","refresh_token_reused","revoked","revoked_token","expired_token"].includes(error.oauthError??"");
|
|
508
|
+
// Local durable-write/read/cleanup failures are operational, not credential
|
|
509
|
+
// death: the provider credential was never rejected or consumed. Never mark
|
|
510
|
+
// the account needsReauth for broken local persistence infrastructure.
|
|
511
|
+
if (error instanceof RefreshIntentIOError) return false;
|
|
379
512
|
return isTerminalRefreshError(error);
|
|
380
513
|
}
|
|
381
514
|
function authoritative(stored:OAuthCredentials,active:boolean,now:()=>number):OAuthCredentials{if(stored.source!=="local-cli")return stored;const disk=detectGrokCliToken();if(!disk)return stored;const allowed=isSameGrokIdentity(stored,disk)||(active&&!hasComparableGrokIdentity(stored,disk));return allowed&&shouldAdoptGrokGeneration(stored,disk,now(),REFRESH_SKEW_MS)?disk:stored;}
|
|
@@ -399,6 +532,47 @@ function newerClaudeCredential(stored: OAuthCredentials, now: number): OAuthCred
|
|
|
399
532
|
return credentialGeneration(disk) !== credentialGeneration(stored) ? disk : undefined;
|
|
400
533
|
}
|
|
401
534
|
|
|
535
|
+
/**
|
|
536
|
+
* Preserve an already-rotated Nous refresh token (RT-B) after a terminal refresh
|
|
537
|
+
* error (e.g. the returned access JWT lacked `inference:invoke`). The unusable
|
|
538
|
+
* access token is NOT persisted as valid: the recovery credential carries an
|
|
539
|
+
* empty access placeholder with a past expiry so it can never be routed, and the
|
|
540
|
+
* account is marked needsReauth by the caller. Generation-safe: a concurrent
|
|
541
|
+
* newer write wins and is never overwritten.
|
|
542
|
+
*/
|
|
543
|
+
async function preserveNousRotatedRefresh(
|
|
544
|
+
provider: string,
|
|
545
|
+
accountId: string,
|
|
546
|
+
rotatedRefresh: string,
|
|
547
|
+
expectedGeneration: string,
|
|
548
|
+
previous: OAuthCredentials,
|
|
549
|
+
): Promise<{ kind: "persisted"; generation: string } | { kind: "superseded" } | { kind: "failed" }> {
|
|
550
|
+
try {
|
|
551
|
+
const recovery: OAuthCredentials = {
|
|
552
|
+
refresh: rotatedRefresh,
|
|
553
|
+
// Never persist the unusable access token: an empty placeholder with a
|
|
554
|
+
// past expiry can never be observed as a valid credential.
|
|
555
|
+
access: "",
|
|
556
|
+
expires: 0,
|
|
557
|
+
...(previous.accountId ? { accountId: previous.accountId } : {}),
|
|
558
|
+
...(previous.email ? { email: previous.email } : {}),
|
|
559
|
+
...(previous.source ? { source: previous.source } : {}),
|
|
560
|
+
};
|
|
561
|
+
const outcome = await mergeAccountCredential(provider, accountId, recovery, { expectedGeneration });
|
|
562
|
+
if (outcome.superseded) return { kind: "superseded" };
|
|
563
|
+
// Return the exact generation this write produced, so the caller never
|
|
564
|
+
// re-reads the store (a concurrent writer could otherwise supply a different
|
|
565
|
+
// credential generation and be marked needsReauth by mistake).
|
|
566
|
+
return { kind: "persisted", generation: credentialGeneration(recovery) };
|
|
567
|
+
} catch (error) {
|
|
568
|
+
// A store-mutation busy outcome is transient and retryable; surface it
|
|
569
|
+
// unchanged so the caller can retry rather than treating it as a permanent
|
|
570
|
+
// RT-B persistence failure.
|
|
571
|
+
if (error instanceof OAuthMutationBusyError) throw error;
|
|
572
|
+
return { kind: "failed" };
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
|
|
402
576
|
export async function refreshAnthropicAccountWithLock(
|
|
403
577
|
provider: string,
|
|
404
578
|
accountId: string,
|
|
@@ -512,10 +686,69 @@ export async function refreshGenericAccountWithLock(
|
|
|
512
686
|
throw new OAuthLoginRequiredError(provider);
|
|
513
687
|
}
|
|
514
688
|
logOAuthEvent("OAuth credentials rotated and persisted", { provider, accountId });
|
|
689
|
+
// Best-effort bookkeeping cleanup: the rotated credential is already
|
|
690
|
+
// durably persisted. A failure to unlink the old-token intent file
|
|
691
|
+
// (EACCES/EPERM/EBUSY/EROFS) must not turn a committed rotation into a
|
|
692
|
+
// failed refresh. The stale intent keys the OLD token, which is no longer
|
|
693
|
+
// stored, so leaving it behind blocks nothing and is safe. It must also
|
|
694
|
+
// never route through the generic refresh error path (no needsReauth).
|
|
695
|
+
if (provider === "nous") {
|
|
696
|
+
try {
|
|
697
|
+
clearNousRefreshIntent(stored.refresh);
|
|
698
|
+
} catch (cleanupErr) {
|
|
699
|
+
logOAuthEvent("OAuth refresh intent cleanup failed (non-fatal)", {
|
|
700
|
+
provider,
|
|
701
|
+
accountId,
|
|
702
|
+
cause: cleanupErr instanceof Error ? cleanupErr.message : String(cleanupErr),
|
|
703
|
+
});
|
|
704
|
+
}
|
|
705
|
+
}
|
|
515
706
|
return fresh.access;
|
|
516
707
|
} catch (error) {
|
|
517
708
|
if (error instanceof OAuthMutationBusyError) throw error;
|
|
518
709
|
if (!terminal(error)) throw error;
|
|
710
|
+
// Nous-specific failure-atomicity: a terminal refresh error that carries
|
|
711
|
+
// an already-issued rotated refresh token (e.g. the access JWT lacked the
|
|
712
|
+
// required `inference:invoke` scope) means the server consumed RT-A and
|
|
713
|
+
// issued RT-B. RT-B must be preserved generation-safely BEFORE forcing
|
|
714
|
+
// reauthentication; discarding it would lose the only usable refresh
|
|
715
|
+
// material and force a full re-auth for no reason.
|
|
716
|
+
if (provider === "nous" && error instanceof NousTokenError) {
|
|
717
|
+
const rotated = error.getRotatedRefresh();
|
|
718
|
+
if (rotated !== undefined && rotated !== stored.refresh) {
|
|
719
|
+
const outcome = await preserveNousRotatedRefresh(
|
|
720
|
+
provider,
|
|
721
|
+
accountId,
|
|
722
|
+
rotated,
|
|
723
|
+
generation,
|
|
724
|
+
stored,
|
|
725
|
+
);
|
|
726
|
+
if (outcome.kind === "persisted") {
|
|
727
|
+
// RT-A's intent is cleared only after RT-B is durably persisted;
|
|
728
|
+
// cleanup itself stays best-effort (a stale RT-A intent keys a token
|
|
729
|
+
// that is no longer stored).
|
|
730
|
+
try {
|
|
731
|
+
clearNousRefreshIntent(stored.refresh);
|
|
732
|
+
} catch (cleanupErr) {
|
|
733
|
+
logOAuthEvent("OAuth refresh intent cleanup failed (non-fatal)", {
|
|
734
|
+
provider,
|
|
735
|
+
accountId,
|
|
736
|
+
cause: cleanupErr instanceof Error ? cleanupErr.message : String(cleanupErr),
|
|
737
|
+
});
|
|
738
|
+
}
|
|
739
|
+
// Mark exactly the generation this write produced — never a
|
|
740
|
+
// credential written by a concurrent login between the merge and
|
|
741
|
+
// this step.
|
|
742
|
+
await markAccountNeedsReauthIfGeneration(provider, accountId, outcome.generation, writerGeneration);
|
|
743
|
+
} else {
|
|
744
|
+
// RT-B persistence failed or a newer generation superseded it:
|
|
745
|
+
// never clear RT-A's intent (RT-A was consumed), and mark the old
|
|
746
|
+
// generation needsReauth (a no-op if a newer generation won).
|
|
747
|
+
await markAccountNeedsReauthIfGeneration(provider, accountId, generation, writerGeneration);
|
|
748
|
+
}
|
|
749
|
+
throw new OAuthLoginRequiredError(provider);
|
|
750
|
+
}
|
|
751
|
+
}
|
|
519
752
|
await markAccountNeedsReauthIfGeneration(provider, accountId, generation, writerGeneration);
|
|
520
753
|
throw new OAuthLoginRequiredError(provider);
|
|
521
754
|
}
|
|
@@ -572,31 +805,63 @@ function modelDiscoveryTransportSeed(providerName: string, prov: OcxProviderConf
|
|
|
572
805
|
}
|
|
573
806
|
|
|
574
807
|
/**
|
|
575
|
-
* Provider-correct
|
|
808
|
+
* Provider-correct model-discovery request (URL + headers), so both model-listing paths fetch the
|
|
576
809
|
* LIVE catalog correctly per adapter. Anthropic is the special case: its endpoint is `/v1/models`
|
|
577
810
|
* (not `/models`), it needs `anthropic-version`, and it authenticates with `x-api-key` by default
|
|
578
811
|
* (or `Authorization: Bearer` when `apiKeyTransport = "bearer"`), plus the OAuth beta for oauth
|
|
579
812
|
* mode — not a bare Bearer. Google (ai-studio mode)
|
|
580
813
|
* is the other special case: `x-goog-api-key` + `/v1beta/models`, returning `{ models: [...] }`.
|
|
581
814
|
* The catalog authority gate intentionally degrades that non-OpenAI shape to stale/static data.
|
|
582
|
-
*
|
|
583
|
-
* response.
|
|
815
|
+
* Antigravity uses its CCA `:fetchAvailableModels` RPC; everyone else uses the OpenAI-style
|
|
816
|
+
* `/models` + Bearer with a `{ data: [{ id, owned_by? }] }` response.
|
|
584
817
|
*/
|
|
585
|
-
export
|
|
818
|
+
export interface ModelsRequestObservedAuth {
|
|
819
|
+
readonly oauthApiBaseUrl?: string;
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
export function buildModelsRequest(
|
|
823
|
+
prov: OcxProviderConfig,
|
|
824
|
+
apiKey: string | undefined,
|
|
825
|
+
providerName = "",
|
|
826
|
+
observedAuth?: ModelsRequestObservedAuth,
|
|
827
|
+
): { method?: "POST"; url: string; headers: Record<string, string> } {
|
|
586
828
|
const transportSeed = modelDiscoveryTransportSeed(providerName, prov);
|
|
829
|
+
const copilotApiBaseUrl = observedAuth === undefined
|
|
830
|
+
? (providerName === "github-copilot" ? getOAuthCredentialApiBaseUrl(providerName) : undefined)
|
|
831
|
+
: observedAuth.oauthApiBaseUrl;
|
|
587
832
|
const effectiveProvider = resolveProviderTransport(
|
|
588
833
|
providerName,
|
|
589
834
|
transportSeed,
|
|
590
835
|
undefined,
|
|
591
|
-
|
|
836
|
+
copilotApiBaseUrl,
|
|
592
837
|
);
|
|
593
|
-
|
|
838
|
+
// Model discovery is an upstream request like any other, so it carries the same registry
|
|
839
|
+
// static headers the inference path does. Without this a provider is identified correctly
|
|
840
|
+
// when it answers a completion but anonymously when it lists its own models, which is the
|
|
841
|
+
// kind of split fingerprint an upstream rate limiter reads as two different clients.
|
|
842
|
+
const registryStaticHeaders = providerMatchesRegistryTransport(providerName, effectiveProvider)
|
|
843
|
+
? getProviderRegistryEntry(providerName)?.staticHeaders
|
|
844
|
+
: undefined;
|
|
845
|
+
const headers: Record<string, string> = {
|
|
846
|
+
...(mergeRegistryStaticHeaders(registryStaticHeaders, effectiveProvider.headers) ?? {}),
|
|
847
|
+
};
|
|
594
848
|
const discoveryUrl = (defaultUrl: string): string => resolveProviderModelDiscoveryUrl(
|
|
595
849
|
providerName,
|
|
596
850
|
prov,
|
|
597
851
|
effectiveProvider.baseUrl,
|
|
598
852
|
defaultUrl,
|
|
599
853
|
);
|
|
854
|
+
if (effectiveGoogleMode(providerName, effectiveProvider) === "cloud-code-assist") {
|
|
855
|
+
headers.Accept = "application/json";
|
|
856
|
+
headers["Content-Type"] = "application/json";
|
|
857
|
+
headers["User-Agent"] = ANTIGRAVITY_REQUEST_UA;
|
|
858
|
+
if (apiKey) headers.Authorization = `Bearer ${apiKey}`;
|
|
859
|
+
return {
|
|
860
|
+
method: "POST",
|
|
861
|
+
url: discoveryUrl(`${effectiveProvider.baseUrl.replace(/\/+$/, "")}/v1internal:fetchAvailableModels`),
|
|
862
|
+
headers,
|
|
863
|
+
};
|
|
864
|
+
}
|
|
600
865
|
if (effectiveGoogleMode(providerName, effectiveProvider) === "ai-studio") {
|
|
601
866
|
// Generative Language API: API key goes in x-goog-api-key (never Authorization: Bearer),
|
|
602
867
|
// models live under /v1beta (v1 misses preview models), and pageSize maxes at 1000 —
|
|
@@ -630,9 +895,7 @@ export function buildModelsRequest(prov: OcxProviderConfig, apiKey: string | und
|
|
|
630
895
|
*
|
|
631
896
|
* Only touches providers that are registry-managed AND still `authMode: "oauth"`. Preset fields
|
|
632
897
|
* are refreshed, while the registry's `liveModels` default is normally filled only when no value
|
|
633
|
-
* is stored.
|
|
634
|
-
* cannot be distinguished from a hand-written pre-migration `true`. Persists + returns true when
|
|
635
|
-
* anything changed.
|
|
898
|
+
* is stored. Persists + returns true when anything changed.
|
|
636
899
|
*/
|
|
637
900
|
function cloneProviderField(value: unknown): unknown {
|
|
638
901
|
if (Array.isArray(value)) return [...value];
|
|
@@ -659,31 +922,62 @@ const OAUTH_RECONCILE_FIELDS: (keyof OcxProviderConfig)[] = [
|
|
|
659
922
|
"autoToolChoiceOnlyModels",
|
|
660
923
|
"preserveReasoningContentModels",
|
|
661
924
|
];
|
|
925
|
+
// `requiresReasoningPlaceholderModels` is deliberately NOT reconciled here: no
|
|
926
|
+
// OAuth preset seeds it, so the delete-when-preset-undefined branch would wipe
|
|
927
|
+
// an explicit user opt-out (`[]`) on every startup. Registry seeds still reach
|
|
928
|
+
// existing rows through enrichProviderFromRegistry, which is fill-only and
|
|
929
|
+
// preserves explicit saved values.
|
|
662
930
|
|
|
663
931
|
const GOOGLE_ANTIGRAVITY_PROVIDER = "google-antigravity";
|
|
664
|
-
const
|
|
932
|
+
const GOOGLE_ANTIGRAVITY_LIVE_DISCOVERY_VERSION = 2 as const;
|
|
933
|
+
|
|
934
|
+
/** Only migrate the three-model experimental seed; an operator's later `liveModels: false` wins. */
|
|
935
|
+
function isLegacyCommandCodeStaticCatalog(provider: OcxProviderConfig): boolean {
|
|
936
|
+
return provider.liveModels === false
|
|
937
|
+
&& provider.defaultModel === "deepseek-v4-flash"
|
|
938
|
+
&& JSON.stringify(provider.models) === JSON.stringify(["deepseek-v4-flash", "kimi-k3", "glm-5.2"]);
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
function isLegacyAntigravityStaticCatalog(provider: OcxProviderConfig): boolean {
|
|
942
|
+
// A fingerprint of the shape version 1 actually shipped, NOT of the current registry.
|
|
943
|
+
// These literals must stay frozen as the model list moves on: matching them is how we
|
|
944
|
+
// know the row is the untouched v1 seed rather than a user's own selection.
|
|
945
|
+
return provider.liveModels === false
|
|
946
|
+
&& provider.adapter === "google"
|
|
947
|
+
&& provider.baseUrl === "https://daily-cloudcode-pa.googleapis.com"
|
|
948
|
+
&& provider.authMode === "oauth"
|
|
949
|
+
&& provider.googleMode === "cloud-code-assist"
|
|
950
|
+
&& provider.defaultModel === "gemini-3.6-flash"
|
|
951
|
+
&& JSON.stringify(provider.models) === JSON.stringify([
|
|
952
|
+
"gemini-3.6-flash",
|
|
953
|
+
"gemini-3.1-pro",
|
|
954
|
+
"gemini-3.1-flash-image",
|
|
955
|
+
"claude-sonnet-4-6",
|
|
956
|
+
"claude-opus-4-6-thinking",
|
|
957
|
+
"gpt-oss-120b-medium",
|
|
958
|
+
]);
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
/** Promote only the versioned canonical static seed; unmarked `liveModels: false` remains user intent. */
|
|
962
|
+
function migrateLegacyAntigravityStaticCatalog(config: OcxConfig): boolean {
|
|
963
|
+
if (config.googleAntigravityStaticCatalogVersion !== 1) return false;
|
|
964
|
+
const provider = config.providers[GOOGLE_ANTIGRAVITY_PROVIDER];
|
|
965
|
+
if (provider && isLegacyAntigravityStaticCatalog(provider)) provider.liveModels = true;
|
|
966
|
+
config.googleAntigravityStaticCatalogVersion = GOOGLE_ANTIGRAVITY_LIVE_DISCOVERY_VERSION;
|
|
967
|
+
return true;
|
|
968
|
+
}
|
|
665
969
|
|
|
666
970
|
export function reconcileOAuthProviders(config: OcxConfig): boolean {
|
|
667
|
-
let changed =
|
|
668
|
-
const migrateAntigravityStaticCatalog =
|
|
669
|
-
config.googleAntigravityStaticCatalogVersion !== GOOGLE_ANTIGRAVITY_STATIC_CATALOG_VERSION;
|
|
971
|
+
let changed = migrateLegacyAntigravityStaticCatalog(config);
|
|
670
972
|
for (const [name, prov] of Object.entries(config.providers)) {
|
|
671
973
|
const def = OAUTH_PROVIDERS[name];
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
// rows do not get stamped without actually receiving the new static default.
|
|
677
|
-
if (name === GOOGLE_ANTIGRAVITY_PROVIDER && migrateAntigravityStaticCatalog && prov.liveModels !== false) {
|
|
678
|
-
prov.liveModels = false;
|
|
974
|
+
if (name === "command-code" && isLegacyCommandCodeStaticCatalog(prov)) {
|
|
975
|
+
// The former experimental preset was the exact three-model seed above. It was not a user
|
|
976
|
+
// choice to disable discovery, so promote only that shape to the account live catalog.
|
|
977
|
+
prov.liveModels = true;
|
|
679
978
|
changed = true;
|
|
680
979
|
}
|
|
681
|
-
|
|
682
|
-
// fields when authMode is omitted or non-oauth. Otherwise liveModels flips to static while
|
|
683
|
-
// a stale models[] remains the published catalog forever.
|
|
684
|
-
const migrateAntigravityCatalogFields =
|
|
685
|
-
name === GOOGLE_ANTIGRAVITY_PROVIDER && migrateAntigravityStaticCatalog;
|
|
686
|
-
if (!def || (prov.authMode !== "oauth" && !migrateAntigravityCatalogFields)) continue;
|
|
980
|
+
if (!def || prov.authMode !== "oauth") continue;
|
|
687
981
|
const preset = def.providerConfig;
|
|
688
982
|
for (const field of OAUTH_RECONCILE_FIELDS) {
|
|
689
983
|
if (JSON.stringify(prov[field]) === JSON.stringify(preset[field])) continue;
|
|
@@ -694,23 +988,19 @@ export function reconcileOAuthProviders(config: OcxConfig): boolean {
|
|
|
694
988
|
}
|
|
695
989
|
changed = true;
|
|
696
990
|
}
|
|
697
|
-
// Before this marker existed, the GUI materialized an omitted `liveModels` as `true` on any
|
|
698
|
-
// settings save. Since persisted values have no provenance, the pre-guard normalization above
|
|
699
|
-
// intentionally resets all pre-marker `true` values once. Later choices are version-bounded.
|
|
700
991
|
if (prov.liveModels === undefined && preset.liveModels !== undefined) {
|
|
701
992
|
prov.liveModels = preset.liveModels;
|
|
702
993
|
changed = true;
|
|
703
994
|
}
|
|
704
995
|
// Heal a defaultModel that no longer exists in the refreshed list (e.g. a deprecated snapshot).
|
|
705
|
-
|
|
996
|
+
// Skip providers without a static preset `models` list: for live-discovery providers
|
|
997
|
+
// (e.g. command-code OAuth) the account-scoped catalog is not enumerable here, so any
|
|
998
|
+
// persisted defaultModel is a user selection and must not be overwritten by the seed.
|
|
999
|
+
if (prov.defaultModel && preset.defaultModel && preset.models && preset.models.length > 0 && !(prov.models ?? []).includes(prov.defaultModel)) {
|
|
706
1000
|
prov.defaultModel = preset.defaultModel;
|
|
707
1001
|
changed = true;
|
|
708
1002
|
}
|
|
709
1003
|
}
|
|
710
|
-
if (migrateAntigravityStaticCatalog) {
|
|
711
|
-
config.googleAntigravityStaticCatalogVersion = GOOGLE_ANTIGRAVITY_STATIC_CATALOG_VERSION;
|
|
712
|
-
changed = true;
|
|
713
|
-
}
|
|
714
1004
|
if (changed) saveConfig(config);
|
|
715
1005
|
return changed;
|
|
716
1006
|
}
|
|
@@ -767,19 +1057,28 @@ export function upsertOAuthProvider(config: OcxConfig, provider: string): void {
|
|
|
767
1057
|
if (provider === "chatgpt") return;
|
|
768
1058
|
const def = OAUTH_PROVIDERS[provider];
|
|
769
1059
|
if (!def) return;
|
|
1060
|
+
if (provider === GOOGLE_ANTIGRAVITY_PROVIDER) migrateLegacyAntigravityStaticCatalog(config);
|
|
770
1061
|
const namespaceCollision = codexAccountNamespaceProviderCollisionError(config.codexAccountNamespaces, provider);
|
|
771
1062
|
if (namespaceCollision) throw new Error(namespaceCollision);
|
|
772
1063
|
const existing = config.providers[provider];
|
|
773
1064
|
const next: OcxProviderConfig = { ...def.providerConfig };
|
|
774
|
-
// `liveModels` is a user-facing provider toggle.
|
|
775
|
-
//
|
|
776
|
-
|
|
777
|
-
// reset once; users who deliberately forced discovery can re-enable it after migration.
|
|
778
|
-
const preserveExistingLiveModels = provider !== GOOGLE_ANTIGRAVITY_PROVIDER
|
|
779
|
-
|| config.googleAntigravityStaticCatalogVersion === GOOGLE_ANTIGRAVITY_STATIC_CATALOG_VERSION;
|
|
780
|
-
if (preserveExistingLiveModels && typeof existing?.liveModels === "boolean") {
|
|
1065
|
+
// `liveModels` is a user-facing provider toggle. Preserve either explicit setting across login;
|
|
1066
|
+
// Antigravity's CCA discovery now uses its real RPC, so legacy `true` remains a valid choice.
|
|
1067
|
+
if (typeof existing?.liveModels === "boolean" && !isLegacyCommandCodeStaticCatalog(existing)) {
|
|
781
1068
|
next.liveModels = existing.liveModels;
|
|
782
1069
|
}
|
|
1070
|
+
// The Command Code protocol-version pin is an operator compatibility control. A re-login,
|
|
1071
|
+
// add-account, or reauth rebuilds the row from the preset, which has no version; carry the
|
|
1072
|
+
// existing pin so authentication changes do not silently revert the documented control.
|
|
1073
|
+
if (existing?.commandCodeVersion !== undefined) {
|
|
1074
|
+
next.commandCodeVersion = existing.commandCodeVersion;
|
|
1075
|
+
}
|
|
1076
|
+
// User-configured price overlays are operator data, not preset state; a
|
|
1077
|
+
// re-login, add-account, or reauth must not silently drop them from the
|
|
1078
|
+
// Logs/Usage estimates.
|
|
1079
|
+
if (existing?.modelCosts !== undefined) {
|
|
1080
|
+
next.modelCosts = existing.modelCosts;
|
|
1081
|
+
}
|
|
783
1082
|
if (existing && getProviderRegistryEntry(provider)?.allowKeyAuthOverride === true) {
|
|
784
1083
|
// Shared sanitizeApiKeyValue trim / no-CRLF checks from api-key pool writes.
|
|
785
1084
|
let storedApiKey = sanitizeApiKeyValue(existing.apiKey);
|
|
@@ -803,9 +1102,6 @@ export function upsertOAuthProvider(config: OcxConfig, provider: string): void {
|
|
|
803
1102
|
}
|
|
804
1103
|
}
|
|
805
1104
|
config.providers[provider] = next;
|
|
806
|
-
if (provider === GOOGLE_ANTIGRAVITY_PROVIDER) {
|
|
807
|
-
config.googleAntigravityStaticCatalogVersion = GOOGLE_ANTIGRAVITY_STATIC_CATALOG_VERSION;
|
|
808
|
-
}
|
|
809
1105
|
}
|
|
810
1106
|
|
|
811
1107
|
interface RunLoginDeps {
|
|
@@ -816,6 +1112,7 @@ interface RunLoginDeps {
|
|
|
816
1112
|
settleKiroLoginTransaction?: typeof settleKiroLoginTransaction;
|
|
817
1113
|
removeAccount?: typeof removeAccount;
|
|
818
1114
|
setActiveAccount?: typeof setActiveAccount;
|
|
1115
|
+
assertCurrentOwner?: () => void;
|
|
819
1116
|
}
|
|
820
1117
|
|
|
821
1118
|
/** Roll back only accounts created by this forced login, preserving concurrent refreshes of others. */
|
|
@@ -865,6 +1162,7 @@ export async function runLogin(
|
|
|
865
1162
|
const cred: OAuthCredentials = rawCred.source ? rawCred : { ...rawCred, source: "oauth" };
|
|
866
1163
|
const settleKiroTransaction = deps.settleKiroLoginTransaction ?? settleKiroLoginTransaction;
|
|
867
1164
|
try {
|
|
1165
|
+
deps.assertCurrentOwner?.();
|
|
868
1166
|
// Validate the provider row before credential persistence. A namespace claimed during the
|
|
869
1167
|
// credential write is handled again below before the latest row is re-upserted.
|
|
870
1168
|
if (provider !== "chatgpt") {
|
|
@@ -875,7 +1173,7 @@ export async function runLogin(
|
|
|
875
1173
|
const existing = getAccountCredential(provider, opts.reauthAccountId);
|
|
876
1174
|
if (!existing) throw new Error(`Unknown account for reauth: ${opts.reauthAccountId}`);
|
|
877
1175
|
if (!existing.accountId && !existing.email) {
|
|
878
|
-
throw new
|
|
1176
|
+
throw new OAuthReauthIdentityUnverifiedError();
|
|
879
1177
|
}
|
|
880
1178
|
const identityMatches = existing.accountId && cred.accountId
|
|
881
1179
|
? existing.accountId === cred.accountId
|
|
@@ -883,12 +1181,15 @@ export async function runLogin(
|
|
|
883
1181
|
? existing.email.toLowerCase() === cred.email.toLowerCase()
|
|
884
1182
|
: false;
|
|
885
1183
|
if (!identityMatches) {
|
|
886
|
-
throw new
|
|
1184
|
+
throw new OAuthReauthIdentityMismatchError();
|
|
887
1185
|
}
|
|
888
|
-
await (deps.saveAccountCredential ?? saveAccountCredential)(provider, opts.reauthAccountId, cred
|
|
1186
|
+
await (deps.saveAccountCredential ?? saveAccountCredential)(provider, opts.reauthAccountId, cred, {
|
|
1187
|
+
assertBeforePersist: deps.assertCurrentOwner,
|
|
1188
|
+
});
|
|
889
1189
|
} else {
|
|
890
1190
|
await (deps.saveCredential ?? saveCredential)(provider, cred, {
|
|
891
|
-
preserveIdentityless:
|
|
1191
|
+
preserveIdentityless: opts?.forceLogin === true,
|
|
1192
|
+
assertBeforePersist: deps.assertCurrentOwner,
|
|
892
1193
|
});
|
|
893
1194
|
}
|
|
894
1195
|
if (provider !== "chatgpt") {
|
|
@@ -900,10 +1201,7 @@ export async function runLogin(
|
|
|
900
1201
|
provider,
|
|
901
1202
|
);
|
|
902
1203
|
if (lateCollision) {
|
|
903
|
-
throw new
|
|
904
|
-
`${lateCollision}. The credential for "${provider}" was saved, but the provider entry was not written. `
|
|
905
|
-
+ "Rename the account selector, then re-run the login.",
|
|
906
|
-
);
|
|
1204
|
+
throw new OAuthProviderPublicationError();
|
|
907
1205
|
}
|
|
908
1206
|
upsertOAuthProvider(latestConfig, provider);
|
|
909
1207
|
saveLatestConfig(latestConfig);
|
|
@@ -933,11 +1231,15 @@ export async function runLogin(
|
|
|
933
1231
|
settleKiroTransaction(rawCred, true);
|
|
934
1232
|
if (provider !== "chatgpt") {
|
|
935
1233
|
try {
|
|
1234
|
+
const { clearModelCache } = await import("../codex/model-cache");
|
|
1235
|
+
const { clearGatherRoutedModelsInflight } = await import("../codex/catalog");
|
|
1236
|
+
clearModelCache(provider);
|
|
1237
|
+
clearGatherRoutedModelsInflight();
|
|
936
1238
|
const { clearAccountQuotaCache, clearProviderQuotaCache } = await import("../providers/quota");
|
|
937
1239
|
clearProviderQuotaCache();
|
|
938
1240
|
clearAccountQuotaCache(provider);
|
|
939
1241
|
} catch {
|
|
940
|
-
//
|
|
1242
|
+
// Optional state modules may be unavailable in tightly scoped unit tests.
|
|
941
1243
|
}
|
|
942
1244
|
}
|
|
943
1245
|
return cred;
|
|
@@ -954,6 +1256,7 @@ export async function runLogin(
|
|
|
954
1256
|
*/
|
|
955
1257
|
const loginState = new Map<string, { error?: string; done: boolean }>();
|
|
956
1258
|
const loginAbort = new Map<string, AbortController>();
|
|
1259
|
+
const kiroLoginSettling = new Set<string>();
|
|
957
1260
|
|
|
958
1261
|
/** Pending paste for a login in progress: either a waiter or a stashed early submission. */
|
|
959
1262
|
interface ManualCodeSlot {
|
|
@@ -1036,7 +1339,11 @@ export function submitManualLoginCode(provider: string, input: string): { ok: tr
|
|
|
1036
1339
|
// protected. Early posts (flow not yet waiting, no expectedState) are stashed and
|
|
1037
1340
|
// re-validated by the callback loop.
|
|
1038
1341
|
const parsed = parseCallbackInput(trimmed);
|
|
1039
|
-
|
|
1342
|
+
// Command Code's manual fallback accepts a pasted JSON callback payload
|
|
1343
|
+
// (`{ apiKey, state, ... }`) which has no `code` param. Let it through the
|
|
1344
|
+
// shared gate so the provider-specific parser can validate it.
|
|
1345
|
+
const isCommandCodeJson = provider === "command-code" && trimmed.startsWith("{") && !parsed.code;
|
|
1346
|
+
if (!parsed.code && !isCommandCodeJson) return { ok: false, error: "no authorization code found in input" };
|
|
1040
1347
|
if (parsed.kind !== "raw" && slot.expectedState !== undefined) {
|
|
1041
1348
|
if (parsed.state === undefined) return { ok: false, error: "redirect URL is missing the state parameter" };
|
|
1042
1349
|
if (parsed.state !== slot.expectedState) return { ok: false, error: "state mismatch — paste the redirect URL from THIS login attempt" };
|
|
@@ -1058,19 +1365,27 @@ export function getLoginStatus(provider: string): { loggedIn: boolean; email?: s
|
|
|
1058
1365
|
const cred = getCredential(provider);
|
|
1059
1366
|
const st = loginState.get(provider);
|
|
1060
1367
|
const set = getAccountSet(provider);
|
|
1061
|
-
//
|
|
1062
|
-
//
|
|
1368
|
+
// Masked emails on purpose: status DTOs are shared with GUI/CLI surfaces that
|
|
1369
|
+
// must not leak full local-parts. Tokens stay out of this surface entirely.
|
|
1063
1370
|
const accounts: OAuthAccountSummary[] | undefined = set?.accounts.map(a => ({
|
|
1064
1371
|
id: a.id,
|
|
1065
1372
|
...(a.alias ? { alias: a.alias } : {}),
|
|
1066
|
-
email: a.credential.email ?? undefined,
|
|
1373
|
+
email: maskEmail(a.credential.email) ?? undefined,
|
|
1067
1374
|
active: a.id === set.activeAccountId,
|
|
1068
1375
|
...(a.needsReauth ? { needsReauth: true } : {}),
|
|
1069
1376
|
expiresAt: a.credential.expires,
|
|
1070
1377
|
}));
|
|
1378
|
+
|
|
1379
|
+
// A stored credential counts as "logged in" when it exists and is not marked for
|
|
1380
|
+
// re-authentication. An expired access token with a valid refresh token is still
|
|
1381
|
+
// logged in: request resolution refreshes expired/near-expiry credentials lazily.
|
|
1382
|
+
// Invalid/unknown local-import expiries are handled at parse/adoption time
|
|
1383
|
+
// (local-token-detect.ts), never by over-reporting login state here.
|
|
1384
|
+
const activeNeedsReauth = set?.accounts
|
|
1385
|
+
.find(a => a.id === set.activeAccountId)?.needsReauth === true;
|
|
1071
1386
|
return {
|
|
1072
|
-
loggedIn: !!cred,
|
|
1073
|
-
email: cred?.email ?? undefined,
|
|
1387
|
+
loggedIn: !!cred && !activeNeedsReauth,
|
|
1388
|
+
email: maskEmail(cred?.email) ?? undefined,
|
|
1074
1389
|
source: cred?.source,
|
|
1075
1390
|
error: st?.error,
|
|
1076
1391
|
done: st?.done ?? false,
|
|
@@ -1112,13 +1427,14 @@ export async function startLoginFlow(
|
|
|
1112
1427
|
const def = OAUTH_PROVIDERS[provider];
|
|
1113
1428
|
if (!def) throw new UnsupportedOAuthProviderError(provider);
|
|
1114
1429
|
const existing = loginState.get(provider);
|
|
1115
|
-
if (existing && !existing.done) {
|
|
1430
|
+
if ((existing && !existing.done) || (provider === "kiro" && kiroLoginSettling.has(provider))) {
|
|
1116
1431
|
throw new Error(`A login for ${provider} is already in progress`);
|
|
1117
1432
|
}
|
|
1118
1433
|
clearManualCodeSlot(provider);
|
|
1119
1434
|
loginState.set(provider, { done: false });
|
|
1120
1435
|
const abort = new AbortController();
|
|
1121
1436
|
loginAbort.set(provider, abort);
|
|
1437
|
+
if (provider === "kiro") kiroLoginSettling.add(provider);
|
|
1122
1438
|
return new Promise((resolve, reject) => {
|
|
1123
1439
|
let urlResolved = false;
|
|
1124
1440
|
const ctrl: OAuthController = {
|
|
@@ -1131,7 +1447,16 @@ export async function startLoginFlow(
|
|
|
1131
1447
|
onManualCodeInput: (expectedState?: string) => waitForManualLoginCode(provider, abort.signal, expectedState),
|
|
1132
1448
|
signal: abort.signal,
|
|
1133
1449
|
};
|
|
1450
|
+
const abandonIfNotOwner = (error?: unknown): boolean => {
|
|
1451
|
+
if (loginAbort.get(provider) === abort) return false;
|
|
1452
|
+
if (!urlResolved) reject(error ?? new Error("OAuth login was superseded"));
|
|
1453
|
+
return true;
|
|
1454
|
+
};
|
|
1134
1455
|
const settle = async (error?: unknown): Promise<void> => {
|
|
1456
|
+
// Cancellation deletes this controller and records its own terminal result. A late provider
|
|
1457
|
+
// rejection (or an older flow settling after a replacement starts) must not overwrite that
|
|
1458
|
+
// state or delete the replacement flow's controller/manual-code slot.
|
|
1459
|
+
if (abandonIfNotOwner(error)) return;
|
|
1135
1460
|
let finalError = error;
|
|
1136
1461
|
try {
|
|
1137
1462
|
await lifecycle?.onSettled?.();
|
|
@@ -1140,6 +1465,7 @@ export async function startLoginFlow(
|
|
|
1140
1465
|
// runtime config. For an already-failed login, keep the original recovery error.
|
|
1141
1466
|
if (finalError === undefined) finalError = settleError;
|
|
1142
1467
|
}
|
|
1468
|
+
if (abandonIfNotOwner(finalError)) return;
|
|
1143
1469
|
if (finalError === undefined) {
|
|
1144
1470
|
loginAbort.delete(provider);
|
|
1145
1471
|
clearManualCodeSlot(provider);
|
|
@@ -1153,22 +1479,28 @@ export async function startLoginFlow(
|
|
|
1153
1479
|
const e = finalError;
|
|
1154
1480
|
loginAbort.delete(provider);
|
|
1155
1481
|
clearManualCodeSlot(provider);
|
|
1156
|
-
const msg =
|
|
1482
|
+
const msg = publicOAuthAuthenticationErrorMessage(e);
|
|
1157
1483
|
loginState.set(provider, { done: true, error: msg });
|
|
1158
1484
|
if (!urlResolved) reject(e);
|
|
1159
1485
|
};
|
|
1160
1486
|
// Background: runLogin persists the credential + provider entry to disk. The lifecycle hook
|
|
1161
1487
|
// lets a long-lived server config adopt that settled state before clients observe done=true.
|
|
1162
|
-
|
|
1488
|
+
const assertCurrentOwner = (): void => {
|
|
1489
|
+
if (loginAbort.get(provider) !== abort) throw new OAuthLoginSupersededError();
|
|
1490
|
+
};
|
|
1491
|
+
void runLogin(provider, ctrl, opts, { assertCurrentOwner }).then(
|
|
1163
1492
|
() => settle(),
|
|
1164
1493
|
(e: unknown) => settle(e),
|
|
1165
1494
|
).catch((e: unknown) => {
|
|
1166
1495
|
// settle catches lifecycle failures, so this is only a defensive promise-boundary guard.
|
|
1496
|
+
if (abandonIfNotOwner(e)) return;
|
|
1167
1497
|
loginAbort.delete(provider);
|
|
1168
1498
|
clearManualCodeSlot(provider);
|
|
1169
|
-
const msg =
|
|
1499
|
+
const msg = publicOAuthAuthenticationErrorMessage(e);
|
|
1170
1500
|
loginState.set(provider, { done: true, error: msg });
|
|
1171
1501
|
if (!urlResolved) reject(e);
|
|
1502
|
+
}).finally(() => {
|
|
1503
|
+
if (provider === "kiro") kiroLoginSettling.delete(provider);
|
|
1172
1504
|
});
|
|
1173
1505
|
});
|
|
1174
1506
|
}
|