@linxiraos/pi-ai 1.0.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/CHANGELOG.md +5066 -0
- package/README.md +1195 -0
- package/dist/types/api-registry.d.ts +30 -0
- package/dist/types/auth/sqlite-credential-store.d.ts +117 -0
- package/dist/types/auth-broker/client.d.ts +93 -0
- package/dist/types/auth-broker/discover.d.ts +39 -0
- package/dist/types/auth-broker/index.d.ts +7 -0
- package/dist/types/auth-broker/refresher.d.ts +25 -0
- package/dist/types/auth-broker/remote-store.d.ts +136 -0
- package/dist/types/auth-broker/server.d.ts +48 -0
- package/dist/types/auth-broker/snapshot-cache.d.ts +17 -0
- package/dist/types/auth-broker/types.d.ts +152 -0
- package/dist/types/auth-broker/wire-schema-resource.d.ts +53 -0
- package/dist/types/auth-broker/wire-schemas.d.ts +5 -0
- package/dist/types/auth-gateway/http.d.ts +56 -0
- package/dist/types/auth-gateway/index.d.ts +3 -0
- package/dist/types/auth-gateway/server.d.ts +36 -0
- package/dist/types/auth-gateway/types.d.ts +127 -0
- package/dist/types/auth-retry.d.ts +150 -0
- package/dist/types/auth-storage.d.ts +1258 -0
- package/dist/types/dialect/anthropic.d.ts +15 -0
- package/dist/types/dialect/catalog.d.ts +3 -0
- package/dist/types/dialect/coercion.d.ts +23 -0
- package/dist/types/dialect/deepseek.d.ts +14 -0
- package/dist/types/dialect/demotion.d.ts +30 -0
- package/dist/types/dialect/examples.d.ts +18 -0
- package/dist/types/dialect/factory.d.ts +3 -0
- package/dist/types/dialect/fenced-thinking.d.ts +53 -0
- package/dist/types/dialect/gemini.d.ts +17 -0
- package/dist/types/dialect/gemma.d.ts +15 -0
- package/dist/types/dialect/glm.d.ts +9 -0
- package/dist/types/dialect/harmony.d.ts +8 -0
- package/dist/types/dialect/hermes.d.ts +9 -0
- package/dist/types/dialect/history.d.ts +3 -0
- package/dist/types/dialect/index.d.ts +11 -0
- package/dist/types/dialect/inventory.d.ts +9 -0
- package/dist/types/dialect/kimi.d.ts +14 -0
- package/dist/types/dialect/minimax.d.ts +3 -0
- package/dist/types/dialect/owned-stream.d.ts +4 -0
- package/dist/types/dialect/qwen3.d.ts +9 -0
- package/dist/types/dialect/rendering.d.ts +54 -0
- package/dist/types/dialect/thinking.d.ts +6 -0
- package/dist/types/dialect/types.d.ts +68 -0
- package/dist/types/dialect/xml.d.ts +9 -0
- package/dist/types/error/abort.d.ts +14 -0
- package/dist/types/error/auth-classify.d.ts +20 -0
- package/dist/types/error/auth.d.ts +27 -0
- package/dist/types/error/aws.d.ts +27 -0
- package/dist/types/error/classes.d.ts +106 -0
- package/dist/types/error/finalize.d.ts +39 -0
- package/dist/types/error/flags.d.ts +94 -0
- package/dist/types/error/format.d.ts +20 -0
- package/dist/types/error/gateway.d.ts +20 -0
- package/dist/types/error/index.d.ts +14 -0
- package/dist/types/error/oauth.d.ts +43 -0
- package/dist/types/error/provider.d.ts +42 -0
- package/dist/types/error/rate-limit.d.ts +80 -0
- package/dist/types/error/retryable.d.ts +27 -0
- package/dist/types/error/validation.d.ts +32 -0
- package/dist/types/index.d.ts +51 -0
- package/dist/types/provider-details.d.ts +24 -0
- package/dist/types/providers/amazon-bedrock.d.ts +39 -0
- package/dist/types/providers/anthropic-client.d.ts +106 -0
- package/dist/types/providers/anthropic-messages-server-schema.d.ts +937 -0
- package/dist/types/providers/anthropic-messages-server.d.ts +17 -0
- package/dist/types/providers/anthropic-wire.d.ts +345 -0
- package/dist/types/providers/anthropic.d.ts +261 -0
- package/dist/types/providers/aws-credentials.d.ts +48 -0
- package/dist/types/providers/aws-eventstream.d.ts +39 -0
- package/dist/types/providers/aws-sigv4.d.ts +55 -0
- package/dist/types/providers/azure-openai-responses.d.ts +16 -0
- package/dist/types/providers/bedrock-mantle.d.ts +13 -0
- package/dist/types/providers/claude-code-fingerprint.d.ts +19 -0
- package/dist/types/providers/cowork-fetch.d.ts +3 -0
- package/dist/types/providers/cursor/exec-modern.d.ts +98 -0
- package/dist/types/providers/cursor-pi-args.d.ts +105 -0
- package/dist/types/providers/cursor.d.ts +221 -0
- package/dist/types/providers/devin.d.ts +12 -0
- package/dist/types/providers/error-message.d.ts +25 -0
- package/dist/types/providers/github-copilot-headers.d.ts +40 -0
- package/dist/types/providers/gitlab-duo-workflow.d.ts +254 -0
- package/dist/types/providers/gitlab-duo.d.ts +27 -0
- package/dist/types/providers/google-auth.d.ts +24 -0
- package/dist/types/providers/google-gemini-cli.d.ts +120 -0
- package/dist/types/providers/google-shared.d.ts +203 -0
- package/dist/types/providers/google-types.d.ts +155 -0
- package/dist/types/providers/google-vertex.d.ts +7 -0
- package/dist/types/providers/google.d.ts +4 -0
- package/dist/types/providers/grammar.d.ts +1 -0
- package/dist/types/providers/kimi.d.ts +27 -0
- package/dist/types/providers/mock.d.ts +179 -0
- package/dist/types/providers/ollama.d.ts +8 -0
- package/dist/types/providers/openai-anthropic-shim.d.ts +35 -0
- package/dist/types/providers/openai-chat-server-schema.d.ts +1311 -0
- package/dist/types/providers/openai-chat-server.d.ts +16 -0
- package/dist/types/providers/openai-chat-wire.d.ts +669 -0
- package/dist/types/providers/openai-codex/request-transformer.d.ts +109 -0
- package/dist/types/providers/openai-codex/response-handler.d.ts +26 -0
- package/dist/types/providers/openai-codex-responses.d.ts +238 -0
- package/dist/types/providers/openai-completions.d.ts +48 -0
- package/dist/types/providers/openai-reasoning-fallback.d.ts +25 -0
- package/dist/types/providers/openai-responses-server-schema.d.ts +1314 -0
- package/dist/types/providers/openai-responses-server.d.ts +17 -0
- package/dist/types/providers/openai-responses-wire.d.ts +6099 -0
- package/dist/types/providers/openai-responses.d.ts +131 -0
- package/dist/types/providers/openai-shared.d.ts +624 -0
- package/dist/types/providers/pi-native-client.d.ts +13 -0
- package/dist/types/providers/pi-native-server.d.ts +69 -0
- package/dist/types/providers/register-builtins.d.ts +37 -0
- package/dist/types/providers/synthetic.d.ts +26 -0
- package/dist/types/providers/transform-messages.d.ts +32 -0
- package/dist/types/providers/vision-guard.d.ts +20 -0
- package/dist/types/registry/aiand.d.ts +7 -0
- package/dist/types/registry/aimlapi.d.ts +4 -0
- package/dist/types/registry/alibaba-coding-plan.d.ts +8 -0
- package/dist/types/registry/alibaba-token-plan.d.ts +18 -0
- package/dist/types/registry/amazon-bedrock.d.ts +5 -0
- package/dist/types/registry/anthropic.d.ts +10 -0
- package/dist/types/registry/api-key-login.d.ts +42 -0
- package/dist/types/registry/api-key-validation.d.ts +43 -0
- package/dist/types/registry/aws.d.ts +13 -0
- package/dist/types/registry/azure.d.ts +4 -0
- package/dist/types/registry/baseten.d.ts +7 -0
- package/dist/types/registry/bedrock-mantle.d.ts +22 -0
- package/dist/types/registry/cerebras.d.ts +7 -0
- package/dist/types/registry/cloudflare-ai-gateway.d.ts +13 -0
- package/dist/types/registry/coreweave.d.ts +7 -0
- package/dist/types/registry/cursor.d.ts +7 -0
- package/dist/types/registry/deepseek.d.ts +8 -0
- package/dist/types/registry/derived.d.ts +5 -0
- package/dist/types/registry/devin.d.ts +8 -0
- package/dist/types/registry/exa.d.ts +8 -0
- package/dist/types/registry/firepass.d.ts +16 -0
- package/dist/types/registry/fireworks.d.ts +7 -0
- package/dist/types/registry/github-copilot.d.ts +7 -0
- package/dist/types/registry/gitlab-duo-workflow.d.ts +10 -0
- package/dist/types/registry/gitlab-duo.d.ts +9 -0
- package/dist/types/registry/gmi-cloud.d.ts +7 -0
- package/dist/types/registry/google-antigravity.d.ts +9 -0
- package/dist/types/registry/google-gemini-cli.d.ts +9 -0
- package/dist/types/registry/google-vertex.d.ts +5 -0
- package/dist/types/registry/google.d.ts +4 -0
- package/dist/types/registry/groq.d.ts +4 -0
- package/dist/types/registry/huggingface.d.ts +7 -0
- package/dist/types/registry/index.d.ts +4 -0
- package/dist/types/registry/kagi.d.ts +14 -0
- package/dist/types/registry/kilo.d.ts +7 -0
- package/dist/types/registry/kimi-code.d.ts +7 -0
- package/dist/types/registry/litellm.d.ts +13 -0
- package/dist/types/registry/llama-cpp.d.ts +8 -0
- package/dist/types/registry/lm-studio.d.ts +8 -0
- package/dist/types/registry/meta.d.ts +7 -0
- package/dist/types/registry/minimax-code-cn.d.ts +6 -0
- package/dist/types/registry/minimax-code.d.ts +6 -0
- package/dist/types/registry/minimax.d.ts +4 -0
- package/dist/types/registry/mistral.d.ts +4 -0
- package/dist/types/registry/moonshot.d.ts +7 -0
- package/dist/types/registry/nanogpt.d.ts +7 -0
- package/dist/types/registry/novita.d.ts +6 -0
- package/dist/types/registry/nvidia.d.ts +7 -0
- package/dist/types/registry/oauth/anthropic-constants.d.ts +12 -0
- package/dist/types/registry/oauth/anthropic.d.ts +24 -0
- package/dist/types/registry/oauth/callback-server.d.ts +74 -0
- package/dist/types/registry/oauth/cursor.d.ts +16 -0
- package/dist/types/registry/oauth/device-code.d.ts +25 -0
- package/dist/types/registry/oauth/devin.d.ts +5 -0
- package/dist/types/registry/oauth/github-copilot.d.ts +30 -0
- package/dist/types/registry/oauth/gitlab-duo-workflow.d.ts +6 -0
- package/dist/types/registry/oauth/gitlab-duo.d.ts +3 -0
- package/dist/types/registry/oauth/google-antigravity.d.ts +11 -0
- package/dist/types/registry/oauth/google-gemini-cli.d.ts +22 -0
- package/dist/types/registry/oauth/google-oauth-shared.d.ts +56 -0
- package/dist/types/registry/oauth/index.d.ts +45 -0
- package/dist/types/registry/oauth/kimi.d.ts +21 -0
- package/dist/types/registry/oauth/minimax-code.d.ts +27 -0
- package/dist/types/registry/oauth/openai-codex.d.ts +33 -0
- package/dist/types/registry/oauth/opencode.d.ts +18 -0
- package/dist/types/registry/oauth/perplexity.d.ts +9 -0
- package/dist/types/registry/oauth/pkce.d.ts +8 -0
- package/dist/types/registry/oauth/types.d.ts +87 -0
- package/dist/types/registry/oauth/wafer.d.ts +1 -0
- package/dist/types/registry/oauth/xai-oauth.d.ts +46 -0
- package/dist/types/registry/oauth/xiaomi.d.ts +25 -0
- package/dist/types/registry/oauth/zai.d.ts +25 -0
- package/dist/types/registry/ollama-cloud.d.ts +7 -0
- package/dist/types/registry/ollama.d.ts +12 -0
- package/dist/types/registry/openai-codex-device.d.ts +8 -0
- package/dist/types/registry/openai-codex.d.ts +9 -0
- package/dist/types/registry/openai.d.ts +4 -0
- package/dist/types/registry/opencode-go.d.ts +6 -0
- package/dist/types/registry/opencode-zen.d.ts +6 -0
- package/dist/types/registry/openrouter.d.ts +13 -0
- package/dist/types/registry/parallel.d.ts +14 -0
- package/dist/types/registry/perplexity.d.ts +7 -0
- package/dist/types/registry/qianfan.d.ts +7 -0
- package/dist/types/registry/qwen-portal.d.ts +7 -0
- package/dist/types/registry/registry.d.ts +367 -0
- package/dist/types/registry/sakana.d.ts +7 -0
- package/dist/types/registry/siliconflow-cn.d.ts +7 -0
- package/dist/types/registry/siliconflow.d.ts +7 -0
- package/dist/types/registry/synthetic.d.ts +6 -0
- package/dist/types/registry/tavily.d.ts +14 -0
- package/dist/types/registry/together.d.ts +6 -0
- package/dist/types/registry/types.d.ts +75 -0
- package/dist/types/registry/umans.d.ts +7 -0
- package/dist/types/registry/venice.d.ts +13 -0
- package/dist/types/registry/vercel-ai-gateway.d.ts +7 -0
- package/dist/types/registry/vllm.d.ts +7 -0
- package/dist/types/registry/wafer-serverless.d.ts +6 -0
- package/dist/types/registry/xai-oauth.d.ts +7 -0
- package/dist/types/registry/xai.d.ts +7 -0
- package/dist/types/registry/xiaomi-token-plan-ams.d.ts +6 -0
- package/dist/types/registry/xiaomi-token-plan-cn.d.ts +6 -0
- package/dist/types/registry/xiaomi-token-plan-sgp.d.ts +6 -0
- package/dist/types/registry/xiaomi.d.ts +6 -0
- package/dist/types/registry/zai.d.ts +15 -0
- package/dist/types/registry/zenmux.d.ts +7 -0
- package/dist/types/registry/zhipu-coding-plan.d.ts +7 -0
- package/dist/types/stream.d.ts +46 -0
- package/dist/types/types.d.ts +1064 -0
- package/dist/types/usage/alibaba-token-plan.d.ts +3 -0
- package/dist/types/usage/claude.d.ts +4 -0
- package/dist/types/usage/cursor.d.ts +4 -0
- package/dist/types/usage/gemini.d.ts +2 -0
- package/dist/types/usage/github-copilot.d.ts +7 -0
- package/dist/types/usage/google-antigravity.d.ts +15 -0
- package/dist/types/usage/kimi.d.ts +2 -0
- package/dist/types/usage/minimax-code.d.ts +3 -0
- package/dist/types/usage/ollama.d.ts +5 -0
- package/dist/types/usage/openai-codex-base-url.d.ts +18 -0
- package/dist/types/usage/openai-codex-reset.d.ts +88 -0
- package/dist/types/usage/openai-codex.d.ts +10 -0
- package/dist/types/usage/opencode-go.d.ts +2 -0
- package/dist/types/usage/shared.d.ts +1 -0
- package/dist/types/usage/synthetic.d.ts +2 -0
- package/dist/types/usage/umans.d.ts +2 -0
- package/dist/types/usage/xai-oauth.d.ts +12 -0
- package/dist/types/usage/zai.d.ts +3 -0
- package/dist/types/usage.d.ts +527 -0
- package/dist/types/utils/abort.d.ts +25 -0
- package/dist/types/utils/anthropic-auth.d.ts +35 -0
- package/dist/types/utils/aws-profile.d.ts +17 -0
- package/dist/types/utils/block-symbols.d.ts +62 -0
- package/dist/types/utils/deterministic-id.d.ts +16 -0
- package/dist/types/utils/empty-completion-retry.d.ts +21 -0
- package/dist/types/utils/event-stream.d.ts +39 -0
- package/dist/types/utils/foundry.d.ts +1 -0
- package/dist/types/utils/google-validation.d.ts +2 -0
- package/dist/types/utils/harmony-leak.d.ts +135 -0
- package/dist/types/utils/http-inspector.d.ts +49 -0
- package/dist/types/utils/idle-iterator.d.ts +149 -0
- package/dist/types/utils/leaked-thinking-stream.d.ts +33 -0
- package/dist/types/utils/openai-http.d.ts +48 -0
- package/dist/types/utils/openrouter-headers.d.ts +1 -0
- package/dist/types/utils/parse-bind.d.ts +23 -0
- package/dist/types/utils/provider-response.d.ts +3 -0
- package/dist/types/utils/proxy.d.ts +39 -0
- package/dist/types/utils/request-debug.d.ts +29 -0
- package/dist/types/utils/retry-after.d.ts +4 -0
- package/dist/types/utils/retry.d.ts +14 -0
- package/dist/types/utils/schema/adapt.d.ts +24 -0
- package/dist/types/utils/schema/compatibility.d.ts +30 -0
- package/dist/types/utils/schema/dereference.d.ts +11 -0
- package/dist/types/utils/schema/draft.d.ts +10 -0
- package/dist/types/utils/schema/equality.d.ts +4 -0
- package/dist/types/utils/schema/fields.d.ts +54 -0
- package/dist/types/utils/schema/index.d.ts +14 -0
- package/dist/types/utils/schema/json-schema-validator.d.ts +20 -0
- package/dist/types/utils/schema/meta-validator.d.ts +2 -0
- package/dist/types/utils/schema/normalize.d.ts +153 -0
- package/dist/types/utils/schema/spill.d.ts +8 -0
- package/dist/types/utils/schema/stamps.d.ts +17 -0
- package/dist/types/utils/schema/strict-tool-validation.d.ts +16 -0
- package/dist/types/utils/schema/types.d.ts +4 -0
- package/dist/types/utils/schema/typescript.d.ts +24 -0
- package/dist/types/utils/schema/wire.d.ts +52 -0
- package/dist/types/utils/sdk-stream-timeout.d.ts +33 -0
- package/dist/types/utils/sse-debug.d.ts +5 -0
- package/dist/types/utils/stream-markup-healing.d.ts +87 -0
- package/dist/types/utils/thinking-loop.d.ts +102 -0
- package/dist/types/utils/tool-call-loop-guard.d.ts +26 -0
- package/dist/types/utils/tool-choice.d.ts +52 -0
- package/dist/types/utils/validation.d.ts +28 -0
- package/dist/types/utils.d.ts +57 -0
- package/package.json +138 -0
- package/src/api-registry.ts +109 -0
- package/src/auth/sqlite-credential-store.ts +2066 -0
- package/src/auth-broker/client.ts +471 -0
- package/src/auth-broker/discover.ts +310 -0
- package/src/auth-broker/index.ts +7 -0
- package/src/auth-broker/refresher.ts +117 -0
- package/src/auth-broker/remote-store.ts +1332 -0
- package/src/auth-broker/server.ts +898 -0
- package/src/auth-broker/snapshot-cache.ts +200 -0
- package/src/auth-broker/types.ts +193 -0
- package/src/auth-broker/wire-schema-resource.ts +487 -0
- package/src/auth-broker/wire-schemas.ts +43 -0
- package/src/auth-gateway/http.ts +227 -0
- package/src/auth-gateway/index.ts +3 -0
- package/src/auth-gateway/server.ts +836 -0
- package/src/auth-gateway/types.ts +153 -0
- package/src/auth-retry.ts +401 -0
- package/src/auth-storage.ts +6540 -0
- package/src/dialect/anthropic.md +31 -0
- package/src/dialect/anthropic.ts +608 -0
- package/src/dialect/catalog.ts +29 -0
- package/src/dialect/coercion.ts +136 -0
- package/src/dialect/deepseek.md +24 -0
- package/src/dialect/deepseek.ts +609 -0
- package/src/dialect/demotion.ts +40 -0
- package/src/dialect/examples.ts +71 -0
- package/src/dialect/factory.ts +34 -0
- package/src/dialect/fenced-thinking.ts +184 -0
- package/src/dialect/gemini.md +44 -0
- package/src/dialect/gemini.ts +583 -0
- package/src/dialect/gemma.md +33 -0
- package/src/dialect/gemma.ts +387 -0
- package/src/dialect/glm.md +32 -0
- package/src/dialect/glm.ts +579 -0
- package/src/dialect/harmony.md +31 -0
- package/src/dialect/harmony.ts +345 -0
- package/src/dialect/hermes.md +25 -0
- package/src/dialect/hermes.ts +206 -0
- package/src/dialect/history.ts +81 -0
- package/src/dialect/index.ts +15 -0
- package/src/dialect/inventory.ts +30 -0
- package/src/dialect/kimi.md +24 -0
- package/src/dialect/kimi.ts +340 -0
- package/src/dialect/minimax.md +31 -0
- package/src/dialect/minimax.ts +95 -0
- package/src/dialect/owned-stream.ts +481 -0
- package/src/dialect/prompt-template.md +12 -0
- package/src/dialect/qwen3.md +28 -0
- package/src/dialect/qwen3.ts +240 -0
- package/src/dialect/rendering.ts +304 -0
- package/src/dialect/thinking.ts +292 -0
- package/src/dialect/types.ts +56 -0
- package/src/dialect/xml.md +22 -0
- package/src/dialect/xml.ts +90 -0
- package/src/error/abort.ts +18 -0
- package/src/error/auth-classify.ts +47 -0
- package/src/error/auth.ts +48 -0
- package/src/error/aws.ts +35 -0
- package/src/error/classes.ts +281 -0
- package/src/error/finalize.ts +69 -0
- package/src/error/flags.ts +602 -0
- package/src/error/format.ts +45 -0
- package/src/error/gateway.ts +96 -0
- package/src/error/index.ts +14 -0
- package/src/error/oauth.ts +58 -0
- package/src/error/provider.ts +63 -0
- package/src/error/rate-limit.ts +303 -0
- package/src/error/retryable.ts +70 -0
- package/src/error/validation.ts +44 -0
- package/src/index.ts +51 -0
- package/src/provider-details.ts +90 -0
- package/src/providers/amazon-bedrock.ts +1064 -0
- package/src/providers/anthropic-client.ts +317 -0
- package/src/providers/anthropic-messages-server-schema.ts +252 -0
- package/src/providers/anthropic-messages-server.ts +818 -0
- package/src/providers/anthropic-wire.ts +359 -0
- package/src/providers/anthropic.ts +4539 -0
- package/src/providers/aws-credentials.ts +772 -0
- package/src/providers/aws-eventstream.ts +181 -0
- package/src/providers/aws-sigv4.ts +218 -0
- package/src/providers/azure-openai-responses.ts +438 -0
- package/src/providers/bedrock-mantle.ts +110 -0
- package/src/providers/claude-code-fingerprint.ts +20 -0
- package/src/providers/cowork-fetch.ts +201 -0
- package/src/providers/cursor/exec-modern.ts +496 -0
- package/src/providers/cursor/proto/agent.proto +4533 -0
- package/src/providers/cursor/proto/buf.gen.yaml +6 -0
- package/src/providers/cursor/proto/buf.yaml +17 -0
- package/src/providers/cursor-pi-args.ts +165 -0
- package/src/providers/cursor.ts +4689 -0
- package/src/providers/devin/proto/buf/validate/validate.proto +468 -0
- package/src/providers/devin/proto/buf.gen.yaml +33 -0
- package/src/providers/devin/proto/buf.yaml +17 -0
- package/src/providers/devin/proto/cel/expr/checked.proto +103 -0
- package/src/providers/devin/proto/cel/expr/eval.proto +38 -0
- package/src/providers/devin/proto/cel/expr/explain.proto +15 -0
- package/src/providers/devin/proto/cel/expr/syntax.proto +113 -0
- package/src/providers/devin/proto/cel/expr/value.proto +41 -0
- package/src/providers/devin/proto/connectext/grpc/status/v1/status.proto +11 -0
- package/src/providers/devin/proto/errorspb/errors.proto +56 -0
- package/src/providers/devin/proto/errorspb/hintdetail.proto +7 -0
- package/src/providers/devin/proto/errorspb/markers.proto +10 -0
- package/src/providers/devin/proto/errorspb/tags.proto +12 -0
- package/src/providers/devin/proto/errorspb/testing.proto +6 -0
- package/src/providers/devin/proto/exa/analytics_pb/analytics.proto +188 -0
- package/src/providers/devin/proto/exa/api_server_pb/api_server.proto +2461 -0
- package/src/providers/devin/proto/exa/auth_pb/auth.proto +19 -0
- package/src/providers/devin/proto/exa/auto_cascade_common_pb/auto_cascade_common.proto +79 -0
- package/src/providers/devin/proto/exa/browser_preview_pb/browser_preview.proto +32 -0
- package/src/providers/devin/proto/exa/bug_checker_pb/bug_checker.proto +22 -0
- package/src/providers/devin/proto/exa/cascade_plugins_pb/cascade_plugins.proto +262 -0
- package/src/providers/devin/proto/exa/chat_client_server_pb/chat_client_server.proto +57 -0
- package/src/providers/devin/proto/exa/chat_pb/chat.proto +449 -0
- package/src/providers/devin/proto/exa/code_edit/code_edit_pb/code_edit.proto +186 -0
- package/src/providers/devin/proto/exa/codeium_common_pb/codeium_common.proto +4157 -0
- package/src/providers/devin/proto/exa/context_module_pb/context_module.proto +175 -0
- package/src/providers/devin/proto/exa/cortex_pb/cortex.proto +3268 -0
- package/src/providers/devin/proto/exa/dev_pb/dev.proto +26 -0
- package/src/providers/devin/proto/exa/diff_action_pb/diff_action.proto +75 -0
- package/src/providers/devin/proto/exa/eval/pr_eval/datasets_pb/datasets.proto +103 -0
- package/src/providers/devin/proto/exa/eval_pb/eval.proto +1315 -0
- package/src/providers/devin/proto/exa/extension_server_pb/extension_server.proto +556 -0
- package/src/providers/devin/proto/exa/file_system_provider_pb/file_system_provider.proto +75 -0
- package/src/providers/devin/proto/exa/index_pb/index.proto +461 -0
- package/src/providers/devin/proto/exa/knowledge_base_pb/knowledge_base.proto +144 -0
- package/src/providers/devin/proto/exa/language_server_pb/language_server.proto +2385 -0
- package/src/providers/devin/proto/exa/model_management_pb/model_management.proto +186 -0
- package/src/providers/devin/proto/exa/opensearch_clients_pb/opensearch_clients.proto +503 -0
- package/src/providers/devin/proto/exa/product_analytics_pb/product_analytics.proto +37 -0
- package/src/providers/devin/proto/exa/prompt_pb/prompt.proto +92 -0
- package/src/providers/devin/proto/exa/reactive_component_pb/reactive_component.proto +96 -0
- package/src/providers/devin/proto/exa/seat_management_pb/seat_management.proto +2680 -0
- package/src/providers/devin/proto/exa/tokenizer_pb/tokenizer.proto +37 -0
- package/src/providers/devin/proto/exa/trainer_pb/config.proto +647 -0
- package/src/providers/devin/proto/exa/tree_sitter/language_data_pb/language_data.proto +14 -0
- package/src/providers/devin/proto/exa/trust_pb/trust.proto +157 -0
- package/src/providers/devin/proto/exa/user_analytics_pb/user_analytics.proto +519 -0
- package/src/providers/devin/proto/google.golang.org/appengine/internal/base/api_base.proto +28 -0
- package/src/providers/devin/proto/google.golang.org/appengine/internal/datastore/datastore_v3.proto +484 -0
- package/src/providers/devin/proto/google.golang.org/appengine/internal/log/log_service.proto +136 -0
- package/src/providers/devin/proto/google.golang.org/appengine/internal/remote_api/remote_api.proto +42 -0
- package/src/providers/devin/proto/google.golang.org/appengine/internal/urlfetch/urlfetch_service.proto +61 -0
- package/src/providers/devin/proto/grpc/binlog/v1/binarylog.proto +84 -0
- package/src/providers/devin/proto/io/prometheus/client/metrics.proto +98 -0
- package/src/providers/devin.ts +679 -0
- package/src/providers/error-message.ts +23 -0
- package/src/providers/github-copilot-headers.ts +141 -0
- package/src/providers/gitlab-duo-workflow-chatml-note.md +1 -0
- package/src/providers/gitlab-duo-workflow.ts +3135 -0
- package/src/providers/gitlab-duo.ts +399 -0
- package/src/providers/google-auth.ts +330 -0
- package/src/providers/google-gemini-cli.ts +1370 -0
- package/src/providers/google-shared.ts +1122 -0
- package/src/providers/google-types.ts +180 -0
- package/src/providers/google-vertex.ts +135 -0
- package/src/providers/google.ts +47 -0
- package/src/providers/grammar.ts +70 -0
- package/src/providers/kimi.ts +51 -0
- package/src/providers/mock.ts +514 -0
- package/src/providers/ollama.ts +776 -0
- package/src/providers/openai-anthropic-shim.ts +166 -0
- package/src/providers/openai-chat-server-schema.ts +243 -0
- package/src/providers/openai-chat-server.ts +752 -0
- package/src/providers/openai-chat-wire.ts +859 -0
- package/src/providers/openai-codex/request-transformer.ts +491 -0
- package/src/providers/openai-codex/response-handler.ts +102 -0
- package/src/providers/openai-codex-responses.ts +4716 -0
- package/src/providers/openai-completions.ts +2389 -0
- package/src/providers/openai-reasoning-fallback.ts +269 -0
- package/src/providers/openai-responses-server-schema.ts +397 -0
- package/src/providers/openai-responses-server.ts +1466 -0
- package/src/providers/openai-responses-wire.ts +6416 -0
- package/src/providers/openai-responses.ts +1393 -0
- package/src/providers/openai-shared.ts +3500 -0
- package/src/providers/pi-native-client.ts +275 -0
- package/src/providers/pi-native-server.ts +245 -0
- package/src/providers/register-builtins.ts +503 -0
- package/src/providers/synthetic.ts +50 -0
- package/src/providers/transform-messages.ts +1083 -0
- package/src/providers/vision-guard.ts +54 -0
- package/src/registry/aiand.ts +22 -0
- package/src/registry/aimlapi.ts +6 -0
- package/src/registry/alibaba-coding-plan.ts +104 -0
- package/src/registry/alibaba-token-plan.ts +125 -0
- package/src/registry/amazon-bedrock.ts +22 -0
- package/src/registry/anthropic.ts +26 -0
- package/src/registry/api-key-login.ts +115 -0
- package/src/registry/api-key-validation.ts +145 -0
- package/src/registry/aws.ts +57 -0
- package/src/registry/azure.ts +6 -0
- package/src/registry/baseten.ts +22 -0
- package/src/registry/bedrock-mantle.ts +34 -0
- package/src/registry/cerebras.ts +23 -0
- package/src/registry/cloudflare-ai-gateway.ts +45 -0
- package/src/registry/coreweave.ts +40 -0
- package/src/registry/cursor.ts +20 -0
- package/src/registry/deepseek.ts +46 -0
- package/src/registry/derived.ts +9 -0
- package/src/registry/devin.ts +15 -0
- package/src/registry/exa.ts +19 -0
- package/src/registry/firepass.ts +32 -0
- package/src/registry/fireworks.ts +28 -0
- package/src/registry/github-copilot.ts +22 -0
- package/src/registry/gitlab-duo-workflow.ts +20 -0
- package/src/registry/gitlab-duo.ts +19 -0
- package/src/registry/gmi-cloud.ts +22 -0
- package/src/registry/google-antigravity.ts +22 -0
- package/src/registry/google-gemini-cli.ts +22 -0
- package/src/registry/google-vertex.ts +38 -0
- package/src/registry/google.ts +6 -0
- package/src/registry/groq.ts +6 -0
- package/src/registry/huggingface.ts +29 -0
- package/src/registry/index.ts +4 -0
- package/src/registry/kagi.ts +46 -0
- package/src/registry/kilo.ts +114 -0
- package/src/registry/kimi-code.ts +17 -0
- package/src/registry/litellm.ts +45 -0
- package/src/registry/llama-cpp.ts +35 -0
- package/src/registry/lm-studio.ts +31 -0
- package/src/registry/meta.ts +22 -0
- package/src/registry/minimax-code-cn.ts +12 -0
- package/src/registry/minimax-code.ts +12 -0
- package/src/registry/minimax.ts +6 -0
- package/src/registry/mistral.ts +6 -0
- package/src/registry/moonshot.ts +28 -0
- package/src/registry/nanogpt.ts +22 -0
- package/src/registry/novita.ts +25 -0
- package/src/registry/nvidia.ts +61 -0
- package/src/registry/oauth/anthropic-constants.ts +12 -0
- package/src/registry/oauth/anthropic.ts +346 -0
- package/src/registry/oauth/callback-server.ts +438 -0
- package/src/registry/oauth/cursor.ts +187 -0
- package/src/registry/oauth/device-code.ts +92 -0
- package/src/registry/oauth/devin.ts +124 -0
- package/src/registry/oauth/github-copilot.ts +369 -0
- package/src/registry/oauth/gitlab-duo-workflow.ts +146 -0
- package/src/registry/oauth/gitlab-duo.ts +222 -0
- package/src/registry/oauth/google-antigravity.ts +225 -0
- package/src/registry/oauth/google-gemini-cli.ts +297 -0
- package/src/registry/oauth/google-oauth-shared.ts +211 -0
- package/src/registry/oauth/index.ts +187 -0
- package/src/registry/oauth/kimi.ts +297 -0
- package/src/registry/oauth/minimax-code.ts +53 -0
- package/src/registry/oauth/oauth.html +317 -0
- package/src/registry/oauth/openai-codex.ts +384 -0
- package/src/registry/oauth/opencode.ts +50 -0
- package/src/registry/oauth/perplexity.ts +228 -0
- package/src/registry/oauth/pkce.ts +18 -0
- package/src/registry/oauth/types.ts +96 -0
- package/src/registry/oauth/wafer.ts +24 -0
- package/src/registry/oauth/xai-oauth.ts +559 -0
- package/src/registry/oauth/xiaomi.ts +211 -0
- package/src/registry/oauth/zai.ts +285 -0
- package/src/registry/ollama-cloud.ts +36 -0
- package/src/registry/ollama.ts +43 -0
- package/src/registry/openai-codex-device.ts +18 -0
- package/src/registry/openai-codex.ts +19 -0
- package/src/registry/openai.ts +6 -0
- package/src/registry/opencode-go.ts +12 -0
- package/src/registry/opencode-zen.ts +12 -0
- package/src/registry/openrouter.ts +28 -0
- package/src/registry/parallel.ts +45 -0
- package/src/registry/perplexity.ts +13 -0
- package/src/registry/qianfan.ts +27 -0
- package/src/registry/qwen-portal.ts +50 -0
- package/src/registry/registry.ts +182 -0
- package/src/registry/sakana.ts +22 -0
- package/src/registry/siliconflow-cn.ts +22 -0
- package/src/registry/siliconflow.ts +22 -0
- package/src/registry/synthetic.ts +21 -0
- package/src/registry/tavily.ts +45 -0
- package/src/registry/together.ts +22 -0
- package/src/registry/types.ts +86 -0
- package/src/registry/umans.ts +23 -0
- package/src/registry/venice.ts +33 -0
- package/src/registry/vercel-ai-gateway.ts +38 -0
- package/src/registry/vllm.ts +34 -0
- package/src/registry/wafer-serverless.ts +12 -0
- package/src/registry/xai-oauth.ts +17 -0
- package/src/registry/xai.ts +22 -0
- package/src/registry/xiaomi-token-plan-ams.ts +12 -0
- package/src/registry/xiaomi-token-plan-cn.ts +12 -0
- package/src/registry/xiaomi-token-plan-sgp.ts +12 -0
- package/src/registry/xiaomi.ts +12 -0
- package/src/registry/zai.ts +41 -0
- package/src/registry/zenmux.ts +22 -0
- package/src/registry/zhipu-coding-plan.ts +27 -0
- package/src/stream.ts +1944 -0
- package/src/types.ts +1243 -0
- package/src/usage/alibaba-token-plan.ts +230 -0
- package/src/usage/claude.ts +830 -0
- package/src/usage/cursor.ts +335 -0
- package/src/usage/gemini.ts +258 -0
- package/src/usage/github-copilot.ts +424 -0
- package/src/usage/google-antigravity.ts +497 -0
- package/src/usage/kimi.ts +277 -0
- package/src/usage/minimax-code.ts +291 -0
- package/src/usage/ollama.ts +41 -0
- package/src/usage/openai-codex-base-url.ts +35 -0
- package/src/usage/openai-codex-reset.ts +205 -0
- package/src/usage/openai-codex.ts +627 -0
- package/src/usage/opencode-go.ts +89 -0
- package/src/usage/shared.ts +10 -0
- package/src/usage/synthetic.ts +180 -0
- package/src/usage/umans.ts +192 -0
- package/src/usage/xai-oauth.ts +414 -0
- package/src/usage/zai.ts +370 -0
- package/src/usage.ts +411 -0
- package/src/utils/abort.ts +67 -0
- package/src/utils/anthropic-auth.ts +93 -0
- package/src/utils/aws-profile.ts +88 -0
- package/src/utils/block-symbols.ts +78 -0
- package/src/utils/deterministic-id.ts +20 -0
- package/src/utils/empty-completion-retry.ts +161 -0
- package/src/utils/event-stream.ts +202 -0
- package/src/utils/foundry.ts +8 -0
- package/src/utils/google-validation.ts +25 -0
- package/src/utils/harmony-leak.ts +500 -0
- package/src/utils/http-inspector.ts +196 -0
- package/src/utils/idle-iterator.ts +531 -0
- package/src/utils/leaked-thinking-stream.ts +483 -0
- package/src/utils/openai-http.ts +119 -0
- package/src/utils/openrouter-headers.ts +12 -0
- package/src/utils/parse-bind.ts +56 -0
- package/src/utils/provider-response.ts +30 -0
- package/src/utils/proxy.ts +314 -0
- package/src/utils/request-debug.ts +351 -0
- package/src/utils/retry-after.ts +121 -0
- package/src/utils/retry.ts +77 -0
- package/src/utils/schema/CONSTRAINTS.md +168 -0
- package/src/utils/schema/adapt.ts +36 -0
- package/src/utils/schema/compatibility.ts +435 -0
- package/src/utils/schema/dereference.ts +98 -0
- package/src/utils/schema/draft.ts +341 -0
- package/src/utils/schema/equality.ts +97 -0
- package/src/utils/schema/fields.ts +210 -0
- package/src/utils/schema/index.ts +14 -0
- package/src/utils/schema/json-schema-validator.ts +595 -0
- package/src/utils/schema/meta-validator.ts +167 -0
- package/src/utils/schema/normalize.ts +2314 -0
- package/src/utils/schema/spill.ts +43 -0
- package/src/utils/schema/stamps.ts +109 -0
- package/src/utils/schema/strict-tool-validation.ts +117 -0
- package/src/utils/schema/types.ts +10 -0
- package/src/utils/schema/typescript.ts +212 -0
- package/src/utils/schema/wire.ts +662 -0
- package/src/utils/sdk-stream-timeout.ts +43 -0
- package/src/utils/sse-debug.ts +18 -0
- package/src/utils/stream-markup-healing.ts +247 -0
- package/src/utils/thinking-loop.ts +552 -0
- package/src/utils/tool-call-loop-guard.ts +107 -0
- package/src/utils/tool-choice.ts +101 -0
- package/src/utils/validation.ts +1932 -0
- package/src/utils.ts +492 -0
|
@@ -0,0 +1,438 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Abstract base class for OAuth flows with local callback servers.
|
|
3
|
+
*
|
|
4
|
+
* Handles:
|
|
5
|
+
* - Port allocation (tries expected port, falls back to random)
|
|
6
|
+
* - Callback server setup and request handling
|
|
7
|
+
* - Common OAuth flow logic
|
|
8
|
+
*
|
|
9
|
+
* Providers extend this and implement:
|
|
10
|
+
* - generateAuthUrl(): Build provider-specific authorization URL
|
|
11
|
+
* - exchangeToken(): Exchange authorization code for tokens
|
|
12
|
+
*/
|
|
13
|
+
import * as AIError from "../../error";
|
|
14
|
+
import templateHtml from "./oauth.html" with { type: "text" };
|
|
15
|
+
import type { OAuthController, OAuthCredentials } from "./types";
|
|
16
|
+
|
|
17
|
+
const DEFAULT_TIMEOUT = 300_000;
|
|
18
|
+
const DEFAULT_HOSTNAME = "localhost";
|
|
19
|
+
const CALLBACK_PATH = "/callback";
|
|
20
|
+
/**
|
|
21
|
+
* Path served by {@link OAuthCallbackFlow} that 302-redirects to the pending
|
|
22
|
+
* authorization URL. Kept out of {@link OAuthCallbackFlowOptions} because it
|
|
23
|
+
* lives on the loopback callback server alongside {@link CALLBACK_PATH} and
|
|
24
|
+
* must never clash with a provider-registered redirect URI (all known
|
|
25
|
+
* providers register `/callback`-shaped paths).
|
|
26
|
+
*/
|
|
27
|
+
const LAUNCH_PATH = "/launch";
|
|
28
|
+
|
|
29
|
+
export type CallbackResult = { code: string; state: string };
|
|
30
|
+
|
|
31
|
+
export interface OAuthCallbackFlowOptions {
|
|
32
|
+
preferredPort: number;
|
|
33
|
+
callbackPath?: string;
|
|
34
|
+
callbackHostname?: string;
|
|
35
|
+
/** Exact redirect URI advertised to the provider; disables port fallback. */
|
|
36
|
+
redirectUri?: string;
|
|
37
|
+
/**
|
|
38
|
+
* Whether the flow may bind to a random port when {@link preferredPort} is
|
|
39
|
+
* unavailable. Defaults to `true` so historical AI-provider flows (which
|
|
40
|
+
* pick uncommon ports and tolerate any loopback callback) keep working.
|
|
41
|
+
*
|
|
42
|
+
* Set to `false` for providers that validate the redirect URI against a
|
|
43
|
+
* registered callback — silently advertising a random-port URI would be
|
|
44
|
+
* rejected by the authorization server, leaving the browser on an opaque
|
|
45
|
+
* 500 page and the local callback waiting until the 5-minute timeout fires.
|
|
46
|
+
* With fallback disabled, {@link OAuthCallbackFlow.login} throws a
|
|
47
|
+
* {@link AIError.ConfigurationError} immediately so the caller can surface
|
|
48
|
+
* an actionable message before opening the browser.
|
|
49
|
+
*/
|
|
50
|
+
allowPortFallback?: boolean;
|
|
51
|
+
/** Skip the local callback server entirely; the user pastes the code or redirect URL back. */
|
|
52
|
+
manualInputOnly?: boolean;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Abstract base class for OAuth flows with local callback servers.
|
|
57
|
+
*/
|
|
58
|
+
export abstract class OAuthCallbackFlow {
|
|
59
|
+
ctrl: OAuthController;
|
|
60
|
+
preferredPort: number;
|
|
61
|
+
callbackPath: string;
|
|
62
|
+
callbackHostname: string;
|
|
63
|
+
redirectUri?: string;
|
|
64
|
+
allowPortFallback: boolean;
|
|
65
|
+
#manualInputOnly: boolean;
|
|
66
|
+
#callbackResolve?: (result: CallbackResult) => void;
|
|
67
|
+
#callbackReject?: (error: Error) => void;
|
|
68
|
+
/**
|
|
69
|
+
* Authorization URL the `/launch` route currently redirects to. Set by
|
|
70
|
+
* {@link login} after {@link generateAuthUrl} and before {@link OAuthController.onAuth}
|
|
71
|
+
* fires, cleared when the server stops. `undefined` before the flow reaches
|
|
72
|
+
* that point and after it finishes, so `/launch` returns 503 rather than
|
|
73
|
+
* a stale URL.
|
|
74
|
+
*/
|
|
75
|
+
#pendingAuthUrl?: string;
|
|
76
|
+
|
|
77
|
+
constructor(
|
|
78
|
+
ctrl: OAuthController,
|
|
79
|
+
preferredPortOrOptions: number | OAuthCallbackFlowOptions,
|
|
80
|
+
callbackPath: string = CALLBACK_PATH,
|
|
81
|
+
) {
|
|
82
|
+
this.ctrl = ctrl;
|
|
83
|
+
if (typeof preferredPortOrOptions === "number") {
|
|
84
|
+
this.preferredPort = preferredPortOrOptions;
|
|
85
|
+
this.callbackPath = callbackPath;
|
|
86
|
+
this.callbackHostname = DEFAULT_HOSTNAME;
|
|
87
|
+
this.allowPortFallback = true;
|
|
88
|
+
this.#manualInputOnly = false;
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
this.preferredPort = preferredPortOrOptions.preferredPort;
|
|
93
|
+
this.callbackPath = preferredPortOrOptions.callbackPath ?? CALLBACK_PATH;
|
|
94
|
+
this.callbackHostname = preferredPortOrOptions.callbackHostname ?? DEFAULT_HOSTNAME;
|
|
95
|
+
this.redirectUri = preferredPortOrOptions.redirectUri;
|
|
96
|
+
this.allowPortFallback = preferredPortOrOptions.allowPortFallback ?? true;
|
|
97
|
+
this.#manualInputOnly = preferredPortOrOptions.manualInputOnly ?? false;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Generate provider-specific authorization URL.
|
|
102
|
+
* @param state - CSRF state token
|
|
103
|
+
* @param redirectUri - The actual redirect URI to use (may differ from expected if port fallback occurred)
|
|
104
|
+
* @returns Authorization URL and optional instructions
|
|
105
|
+
*/
|
|
106
|
+
abstract generateAuthUrl(state: string, redirectUri: string): Promise<{ url: string; instructions?: string }>;
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Exchange authorization code for OAuth tokens.
|
|
110
|
+
* @param code - Authorization code from callback
|
|
111
|
+
* @param state - CSRF state token
|
|
112
|
+
* @param redirectUri - The actual redirect URI used (must match authorization request)
|
|
113
|
+
* @returns OAuth credentials
|
|
114
|
+
*/
|
|
115
|
+
abstract exchangeToken(code: string, state: string, redirectUri: string): Promise<OAuthCredentials>;
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Generate CSRF state token. Override if provider needs custom state generation.
|
|
119
|
+
*/
|
|
120
|
+
generateState(): string {
|
|
121
|
+
const bytes = new Uint8Array(16);
|
|
122
|
+
crypto.getRandomValues(bytes);
|
|
123
|
+
return Array.from(bytes)
|
|
124
|
+
.map(value => value.toString(16).padStart(2, "0"))
|
|
125
|
+
.join("");
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
#loginCancelledError(): AIError.LoginCancelledError {
|
|
129
|
+
return new AIError.LoginCancelledError(`OAuth callback cancelled: ${this.ctrl.signal?.reason}`);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
#throwIfCancelled(): void {
|
|
133
|
+
if (this.ctrl.signal?.aborted) throw this.#loginCancelledError();
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Execute the OAuth login flow.
|
|
138
|
+
*/
|
|
139
|
+
async login(): Promise<OAuthCredentials> {
|
|
140
|
+
const state = this.generateState();
|
|
141
|
+
this.#throwIfCancelled();
|
|
142
|
+
|
|
143
|
+
// Start callback server first to get actual redirect URI. Manual-only
|
|
144
|
+
// flows never bind a server — the advertised redirect URI is fixed and
|
|
145
|
+
// the user pastes the code/redirect URL back instead.
|
|
146
|
+
const { server, redirectUri, launchUrl } = this.#manualInputOnly
|
|
147
|
+
? { server: undefined, redirectUri: this.#buildRedirectUri(), launchUrl: undefined }
|
|
148
|
+
: await this.#startCallbackServer(state);
|
|
149
|
+
|
|
150
|
+
try {
|
|
151
|
+
this.#throwIfCancelled();
|
|
152
|
+
// Generate auth URL with the ACTUAL redirect URI (may differ from expected if port was busy)
|
|
153
|
+
const { url: authUrl, instructions } = await this.generateAuthUrl(state, redirectUri);
|
|
154
|
+
this.#throwIfCancelled();
|
|
155
|
+
|
|
156
|
+
// Publish the auth URL to the `/launch` route BEFORE handing it to
|
|
157
|
+
// callers. `onAuth` immediately renders a UI that advertises the
|
|
158
|
+
// launch URL as a copy target, so `/launch` must already resolve if
|
|
159
|
+
// the user clicks/pastes it during the same render pass.
|
|
160
|
+
this.#pendingAuthUrl = authUrl;
|
|
161
|
+
|
|
162
|
+
// Notify controller that auth is ready
|
|
163
|
+
this.ctrl.onAuth?.({ url: authUrl, launchUrl, instructions });
|
|
164
|
+
this.ctrl.onProgress?.(
|
|
165
|
+
this.#manualInputOnly
|
|
166
|
+
? "Waiting for pasted authorization code..."
|
|
167
|
+
: "Waiting for browser authentication...",
|
|
168
|
+
);
|
|
169
|
+
|
|
170
|
+
const { code } = await this.#waitForCallback(state);
|
|
171
|
+
this.#throwIfCancelled();
|
|
172
|
+
|
|
173
|
+
this.ctrl.onProgress?.("Exchanging authorization code for tokens...");
|
|
174
|
+
|
|
175
|
+
return await this.exchangeToken(code, state, redirectUri);
|
|
176
|
+
} finally {
|
|
177
|
+
this.#pendingAuthUrl = undefined;
|
|
178
|
+
server?.stop();
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
#buildRedirectUri(): string {
|
|
183
|
+
return this.redirectUri ?? `http://${this.callbackHostname}:${this.preferredPort}${this.callbackPath}`;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Start callback server, trying preferred port first, falling back to random.
|
|
188
|
+
* `launchUrl` is `undefined` when the caller configured `callbackPath` to
|
|
189
|
+
* collide with {@link LAUNCH_PATH} — the callback handler resolves the real
|
|
190
|
+
* callback in that case, so advertising a self-redirecting URL would be
|
|
191
|
+
* incorrect.
|
|
192
|
+
*/
|
|
193
|
+
async #startCallbackServer(
|
|
194
|
+
expectedState: string,
|
|
195
|
+
): Promise<{ server: Bun.Server<unknown>; redirectUri: string; launchUrl: string | undefined }> {
|
|
196
|
+
try {
|
|
197
|
+
const server = this.#createServer(this.preferredPort, expectedState);
|
|
198
|
+
// `preferredPort: 0` opts into a random port — read the actual bound
|
|
199
|
+
// port from the server so both the redirect URI and launch URL point at
|
|
200
|
+
// a reachable socket, not the sentinel.
|
|
201
|
+
const actualPort = this.#resolveServerPort(server);
|
|
202
|
+
const launchUrl = this.#launchUrlIfSafe(actualPort);
|
|
203
|
+
if (this.redirectUri) {
|
|
204
|
+
return { server, redirectUri: this.redirectUri, launchUrl };
|
|
205
|
+
}
|
|
206
|
+
const redirectUri = `http://${this.callbackHostname}:${actualPort}${this.callbackPath}`;
|
|
207
|
+
return { server, redirectUri, launchUrl };
|
|
208
|
+
} catch (cause) {
|
|
209
|
+
if (this.redirectUri) {
|
|
210
|
+
throw new AIError.ConfigurationError(
|
|
211
|
+
`OAuth callback port ${this.preferredPort} is in use, but oauth.redirectUri (${this.redirectUri}) requires this exact port. Free port ${this.preferredPort} (e.g. stop the process bound to it) and retry, or change oauth.redirectUri to point at an available port.`,
|
|
212
|
+
{ cause },
|
|
213
|
+
);
|
|
214
|
+
}
|
|
215
|
+
if (!this.allowPortFallback) {
|
|
216
|
+
throw new AIError.ConfigurationError(
|
|
217
|
+
`OAuth callback port ${this.preferredPort} is in use. The OAuth provider validates redirect URIs against its registered callback, so falling back to a random port would be rejected. Free port ${this.preferredPort} (e.g. stop the process bound to it) and retry, or set oauth.callbackPort/oauth.redirectUri to a port the provider has registered.`,
|
|
218
|
+
{ cause },
|
|
219
|
+
);
|
|
220
|
+
}
|
|
221
|
+
const server = this.#createServer(0, expectedState);
|
|
222
|
+
const actualPort = this.#resolveServerPort(server);
|
|
223
|
+
const redirectUri = `http://${this.callbackHostname}:${actualPort}${this.callbackPath}`;
|
|
224
|
+
const launchUrl = this.#launchUrlIfSafe(actualPort);
|
|
225
|
+
this.ctrl.onProgress?.(`Preferred port ${this.preferredPort} unavailable, using port ${actualPort}`);
|
|
226
|
+
return { server, redirectUri, launchUrl };
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Read the numeric port a callback server bound to. `Bun.Server.port` is
|
|
232
|
+
* declared `number | undefined` because Unix-socket servers have no port,
|
|
233
|
+
* but every callback flow uses TCP; a missing port here indicates a
|
|
234
|
+
* configuration error rather than a fallback case.
|
|
235
|
+
*/
|
|
236
|
+
#resolveServerPort(server: Bun.Server<unknown>): number {
|
|
237
|
+
const port = server.port;
|
|
238
|
+
if (typeof port !== "number") {
|
|
239
|
+
throw new AIError.ConfigurationError(
|
|
240
|
+
"OAuth callback server bound to a non-TCP endpoint; expected a numeric port. Check `oauth.callbackPort`/`oauth.redirectUri`.",
|
|
241
|
+
);
|
|
242
|
+
}
|
|
243
|
+
return port;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Build the `/launch` URL served by the callback server bound to `port`, or
|
|
248
|
+
* `undefined` when it must not be advertised:
|
|
249
|
+
* - the configured `callbackPath` (or a `redirectUri` whose pathname
|
|
250
|
+
* resolves to {@link LAUNCH_PATH}) would collide with the launch route;
|
|
251
|
+
* - the flow's `redirectUri` never returns to this loopback server: fixed
|
|
252
|
+
* non-loopback hosts, or custom schemes like GitLab Duo's `vscode://`
|
|
253
|
+
* URI — which `new URL` parses without complaint, so a scheme/host check
|
|
254
|
+
* is required, not just the parse failure path. Advertising a localhost
|
|
255
|
+
* `/launch` target for such flows misrepresents the callback endpoint
|
|
256
|
+
* and hands remote users a URL that resolves nowhere.
|
|
257
|
+
* Kept short (~30 chars) so UIs can advertise it as a
|
|
258
|
+
* viewport-truncation-safe copy target for the full authorization URL.
|
|
259
|
+
*/
|
|
260
|
+
#launchUrlIfSafe(port: number): string | undefined {
|
|
261
|
+
if (this.callbackPath === LAUNCH_PATH) return undefined;
|
|
262
|
+
if (this.redirectUri) {
|
|
263
|
+
try {
|
|
264
|
+
const parsed = new URL(this.redirectUri);
|
|
265
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") return undefined;
|
|
266
|
+
if (parsed.hostname !== "localhost" && parsed.hostname !== "127.0.0.1" && parsed.hostname !== "[::1]") {
|
|
267
|
+
return undefined;
|
|
268
|
+
}
|
|
269
|
+
if (parsed.pathname === LAUNCH_PATH) return undefined;
|
|
270
|
+
} catch {
|
|
271
|
+
// A redirectUri even WHATWG URL cannot parse certainly does not
|
|
272
|
+
// return to this server — never advertise a launch URL for it.
|
|
273
|
+
return undefined;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
return `http://${this.callbackHostname}:${port}${LAUNCH_PATH}`;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Create HTTP server for OAuth callback.
|
|
281
|
+
*/
|
|
282
|
+
#createServer(port: number, expectedState: string): Bun.Server<unknown> {
|
|
283
|
+
const hostname = this.callbackHostname === DEFAULT_HOSTNAME ? "127.0.0.1" : this.callbackHostname;
|
|
284
|
+
return Bun.serve({
|
|
285
|
+
hostname,
|
|
286
|
+
port,
|
|
287
|
+
reusePort: false,
|
|
288
|
+
fetch: req => this.#handleCallback(req, expectedState),
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Handle OAuth callback HTTP request. Two routes on the same loopback server:
|
|
294
|
+
* - `callbackPath` (default `/callback`) — the provider redirect target.
|
|
295
|
+
* - {@link LAUNCH_PATH} (`/launch`) — 302 to the pending authorization URL so
|
|
296
|
+
* viewport-safe copy targets can survive TUI truncation.
|
|
297
|
+
*
|
|
298
|
+
* `callbackPath` wins any collision: an OMP config that pins the provider
|
|
299
|
+
* redirect at `/launch` (via `oauth.callbackPath` or a loopback
|
|
300
|
+
* `oauth.redirectUri`) must resolve the callback normally rather than
|
|
301
|
+
* self-redirect. `#startCallbackServer` also suppresses `launchUrl` in that
|
|
302
|
+
* case, so the launch route is never advertised when it would collide.
|
|
303
|
+
*/
|
|
304
|
+
#handleCallback(req: Request, expectedState: string): Response {
|
|
305
|
+
const url = new URL(req.url);
|
|
306
|
+
|
|
307
|
+
if (url.pathname !== this.callbackPath) {
|
|
308
|
+
if (url.pathname === LAUNCH_PATH) {
|
|
309
|
+
const pending = this.#pendingAuthUrl;
|
|
310
|
+
if (!pending) {
|
|
311
|
+
return new Response("OAuth launch URL is no longer active", { status: 503 });
|
|
312
|
+
}
|
|
313
|
+
return Response.redirect(pending, 302);
|
|
314
|
+
}
|
|
315
|
+
return new Response("Not Found", { status: 404 });
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
const code = url.searchParams.get("code");
|
|
319
|
+
const state = url.searchParams.get("state") || "";
|
|
320
|
+
const error = url.searchParams.get("error") || "";
|
|
321
|
+
const errorDescription = url.searchParams.get("error_description") || error;
|
|
322
|
+
|
|
323
|
+
type OkState = { ok: true; code: string; state: string };
|
|
324
|
+
type ErrorState = { ok?: false; error?: string };
|
|
325
|
+
let resultState: OkState | ErrorState;
|
|
326
|
+
|
|
327
|
+
if (error) {
|
|
328
|
+
resultState = { ok: false, error: `Authorization failed: ${errorDescription}` };
|
|
329
|
+
} else if (!code) {
|
|
330
|
+
resultState = { ok: false, error: "Missing authorization code" };
|
|
331
|
+
} else if (expectedState && state !== expectedState) {
|
|
332
|
+
resultState = { ok: false, error: "State mismatch - possible CSRF attack" };
|
|
333
|
+
} else {
|
|
334
|
+
resultState = { ok: true, code, state };
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
if (resultState.ok) {
|
|
338
|
+
const resolve = this.#callbackResolve;
|
|
339
|
+
queueMicrotask(() => {
|
|
340
|
+
resolve?.({ code: resultState.code, state: resultState.state });
|
|
341
|
+
});
|
|
342
|
+
} else if (error && (!expectedState || state === expectedState)) {
|
|
343
|
+
// The redirect carries our state nonce, so it came from the genuine
|
|
344
|
+
// authorization flow (e.g. the user denied the consent screen).
|
|
345
|
+
// Surface the denial now instead of leaving the login waiting for
|
|
346
|
+
// the 5-minute timeout. Errors WITHOUT the expected state stay
|
|
347
|
+
// ignored — any local process can forge those (#4106).
|
|
348
|
+
const reject = this.#callbackReject;
|
|
349
|
+
const message = resultState.error ?? `Authorization failed: ${errorDescription}`;
|
|
350
|
+
queueMicrotask(() => {
|
|
351
|
+
reject?.(new AIError.OAuthError(message, { kind: "device-auth" }));
|
|
352
|
+
});
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
return new Response(
|
|
356
|
+
(templateHtml as unknown as string).replaceAll("__OAUTH_STATE__", JSON.stringify(resultState)),
|
|
357
|
+
{
|
|
358
|
+
status: resultState.ok ? 200 : 500,
|
|
359
|
+
headers: { "Content-Type": "text/html" },
|
|
360
|
+
},
|
|
361
|
+
);
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
/**
|
|
365
|
+
* Wait for OAuth callback or manual input (whichever comes first).
|
|
366
|
+
*/
|
|
367
|
+
#waitForCallback(expectedState: string): Promise<CallbackResult> {
|
|
368
|
+
const timeoutSignal = AbortSignal.timeout(DEFAULT_TIMEOUT);
|
|
369
|
+
const signal = this.ctrl.signal ? AbortSignal.any([this.ctrl.signal, timeoutSignal]) : timeoutSignal;
|
|
370
|
+
if (signal.aborted) return Promise.reject(this.#loginCancelledError());
|
|
371
|
+
|
|
372
|
+
const callback = Promise.withResolvers<CallbackResult>();
|
|
373
|
+
this.#callbackResolve = callback.resolve;
|
|
374
|
+
this.#callbackReject = callback.reject;
|
|
375
|
+
|
|
376
|
+
signal.addEventListener("abort", () => {
|
|
377
|
+
this.#callbackResolve = undefined;
|
|
378
|
+
this.#callbackReject = undefined;
|
|
379
|
+
callback.reject(new AIError.LoginCancelledError(`OAuth callback cancelled: ${signal.reason}`));
|
|
380
|
+
});
|
|
381
|
+
const callbackPromise = callback.promise;
|
|
382
|
+
|
|
383
|
+
// Manual input race (if supported)
|
|
384
|
+
if (this.ctrl.onManualCodeInput) {
|
|
385
|
+
const requestManualInput = this.ctrl.onManualCodeInput;
|
|
386
|
+
const manualPromise = (async (): Promise<CallbackResult> => {
|
|
387
|
+
while (true) {
|
|
388
|
+
const result = await Promise.race([
|
|
389
|
+
callbackPromise,
|
|
390
|
+
requestManualInput()
|
|
391
|
+
.then((input): CallbackResult | null => {
|
|
392
|
+
const parsed = parseCallbackInput(input);
|
|
393
|
+
if (!parsed.code) return null;
|
|
394
|
+
if (expectedState && parsed.state && parsed.state !== expectedState) return null;
|
|
395
|
+
return { code: parsed.code, state: parsed.state ?? "" };
|
|
396
|
+
})
|
|
397
|
+
.catch((): CallbackResult | null => null),
|
|
398
|
+
]);
|
|
399
|
+
if (result) return result;
|
|
400
|
+
}
|
|
401
|
+
})();
|
|
402
|
+
|
|
403
|
+
return Promise.race([callbackPromise, manualPromise]);
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
return callbackPromise;
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
/**
|
|
411
|
+
* Parse a redirect URL or code string to extract code and state.
|
|
412
|
+
*/
|
|
413
|
+
export function parseCallbackInput(input: string): { code?: string; state?: string } {
|
|
414
|
+
const value = input.trim();
|
|
415
|
+
if (!value) return {};
|
|
416
|
+
|
|
417
|
+
try {
|
|
418
|
+
const url = new URL(value);
|
|
419
|
+
return {
|
|
420
|
+
code: url.searchParams.get("code") ?? undefined,
|
|
421
|
+
state: url.searchParams.get("state") ?? undefined,
|
|
422
|
+
};
|
|
423
|
+
} catch {
|
|
424
|
+
// Not a URL - check for query string format
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
if (value.includes("code=")) {
|
|
428
|
+
const params = new URLSearchParams(value.replace(/^[?#]/, ""));
|
|
429
|
+
return {
|
|
430
|
+
code: params.get("code") ?? undefined,
|
|
431
|
+
state: params.get("state") ?? undefined,
|
|
432
|
+
};
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
// Assume raw code, possibly with state after #
|
|
436
|
+
const [code, state] = value.split("#", 2);
|
|
437
|
+
return { code, state };
|
|
438
|
+
}
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import * as AIError from "../../error";
|
|
2
|
+
import { generatePKCE } from "./pkce";
|
|
3
|
+
import type { OAuthCredentials } from "./types";
|
|
4
|
+
|
|
5
|
+
const CURSOR_LOGIN_URL = "https://cursor.com/loginDeepControl";
|
|
6
|
+
const CURSOR_POLL_URL = "https://api2.cursor.sh/auth/poll";
|
|
7
|
+
const CURSOR_REFRESH_URL = "https://api2.cursor.sh/auth/exchange_user_api_key";
|
|
8
|
+
|
|
9
|
+
const POLL_MAX_ATTEMPTS = 150;
|
|
10
|
+
const POLL_BASE_DELAY = 1000;
|
|
11
|
+
const POLL_MAX_DELAY = 10000;
|
|
12
|
+
const POLL_BACKOFF_MULTIPLIER = 1.2;
|
|
13
|
+
|
|
14
|
+
export interface CursorAuthParams {
|
|
15
|
+
verifier: string;
|
|
16
|
+
challenge: string;
|
|
17
|
+
uuid: string;
|
|
18
|
+
loginUrl: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export async function generateCursorAuthParams(): Promise<CursorAuthParams> {
|
|
22
|
+
const { verifier, challenge } = await generatePKCE();
|
|
23
|
+
const uuid = crypto.randomUUID();
|
|
24
|
+
|
|
25
|
+
const params = new URLSearchParams({
|
|
26
|
+
challenge,
|
|
27
|
+
uuid,
|
|
28
|
+
mode: "login",
|
|
29
|
+
redirectTarget: "cli",
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
const loginUrl = `${CURSOR_LOGIN_URL}?${params.toString()}`;
|
|
33
|
+
|
|
34
|
+
return { verifier, challenge, uuid, loginUrl };
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export async function pollCursorAuth(
|
|
38
|
+
uuid: string,
|
|
39
|
+
verifier: string,
|
|
40
|
+
): Promise<{ accessToken: string; refreshToken: string }> {
|
|
41
|
+
let delay = POLL_BASE_DELAY;
|
|
42
|
+
let consecutiveErrors = 0;
|
|
43
|
+
|
|
44
|
+
for (let attempt = 0; attempt < POLL_MAX_ATTEMPTS; attempt++) {
|
|
45
|
+
await Bun.sleep(delay);
|
|
46
|
+
|
|
47
|
+
try {
|
|
48
|
+
const response = await fetch(`${CURSOR_POLL_URL}?uuid=${uuid}&verifier=${verifier}`);
|
|
49
|
+
|
|
50
|
+
if (response.status === 404) {
|
|
51
|
+
consecutiveErrors = 0;
|
|
52
|
+
delay = Math.min(delay * POLL_BACKOFF_MULTIPLIER, POLL_MAX_DELAY);
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (response.ok) {
|
|
57
|
+
const data = (await response.json()) as {
|
|
58
|
+
accessToken: string;
|
|
59
|
+
refreshToken: string;
|
|
60
|
+
};
|
|
61
|
+
return {
|
|
62
|
+
accessToken: data.accessToken,
|
|
63
|
+
refreshToken: data.refreshToken,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
throw new AIError.OAuthError(`Poll failed: ${response.status}`, {
|
|
68
|
+
kind: "polling",
|
|
69
|
+
provider: "cursor",
|
|
70
|
+
status: response.status,
|
|
71
|
+
});
|
|
72
|
+
} catch {
|
|
73
|
+
consecutiveErrors++;
|
|
74
|
+
if (consecutiveErrors >= 3) {
|
|
75
|
+
throw new AIError.OAuthError("Too many consecutive errors during Cursor auth polling", {
|
|
76
|
+
kind: "polling",
|
|
77
|
+
provider: "cursor",
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
throw new AIError.OAuthError("Cursor authentication polling timeout", {
|
|
84
|
+
kind: "timeout",
|
|
85
|
+
provider: "cursor",
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export async function loginCursor(
|
|
90
|
+
onAuthUrl: (url: string) => void,
|
|
91
|
+
onPollStart?: () => void,
|
|
92
|
+
): Promise<OAuthCredentials> {
|
|
93
|
+
const { verifier, uuid, loginUrl } = await generateCursorAuthParams();
|
|
94
|
+
|
|
95
|
+
onAuthUrl(loginUrl);
|
|
96
|
+
onPollStart?.();
|
|
97
|
+
|
|
98
|
+
const { accessToken, refreshToken } = await pollCursorAuth(uuid, verifier);
|
|
99
|
+
|
|
100
|
+
const expiresAt = getTokenExpiry(accessToken);
|
|
101
|
+
|
|
102
|
+
return {
|
|
103
|
+
access: accessToken,
|
|
104
|
+
refresh: refreshToken,
|
|
105
|
+
expires: expiresAt,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export async function refreshCursorToken(apiKeyOrRefreshToken: string): Promise<OAuthCredentials> {
|
|
110
|
+
const response = await fetch(CURSOR_REFRESH_URL, {
|
|
111
|
+
method: "POST",
|
|
112
|
+
headers: {
|
|
113
|
+
Authorization: `Bearer ${apiKeyOrRefreshToken}`,
|
|
114
|
+
"Content-Type": "application/json",
|
|
115
|
+
},
|
|
116
|
+
body: "{}",
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
if (!response.ok) {
|
|
120
|
+
const error = await response.text();
|
|
121
|
+
throw new AIError.OAuthError(`Cursor token refresh failed: ${error}`, {
|
|
122
|
+
kind: "token-refresh",
|
|
123
|
+
provider: "cursor",
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const data = (await response.json()) as {
|
|
128
|
+
accessToken: string;
|
|
129
|
+
refreshToken: string;
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
const expiresAt = getTokenExpiry(data.accessToken);
|
|
133
|
+
|
|
134
|
+
return {
|
|
135
|
+
access: data.accessToken,
|
|
136
|
+
refresh: data.refreshToken || apiKeyOrRefreshToken,
|
|
137
|
+
expires: expiresAt,
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function decodeCursorAccessTokenPayload(token: string): unknown | undefined {
|
|
142
|
+
const parts = token.split(".");
|
|
143
|
+
if (parts.length !== 3) return undefined;
|
|
144
|
+
const payload = parts[1];
|
|
145
|
+
if (!payload) return undefined;
|
|
146
|
+
return JSON.parse(atob(payload.replace(/-/g, "+").replace(/_/g, "/")));
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export function extractCursorAccessTokenUserId(accessToken: string): string | undefined {
|
|
150
|
+
try {
|
|
151
|
+
const payload = decodeCursorAccessTokenPayload(accessToken);
|
|
152
|
+
if (!payload || typeof payload !== "object" || !("sub" in payload) || typeof payload.sub !== "string") {
|
|
153
|
+
return undefined;
|
|
154
|
+
}
|
|
155
|
+
const { sub } = payload;
|
|
156
|
+
const parts = sub.split("|");
|
|
157
|
+
const userId = (parts.length > 1 ? parts[1] : sub).trim();
|
|
158
|
+
return userId || undefined;
|
|
159
|
+
} catch {
|
|
160
|
+
return undefined;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function getTokenExpiry(token: string): number {
|
|
165
|
+
try {
|
|
166
|
+
const decoded = decodeCursorAccessTokenPayload(token);
|
|
167
|
+
if (decoded && typeof decoded === "object" && "exp" in decoded && typeof decoded.exp === "number") {
|
|
168
|
+
return decoded.exp * 1000 - 5 * 60 * 1000;
|
|
169
|
+
}
|
|
170
|
+
} catch {
|
|
171
|
+
// Ignore parsing errors
|
|
172
|
+
}
|
|
173
|
+
return Date.now() + 3600 * 1000;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export function isCursorTokenExpiringSoon(token: string, thresholdSeconds = 300): boolean {
|
|
177
|
+
try {
|
|
178
|
+
const decoded = decodeCursorAccessTokenPayload(token);
|
|
179
|
+
if (!decoded || typeof decoded !== "object" || !("exp" in decoded) || typeof decoded.exp !== "number") {
|
|
180
|
+
return true;
|
|
181
|
+
}
|
|
182
|
+
const currentTime = Math.floor(Date.now() / 1000);
|
|
183
|
+
return decoded.exp - currentTime < thresholdSeconds;
|
|
184
|
+
} catch {
|
|
185
|
+
return true;
|
|
186
|
+
}
|
|
187
|
+
}
|