@yansigit/opencodex 2.31.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS_INSTALL.md +109 -0
- package/LICENSE +21 -0
- package/README.md +303 -0
- package/assets/architecture.png +0 -0
- package/assets/banner.png +0 -0
- package/assets/claude-code-models.gif +0 -0
- package/assets/codex-app-picker.png +0 -0
- package/bin/ocx.mjs +587 -0
- package/bin/package-main.mjs +9 -0
- package/gui/dist/assets/index-BNJ7r4Gd.js +102 -0
- package/gui/dist/assets/index-CGoDO3uO.css +1 -0
- package/gui/dist/favicon.png +0 -0
- package/gui/dist/icons.svg +24 -0
- package/gui/dist/index.html +25 -0
- package/gui/dist/logo.png +0 -0
- package/gui/dist/provider-icons/alibaba-color.svg +1 -0
- package/gui/dist/provider-icons/antigravity-color.svg +1 -0
- package/gui/dist/provider-icons/claude-color.svg +1 -0
- package/gui/dist/provider-icons/cline-color.svg +16 -0
- package/gui/dist/provider-icons/cloudflare-ai-gateway-color.svg +1 -0
- package/gui/dist/provider-icons/commandcode-color.svg +1 -0
- package/gui/dist/provider-icons/copilot-color.svg +1 -0
- package/gui/dist/provider-icons/cursor-color.svg +2 -0
- package/gui/dist/provider-icons/deepseek-color.svg +1 -0
- package/gui/dist/provider-icons/discord.svg +1 -0
- package/gui/dist/provider-icons/firepass-color.svg +1 -0
- package/gui/dist/provider-icons/fireworks-color.svg +1 -0
- package/gui/dist/provider-icons/gemini-color.svg +1 -0
- package/gui/dist/provider-icons/github-copilot-color.svg +1 -0
- package/gui/dist/provider-icons/gitlab-duo-color.svg +1 -0
- package/gui/dist/provider-icons/grok.svg +1 -0
- package/gui/dist/provider-icons/groq-color.svg +1 -0
- package/gui/dist/provider-icons/huggingface-color.svg +1 -0
- package/gui/dist/provider-icons/kimi-color.svg +1 -0
- package/gui/dist/provider-icons/kiro-color.svg +15 -0
- package/gui/dist/provider-icons/lm-studio-color.svg +1 -0
- package/gui/dist/provider-icons/mistral-color.svg +1 -0
- package/gui/dist/provider-icons/moonshot-color.svg +1 -0
- package/gui/dist/provider-icons/nvidia-color.svg +1 -0
- package/gui/dist/provider-icons/ollama-color.svg +1 -0
- package/gui/dist/provider-icons/openai.svg +1 -0
- package/gui/dist/provider-icons/opencode.svg +2 -0
- package/gui/dist/provider-icons/openrouter-color.svg +1 -0
- package/gui/dist/provider-icons/pi.svg +21 -0
- package/gui/dist/provider-icons/qianfan-color.svg +1 -0
- package/gui/dist/provider-icons/qwen-portal-color.svg +1 -0
- package/gui/dist/provider-icons/telegram.svg +1 -0
- package/gui/dist/provider-icons/vercel-ai-gateway-color.svg +1 -0
- package/gui/dist/provider-icons/vllm-color.svg +1 -0
- package/gui/dist/provider-icons/xiaomi-color.svg +1 -0
- package/package.json +108 -0
- package/src/AGENTS.md +28 -0
- package/src/adapters/anthropic-image-guard.ts +251 -0
- package/src/adapters/anthropic-image-normalize.ts +518 -0
- package/src/adapters/anthropic-output-schema.ts +137 -0
- package/src/adapters/anthropic.ts +1327 -0
- package/src/adapters/azure.ts +36 -0
- package/src/adapters/base.ts +121 -0
- package/src/adapters/client-fingerprint.ts +65 -0
- package/src/adapters/cline-pass-deepseek-v4-tool-replay.ts +69 -0
- package/src/adapters/command-code.ts +601 -0
- package/src/adapters/cursor/arg-codec.ts +38 -0
- package/src/adapters/cursor/arg-normalize.ts +104 -0
- package/src/adapters/cursor/checkpoint-store.ts +303 -0
- package/src/adapters/cursor/cursor-errors.ts +288 -0
- package/src/adapters/cursor/discovery.ts +333 -0
- package/src/adapters/cursor/effort-map.ts +151 -0
- package/src/adapters/cursor/exec-policy.ts +88 -0
- package/src/adapters/cursor/framing.ts +250 -0
- package/src/adapters/cursor/gen/agent_pb.ts +15274 -0
- package/src/adapters/cursor/h2-pool.ts +123 -0
- package/src/adapters/cursor/http1-bidi.ts +361 -0
- package/src/adapters/cursor/images.ts +704 -0
- package/src/adapters/cursor/kv-store.ts +52 -0
- package/src/adapters/cursor/live-models.ts +269 -0
- package/src/adapters/cursor/live-smoke-gate.ts +41 -0
- package/src/adapters/cursor/live-transport.ts +1653 -0
- package/src/adapters/cursor/mcp-config.ts +42 -0
- package/src/adapters/cursor/mcp-manager.ts +333 -0
- package/src/adapters/cursor/message-mapper.ts +49 -0
- package/src/adapters/cursor/native-exec-common.ts +76 -0
- package/src/adapters/cursor/native-exec-desktop.ts +184 -0
- package/src/adapters/cursor/native-exec-fs.ts +332 -0
- package/src/adapters/cursor/native-exec-mcp.ts +153 -0
- package/src/adapters/cursor/native-exec-network.ts +43 -0
- package/src/adapters/cursor/native-exec-shell.ts +547 -0
- package/src/adapters/cursor/native-exec-tools.ts +118 -0
- package/src/adapters/cursor/native-exec.ts +663 -0
- package/src/adapters/cursor/protobuf-events.ts +1381 -0
- package/src/adapters/cursor/protobuf-request.ts +1032 -0
- package/src/adapters/cursor/request-builder.ts +461 -0
- package/src/adapters/cursor/thread-continuity.ts +67 -0
- package/src/adapters/cursor/tool-definitions.ts +735 -0
- package/src/adapters/cursor/tool-result-normalize.ts +92 -0
- package/src/adapters/cursor/transport-retry.ts +132 -0
- package/src/adapters/cursor/transport.ts +79 -0
- package/src/adapters/cursor/types.ts +79 -0
- package/src/adapters/cursor.ts +322 -0
- package/src/adapters/google-antigravity-hosts.ts +48 -0
- package/src/adapters/google-antigravity-replay.ts +827 -0
- package/src/adapters/google-antigravity-tools.ts +105 -0
- package/src/adapters/google-antigravity-wire.ts +141 -0
- package/src/adapters/google-errors.ts +92 -0
- package/src/adapters/google-http.ts +460 -0
- package/src/adapters/google-tool-schema.ts +238 -0
- package/src/adapters/google-truncation.ts +24 -0
- package/src/adapters/google-wire-compiler.ts +232 -0
- package/src/adapters/google.ts +1377 -0
- package/src/adapters/identity.ts +77 -0
- package/src/adapters/image.ts +23 -0
- package/src/adapters/kiro-constants.ts +16 -0
- package/src/adapters/kiro-errors.ts +208 -0
- package/src/adapters/kiro-events.ts +197 -0
- package/src/adapters/kiro-images.ts +129 -0
- package/src/adapters/kiro-retry.ts +312 -0
- package/src/adapters/kiro-thinking.ts +112 -0
- package/src/adapters/kiro-tool-fallback.ts +36 -0
- package/src/adapters/kiro-tools.ts +224 -0
- package/src/adapters/kiro-truncation.ts +33 -0
- package/src/adapters/kiro-wire.ts +129 -0
- package/src/adapters/kiro.ts +1936 -0
- package/src/adapters/mimo-free.ts +280 -0
- package/src/adapters/openai-chat-url.ts +11 -0
- package/src/adapters/openai-chat.ts +1980 -0
- package/src/adapters/openai-responses-url.ts +16 -0
- package/src/adapters/openai-responses.ts +1911 -0
- package/src/adapters/registry.ts +175 -0
- package/src/adapters/responses-tool-schema.ts +67 -0
- package/src/adapters/run-turn-queue.ts +114 -0
- package/src/adapters/tool-call-id.ts +119 -0
- package/src/adapters/tool-catalog-nudge.ts +154 -0
- package/src/adapters/upstream-http-error.ts +48 -0
- package/src/adapters/xai-web-search.ts +185 -0
- package/src/bridge.ts +1986 -0
- package/src/chat/inbound.ts +319 -0
- package/src/chat/outbound.ts +821 -0
- package/src/claude/agents-inject.ts +266 -0
- package/src/claude/alias.ts +149 -0
- package/src/claude/auth-detect.ts +229 -0
- package/src/claude/auth-mode-migration.ts +32 -0
- package/src/claude/auth-mode.ts +62 -0
- package/src/claude/context-windows.ts +205 -0
- package/src/claude/desktop-3p-guard.ts +35 -0
- package/src/claude/desktop-3p-paths.ts +84 -0
- package/src/claude/desktop-3p.ts +615 -0
- package/src/claude/desktop-health.ts +26 -0
- package/src/claude/desktop-profile.ts +263 -0
- package/src/claude/gateway-cache.ts +107 -0
- package/src/claude/inbound-debug.ts +163 -0
- package/src/claude/inbound.ts +578 -0
- package/src/claude/model-info.ts +174 -0
- package/src/claude/outbound.ts +926 -0
- package/src/cli/access.ts +108 -0
- package/src/cli/account-api.ts +302 -0
- package/src/cli/account-auth.ts +250 -0
- package/src/cli/account-catalog-refresh.ts +14 -0
- package/src/cli/account-extended.ts +737 -0
- package/src/cli/account-main.ts +317 -0
- package/src/cli/account.ts +299 -0
- package/src/cli/agent-driven.ts +70 -0
- package/src/cli/agent.ts +290 -0
- package/src/cli/catalog-prewarm.ts +27 -0
- package/src/cli/claude-agent-startup-sync.ts +73 -0
- package/src/cli/claude-desktop.ts +213 -0
- package/src/cli/claude.ts +355 -0
- package/src/cli/codex-log-guard-doctor.ts +103 -0
- package/src/cli/codex-shim-autorestore.ts +47 -0
- package/src/cli/codex-shim-readiness.ts +76 -0
- package/src/cli/combo.ts +127 -0
- package/src/cli/config-command.ts +209 -0
- package/src/cli/debug.ts +228 -0
- package/src/cli/dispatch.ts +585 -0
- package/src/cli/doctor.ts +1202 -0
- package/src/cli/ensure-desired-integrations.ts +152 -0
- package/src/cli/export-command.ts +213 -0
- package/src/cli/help.ts +101 -0
- package/src/cli/index.ts +973 -0
- package/src/cli/init.ts +211 -0
- package/src/cli/integrations.ts +260 -0
- package/src/cli/interactive-confirm.ts +133 -0
- package/src/cli/lab.ts +607 -0
- package/src/cli/launcher-context.ts +77 -0
- package/src/cli/minimax.ts +497 -0
- package/src/cli/models-runtime.ts +245 -0
- package/src/cli/models.ts +422 -0
- package/src/cli/observe.ts +206 -0
- package/src/cli/opencode.ts +588 -0
- package/src/cli/provider-replit.ts +232 -0
- package/src/cli/provider-runtime.ts +179 -0
- package/src/cli/provider.ts +492 -0
- package/src/cli/ready.ts +301 -0
- package/src/cli/registry.ts +422 -0
- package/src/cli/replit-gateway-key-input.ts +138 -0
- package/src/cli/root.ts +86 -0
- package/src/cli/route-policy.ts +92 -0
- package/src/cli/runtime-api.ts +328 -0
- package/src/cli/star-prompt.ts +211 -0
- package/src/cli/status-oauth.ts +78 -0
- package/src/cli/status.ts +328 -0
- package/src/cli/system-command.ts +112 -0
- package/src/cli/system-restart-client.ts +146 -0
- package/src/cli/tray-proxy.ts +199 -0
- package/src/cli/v2.ts +268 -0
- package/src/cli.ts +10 -0
- package/src/clients/config-export.ts +1704 -0
- package/src/codex/account-id.ts +34 -0
- package/src/codex/account-label.ts +47 -0
- package/src/codex/account-lifecycle.ts +172 -0
- package/src/codex/account-namespace-match.ts +63 -0
- package/src/codex/account-namespaces.ts +195 -0
- package/src/codex/account-pause.ts +20 -0
- package/src/codex/account-priority.ts +83 -0
- package/src/codex/account-runtime-state.ts +31 -0
- package/src/codex/account-store.ts +544 -0
- package/src/codex/account-usability.ts +43 -0
- package/src/codex/admission.ts +256 -0
- package/src/codex/affinity-debug.ts +162 -0
- package/src/codex/agent-roles-sync.ts +225 -0
- package/src/codex/agent-roles.ts +238 -0
- package/src/codex/app-server-processes.ts +1143 -0
- package/src/codex/app-server-restart-service.ts +232 -0
- package/src/codex/auth-api.ts +2147 -0
- package/src/codex/auth-collision.ts +109 -0
- package/src/codex/auth-context.ts +665 -0
- package/src/codex/autostart-health.ts +156 -0
- package/src/codex/catalog/account-models.ts +67 -0
- package/src/codex/catalog/aggregation.ts +436 -0
- package/src/codex/catalog/bundled.ts +549 -0
- package/src/codex/catalog/effort.ts +446 -0
- package/src/codex/catalog/filesystem-evidence.ts +302 -0
- package/src/codex/catalog/kinds.ts +2 -0
- package/src/codex/catalog/metadata.ts +664 -0
- package/src/codex/catalog/native-models.ts +72 -0
- package/src/codex/catalog/parsing.ts +650 -0
- package/src/codex/catalog/provider-fetch.ts +2064 -0
- package/src/codex/catalog/sync.ts +1883 -0
- package/src/codex/catalog-admission.ts +199 -0
- package/src/codex/catalog-refresh-status.ts +105 -0
- package/src/codex/catalog-write-serialization.ts +242 -0
- package/src/codex/catalog.ts +14 -0
- package/src/codex/codex-write-lock.ts +384 -0
- package/src/codex/convergence-types.ts +614 -0
- package/src/codex/convergence.ts +651 -0
- package/src/codex/coordinator-doctor.ts +332 -0
- package/src/codex/custom-model-catalog-migration.ts +176 -0
- package/src/codex/data/upstream-models.json +830 -0
- package/src/codex/desired-state.ts +230 -0
- package/src/codex/exec-invocation.ts +22 -0
- package/src/codex/features.ts +1566 -0
- package/src/codex/generation.ts +202 -0
- package/src/codex/history-job.ts +407 -0
- package/src/codex/history-lock.ts +242 -0
- package/src/codex/history-migration-guardian.ts +108 -0
- package/src/codex/history-provider.ts +979 -0
- package/src/codex/history-transition.ts +105 -0
- package/src/codex/history-worker.ts +220 -0
- package/src/codex/home.ts +206 -0
- package/src/codex/inject-coordination.ts +290 -0
- package/src/codex/inject.ts +1733 -0
- package/src/codex/injected-marker.ts +106 -0
- package/src/codex/integration-record.ts +266 -0
- package/src/codex/internal/catalog-writer.ts +203 -0
- package/src/codex/internal/history-writer.ts +80 -0
- package/src/codex/journal.ts +225 -0
- package/src/codex/log-guard/inspect.ts +506 -0
- package/src/codex/log-guard/lock.ts +150 -0
- package/src/codex/log-guard/maintenance.ts +403 -0
- package/src/codex/log-guard/path-safety.ts +88 -0
- package/src/codex/log-guard/policy.ts +44 -0
- package/src/codex/log-guard/processes.ts +205 -0
- package/src/codex/log-guard/protection.ts +489 -0
- package/src/codex/log-guard/sqlite-errors.ts +9 -0
- package/src/codex/main-account-cache.ts +56 -0
- package/src/codex/main-account.ts +68 -0
- package/src/codex/management-convergence.ts +167 -0
- package/src/codex/model-cache.ts +273 -0
- package/src/codex/model-entitlements.ts +353 -0
- package/src/codex/native-main-admission.ts +47 -0
- package/src/codex/native-main-auth-temp.ts +187 -0
- package/src/codex/native-main-claim.ts +178 -0
- package/src/codex/native-main-lock-file.ts +162 -0
- package/src/codex/native-main-owner.ts +329 -0
- package/src/codex/native-profile-api.ts +247 -0
- package/src/codex/native-profile-manager.ts +1531 -0
- package/src/codex/native-profile-processes.ts +121 -0
- package/src/codex/native-profile-recovery.ts +99 -0
- package/src/codex/native-profile-stage-store.ts +387 -0
- package/src/codex/native-profile-startup.ts +492 -0
- package/src/codex/native-profile-store.ts +855 -0
- package/src/codex/native-profile-types.ts +120 -0
- package/src/codex/native-residue.ts +682 -0
- package/src/codex/paths.ts +144 -0
- package/src/codex/plan-from-token.ts +140 -0
- package/src/codex/plan.ts +40 -0
- package/src/codex/plugins-doctor.ts +242 -0
- package/src/codex/pool-rotation.ts +295 -0
- package/src/codex/project-config-warnings.ts +425 -0
- package/src/codex/prompt-journal.ts +352 -0
- package/src/codex/prompt-layers.ts +967 -0
- package/src/codex/prompt-lock.ts +143 -0
- package/src/codex/quota-rejection.ts +298 -0
- package/src/codex/quota.ts +573 -0
- package/src/codex/refresh.ts +62 -0
- package/src/codex/reset-credit-recovery.ts +1044 -0
- package/src/codex/routing.ts +1888 -0
- package/src/codex/runtime.ts +659 -0
- package/src/codex/shim.ts +2170 -0
- package/src/codex/subagent-defaults.ts +550 -0
- package/src/codex/subagent-model-fallback.ts +784 -0
- package/src/codex/sync.ts +319 -0
- package/src/codex/transition-state.ts +612 -0
- package/src/codex/upstream-host-health.ts +368 -0
- package/src/codex/user-identity.ts +557 -0
- package/src/codex/warmup.ts +298 -0
- package/src/codex/websocket-registry.ts +100 -0
- package/src/codex/write-coordination.ts +114 -0
- package/src/combos/failover.ts +160 -0
- package/src/combos/index.ts +45 -0
- package/src/combos/request.ts +94 -0
- package/src/combos/resolve.ts +232 -0
- package/src/combos/types.ts +398 -0
- package/src/config/provider-name.ts +24 -0
- package/src/config.ts +4041 -0
- package/src/fork/register.ts +3 -0
- package/src/generated/compatibility-version.json +3116 -0
- package/src/generated/model-metadata.ts +106 -0
- package/src/github/star-state.ts +203 -0
- package/src/grok/inject.ts +530 -0
- package/src/grok/inspect.ts +45 -0
- package/src/grok/status.ts +121 -0
- package/src/grok/sync.ts +66 -0
- package/src/images/artifacts.ts +516 -0
- package/src/images/fulfill-video.ts +163 -0
- package/src/images/fulfill.ts +149 -0
- package/src/images/index.ts +4 -0
- package/src/images/loop.ts +955 -0
- package/src/images/plan.ts +143 -0
- package/src/images/synthetic-tool.ts +133 -0
- package/src/images/types.ts +41 -0
- package/src/images/xai-client.ts +141 -0
- package/src/images/xai-video-client.ts +163 -0
- package/src/index.ts +22 -0
- package/src/integrations/config-io.ts +269 -0
- package/src/integrations/journal.ts +315 -0
- package/src/integrations/merge.ts +135 -0
- package/src/integrations/mutation-flight.ts +71 -0
- package/src/integrations/native/ownership-preflight.ts +202 -0
- package/src/integrations/omp-yaml-source.ts +358 -0
- package/src/integrations/owned-refresh.ts +74 -0
- package/src/integrations/ownership.ts +111 -0
- package/src/integrations/registry.ts +159 -0
- package/src/integrations/serialize.ts +314 -0
- package/src/integrations/state.ts +361 -0
- package/src/integrations/store.ts +103 -0
- package/src/integrations/writer-lock.ts +98 -0
- package/src/integrations/writer.ts +691 -0
- package/src/lab/artifacts/sanitize.ts +586 -0
- package/src/lab/artifacts/secure-fs.ts +475 -0
- package/src/lab/artifacts/store.ts +310 -0
- package/src/lab/automation/budgets.ts +78 -0
- package/src/lab/automation/config-persistence.ts +256 -0
- package/src/lab/automation/constants.ts +39 -0
- package/src/lab/automation/cooldown.ts +103 -0
- package/src/lab/automation/dispatch.ts +211 -0
- package/src/lab/automation/index.ts +13 -0
- package/src/lab/automation/orchestrator.ts +499 -0
- package/src/lab/automation/persistence.ts +512 -0
- package/src/lab/automation/planner.ts +371 -0
- package/src/lab/automation/policy.ts +136 -0
- package/src/lab/automation/queue.ts +191 -0
- package/src/lab/automation/recovery.ts +24 -0
- package/src/lab/automation/route-context.ts +21 -0
- package/src/lab/automation/run-key.ts +44 -0
- package/src/lab/automation/runs-query.ts +34 -0
- package/src/lab/automation/types.ts +160 -0
- package/src/lab/conformance/assertion.ts +325 -0
- package/src/lab/conformance/digest.ts +22 -0
- package/src/lab/conformance/executor.ts +741 -0
- package/src/lab/conformance/fixture-provider.ts +27 -0
- package/src/lab/conformance/fixtures/live-v1-cases.json +175 -0
- package/src/lab/conformance/fixtures/protocol-v1-cases.json +461 -0
- package/src/lab/conformance/harness-budget.ts +47 -0
- package/src/lab/conformance/index.ts +5 -0
- package/src/lab/conformance/jcs.ts +64 -0
- package/src/lab/conformance/json-pointer.ts +39 -0
- package/src/lab/conformance/manifest.ts +180 -0
- package/src/lab/conformance/mcp-stub.ts +179 -0
- package/src/lab/conformance/negative-controls.ts +164 -0
- package/src/lab/conformance/observation.ts +355 -0
- package/src/lab/conformance/runner.ts +68 -0
- package/src/lab/conformance/sse-normalize.ts +59 -0
- package/src/lab/conformance/suite-manifest.ts +78 -0
- package/src/lab/conformance/types.ts +214 -0
- package/src/lab/constants.ts +126 -0
- package/src/lab/digest.ts +64 -0
- package/src/lab/events/errors.ts +9 -0
- package/src/lab/events/limits.ts +117 -0
- package/src/lab/events/types.ts +229 -0
- package/src/lab/events/validate.ts +781 -0
- package/src/lab/fabric/constants.ts +40 -0
- package/src/lab/fabric/executor.ts +492 -0
- package/src/lab/fabric/index.ts +80 -0
- package/src/lab/fabric/manifest.ts +222 -0
- package/src/lab/fabric/observe.ts +489 -0
- package/src/lab/fabric/patch.ts +79 -0
- package/src/lab/fabric/producer-child.ts +139 -0
- package/src/lab/fabric/producer-isolate.ts +276 -0
- package/src/lab/fabric/producer-protocol.ts +61 -0
- package/src/lab/fabric/scratch.ts +439 -0
- package/src/lab/fabric/subject.ts +106 -0
- package/src/lab/fabric/types.ts +134 -0
- package/src/lab/fabric/verifier.ts +98 -0
- package/src/lab/index.ts +54 -0
- package/src/lab/ledger/artifact-refs.ts +127 -0
- package/src/lab/ledger/invalidation.ts +136 -0
- package/src/lab/ledger/purge.ts +310 -0
- package/src/lab/ledger/store.ts +532 -0
- package/src/lab/live/credential-lease.ts +53 -0
- package/src/lab/live/destination.ts +155 -0
- package/src/lab/live/executor.ts +336 -0
- package/src/lab/live/inert-tools.ts +56 -0
- package/src/lab/live/manifest.ts +85 -0
- package/src/lab/live/mcp-loopback.ts +57 -0
- package/src/lab/live/runner.ts +19 -0
- package/src/lab/live/sandbox.ts +61 -0
- package/src/lab/live/suite-manifest.ts +41 -0
- package/src/lab/live/transport.ts +118 -0
- package/src/lab/live/types.ts +197 -0
- package/src/lab/observe/from-conformance.ts +301 -0
- package/src/lab/observe/from-live.ts +117 -0
- package/src/lab/paths.ts +153 -0
- package/src/lab/projection/rebuild.ts +495 -0
- package/src/lab/projection/schema.ts +135 -0
- package/src/lab/projection/verdicts.ts +474 -0
- package/src/lab/projection/verification.ts +412 -0
- package/src/lab/public/bundle.ts +217 -0
- package/src/lab/public/community-authority.ts +175 -0
- package/src/lab/public/community-files.ts +29 -0
- package/src/lab/public/community.ts +479 -0
- package/src/lab/public/file-safety.ts +155 -0
- package/src/lab/public/ids.ts +26 -0
- package/src/lab/public/index.ts +16 -0
- package/src/lab/public/mutation-lock.ts +424 -0
- package/src/lab/public/operator.ts +353 -0
- package/src/lab/public/origin-purge.ts +79 -0
- package/src/lab/public/origin.ts +203 -0
- package/src/lab/public/privacy.ts +143 -0
- package/src/lab/public/private-file.ts +261 -0
- package/src/lab/public/project.ts +124 -0
- package/src/lab/public/purge-test-fault.ts +21 -0
- package/src/lab/public/purge.ts +223 -0
- package/src/lab/public/registry.ts +44 -0
- package/src/lab/public/revocation.ts +252 -0
- package/src/lab/public/signature.ts +243 -0
- package/src/lab/public/storage.ts +105 -0
- package/src/lab/public/strict-json.ts +206 -0
- package/src/lab/public/time.ts +26 -0
- package/src/lab/public/types.ts +172 -0
- package/src/lab/public/validate.ts +391 -0
- package/src/lab/query/catalog.ts +101 -0
- package/src/lab/query/connection.ts +107 -0
- package/src/lab/query/constants.ts +4 -0
- package/src/lab/query/cursor.ts +132 -0
- package/src/lab/query/dto-map.ts +277 -0
- package/src/lab/query/errors.ts +22 -0
- package/src/lab/query/freshness.ts +53 -0
- package/src/lab/query/index.ts +45 -0
- package/src/lab/query/latest-observation.ts +59 -0
- package/src/lab/query/passive-production.ts +159 -0
- package/src/lab/query/queries.ts +444 -0
- package/src/lab/query/types.ts +266 -0
- package/src/lab/subject/behavior-fingerprint.ts +77 -0
- package/src/lab/subject/installation-salt.ts +112 -0
- package/src/lab/subject/protocol-subject.ts +80 -0
- package/src/lab/subject/route-subject.ts +74 -0
- package/src/lib/abort.ts +146 -0
- package/src/lib/admin-secrets.ts +25 -0
- package/src/lib/admission.ts +83 -0
- package/src/lib/app-owned-memory-stores.ts +195 -0
- package/src/lib/app-owned-memory.ts +265 -0
- package/src/lib/bounded-body.ts +346 -0
- package/src/lib/bun-binary-validator.d.mts +3 -0
- package/src/lib/bun-binary-validator.mjs +18 -0
- package/src/lib/bun-runtime.ts +184 -0
- package/src/lib/bun-stream-caps.ts +130 -0
- package/src/lib/codex-restart-contract.ts +120 -0
- package/src/lib/config-ownership.ts +364 -0
- package/src/lib/crash-guard.ts +344 -0
- package/src/lib/debug-log-buffer.ts +83 -0
- package/src/lib/debug-settings.ts +108 -0
- package/src/lib/debug.ts +31 -0
- package/src/lib/destination-policy.ts +380 -0
- package/src/lib/errors.ts +406 -0
- package/src/lib/eventstream-decoder.ts +253 -0
- package/src/lib/fabric-task-execution-authority.ts +7 -0
- package/src/lib/fabric-task-host.ts +29 -0
- package/src/lib/gcp-adc.ts +341 -0
- package/src/lib/injection-debug-log.ts +58 -0
- package/src/lib/lab-activation.ts +223 -0
- package/src/lib/lab-live-execution-authority.ts +13 -0
- package/src/lib/lab-live-host.ts +30 -0
- package/src/lib/lab-live-pinned-sender.ts +56 -0
- package/src/lib/lab-live-route-production.ts +130 -0
- package/src/lib/lab-passive-linker-registration.ts +26 -0
- package/src/lib/local-management-attestation.ts +51 -0
- package/src/lib/local-management-capability.ts +100 -0
- package/src/lib/local-provider-reload-contract.ts +100 -0
- package/src/lib/open-url.ts +25 -0
- package/src/lib/optional-shutdown-hooks.ts +57 -0
- package/src/lib/pinned-http.ts +270 -0
- package/src/lib/privacy.ts +20 -0
- package/src/lib/process-control.ts +168 -0
- package/src/lib/provider-outbound.ts +210 -0
- package/src/lib/provider-url.ts +14 -0
- package/src/lib/proxy-env.ts +18 -0
- package/src/lib/redact.ts +521 -0
- package/src/lib/retry-after.ts +55 -0
- package/src/lib/self-launch-argv.ts +15 -0
- package/src/lib/server-resource-ownership.ts +71 -0
- package/src/lib/service-secrets.ts +25 -0
- package/src/lib/shadow-call.ts +61 -0
- package/src/lib/sidecar-tracker.ts +52 -0
- package/src/lib/sse-decoder.ts +364 -0
- package/src/lib/state-store-registrations.ts +119 -0
- package/src/lib/state-store-sweeper.ts +184 -0
- package/src/lib/system-restart-contract.ts +73 -0
- package/src/lib/test-home-guard.ts +90 -0
- package/src/lib/token-estimate.ts +86 -0
- package/src/lib/tool-argument-integers.ts +202 -0
- package/src/lib/translator-budget.ts +400 -0
- package/src/lib/upstream-http-version.ts +57 -0
- package/src/lib/upstream-reachability.ts +95 -0
- package/src/lib/upstream-retry.ts +392 -0
- package/src/lib/win-exec.ts +115 -0
- package/src/lib/win-paths.ts +68 -0
- package/src/lib/windows-atomic-replace.ts +156 -0
- package/src/lib/windows-elevation.ts +773 -0
- package/src/lib/windows-secret-acl.ts +854 -0
- package/src/lib/windows-service-wrappers.ts +72 -0
- package/src/lib/windows-text.ts +106 -0
- package/src/lib/windows-user-principal.ts +341 -0
- package/src/lib/winsw.ts +403 -0
- package/src/oauth/account-import/google-antigravity-adapter.ts +74 -0
- package/src/oauth/account-import/index.ts +15 -0
- package/src/oauth/account-import/parser.ts +83 -0
- package/src/oauth/account-import/registry.ts +18 -0
- package/src/oauth/account-import/service.ts +75 -0
- package/src/oauth/account-import/types.ts +91 -0
- package/src/oauth/anthropic-routing.ts +594 -0
- package/src/oauth/anthropic.ts +188 -0
- package/src/oauth/antigravity-routing.ts +151 -0
- package/src/oauth/callback-server.ts +300 -0
- package/src/oauth/chatgpt.ts +161 -0
- package/src/oauth/command-code.ts +239 -0
- package/src/oauth/cursor.ts +252 -0
- package/src/oauth/github-copilot.ts +428 -0
- package/src/oauth/google-antigravity.ts +262 -0
- package/src/oauth/health.ts +407 -0
- package/src/oauth/index.ts +1504 -0
- package/src/oauth/key-providers.ts +124 -0
- package/src/oauth/kimi.ts +227 -0
- package/src/oauth/kiro-credentials.ts +726 -0
- package/src/oauth/kiro.ts +621 -0
- package/src/oauth/local-token-detect.ts +130 -0
- package/src/oauth/log.ts +50 -0
- package/src/oauth/login-cli.ts +223 -0
- package/src/oauth/nous.ts +798 -0
- package/src/oauth/pkce.ts +15 -0
- package/src/oauth/store.ts +728 -0
- package/src/oauth/token-guardian.ts +309 -0
- package/src/oauth/types.ts +62 -0
- package/src/oauth/xai.ts +241 -0
- package/src/providers/alibaba-region-backup.ts +75 -0
- package/src/providers/alibaba-region-migration.ts +156 -0
- package/src/providers/alibaba-region-startup.ts +36 -0
- package/src/providers/antigravity-models.ts +695 -0
- package/src/providers/antigravity-quota.ts +216 -0
- package/src/providers/api-keys.ts +140 -0
- package/src/providers/base-url-choices.ts +74 -0
- package/src/providers/codex-capacity.ts +292 -0
- package/src/providers/command-code-efforts.ts +144 -0
- package/src/providers/context-cap.ts +82 -0
- package/src/providers/cursor-pool.ts +72 -0
- package/src/providers/derive.ts +586 -0
- package/src/providers/fastwire.ts +501 -0
- package/src/providers/free-directory.ts +187 -0
- package/src/providers/github-copilot-transport.ts +56 -0
- package/src/providers/google-vertex-location.ts +14 -0
- package/src/providers/key-failover.ts +271 -0
- package/src/providers/kiro-models.ts +67 -0
- package/src/providers/label.ts +19 -0
- package/src/providers/model-discovery-limits.ts +16 -0
- package/src/providers/model-discovery.ts +449 -0
- package/src/providers/model-rename-migration.ts +255 -0
- package/src/providers/model-rename-startup.ts +28 -0
- package/src/providers/openai-sidecar.ts +243 -0
- package/src/providers/openai-tier-startup.ts +56 -0
- package/src/providers/openai-tiers.ts +423 -0
- package/src/providers/openai-virtual-models.ts +83 -0
- package/src/providers/opencode-zen-rate-limit.ts +102 -0
- package/src/providers/openrouter-routing.ts +102 -0
- package/src/providers/provider-id-rewrite.ts +185 -0
- package/src/providers/quota.ts +2345 -0
- package/src/providers/registry.ts +2918 -0
- package/src/providers/replit/constants.ts +27 -0
- package/src/providers/replit/derive.ts +85 -0
- package/src/providers/replit/headers.ts +28 -0
- package/src/providers/replit/origin.ts +55 -0
- package/src/providers/replit/pair-install-response.ts +72 -0
- package/src/providers/replit/probe.ts +199 -0
- package/src/providers/replit/setup.ts +350 -0
- package/src/providers/request-pacing.ts +310 -0
- package/src/providers/service-tier.ts +277 -0
- package/src/providers/slug-codec.ts +103 -0
- package/src/providers/static-model-discovery.ts +86 -0
- package/src/providers/xai-responses-opt-in.ts +15 -0
- package/src/providers/xai-transport.ts +148 -0
- package/src/reasoning-effort.ts +183 -0
- package/src/responses/compaction.ts +142 -0
- package/src/responses/custom-tool-compat.ts +266 -0
- package/src/responses/hosted-tool-policy.ts +9 -0
- package/src/responses/namespace-tool-compat.ts +355 -0
- package/src/responses/parser.ts +838 -0
- package/src/responses/provider-continuation.ts +98 -0
- package/src/responses/provider-opaque-metadata.ts +73 -0
- package/src/responses/reasoning-envelope.ts +60 -0
- package/src/responses/reasoning-replay-cache.ts +426 -0
- package/src/responses/schema.ts +165 -0
- package/src/responses/spill-store.ts +459 -0
- package/src/responses/state.ts +1433 -0
- package/src/responses/thought-signature-replay.ts +347 -0
- package/src/responses/tool-groups.ts +19 -0
- package/src/responses/tool-search-compat.ts +301 -0
- package/src/responses/truncated-stop-reason.ts +60 -0
- package/src/router.ts +761 -0
- package/src/routing/analytics.ts +378 -0
- package/src/routing/capability.ts +244 -0
- package/src/routing/compatibility/assemble.ts +73 -0
- package/src/routing/compatibility/behavior.ts +278 -0
- package/src/routing/compatibility/catalog.ts +99 -0
- package/src/routing/compatibility/endpoint.ts +52 -0
- package/src/routing/compatibility/lab-evidence-provider.ts +130 -0
- package/src/routing/compatibility/policy.ts +181 -0
- package/src/routing/compatibility/provider-slot.ts +56 -0
- package/src/routing/compatibility/reader.ts +110 -0
- package/src/routing/compatibility/subject.ts +191 -0
- package/src/routing/compatibility/types.ts +64 -0
- package/src/routing/compatibility/version.ts +104 -0
- package/src/routing/cost.ts +77 -0
- package/src/routing/evaluator.ts +495 -0
- package/src/routing/health.ts +412 -0
- package/src/routing/history/cursor.ts +43 -0
- package/src/routing/history/indexer.ts +605 -0
- package/src/routing/history/schema.ts +72 -0
- package/src/routing/profile-namespace.ts +15 -0
- package/src/routing/profile.ts +547 -0
- package/src/routing/quota.ts +145 -0
- package/src/routing/request-evidence.ts +45 -0
- package/src/routing/trace.ts +776 -0
- package/src/server/adapter-resolve.ts +53 -0
- package/src/server/auth-cors.ts +751 -0
- package/src/server/background-lifecycle.ts +182 -0
- package/src/server/chat-completions.ts +442 -0
- package/src/server/chat-native-sse.ts +331 -0
- package/src/server/chat-native.ts +426 -0
- package/src/server/claude-messages.ts +1030 -0
- package/src/server/direct-local-http.ts +347 -0
- package/src/server/effort-policy.ts +190 -0
- package/src/server/github-copilot-responses-repair.ts +338 -0
- package/src/server/gui-static.ts +152 -0
- package/src/server/image-retry.ts +42 -0
- package/src/server/images.ts +568 -0
- package/src/server/index.ts +1813 -0
- package/src/server/lifecycle.ts +498 -0
- package/src/server/live.ts +717 -0
- package/src/server/local-management-read-client.ts +90 -0
- package/src/server/local-provider-reload-client.ts +137 -0
- package/src/server/management/agent-settings-routes.ts +1433 -0
- package/src/server/management/api-access.ts +141 -0
- package/src/server/management/api-key-usage.ts +193 -0
- package/src/server/management/body.ts +41 -0
- package/src/server/management/combo-routes.ts +263 -0
- package/src/server/management/config-routes.ts +835 -0
- package/src/server/management/context.ts +113 -0
- package/src/server/management/integration-routes.ts +498 -0
- package/src/server/management/lab-automation-routes.ts +206 -0
- package/src/server/management/lab-routes.ts +563 -0
- package/src/server/management/logs-usage-routes.ts +586 -0
- package/src/server/management/model-routes.ts +560 -0
- package/src/server/management/model-rows.ts +163 -0
- package/src/server/management/native-integration-routes.ts +769 -0
- package/src/server/management/oauth-account-routes.ts +637 -0
- package/src/server/management/provider-capability-config.ts +48 -0
- package/src/server/management/provider-routes.ts +1033 -0
- package/src/server/management/replit-provider-routes.ts +86 -0
- package/src/server/management/request-history-routes.ts +191 -0
- package/src/server/management/routing-analytics-routes.ts +74 -0
- package/src/server/management/routing-profile-routes.ts +385 -0
- package/src/server/management/shared.ts +286 -0
- package/src/server/management/sidebar-routes.ts +106 -0
- package/src/server/management/storage-log-guard-routes.ts +186 -0
- package/src/server/management/sync-response.ts +69 -0
- package/src/server/management/system-restart.ts +435 -0
- package/src/server/management/system-routes.ts +194 -0
- package/src/server/management/usage-summary-cache.ts +94 -0
- package/src/server/management/vision-sidecar-options.ts +167 -0
- package/src/server/management/web-search-sidecar-options.ts +120 -0
- package/src/server/management-api.ts +314 -0
- package/src/server/management-auth.ts +482 -0
- package/src/server/memory-watchdog.ts +156 -0
- package/src/server/passive-route-linker.ts +66 -0
- package/src/server/port-reclaim.ts +307 -0
- package/src/server/ports.ts +156 -0
- package/src/server/proxy-liveness.ts +328 -0
- package/src/server/readiness.ts +99 -0
- package/src/server/relay-eager.ts +353 -0
- package/src/server/relay.ts +1209 -0
- package/src/server/request-decompress.ts +239 -0
- package/src/server/request-log-conversation.ts +168 -0
- package/src/server/request-log.ts +1259 -0
- package/src/server/responses/agent-task-recovery-cache.ts +143 -0
- package/src/server/responses/agent-task-recovery.ts +465 -0
- package/src/server/responses/collaboration.ts +551 -0
- package/src/server/responses/compact.ts +771 -0
- package/src/server/responses/core.ts +5389 -0
- package/src/server/responses/empty-completion-guard.ts +276 -0
- package/src/server/responses/encrypted-payload.ts +331 -0
- package/src/server/responses/fetch-helpers.ts +232 -0
- package/src/server/responses/input-admission.ts +185 -0
- package/src/server/responses/pacing-overload.ts +13 -0
- package/src/server/responses/passthrough-error.ts +78 -0
- package/src/server/responses/policy-fallback.ts +178 -0
- package/src/server/responses/responses-field-backfill.ts +251 -0
- package/src/server/responses/terminal-guard.ts +251 -0
- package/src/server/responses/upstream-error.ts +53 -0
- package/src/server/responses/ws-upstream.ts +308 -0
- package/src/server/responses-custom-tool-repair.ts +282 -0
- package/src/server/responses-image-gen-repair.ts +132 -0
- package/src/server/responses-item-id-repair.ts +272 -0
- package/src/server/responses-json-events.ts +90 -0
- package/src/server/responses-model-rewrite.ts +29 -0
- package/src/server/responses-reasoning-summary-rewrite.ts +178 -0
- package/src/server/responses-snapshot-repair.ts +621 -0
- package/src/server/responses-terminal-repair.ts +342 -0
- package/src/server/responses-tool-search-repair.ts +267 -0
- package/src/server/responses-undeclared-tool-guard.ts +153 -0
- package/src/server/responses.ts +25 -0
- package/src/server/search.ts +201 -0
- package/src/server/sse-frame-buffer.ts +292 -0
- package/src/server/sse-payload-rewrite.ts +263 -0
- package/src/server/startup-action-control.ts +315 -0
- package/src/server/startup-health-cache.ts +131 -0
- package/src/server/system-env.ts +484 -0
- package/src/server/windows-tcp-drop.ts +184 -0
- package/src/server/windows-tray-control.ts +41 -0
- package/src/server/ws-bridge.ts +472 -0
- package/src/service-manager-probe.ts +892 -0
- package/src/service.ts +3575 -0
- package/src/sidecar/auth.ts +92 -0
- package/src/sidecar/candidates.ts +83 -0
- package/src/stall-timeout.ts +20 -0
- package/src/storage/cleanup-job.ts +57 -0
- package/src/storage/cleanup.ts +3085 -0
- package/src/storage/policy-job.ts +457 -0
- package/src/storage/policy-scheduler.ts +40 -0
- package/src/storage/policy-worker.ts +59 -0
- package/src/storage/policy.ts +527 -0
- package/src/storage/restore-job.ts +299 -0
- package/src/storage/restore-worker.ts +58 -0
- package/src/storage/scanner.ts +238 -0
- package/src/storage/storage-mutation-coordinator.ts +139 -0
- package/src/storage/worker-lifecycle.ts +215 -0
- package/src/tray/assets/opencodex-tray-offline.ico +0 -0
- package/src/tray/assets/opencodex-tray-online.ico +0 -0
- package/src/tray/assets/opencodex-tray-warning.ico +0 -0
- package/src/tray/assets/opencodex-tray.png +0 -0
- package/src/tray/windows-tray.ps1 +364 -0
- package/src/tray/windows.ts +757 -0
- package/src/types/accounts.ts +37 -0
- package/src/types/config.ts +876 -0
- package/src/types/provider.ts +545 -0
- package/src/types/request.ts +384 -0
- package/src/types/tools.ts +131 -0
- package/src/types/wire.ts +80 -0
- package/src/types.ts +106 -0
- package/src/update/badge.ts +72 -0
- package/src/update/index.ts +415 -0
- package/src/update/job.ts +1887 -0
- package/src/update/notify.ts +263 -0
- package/src/update/npm-cache-preflight.d.mts +47 -0
- package/src/update/npm-cache-preflight.mjs +201 -0
- package/src/update/npm-invocation.d.mts +23 -0
- package/src/update/npm-invocation.mjs +94 -0
- package/src/update/transactional-install.d.mts +22 -0
- package/src/update/transactional-install.mjs +259 -0
- package/src/update/tray-update-plan.d.mts +18 -0
- package/src/update/tray-update-plan.mjs +38 -0
- package/src/usage/cost.ts +625 -0
- package/src/usage/debug.ts +97 -0
- package/src/usage/expected-prices.ts +416 -0
- package/src/usage/log.ts +1223 -0
- package/src/usage/summary.ts +753 -0
- package/src/usage/totals.ts +14 -0
- package/src/usage/user-cost-overlay-reconciler.ts +313 -0
- package/src/usage/user-cost-overlays.ts +314 -0
- package/src/vision/anthropic-describe.ts +189 -0
- package/src/vision/backends.ts +97 -0
- package/src/vision/describe.ts +131 -0
- package/src/vision/eligibility.ts +250 -0
- package/src/vision/index.ts +681 -0
- package/src/vision/reasoning.ts +55 -0
- package/src/vision/routed-describe.ts +175 -0
- package/src/vision/timeout-bounds.ts +9 -0
- package/src/web-search/anthropic-executor.ts +195 -0
- package/src/web-search/backends.ts +108 -0
- package/src/web-search/exa-executor.ts +88 -0
- package/src/web-search/executor.ts +113 -0
- package/src/web-search/format-result.ts +89 -0
- package/src/web-search/gemini-executor.ts +141 -0
- package/src/web-search/index.ts +331 -0
- package/src/web-search/loop.ts +896 -0
- package/src/web-search/parse.ts +315 -0
- package/src/web-search/progress-stream.ts +342 -0
- package/src/web-search/sources.ts +60 -0
- package/src/web-search/synthetic-tool.ts +47 -0
- package/src/web-search/xai-executor.ts +219 -0
|
@@ -0,0 +1,428 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GitHub Copilot OAuth (device authorization grant + copilot_internal token exchange).
|
|
3
|
+
*
|
|
4
|
+
* Uses the public VS Code GitHub OAuth app client id (community-proven for Copilot Pro).
|
|
5
|
+
* This is an unofficial bridge — GitHub may tighten or revoke access. See registry note.
|
|
6
|
+
*/
|
|
7
|
+
import type { OAuthController, OAuthCredentials } from "./types";
|
|
8
|
+
|
|
9
|
+
/** VS Code's public GitHub OAuth app — required for copilot_internal/v2/token to succeed. */
|
|
10
|
+
export const GITHUB_COPILOT_OAUTH_CLIENT_ID = "Iv1.b507a08c87ecfe98";
|
|
11
|
+
export const GITHUB_COPILOT_DEFAULT_API_BASE = "https://api.githubcopilot.com";
|
|
12
|
+
export const GITHUB_DEVICE_VERIFY_ORIGIN = "https://github.com";
|
|
13
|
+
export const GITHUB_DEVICE_VERIFY_PATH = "/login/device";
|
|
14
|
+
|
|
15
|
+
const DEVICE_CODE_URL = "https://github.com/login/device/code";
|
|
16
|
+
const ACCESS_TOKEN_URL = "https://github.com/login/oauth/access_token";
|
|
17
|
+
const COPILOT_TOKEN_URL = "https://api.github.com/copilot_internal/v2/token";
|
|
18
|
+
const GITHUB_USER_URL = "https://api.github.com/user";
|
|
19
|
+
|
|
20
|
+
const OAUTH_SCOPE = "read:user";
|
|
21
|
+
const DEFAULT_POLL_INTERVAL_MS = 5000;
|
|
22
|
+
const DEFAULT_DEVICE_FLOW_TTL_MS = 15 * 60 * 1000;
|
|
23
|
+
const OAUTH_EXPIRY_SKEW_MS = 2 * 60 * 1000;
|
|
24
|
+
const MIN_POLL_MS = 1000;
|
|
25
|
+
/** OAuth error codes that make a refresh terminally dead (safe to surface verbatim). */
|
|
26
|
+
const TERMINAL_OAUTH_ERROR_CODES = new Set(["invalid_grant", "access_denied", "expired_token"]);
|
|
27
|
+
const IDENTITY_RETRY_DELAY_MS = 500;
|
|
28
|
+
|
|
29
|
+
/** Honest OpenCodex client fingerprint; VS Code-shaped values only if API requires them later. */
|
|
30
|
+
export const GITHUB_COPILOT_EDITOR_HEADERS: Readonly<Record<string, string>> = {
|
|
31
|
+
"Editor-Version": "opencodex/0.1.0",
|
|
32
|
+
"Editor-Plugin-Version": "opencodex/0.1.0",
|
|
33
|
+
"Copilot-Integration-Id": "vscode-chat",
|
|
34
|
+
"User-Agent": "opencodex",
|
|
35
|
+
Accept: "application/json",
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
interface DeviceAuthorizationResponse {
|
|
39
|
+
user_code?: string;
|
|
40
|
+
device_code?: string;
|
|
41
|
+
verification_uri?: string;
|
|
42
|
+
verification_uri_complete?: string;
|
|
43
|
+
expires_in?: number;
|
|
44
|
+
interval?: number;
|
|
45
|
+
error?: string;
|
|
46
|
+
error_description?: string;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
interface GithubTokenResponse {
|
|
50
|
+
access_token?: string;
|
|
51
|
+
refresh_token?: string;
|
|
52
|
+
expires_in?: number;
|
|
53
|
+
refresh_token_expires_in?: number;
|
|
54
|
+
error?: string;
|
|
55
|
+
error_description?: string;
|
|
56
|
+
interval?: number;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
interface CopilotTokenResponse {
|
|
60
|
+
token?: string;
|
|
61
|
+
expires_at?: number;
|
|
62
|
+
refresh_in?: number;
|
|
63
|
+
endpoints?: { api?: string };
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
interface GithubUserResponse {
|
|
67
|
+
login?: string;
|
|
68
|
+
id?: number;
|
|
69
|
+
email?: string;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function sleep(ms: number, signal?: AbortSignal): Promise<void> {
|
|
73
|
+
return new Promise((resolve, reject) => {
|
|
74
|
+
if (signal?.aborted) return reject(new Error("Login cancelled"));
|
|
75
|
+
const t = setTimeout(resolve, ms);
|
|
76
|
+
signal?.addEventListener("abort", () => {
|
|
77
|
+
clearTimeout(t);
|
|
78
|
+
reject(new Error("Login cancelled"));
|
|
79
|
+
}, { once: true });
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** Status-only errors — never echo response bodies (may contain tokens). */
|
|
84
|
+
export function githubCopilotHttpError(action: string, status: number): Error {
|
|
85
|
+
return new Error(`GitHub Copilot ${action} failed (${status})`);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export function buildGithubDeviceVerifyUrl(userCode: string): string {
|
|
89
|
+
const code = userCode.trim();
|
|
90
|
+
if (!code || !/^[A-Z0-9-]+$/i.test(code)) {
|
|
91
|
+
throw new Error("GitHub Copilot device flow returned an invalid user code");
|
|
92
|
+
}
|
|
93
|
+
return `${GITHUB_DEVICE_VERIFY_ORIGIN}${GITHUB_DEVICE_VERIFY_PATH}?user_code=${encodeURIComponent(code)}`;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Allowlist for browser-open URLs. Prefer {@link buildGithubDeviceVerifyUrl}; this rejects
|
|
98
|
+
* phishing redirects if a caller still passes a server-supplied verification URI.
|
|
99
|
+
*/
|
|
100
|
+
export function isAllowedGithubDeviceVerifyUrl(url: string): boolean {
|
|
101
|
+
try {
|
|
102
|
+
const parsed = new URL(url);
|
|
103
|
+
if (parsed.protocol !== "https:") return false;
|
|
104
|
+
if (parsed.username || parsed.password) return false;
|
|
105
|
+
if (parsed.hostname.toLowerCase() !== "github.com") return false;
|
|
106
|
+
if (parsed.port && parsed.port !== "443") return false;
|
|
107
|
+
const path = parsed.pathname.replace(/\/+$/, "") || "/";
|
|
108
|
+
return path === GITHUB_DEVICE_VERIFY_PATH;
|
|
109
|
+
} catch {
|
|
110
|
+
return false;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Tight allowlist for Copilot API hosts from token `endpoints.api`.
|
|
116
|
+
* Rejects IPs, localhost, non-HTTPS, userinfo, and non-default ports.
|
|
117
|
+
*/
|
|
118
|
+
export function validateCopilotApiBaseUrl(raw: string | undefined | null): string | undefined {
|
|
119
|
+
if (raw === undefined || raw === null) return undefined;
|
|
120
|
+
const trimmed = String(raw).trim();
|
|
121
|
+
if (!trimmed) return undefined;
|
|
122
|
+
let parsed: URL;
|
|
123
|
+
try {
|
|
124
|
+
parsed = new URL(trimmed);
|
|
125
|
+
} catch {
|
|
126
|
+
return undefined;
|
|
127
|
+
}
|
|
128
|
+
if (parsed.protocol !== "https:") return undefined;
|
|
129
|
+
if (parsed.username || parsed.password) return undefined;
|
|
130
|
+
if (parsed.port && parsed.port !== "443") return undefined;
|
|
131
|
+
const host = parsed.hostname.toLowerCase();
|
|
132
|
+
if (host === "localhost" || host === "127.0.0.1" || host === "::1" || host.endsWith(".localhost")) {
|
|
133
|
+
return undefined;
|
|
134
|
+
}
|
|
135
|
+
if (/^\d{1,3}(\.\d{1,3}){3}$/.test(host) || host.includes(":")) return undefined;
|
|
136
|
+
if (host !== "api.githubcopilot.com" && !host.endsWith(".githubcopilot.com")) return undefined;
|
|
137
|
+
// Normalize to origin only (no path/query from the network).
|
|
138
|
+
return `https://${host}`;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export function resolveCopilotApiBaseUrl(raw: string | undefined | null): string {
|
|
142
|
+
return validateCopilotApiBaseUrl(raw) ?? GITHUB_COPILOT_DEFAULT_API_BASE;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
async function requestDeviceAuthorization(signal?: AbortSignal): Promise<{
|
|
146
|
+
userCode: string;
|
|
147
|
+
deviceCode: string;
|
|
148
|
+
verifyUrl: string;
|
|
149
|
+
expiresInMs: number;
|
|
150
|
+
intervalMs: number;
|
|
151
|
+
}> {
|
|
152
|
+
const response = await fetch(DEVICE_CODE_URL, {
|
|
153
|
+
method: "POST",
|
|
154
|
+
headers: {
|
|
155
|
+
Accept: "application/json",
|
|
156
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
157
|
+
"User-Agent": "opencodex",
|
|
158
|
+
},
|
|
159
|
+
body: new URLSearchParams({
|
|
160
|
+
client_id: GITHUB_COPILOT_OAUTH_CLIENT_ID,
|
|
161
|
+
scope: OAUTH_SCOPE,
|
|
162
|
+
}),
|
|
163
|
+
signal,
|
|
164
|
+
});
|
|
165
|
+
if (!response.ok) throw githubCopilotHttpError("device authorization", response.status);
|
|
166
|
+
const payload = (await response.json()) as DeviceAuthorizationResponse;
|
|
167
|
+
if (!payload.user_code || !payload.device_code) {
|
|
168
|
+
throw new Error("GitHub Copilot device authorization response missing required fields");
|
|
169
|
+
}
|
|
170
|
+
// Construct verify URL ourselves — never trust verification_uri_complete for openUrl.
|
|
171
|
+
const verifyUrl = buildGithubDeviceVerifyUrl(payload.user_code);
|
|
172
|
+
return {
|
|
173
|
+
userCode: payload.user_code,
|
|
174
|
+
deviceCode: payload.device_code,
|
|
175
|
+
verifyUrl,
|
|
176
|
+
expiresInMs: typeof payload.expires_in === "number" ? payload.expires_in * 1000 : DEFAULT_DEVICE_FLOW_TTL_MS,
|
|
177
|
+
intervalMs: typeof payload.interval === "number" && payload.interval > 0
|
|
178
|
+
? payload.interval * 1000
|
|
179
|
+
: DEFAULT_POLL_INTERVAL_MS,
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
async function pollGithubDeviceToken(
|
|
184
|
+
deviceCode: string,
|
|
185
|
+
intervalMs: number,
|
|
186
|
+
expiresInMs: number,
|
|
187
|
+
signal?: AbortSignal,
|
|
188
|
+
): Promise<{ access: string; refresh?: string }> {
|
|
189
|
+
const deadline = Date.now() + expiresInMs;
|
|
190
|
+
let waitMs = Math.max(MIN_POLL_MS, intervalMs);
|
|
191
|
+
while (Date.now() < deadline) {
|
|
192
|
+
if (signal?.aborted) throw new Error("Login cancelled");
|
|
193
|
+
// RFC 8628: wait the interval BEFORE every poll — an immediate first request is a
|
|
194
|
+
// cadence violation GitHub may answer with slow_down.
|
|
195
|
+
await sleep(waitMs, signal);
|
|
196
|
+
if (Date.now() >= deadline) break;
|
|
197
|
+
const response = await fetch(ACCESS_TOKEN_URL, {
|
|
198
|
+
method: "POST",
|
|
199
|
+
headers: {
|
|
200
|
+
Accept: "application/json",
|
|
201
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
202
|
+
"User-Agent": "opencodex",
|
|
203
|
+
},
|
|
204
|
+
body: new URLSearchParams({
|
|
205
|
+
client_id: GITHUB_COPILOT_OAUTH_CLIENT_ID,
|
|
206
|
+
device_code: deviceCode,
|
|
207
|
+
grant_type: "urn:ietf:params:oauth:grant-type:device_code",
|
|
208
|
+
}),
|
|
209
|
+
// Bound each poll by the remaining lifetime so a hung fetch cannot outlive the flow.
|
|
210
|
+
signal: signal
|
|
211
|
+
? AbortSignal.any([signal, AbortSignal.timeout(Math.max(MIN_POLL_MS, deadline - Date.now()))])
|
|
212
|
+
: AbortSignal.timeout(Math.max(MIN_POLL_MS, deadline - Date.now())),
|
|
213
|
+
});
|
|
214
|
+
const payload = (await response.json().catch(() => ({}))) as GithubTokenResponse;
|
|
215
|
+
if (response.ok && payload.access_token) {
|
|
216
|
+
// Classic OAuth apps issue a non-expiring `gho_` access token with NO refresh token;
|
|
217
|
+
// expiring-token apps add `ghr_`. Both are valid device-flow successes.
|
|
218
|
+
return {
|
|
219
|
+
access: payload.access_token,
|
|
220
|
+
...(payload.refresh_token ? { refresh: payload.refresh_token } : {}),
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
const error = payload.error;
|
|
224
|
+
if (error === "authorization_pending") {
|
|
225
|
+
continue;
|
|
226
|
+
}
|
|
227
|
+
if (error === "slow_down") {
|
|
228
|
+
// RFC 8628 §3.5: slow_down means increase the interval by 5 seconds; the
|
|
229
|
+
// server-provided `interval` wins when it demands even more.
|
|
230
|
+
const retryAfter = typeof payload.interval === "number" && payload.interval > 0
|
|
231
|
+
? payload.interval * 1000
|
|
232
|
+
: 0;
|
|
233
|
+
waitMs = Math.max(waitMs + 5000, retryAfter);
|
|
234
|
+
continue;
|
|
235
|
+
}
|
|
236
|
+
if (error === "expired_token") throw new Error("GitHub Copilot device authorization expired");
|
|
237
|
+
if (error === "access_denied") throw new Error("GitHub Copilot device authorization denied");
|
|
238
|
+
if (error === "unsupported_grant_type" || error === "incorrect_device_code") {
|
|
239
|
+
throw new Error(`GitHub Copilot device flow failed (${error})`);
|
|
240
|
+
}
|
|
241
|
+
if (!response.ok) throw githubCopilotHttpError("device token poll", response.status);
|
|
242
|
+
throw new Error(`GitHub Copilot device flow failed (${error ?? "unknown"})`);
|
|
243
|
+
}
|
|
244
|
+
throw new Error("GitHub Copilot device flow timed out");
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
async function refreshGithubAccessToken(refreshToken: string, signal?: AbortSignal): Promise<{ access: string; refresh: string }> {
|
|
248
|
+
const response = await fetch(ACCESS_TOKEN_URL, {
|
|
249
|
+
method: "POST",
|
|
250
|
+
headers: {
|
|
251
|
+
Accept: "application/json",
|
|
252
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
253
|
+
"User-Agent": "opencodex",
|
|
254
|
+
},
|
|
255
|
+
body: new URLSearchParams({
|
|
256
|
+
client_id: GITHUB_COPILOT_OAUTH_CLIENT_ID,
|
|
257
|
+
grant_type: "refresh_token",
|
|
258
|
+
refresh_token: refreshToken,
|
|
259
|
+
}),
|
|
260
|
+
signal,
|
|
261
|
+
});
|
|
262
|
+
const payload = (await response.json().catch(() => ({}))) as GithubTokenResponse;
|
|
263
|
+
if (!response.ok) {
|
|
264
|
+
// Extract ONLY the allowlisted OAuth error code — never the body or error_description,
|
|
265
|
+
// which can echo credential material. The code lets the shared terminal-refresh
|
|
266
|
+
// detector mark the account needsReauth instead of retrying a revoked grant forever.
|
|
267
|
+
const code = payload.error && TERMINAL_OAUTH_ERROR_CODES.has(payload.error) ? payload.error : undefined;
|
|
268
|
+
throw new Error(
|
|
269
|
+
code
|
|
270
|
+
? `GitHub Copilot token refresh failed: ${code} (HTTP ${response.status})`
|
|
271
|
+
: `GitHub Copilot token refresh failed (${response.status})`,
|
|
272
|
+
);
|
|
273
|
+
}
|
|
274
|
+
if (!payload.access_token) throw new Error("GitHub Copilot token refresh missing access token");
|
|
275
|
+
return {
|
|
276
|
+
access: payload.access_token,
|
|
277
|
+
refresh: payload.refresh_token ?? refreshToken,
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
async function exchangeCopilotToken(githubAccessToken: string, signal?: AbortSignal): Promise<{
|
|
282
|
+
access: string;
|
|
283
|
+
expires: number;
|
|
284
|
+
apiBaseUrl: string;
|
|
285
|
+
}> {
|
|
286
|
+
const response = await fetch(COPILOT_TOKEN_URL, {
|
|
287
|
+
method: "GET",
|
|
288
|
+
headers: {
|
|
289
|
+
...GITHUB_COPILOT_EDITOR_HEADERS,
|
|
290
|
+
Authorization: `token ${githubAccessToken}`,
|
|
291
|
+
},
|
|
292
|
+
signal,
|
|
293
|
+
});
|
|
294
|
+
if (!response.ok) throw githubCopilotHttpError("token exchange", response.status);
|
|
295
|
+
const payload = (await response.json().catch(() => ({}))) as CopilotTokenResponse;
|
|
296
|
+
if (!payload.token || typeof payload.token !== "string") {
|
|
297
|
+
throw new Error("GitHub Copilot token exchange missing token");
|
|
298
|
+
}
|
|
299
|
+
let expires: number;
|
|
300
|
+
if (typeof payload.expires_at === "number" && Number.isFinite(payload.expires_at)) {
|
|
301
|
+
// expires_at is unix seconds from GitHub.
|
|
302
|
+
expires = payload.expires_at * 1000 - OAUTH_EXPIRY_SKEW_MS;
|
|
303
|
+
} else if (typeof payload.refresh_in === "number" && payload.refresh_in > 0) {
|
|
304
|
+
expires = Date.now() + payload.refresh_in * 1000 - OAUTH_EXPIRY_SKEW_MS;
|
|
305
|
+
} else {
|
|
306
|
+
expires = Date.now() + 25 * 60 * 1000 - OAUTH_EXPIRY_SKEW_MS;
|
|
307
|
+
}
|
|
308
|
+
return {
|
|
309
|
+
access: payload.token,
|
|
310
|
+
expires,
|
|
311
|
+
apiBaseUrl: resolveCopilotApiBaseUrl(payload.endpoints?.api),
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
async function fetchGithubIdentityOnce(githubAccessToken: string, signal?: AbortSignal): Promise<{
|
|
316
|
+
email?: string;
|
|
317
|
+
accountId?: string;
|
|
318
|
+
}> {
|
|
319
|
+
const response = await fetch(GITHUB_USER_URL, {
|
|
320
|
+
headers: {
|
|
321
|
+
Accept: "application/vnd.github+json",
|
|
322
|
+
Authorization: `Bearer ${githubAccessToken}`,
|
|
323
|
+
"User-Agent": "opencodex",
|
|
324
|
+
"X-GitHub-Api-Version": "2022-11-28",
|
|
325
|
+
},
|
|
326
|
+
signal,
|
|
327
|
+
});
|
|
328
|
+
if (!response.ok) throw githubCopilotHttpError("identity lookup", response.status);
|
|
329
|
+
const user = (await response.json()) as GithubUserResponse;
|
|
330
|
+
// Prefer numeric id for multiauth stability. Only persist email when GitHub returns one —
|
|
331
|
+
// do not fabricate noreply addresses (privacy-scan + PII hygiene).
|
|
332
|
+
const accountId = typeof user.id === "number"
|
|
333
|
+
? String(user.id)
|
|
334
|
+
: (typeof user.login === "string" && user.login ? user.login : undefined);
|
|
335
|
+
const email = typeof user.email === "string" && user.email.includes("@") ? user.email : undefined;
|
|
336
|
+
return {
|
|
337
|
+
...(email ? { email } : {}),
|
|
338
|
+
...(accountId ? { accountId } : {}),
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/**
|
|
343
|
+
* Identity is REQUIRED for multi-account safety: an identity-less credential would
|
|
344
|
+
* replace the active slot in the auth store and clobber another GitHub account. One
|
|
345
|
+
* retry covers transient /user failures; a persistent failure fails the login rather
|
|
346
|
+
* than persisting an anonymous credential.
|
|
347
|
+
*/
|
|
348
|
+
async function fetchGithubIdentity(githubAccessToken: string, signal?: AbortSignal): Promise<{
|
|
349
|
+
email?: string;
|
|
350
|
+
accountId?: string;
|
|
351
|
+
}> {
|
|
352
|
+
try {
|
|
353
|
+
const identity = await fetchGithubIdentityOnce(githubAccessToken, signal);
|
|
354
|
+
if (identity.accountId) return identity;
|
|
355
|
+
} catch { /* retry once below */ }
|
|
356
|
+
await sleep(IDENTITY_RETRY_DELAY_MS, signal);
|
|
357
|
+
const identity = await fetchGithubIdentityOnce(githubAccessToken, signal);
|
|
358
|
+
if (!identity.accountId) {
|
|
359
|
+
throw new Error("Could not verify GitHub account identity — retry the login");
|
|
360
|
+
}
|
|
361
|
+
return identity;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
/**
|
|
365
|
+
* The credential `refresh` field carries the DURABLE GitHub grant:
|
|
366
|
+
* - `ghr_…` refresh token (expiring-token apps) → renewed via the refresh grant;
|
|
367
|
+
* - `gho_…` access token (classic apps, no refresh token) → re-exchanged directly.
|
|
368
|
+
* The `access` field always holds the short-lived Copilot API token.
|
|
369
|
+
*/
|
|
370
|
+
async function credentialsFromGithubAccess(
|
|
371
|
+
githubAccess: string,
|
|
372
|
+
durableGrant: string,
|
|
373
|
+
signal?: AbortSignal,
|
|
374
|
+
): Promise<OAuthCredentials> {
|
|
375
|
+
const [copilot, identity] = await Promise.all([
|
|
376
|
+
exchangeCopilotToken(githubAccess, signal),
|
|
377
|
+
fetchGithubIdentity(githubAccess, signal),
|
|
378
|
+
]);
|
|
379
|
+
return {
|
|
380
|
+
access: copilot.access,
|
|
381
|
+
refresh: durableGrant,
|
|
382
|
+
expires: copilot.expires,
|
|
383
|
+
apiBaseUrl: copilot.apiBaseUrl,
|
|
384
|
+
source: "oauth",
|
|
385
|
+
...(identity.email ? { email: identity.email } : {}),
|
|
386
|
+
...(identity.accountId ? { accountId: identity.accountId } : {}),
|
|
387
|
+
};
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
export async function loginGithubCopilot(ctrl: OAuthController): Promise<OAuthCredentials> {
|
|
391
|
+
const device = await requestDeviceAuthorization(ctrl.signal);
|
|
392
|
+
if (!isAllowedGithubDeviceVerifyUrl(device.verifyUrl)) {
|
|
393
|
+
throw new Error("GitHub Copilot refused to open a non-allowlisted verification URL");
|
|
394
|
+
}
|
|
395
|
+
ctrl.onAuth?.({
|
|
396
|
+
url: device.verifyUrl,
|
|
397
|
+
instructions: `Enter code: ${device.userCode}`,
|
|
398
|
+
deviceCode: device.userCode,
|
|
399
|
+
});
|
|
400
|
+
ctrl.onProgress?.("Waiting for GitHub device authorization…");
|
|
401
|
+
const github = await pollGithubDeviceToken(
|
|
402
|
+
device.deviceCode,
|
|
403
|
+
device.intervalMs,
|
|
404
|
+
device.expiresInMs,
|
|
405
|
+
ctrl.signal,
|
|
406
|
+
);
|
|
407
|
+
ctrl.onProgress?.("Exchanging GitHub token for Copilot access…");
|
|
408
|
+
// Access-only responses (classic gho_ tokens) store the access token itself as the
|
|
409
|
+
// durable grant; expiring-token apps store the ghr_ refresh token.
|
|
410
|
+
return credentialsFromGithubAccess(github.access, github.refresh ?? github.access, ctrl.signal);
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
/**
|
|
414
|
+
* Refresh = renew the durable GitHub grant, then Copilot re-exchange.
|
|
415
|
+
* `refreshToken` is credentials.refresh: a `ghr_` token runs the GitHub refresh grant;
|
|
416
|
+
* anything else (a durable `gho_` access token) re-exchanges directly — both shapes
|
|
417
|
+
* recover expiry AND upstream 401s.
|
|
418
|
+
*/
|
|
419
|
+
export async function refreshGithubCopilotToken(
|
|
420
|
+
refreshToken: string,
|
|
421
|
+
signal?: AbortSignal,
|
|
422
|
+
): Promise<OAuthCredentials> {
|
|
423
|
+
if (refreshToken.startsWith("ghr_")) {
|
|
424
|
+
const github = await refreshGithubAccessToken(refreshToken, signal);
|
|
425
|
+
return credentialsFromGithubAccess(github.access, github.refresh, signal);
|
|
426
|
+
}
|
|
427
|
+
return credentialsFromGithubAccess(refreshToken, refreshToken, signal);
|
|
428
|
+
}
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Google Antigravity (Cloud Code Assist) OAuth + project discovery.
|
|
3
|
+
*
|
|
4
|
+
* Mirrors CLIProxyAPI `internal/auth/antigravity/*`. Flow: standard Google OAuth (PKCE) → discover
|
|
5
|
+
* the Cloud Code Assist project via `loadCodeAssist`, onboarding via `onboardUser` when the account
|
|
6
|
+
* has no project yet. The discovered `projectId` is stored on the credential and injected into the
|
|
7
|
+
* CCA request envelope by the google adapter.
|
|
8
|
+
*
|
|
9
|
+
* The client id/secret below are the public OAuth client identifiers embedded in the Antigravity
|
|
10
|
+
* desktop client (overridable via env), not user secrets. Tokens/refresh are never logged.
|
|
11
|
+
*/
|
|
12
|
+
import { OAuthCallbackFlow, type OAuthCallbackFlowOptions } from "./callback-server";
|
|
13
|
+
import { generatePKCE } from "./pkce";
|
|
14
|
+
import type { OAuthController, OAuthCredentials } from "./types";
|
|
15
|
+
import { antigravityUserAgent, ANTIGRAVITY_IDE_VERSION } from "../adapters/client-fingerprint";
|
|
16
|
+
|
|
17
|
+
const CLIENT_ID = process.env.GOOGLE_ANTIGRAVITY_CLIENT_ID
|
|
18
|
+
|| "1071006060591-tmhssin2h21lcre235vtolojh4g403ep.apps.googleusercontent.com";
|
|
19
|
+
const CLIENT_SECRET = process.env.GOOGLE_ANTIGRAVITY_CLIENT_SECRET
|
|
20
|
+
|| "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";
|
|
21
|
+
const AUTH_ENDPOINT = "https://accounts.google.com/o/oauth2/v2/auth";
|
|
22
|
+
const TOKEN_ENDPOINT = "https://oauth2.googleapis.com/token";
|
|
23
|
+
const USERINFO_ENDPOINT = "https://www.googleapis.com/oauth2/v2/userinfo";
|
|
24
|
+
const PROD_API = "https://cloudcode-pa.googleapis.com";
|
|
25
|
+
const DAILY_API = "https://daily-cloudcode-pa.googleapis.com";
|
|
26
|
+
const API_VERSION = "v1internal";
|
|
27
|
+
const SCOPES = [
|
|
28
|
+
"https://www.googleapis.com/auth/cloud-platform",
|
|
29
|
+
"https://www.googleapis.com/auth/userinfo.email",
|
|
30
|
+
"https://www.googleapis.com/auth/userinfo.profile",
|
|
31
|
+
"https://www.googleapis.com/auth/cclog",
|
|
32
|
+
"https://www.googleapis.com/auth/experimentsandconfigs",
|
|
33
|
+
];
|
|
34
|
+
const CALLBACK_PORT = 51121;
|
|
35
|
+
const CALLBACK_PATH = "/callback";
|
|
36
|
+
// Keep provider-side margins small: the shared OAuth freshness gate applies an additional minute.
|
|
37
|
+
const REFRESH_SKEW_MS = 5 * 60 * 1000;
|
|
38
|
+
const REQUEST_TIMEOUT_MS = 30_000;
|
|
39
|
+
const ONBOARD_ATTEMPTS = 5;
|
|
40
|
+
const ONBOARD_POLL_MS = 2_000;
|
|
41
|
+
|
|
42
|
+
function requestSignal(signal: AbortSignal | undefined): AbortSignal {
|
|
43
|
+
const timeout = AbortSignal.timeout(REQUEST_TIMEOUT_MS);
|
|
44
|
+
return signal ? AbortSignal.any([signal, timeout]) : timeout;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
interface GoogleTokenPayload {
|
|
48
|
+
access_token?: unknown;
|
|
49
|
+
refresh_token?: unknown;
|
|
50
|
+
expires_in?: unknown;
|
|
51
|
+
id_token?: unknown;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function decodeJwtPayload(token: string): Record<string, unknown> | undefined {
|
|
55
|
+
const part = token.split(".")[1];
|
|
56
|
+
if (!part) return undefined;
|
|
57
|
+
try {
|
|
58
|
+
return JSON.parse(Buffer.from(part, "base64url").toString("utf8")) as Record<string, unknown>;
|
|
59
|
+
} catch {
|
|
60
|
+
return undefined;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function emailFromToken(accessToken: string, idToken: string | undefined): string | undefined {
|
|
65
|
+
const payload = (idToken ? decodeJwtPayload(idToken) : undefined) ?? decodeJwtPayload(accessToken);
|
|
66
|
+
const email = payload?.email;
|
|
67
|
+
return typeof email === "string" && email.length > 0 ? email.toLowerCase() : undefined;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
async function postToken(body: Record<string, string>, signal?: AbortSignal): Promise<GoogleTokenPayload> {
|
|
71
|
+
const response = await fetch(TOKEN_ENDPOINT, {
|
|
72
|
+
method: "POST",
|
|
73
|
+
headers: { Accept: "application/json", "Content-Type": "application/x-www-form-urlencoded" },
|
|
74
|
+
body: new URLSearchParams(body).toString(),
|
|
75
|
+
signal: requestSignal(signal),
|
|
76
|
+
});
|
|
77
|
+
if (!response.ok) {
|
|
78
|
+
// Status only — the body can carry grant/account details.
|
|
79
|
+
throw new Error(`Antigravity token request failed: ${response.status}`);
|
|
80
|
+
}
|
|
81
|
+
return (await response.json()) as GoogleTokenPayload;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** Pull a Cloud Code Assist project id out of a loadCodeAssist/onboardUser response shape. */
|
|
85
|
+
function extractProjectId(data: Record<string, unknown> | undefined): string | undefined {
|
|
86
|
+
if (!data) return undefined;
|
|
87
|
+
for (const key of ["cloudaicompanionProject", "projectId", "project"]) {
|
|
88
|
+
const value = data[key];
|
|
89
|
+
if (typeof value === "string" && value.length > 0) return value;
|
|
90
|
+
if (value && typeof value === "object" && typeof (value as { id?: unknown }).id === "string") {
|
|
91
|
+
return (value as { id: string }).id;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return undefined;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
async function loadCodeAssistProject(accessToken: string, signal?: AbortSignal): Promise<string | undefined> {
|
|
98
|
+
const response = await fetch(`${PROD_API}/${API_VERSION}:loadCodeAssist`, {
|
|
99
|
+
method: "POST",
|
|
100
|
+
headers: { Authorization: `Bearer ${accessToken}`, Accept: "*/*", "Content-Type": "application/json", "User-Agent": antigravityUserAgent() },
|
|
101
|
+
body: JSON.stringify({ metadata: { ideType: "ANTIGRAVITY" } }),
|
|
102
|
+
signal: requestSignal(signal),
|
|
103
|
+
});
|
|
104
|
+
if (!response.ok) return undefined;
|
|
105
|
+
return extractProjectId((await response.json().catch(() => undefined)) as Record<string, unknown> | undefined);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
async function onboardProject(accessToken: string, signal?: AbortSignal): Promise<string | undefined> {
|
|
109
|
+
for (let attempt = 0; attempt < ONBOARD_ATTEMPTS; attempt++) {
|
|
110
|
+
if (signal?.aborted) throw signal.reason ?? new Error("Antigravity onboarding aborted");
|
|
111
|
+
const response = await fetch(`${DAILY_API}/${API_VERSION}:onboardUser`, {
|
|
112
|
+
method: "POST",
|
|
113
|
+
headers: { Authorization: `Bearer ${accessToken}`, Accept: "*/*", "Content-Type": "application/json", "User-Agent": antigravityUserAgent() },
|
|
114
|
+
// `ide_version` is a version, not a User-Agent. `antigravityUserAgent()` returns the whole
|
|
115
|
+
// header — `antigravity/ide/2.5.5 (aidev_client; os_type=...; arch=...)` — so onboarding was
|
|
116
|
+
// sending a parenthesized UA string in a field the real client fills with `2.5.5`. It is a
|
|
117
|
+
// fingerprint mismatch rather than a crash, which is why nothing failed: the request still
|
|
118
|
+
// succeeds, it just does not look like Antigravity.
|
|
119
|
+
body: JSON.stringify({ tier_id: "free-tier", metadata: { ide_type: "ANTIGRAVITY", ide_name: "antigravity", ide_version: ANTIGRAVITY_IDE_VERSION } }),
|
|
120
|
+
signal: requestSignal(signal),
|
|
121
|
+
});
|
|
122
|
+
if (!response.ok) {
|
|
123
|
+
// Transient (429/5xx): keep polling within the attempt budget. Hard 4xx: give up now.
|
|
124
|
+
if (response.status === 429 || response.status >= 500) {
|
|
125
|
+
await new Promise(resolve => setTimeout(resolve, ONBOARD_POLL_MS));
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
return undefined;
|
|
129
|
+
}
|
|
130
|
+
const data = (await response.json().catch(() => ({}))) as Record<string, unknown>;
|
|
131
|
+
if (data.done === true) {
|
|
132
|
+
return extractProjectId(data.response as Record<string, unknown> | undefined);
|
|
133
|
+
}
|
|
134
|
+
await new Promise(resolve => setTimeout(resolve, ONBOARD_POLL_MS));
|
|
135
|
+
}
|
|
136
|
+
return undefined;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/** Discover the CCA project for an access token (loadCodeAssist → onboardUser fallback). */
|
|
140
|
+
export async function discoverAntigravityProject(accessToken: string, signal?: AbortSignal): Promise<string | undefined> {
|
|
141
|
+
return (await loadCodeAssistProject(accessToken, signal)) ?? (await onboardProject(accessToken, signal));
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function credentialsFromPayload(payload: GoogleTokenPayload, refreshFallback = ""): OAuthCredentials {
|
|
145
|
+
if (typeof payload.access_token !== "string" || payload.access_token.length === 0) {
|
|
146
|
+
throw new Error("Antigravity token response did not include an access token");
|
|
147
|
+
}
|
|
148
|
+
const refresh = typeof payload.refresh_token === "string" && payload.refresh_token.length > 0
|
|
149
|
+
? payload.refresh_token
|
|
150
|
+
: refreshFallback;
|
|
151
|
+
if (!refresh) throw new Error("Antigravity token response did not include a refresh token");
|
|
152
|
+
const expiresIn = typeof payload.expires_in === "number" && Number.isFinite(payload.expires_in) ? payload.expires_in : 3600;
|
|
153
|
+
const idToken = typeof payload.id_token === "string" ? payload.id_token : undefined;
|
|
154
|
+
return {
|
|
155
|
+
refresh,
|
|
156
|
+
access: payload.access_token,
|
|
157
|
+
expires: Date.now() + expiresIn * 1000 - REFRESH_SKEW_MS,
|
|
158
|
+
email: emailFromToken(payload.access_token, idToken),
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
class AntigravityOAuthFlow extends OAuthCallbackFlow {
|
|
163
|
+
#verifier = "";
|
|
164
|
+
#forceAccountSelect: boolean;
|
|
165
|
+
|
|
166
|
+
constructor(ctrl: OAuthController, opts?: { forceAccountSelect?: boolean }) {
|
|
167
|
+
super(ctrl, {
|
|
168
|
+
preferredPort: CALLBACK_PORT,
|
|
169
|
+
callbackPath: CALLBACK_PATH,
|
|
170
|
+
callbackHostname: "127.0.0.1",
|
|
171
|
+
callbackBindHostname: "127.0.0.1",
|
|
172
|
+
redirectUri: `http://127.0.0.1:${CALLBACK_PORT}${CALLBACK_PATH}`,
|
|
173
|
+
} satisfies OAuthCallbackFlowOptions);
|
|
174
|
+
this.#forceAccountSelect = opts?.forceAccountSelect === true;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
async generateAuthUrl(state: string, redirectUri: string): Promise<{ url: string; instructions?: string }> {
|
|
178
|
+
const pkce = await generatePKCE();
|
|
179
|
+
this.#verifier = pkce.verifier;
|
|
180
|
+
const params = new URLSearchParams({
|
|
181
|
+
response_type: "code",
|
|
182
|
+
client_id: CLIENT_ID,
|
|
183
|
+
redirect_uri: redirectUri,
|
|
184
|
+
scope: SCOPES.join(" "),
|
|
185
|
+
code_challenge: pkce.challenge,
|
|
186
|
+
code_challenge_method: "S256",
|
|
187
|
+
access_type: "offline",
|
|
188
|
+
// select_account lets the user pick a DIFFERENT Google account when adding a second one.
|
|
189
|
+
prompt: this.#forceAccountSelect ? "consent select_account" : "consent",
|
|
190
|
+
state,
|
|
191
|
+
});
|
|
192
|
+
return {
|
|
193
|
+
url: `${AUTH_ENDPOINT}?${params.toString()}`,
|
|
194
|
+
instructions: "Complete Google (Antigravity) login in your browser, then paste the redirect URL or code if prompted.",
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
async exchangeToken(code: string, _state: string, redirectUri: string): Promise<OAuthCredentials> {
|
|
199
|
+
if (!this.#verifier) throw new Error("Antigravity OAuth PKCE verifier was not initialized");
|
|
200
|
+
const payload = await postToken({
|
|
201
|
+
grant_type: "authorization_code",
|
|
202
|
+
client_id: CLIENT_ID,
|
|
203
|
+
client_secret: CLIENT_SECRET,
|
|
204
|
+
code,
|
|
205
|
+
redirect_uri: redirectUri,
|
|
206
|
+
code_verifier: this.#verifier,
|
|
207
|
+
}, this.ctrl.signal);
|
|
208
|
+
const creds = credentialsFromPayload(payload);
|
|
209
|
+
this.ctrl.onProgress?.("Discovering Cloud Code Assist project");
|
|
210
|
+
const projectId = await discoverAntigravityProject(creds.access, this.ctrl.signal);
|
|
211
|
+
if (!projectId) {
|
|
212
|
+
// Fail the login rather than persisting a credential that every request would reject for a
|
|
213
|
+
// missing CCA project — otherwise status shows "logged in" while all calls fail closed.
|
|
214
|
+
throw new Error("Antigravity login could not discover a Cloud Code Assist project for this account. Ensure the account has Antigravity/Cloud Code Assist access and try again.");
|
|
215
|
+
}
|
|
216
|
+
return { ...creds, projectId };
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
export async function loginAntigravity(ctrl: OAuthController, opts?: { forceAccountSelect?: boolean }): Promise<OAuthCredentials> {
|
|
221
|
+
return new AntigravityOAuthFlow(ctrl, opts).login();
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
export async function refreshAntigravityToken(refreshToken: string, signal?: AbortSignal): Promise<OAuthCredentials> {
|
|
225
|
+
if (!refreshToken) throw new Error("Antigravity credentials are expired and do not include a refresh token");
|
|
226
|
+
const payload = await postToken({
|
|
227
|
+
grant_type: "refresh_token",
|
|
228
|
+
client_id: CLIENT_ID,
|
|
229
|
+
client_secret: CLIENT_SECRET,
|
|
230
|
+
refresh_token: refreshToken,
|
|
231
|
+
}, signal);
|
|
232
|
+
const creds = credentialsFromPayload(payload, refreshToken);
|
|
233
|
+
// Re-discover the project on refresh so a newly-onboarded account fills in projectId.
|
|
234
|
+
const projectId = await discoverAntigravityProject(creds.access, signal).catch(() => undefined);
|
|
235
|
+
return projectId ? { ...creds, projectId } : creds;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Refresh and derive import identity from Google's pinned userinfo endpoint. Imports may not
|
|
240
|
+
* borrow the email written in a local file: that would let one valid token overwrite another
|
|
241
|
+
* account's slot when a refresh response has no id_token.
|
|
242
|
+
*/
|
|
243
|
+
export async function validateAntigravityImportCredential(
|
|
244
|
+
refreshToken: string,
|
|
245
|
+
signal?: AbortSignal,
|
|
246
|
+
): Promise<OAuthCredentials> {
|
|
247
|
+
const credential = await refreshAntigravityToken(refreshToken, signal);
|
|
248
|
+
const response = await fetch(USERINFO_ENDPOINT, {
|
|
249
|
+
method: "GET",
|
|
250
|
+
headers: { Accept: "application/json", Authorization: `Bearer ${credential.access}` },
|
|
251
|
+
signal: requestSignal(signal),
|
|
252
|
+
});
|
|
253
|
+
if (!response.ok) throw new Error(`Antigravity identity request failed: ${response.status}`);
|
|
254
|
+
const body = (await response.json().catch(() => undefined)) as { email?: unknown; id?: unknown } | undefined;
|
|
255
|
+
if (typeof body?.email !== "string" || body.email.length === 0) {
|
|
256
|
+
throw new Error("Antigravity identity response did not include an email");
|
|
257
|
+
}
|
|
258
|
+
if (typeof body.id !== "string" || body.id.length === 0) {
|
|
259
|
+
throw new Error("Antigravity identity response did not include an account id");
|
|
260
|
+
}
|
|
261
|
+
return { ...credential, accountId: body.id, email: body.email.toLowerCase() };
|
|
262
|
+
}
|