@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,30 @@
|
|
|
1
|
+
import type { Api, Model, ProviderResponseMetadata, StreamOptions } from "../types";
|
|
2
|
+
|
|
3
|
+
export function normalizeProviderResponse(
|
|
4
|
+
response: Response,
|
|
5
|
+
requestId?: string | null,
|
|
6
|
+
metadata?: Record<string, unknown>,
|
|
7
|
+
): ProviderResponseMetadata {
|
|
8
|
+
const headers: Record<string, string> = {};
|
|
9
|
+
response.headers.forEach((value, key) => {
|
|
10
|
+
headers[key.toLowerCase()] = value;
|
|
11
|
+
});
|
|
12
|
+
const providerResponse: ProviderResponseMetadata = {
|
|
13
|
+
status: response.status,
|
|
14
|
+
headers,
|
|
15
|
+
};
|
|
16
|
+
if (requestId !== undefined) providerResponse.requestId = requestId;
|
|
17
|
+
if (metadata !== undefined) providerResponse.metadata = metadata;
|
|
18
|
+
return providerResponse;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export async function notifyProviderResponse(
|
|
22
|
+
options: Pick<StreamOptions, "onResponse"> | undefined,
|
|
23
|
+
response: Response,
|
|
24
|
+
model?: Model<Api>,
|
|
25
|
+
requestId?: string | null,
|
|
26
|
+
metadata?: Record<string, unknown>,
|
|
27
|
+
): Promise<void> {
|
|
28
|
+
if (!options?.onResponse) return;
|
|
29
|
+
await options.onResponse(normalizeProviderResponse(response, requestId, metadata), model);
|
|
30
|
+
}
|
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
import * as net from "node:net";
|
|
2
|
+
import * as tls from "node:tls";
|
|
3
|
+
import * as AIError from "../error";
|
|
4
|
+
import type { FetchImpl } from "../types";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Checks if a host is local or cloud metadata, which should always bypass the proxy
|
|
8
|
+
* (e.g. localhost, 127/8, ::1, 169.254.169.254, metadata.google.internal).
|
|
9
|
+
*/
|
|
10
|
+
export function isLocalOrMetadataHost(host: string): boolean {
|
|
11
|
+
const lowerHost = host.toLowerCase();
|
|
12
|
+
|
|
13
|
+
// Hostnames: localhost and the cloud metadata service.
|
|
14
|
+
if (lowerHost === "localhost" || lowerHost.endsWith(".localhost") || lowerHost === "metadata.google.internal") {
|
|
15
|
+
return true;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Strip IPv6 brackets before numeric checks.
|
|
19
|
+
const ip = lowerHost.replace(/^\[|\]$/g, "");
|
|
20
|
+
|
|
21
|
+
// IPv4 loopback (127/8), unspecified (0/8), RFC1918 private (10/8, 172.16/12,
|
|
22
|
+
// 192.168/16) and link-local (169.254/16 — covers IMDS 169.254.169.254 and
|
|
23
|
+
// ECS credentials 169.254.170.2). None are reachable through a remote egress
|
|
24
|
+
// proxy, and credential/metadata probes must never leak to one.
|
|
25
|
+
const v4 = ip.match(/^(\d{1,3})\.(\d{1,3})\.\d{1,3}\.\d{1,3}$/);
|
|
26
|
+
if (v4) {
|
|
27
|
+
const a = Number(v4[1]);
|
|
28
|
+
const b = Number(v4[2]);
|
|
29
|
+
if (a === 127 || a === 10 || a === 0) return true;
|
|
30
|
+
if (a === 169 && b === 254) return true;
|
|
31
|
+
if (a === 192 && b === 168) return true;
|
|
32
|
+
if (a === 172 && b >= 16 && b <= 31) return true;
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// IPv6 loopback (::1), unspecified (::), link-local (fe80::/10) and
|
|
37
|
+
// unique-local (fc00::/7 — covers EC2 IPv6 IMDS fd00:ec2::254).
|
|
38
|
+
if (ip === "::1" || ip === "::") return true;
|
|
39
|
+
if (/^fe[89ab][0-9a-f]:/.test(ip)) return true;
|
|
40
|
+
if (/^f[cd][0-9a-f]{2}:/.test(ip)) return true;
|
|
41
|
+
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Check if the url should bypass the proxy due to hard-coded localhost/metadata checks
|
|
47
|
+
* or custom NO_PROXY/no_proxy environment variables rules.
|
|
48
|
+
*/
|
|
49
|
+
export function shouldBypassProxy(urlObj: URL): boolean {
|
|
50
|
+
if (isLocalOrMetadataHost(urlObj.hostname)) {
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const noProxyVal = Bun.env.NO_PROXY || Bun.env.no_proxy;
|
|
55
|
+
if (!noProxyVal) {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const rules = noProxyVal
|
|
60
|
+
.split(/[,\s]+/)
|
|
61
|
+
.map(r => r.trim())
|
|
62
|
+
.filter(Boolean);
|
|
63
|
+
const targetHost = urlObj.hostname.toLowerCase();
|
|
64
|
+
const targetPort = urlObj.port || (urlObj.protocol === "https:" || urlObj.protocol === "wss:" ? "443" : "80");
|
|
65
|
+
|
|
66
|
+
for (const rule of rules) {
|
|
67
|
+
if (rule === "*") {
|
|
68
|
+
return true;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
let ruleHost = rule.toLowerCase();
|
|
72
|
+
let rulePort: string | undefined;
|
|
73
|
+
|
|
74
|
+
if (ruleHost.includes("]:")) {
|
|
75
|
+
const lastColon = ruleHost.lastIndexOf(":");
|
|
76
|
+
rulePort = ruleHost.slice(lastColon + 1);
|
|
77
|
+
ruleHost = ruleHost.slice(0, lastColon);
|
|
78
|
+
} else if (!ruleHost.includes("]") && ruleHost.includes(":")) {
|
|
79
|
+
const lastColon = ruleHost.lastIndexOf(":");
|
|
80
|
+
rulePort = ruleHost.slice(lastColon + 1);
|
|
81
|
+
ruleHost = ruleHost.slice(0, lastColon);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Strip IPv6 brackets
|
|
85
|
+
ruleHost = ruleHost.replace(/^\[|\]$/g, "");
|
|
86
|
+
|
|
87
|
+
if (rulePort && rulePort !== targetPort) {
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Match host part
|
|
92
|
+
if (ruleHost.startsWith(".")) {
|
|
93
|
+
const suffix = ruleHost;
|
|
94
|
+
const cleanRule = ruleHost.slice(1);
|
|
95
|
+
if (targetHost === cleanRule || targetHost.endsWith(suffix)) {
|
|
96
|
+
return true;
|
|
97
|
+
}
|
|
98
|
+
} else {
|
|
99
|
+
if (targetHost === ruleHost || targetHost.endsWith(`.${ruleHost}`)) {
|
|
100
|
+
return true;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return false;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const proxyCache = new Map<string, string | undefined>();
|
|
109
|
+
|
|
110
|
+
/** Test seam: clears the provider proxy cache. */
|
|
111
|
+
export function __resetProxyCache(): void {
|
|
112
|
+
proxyCache.clear();
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Normalizes provider id (e.g. github-copilot -> PI_PROXY_GITHUB_COPILOT) and looks it up.
|
|
117
|
+
* If not found, falls back to PI_PROXY. Results are memoized because env values are static
|
|
118
|
+
* for the lifetime of the process and this function is called for every outgoing request.
|
|
119
|
+
*/
|
|
120
|
+
export function getProxyForProvider(provider: string): string | undefined {
|
|
121
|
+
if (proxyCache.has(provider)) {
|
|
122
|
+
return proxyCache.get(provider);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const normalized = provider.toUpperCase().replace(/[^A-Z0-9]/g, "_");
|
|
126
|
+
const envKey = `PI_PROXY_${normalized}`;
|
|
127
|
+
const value = Bun.env[envKey] || Bun.env.PI_PROXY;
|
|
128
|
+
proxyCache.set(provider, value);
|
|
129
|
+
return value;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/** Resolves provider-specific and standard proxy variables for a target URL, honoring NO_PROXY. */
|
|
133
|
+
export function getProxyForUrl(provider: string, url: URL): string | undefined {
|
|
134
|
+
if (shouldBypassProxy(url)) return undefined;
|
|
135
|
+
const protocolProxy =
|
|
136
|
+
url.protocol === "https:" || url.protocol === "wss:"
|
|
137
|
+
? Bun.env.HTTPS_PROXY || Bun.env.https_proxy
|
|
138
|
+
: Bun.env.HTTP_PROXY || Bun.env.http_proxy;
|
|
139
|
+
return getProxyForProvider(provider) || protocolProxy || Bun.env.ALL_PROXY || Bun.env.all_proxy || undefined;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Wraps a fetch implementation to inject proxy options for non-local hosts.
|
|
144
|
+
*/
|
|
145
|
+
export function wrapFetchForProxy(fetchImpl: FetchImpl, provider: string): FetchImpl {
|
|
146
|
+
const proxyUrl = getProxyForProvider(provider);
|
|
147
|
+
if (!proxyUrl) {
|
|
148
|
+
return fetchImpl;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const wrapped = async (input: string | URL | Request, init?: RequestInit): Promise<Response> => {
|
|
152
|
+
const urlStr = input instanceof Request ? input.url : input.toString();
|
|
153
|
+
let urlObj: URL;
|
|
154
|
+
try {
|
|
155
|
+
urlObj = new URL(urlStr);
|
|
156
|
+
} catch {
|
|
157
|
+
// Fallback to calling fetch unmodified if URL is unparseable
|
|
158
|
+
return fetchImpl(input, init);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if (shouldBypassProxy(urlObj)) {
|
|
162
|
+
return fetchImpl(input, init);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const mergedInit = { ...(init ?? {}), proxy: proxyUrl };
|
|
166
|
+
return fetchImpl(input, mergedInit);
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
if (fetchImpl.preconnect) {
|
|
170
|
+
wrapped.preconnect = fetchImpl.preconnect;
|
|
171
|
+
}
|
|
172
|
+
return wrapped;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export interface ConnectProxiedSocketOptions {
|
|
176
|
+
/** Caller cancellation for the proxy TCP/TLS handshake and CONNECT tunnel. */
|
|
177
|
+
signal?: AbortSignal;
|
|
178
|
+
/** Maximum wall-clock time to establish the final TLS tunnel. Disabled when absent or non-positive. */
|
|
179
|
+
timeoutMs?: number;
|
|
180
|
+
/** Target TLS profile. Cursor defaults to HTTP/2 when this is absent. */
|
|
181
|
+
tls?: tls.ConnectionOptions;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Tunnel a socket connection through an HTTP CONNECT proxy.
|
|
186
|
+
* This is used specifically to wrap Node's `http2.connect(baseUrl, { createConnection })` for Cursor.
|
|
187
|
+
*/
|
|
188
|
+
export async function connectProxiedSocket(
|
|
189
|
+
proxyUrlStr: string,
|
|
190
|
+
targetUrlStr: string,
|
|
191
|
+
options?: ConnectProxiedSocketOptions,
|
|
192
|
+
): Promise<tls.TLSSocket> {
|
|
193
|
+
if (options?.signal?.aborted) {
|
|
194
|
+
throw new AIError.AbortError("Proxy tunnel aborted");
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const proxyUrl = new URL(proxyUrlStr);
|
|
198
|
+
const targetUrl = new URL(targetUrlStr);
|
|
199
|
+
|
|
200
|
+
const useProxySsl = proxyUrl.protocol === "https:";
|
|
201
|
+
const proxyPort = proxyUrl.port ? parseInt(proxyUrl.port, 10) : useProxySsl ? 443 : 80;
|
|
202
|
+
const proxyHost = proxyUrl.hostname;
|
|
203
|
+
|
|
204
|
+
const targetPort = targetUrl.port ? parseInt(targetUrl.port, 10) : 443;
|
|
205
|
+
const targetHost = targetUrl.hostname;
|
|
206
|
+
|
|
207
|
+
const { promise, resolve, reject } = Promise.withResolvers<tls.TLSSocket>();
|
|
208
|
+
|
|
209
|
+
const readyEvent = useProxySsl ? "secureConnect" : "connect";
|
|
210
|
+
let rawSocket: net.Socket | undefined;
|
|
211
|
+
let tunnelSocket: tls.TLSSocket | undefined;
|
|
212
|
+
let timeout: NodeJS.Timeout | undefined;
|
|
213
|
+
let responseData = "";
|
|
214
|
+
let settled = false;
|
|
215
|
+
|
|
216
|
+
const cleanup = (): void => {
|
|
217
|
+
if (timeout) {
|
|
218
|
+
clearTimeout(timeout);
|
|
219
|
+
timeout = undefined;
|
|
220
|
+
}
|
|
221
|
+
options?.signal?.removeEventListener("abort", onAbort);
|
|
222
|
+
rawSocket?.off("error", onRawError);
|
|
223
|
+
rawSocket?.off(readyEvent, onProxyReady);
|
|
224
|
+
rawSocket?.off("data", onProxyData);
|
|
225
|
+
tunnelSocket?.off("secureConnect", onTunnelReady);
|
|
226
|
+
tunnelSocket?.off("error", onTunnelError);
|
|
227
|
+
};
|
|
228
|
+
const destroyInProgress = (): void => {
|
|
229
|
+
tunnelSocket?.destroy();
|
|
230
|
+
rawSocket?.destroy();
|
|
231
|
+
};
|
|
232
|
+
const rejectOnce = (error: Error): void => {
|
|
233
|
+
if (settled) return;
|
|
234
|
+
settled = true;
|
|
235
|
+
cleanup();
|
|
236
|
+
destroyInProgress();
|
|
237
|
+
reject(error);
|
|
238
|
+
};
|
|
239
|
+
const resolveOnce = (socket: tls.TLSSocket): void => {
|
|
240
|
+
if (settled) return;
|
|
241
|
+
settled = true;
|
|
242
|
+
cleanup();
|
|
243
|
+
resolve(socket);
|
|
244
|
+
};
|
|
245
|
+
const onAbort = (): void => rejectOnce(new AIError.AbortError("Proxy tunnel aborted"));
|
|
246
|
+
const onRawError = (error: Error): void => rejectOnce(error);
|
|
247
|
+
const onTunnelError = (error: Error): void => rejectOnce(error);
|
|
248
|
+
const onTunnelReady = (): void => {
|
|
249
|
+
if (!tunnelSocket) return;
|
|
250
|
+
resolveOnce(tunnelSocket);
|
|
251
|
+
};
|
|
252
|
+
const onProxyData = (chunk: Buffer): void => {
|
|
253
|
+
if (!rawSocket) return;
|
|
254
|
+
responseData += chunk.toString("binary");
|
|
255
|
+
if (!responseData.includes("\r\n\r\n")) return;
|
|
256
|
+
|
|
257
|
+
rawSocket.off("data", onProxyData);
|
|
258
|
+
rawSocket.off("error", onRawError);
|
|
259
|
+
|
|
260
|
+
const firstLine = responseData.split("\r\n")[0];
|
|
261
|
+
if (!firstLine.includes(" 200 ")) {
|
|
262
|
+
rejectOnce(new AIError.ValidationError(`Proxy tunnel failed: ${firstLine}`));
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
const tlsOptions = options?.tls;
|
|
267
|
+
tunnelSocket = tls.connect({
|
|
268
|
+
...tlsOptions,
|
|
269
|
+
socket: rawSocket,
|
|
270
|
+
servername: tlsOptions?.servername ?? targetHost,
|
|
271
|
+
ALPNProtocols: tlsOptions?.ALPNProtocols ?? ["h2"],
|
|
272
|
+
});
|
|
273
|
+
tunnelSocket.once("secureConnect", onTunnelReady);
|
|
274
|
+
tunnelSocket.once("error", onTunnelError);
|
|
275
|
+
};
|
|
276
|
+
const onProxyReady = (): void => {
|
|
277
|
+
if (!rawSocket) return;
|
|
278
|
+
let connectReq = `CONNECT ${targetHost}:${targetPort} HTTP/1.1\r\n` + `Host: ${targetHost}:${targetPort}\r\n`;
|
|
279
|
+
|
|
280
|
+
if (proxyUrl.username || proxyUrl.password) {
|
|
281
|
+
const creds = Buffer.from(
|
|
282
|
+
`${decodeURIComponent(proxyUrl.username)}:${decodeURIComponent(proxyUrl.password)}`,
|
|
283
|
+
).toString("base64");
|
|
284
|
+
connectReq += `Proxy-Authorization: Basic ${creds}\r\n`;
|
|
285
|
+
}
|
|
286
|
+
connectReq += "\r\n";
|
|
287
|
+
|
|
288
|
+
rawSocket.write(connectReq);
|
|
289
|
+
rawSocket.on("data", onProxyData);
|
|
290
|
+
};
|
|
291
|
+
|
|
292
|
+
options?.signal?.addEventListener("abort", onAbort, { once: true });
|
|
293
|
+
if (options?.timeoutMs !== undefined && Number.isFinite(options.timeoutMs) && options.timeoutMs > 0) {
|
|
294
|
+
const timeoutMs = Math.trunc(options.timeoutMs);
|
|
295
|
+
timeout = setTimeout(() => {
|
|
296
|
+
rejectOnce(new AIError.StreamTimeoutError(`Proxy tunnel timed out after ${timeoutMs}ms`));
|
|
297
|
+
}, timeoutMs);
|
|
298
|
+
timeout.unref?.();
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
rawSocket = useProxySsl
|
|
302
|
+
? tls.connect({
|
|
303
|
+
host: proxyHost,
|
|
304
|
+
port: proxyPort,
|
|
305
|
+
})
|
|
306
|
+
: net.connect({
|
|
307
|
+
host: proxyHost,
|
|
308
|
+
port: proxyPort,
|
|
309
|
+
});
|
|
310
|
+
rawSocket.once("error", onRawError);
|
|
311
|
+
rawSocket.once(readyEvent, onProxyReady);
|
|
312
|
+
|
|
313
|
+
return promise;
|
|
314
|
+
}
|
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
import { Buffer } from "node:buffer";
|
|
2
|
+
import * as fs from "node:fs/promises";
|
|
3
|
+
import type { FetchImpl } from "../types";
|
|
4
|
+
|
|
5
|
+
const REQUEST_DEBUG_ENV = "PI_REQ_DEBUG";
|
|
6
|
+
const DEBUG_FETCH_MARKER = Symbol("omp.requestDebugFetch");
|
|
7
|
+
const textEncoder = new TextEncoder();
|
|
8
|
+
const utf8Decoder = new TextDecoder("utf-8", { fatal: true });
|
|
9
|
+
|
|
10
|
+
let nextSessionId = 1;
|
|
11
|
+
|
|
12
|
+
type DebugFetch = FetchImpl & { [DEBUG_FETCH_MARKER]?: true };
|
|
13
|
+
type RequestBodyInit = NonNullable<RequestInit["body"]>;
|
|
14
|
+
|
|
15
|
+
type RequestDebugBody = { body: unknown } | { bodyText: string } | { bodyBase64: string } | { bodyUnavailable: string };
|
|
16
|
+
|
|
17
|
+
export type RequestDebugHeaders = Headers | Record<string, string | string[] | number | undefined | null> | undefined;
|
|
18
|
+
|
|
19
|
+
export interface RequestDebugPayload {
|
|
20
|
+
method: string;
|
|
21
|
+
url: string;
|
|
22
|
+
headers?: RequestDebugHeaders;
|
|
23
|
+
body?: unknown;
|
|
24
|
+
bodyText?: string;
|
|
25
|
+
bodyBase64?: string;
|
|
26
|
+
bodyUnavailable?: string;
|
|
27
|
+
protocol?: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
interface ReservedRequestDebugFile {
|
|
31
|
+
id: number;
|
|
32
|
+
requestPath: string;
|
|
33
|
+
responsePath: string;
|
|
34
|
+
handle: fs.FileHandle;
|
|
35
|
+
overwrite: boolean;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface RequestDebugResponseLog {
|
|
39
|
+
write(chunk: Uint8Array | string): void;
|
|
40
|
+
close(): Promise<void>;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface RequestDebugSession {
|
|
44
|
+
readonly id: number;
|
|
45
|
+
readonly requestPath: string;
|
|
46
|
+
readonly responsePath: string;
|
|
47
|
+
openResponseLog(statusLine: string, headers?: RequestDebugHeaders): Promise<RequestDebugResponseLog>;
|
|
48
|
+
wrapResponse(response: Response): Promise<Response>;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function isRequestDebugEnvEnabled(): boolean {
|
|
52
|
+
return Bun.env[REQUEST_DEBUG_ENV] === "1";
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function isRequestDebugEnabled(): boolean {
|
|
56
|
+
return isRequestDebugEnvEnabled();
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function wrapFetchForRequestDebug(fetchImpl: FetchImpl): FetchImpl {
|
|
60
|
+
if (!isRequestDebugEnabled()) return fetchImpl;
|
|
61
|
+
const maybeWrapped = fetchImpl as DebugFetch;
|
|
62
|
+
if (maybeWrapped[DEBUG_FETCH_MARKER]) return fetchImpl;
|
|
63
|
+
|
|
64
|
+
const wrapped = Object.assign(
|
|
65
|
+
async (input: string | URL | Request, init?: RequestInit): Promise<Response> => {
|
|
66
|
+
if (!isRequestDebugEnabled()) return fetchImpl(input, init);
|
|
67
|
+
const session = await createFetchRequestDebugSession(input, init);
|
|
68
|
+
const response = await fetchImpl(input, init);
|
|
69
|
+
return session.wrapResponse(response);
|
|
70
|
+
},
|
|
71
|
+
fetchImpl.preconnect ? { preconnect: fetchImpl.preconnect } : {},
|
|
72
|
+
{ [DEBUG_FETCH_MARKER]: true as const },
|
|
73
|
+
);
|
|
74
|
+
return wrapped;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function withRequestDebugFetch<T extends { fetch?: FetchImpl } | undefined>(options: T): T {
|
|
78
|
+
if (!isRequestDebugEnabled()) return options;
|
|
79
|
+
const fetchImpl = options?.fetch ?? (globalThis.fetch as FetchImpl);
|
|
80
|
+
const wrapped = wrapFetchForRequestDebug(fetchImpl);
|
|
81
|
+
return { ...(options ?? {}), fetch: wrapped } as T;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export async function createRequestDebugSession(payload: RequestDebugPayload): Promise<RequestDebugSession> {
|
|
85
|
+
const { id, requestPath, responsePath, handle, overwrite } = await reserveRequestDebugFile();
|
|
86
|
+
const requestDump: Record<string, unknown> = {
|
|
87
|
+
id,
|
|
88
|
+
protocol: payload.protocol ?? "http",
|
|
89
|
+
method: payload.method,
|
|
90
|
+
url: payload.url,
|
|
91
|
+
};
|
|
92
|
+
const headers = headersToRecord(payload.headers);
|
|
93
|
+
if (headers) requestDump.headers = headers;
|
|
94
|
+
if (payload.body !== undefined) requestDump.body = payload.body;
|
|
95
|
+
if (payload.bodyText !== undefined) requestDump.bodyText = payload.bodyText;
|
|
96
|
+
if (payload.bodyBase64 !== undefined) requestDump.bodyBase64 = payload.bodyBase64;
|
|
97
|
+
if (payload.bodyUnavailable !== undefined) requestDump.bodyUnavailable = payload.bodyUnavailable;
|
|
98
|
+
|
|
99
|
+
try {
|
|
100
|
+
await handle.writeFile(`${JSON.stringify(requestDump, null, 2)}\n`, "utf8");
|
|
101
|
+
} finally {
|
|
102
|
+
await handle.close();
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return new FileRequestDebugSession(id, requestPath, responsePath, overwrite);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
async function createFetchRequestDebugSession(
|
|
109
|
+
input: string | URL | Request,
|
|
110
|
+
init: RequestInit | undefined,
|
|
111
|
+
): Promise<RequestDebugSession> {
|
|
112
|
+
const headers = resolveRequestHeaders(input, init);
|
|
113
|
+
const body = await snapshotRequestBody(input, init, headers.get("content-type"));
|
|
114
|
+
return createRequestDebugSession({
|
|
115
|
+
method: resolveRequestMethod(input, init),
|
|
116
|
+
url: resolveRequestUrl(input),
|
|
117
|
+
headers,
|
|
118
|
+
...body,
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
class FileRequestDebugSession implements RequestDebugSession {
|
|
123
|
+
readonly id: number;
|
|
124
|
+
readonly requestPath: string;
|
|
125
|
+
readonly responsePath: string;
|
|
126
|
+
readonly #overwriteResponseLog: boolean;
|
|
127
|
+
|
|
128
|
+
constructor(id: number, requestPath: string, responsePath: string, overwriteResponseLog: boolean) {
|
|
129
|
+
this.id = id;
|
|
130
|
+
this.requestPath = requestPath;
|
|
131
|
+
this.responsePath = responsePath;
|
|
132
|
+
this.#overwriteResponseLog = overwriteResponseLog;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
async openResponseLog(statusLine: string, headers?: RequestDebugHeaders): Promise<RequestDebugResponseLog> {
|
|
136
|
+
const handle = await fs.open(this.responsePath, this.#overwriteResponseLog ? "w" : "wx");
|
|
137
|
+
const headerBlock = formatResponseHeaderBlock(statusLine, headers);
|
|
138
|
+
await handle.write(textEncoder.encode(headerBlock));
|
|
139
|
+
return new FileRequestDebugResponseLog(handle);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
async wrapResponse(response: Response): Promise<Response> {
|
|
143
|
+
const log = await this.openResponseLog(`HTTP ${response.status} ${response.statusText}`.trim(), response.headers);
|
|
144
|
+
if (!response.body) {
|
|
145
|
+
await log.close();
|
|
146
|
+
return response;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const reader = response.body.getReader();
|
|
150
|
+
const teed = new ReadableStream<Uint8Array>({
|
|
151
|
+
async pull(controller) {
|
|
152
|
+
try {
|
|
153
|
+
const { done, value } = await reader.read();
|
|
154
|
+
if (done) {
|
|
155
|
+
await log.close();
|
|
156
|
+
controller.close();
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
log.write(value);
|
|
160
|
+
controller.enqueue(value);
|
|
161
|
+
} catch (error) {
|
|
162
|
+
await log.close().catch(() => undefined);
|
|
163
|
+
controller.error(error);
|
|
164
|
+
}
|
|
165
|
+
},
|
|
166
|
+
async cancel(reason) {
|
|
167
|
+
try {
|
|
168
|
+
await reader.cancel(reason);
|
|
169
|
+
} finally {
|
|
170
|
+
await log.close();
|
|
171
|
+
}
|
|
172
|
+
},
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
const wrapped = new Response(teed, {
|
|
176
|
+
status: response.status,
|
|
177
|
+
statusText: response.statusText,
|
|
178
|
+
headers: response.headers,
|
|
179
|
+
});
|
|
180
|
+
copyResponseMetadata(wrapped, response);
|
|
181
|
+
return wrapped;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
class FileRequestDebugResponseLog implements RequestDebugResponseLog {
|
|
186
|
+
#handle: fs.FileHandle | undefined;
|
|
187
|
+
#pending: Promise<void> = Promise.resolve();
|
|
188
|
+
#closed: Promise<void> | undefined;
|
|
189
|
+
|
|
190
|
+
constructor(handle: fs.FileHandle) {
|
|
191
|
+
this.#handle = handle;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
write(chunk: Uint8Array | string): void {
|
|
195
|
+
const handle = this.#handle;
|
|
196
|
+
if (!handle) return;
|
|
197
|
+
const bytes = typeof chunk === "string" ? textEncoder.encode(chunk) : chunk.slice();
|
|
198
|
+
this.#pending = this.#pending.then(async () => {
|
|
199
|
+
await handle.write(bytes);
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
close(): Promise<void> {
|
|
204
|
+
if (this.#closed) return this.#closed;
|
|
205
|
+
const handle = this.#handle;
|
|
206
|
+
if (!handle) return Promise.resolve();
|
|
207
|
+
this.#handle = undefined;
|
|
208
|
+
this.#closed = (async () => {
|
|
209
|
+
try {
|
|
210
|
+
await this.#pending;
|
|
211
|
+
} finally {
|
|
212
|
+
await handle.close();
|
|
213
|
+
}
|
|
214
|
+
})();
|
|
215
|
+
return this.#closed;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function copyResponseMetadata(target: Response, source: Response): void {
|
|
220
|
+
const sourceUrl = source.url;
|
|
221
|
+
if (!sourceUrl) return;
|
|
222
|
+
try {
|
|
223
|
+
Object.defineProperty(target, "url", { value: sourceUrl, configurable: true });
|
|
224
|
+
} catch {
|
|
225
|
+
// Some runtimes may expose Response.url as non-configurable. The body
|
|
226
|
+
// capture remains correct; callers that need url already tolerate the
|
|
227
|
+
// platform default on other response wrappers in this package.
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
async function reserveRequestDebugFile(): Promise<ReservedRequestDebugFile> {
|
|
232
|
+
for (;;) {
|
|
233
|
+
const id = nextSessionId++;
|
|
234
|
+
const requestPath = `rr-session-${id}.json`;
|
|
235
|
+
try {
|
|
236
|
+
const handle = await fs.open(requestPath, "wx");
|
|
237
|
+
return { id, requestPath, responsePath: `rr-session-${id}.res.log`, handle, overwrite: false };
|
|
238
|
+
} catch (error) {
|
|
239
|
+
if (isFileExistsError(error)) continue;
|
|
240
|
+
throw error;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
function resolveRequestMethod(input: string | URL | Request, init: RequestInit | undefined): string {
|
|
246
|
+
return (init?.method ?? (input instanceof Request ? input.method : "GET")).toUpperCase();
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
function resolveRequestUrl(input: string | URL | Request): string {
|
|
250
|
+
return input instanceof Request ? input.url : input.toString();
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
function resolveRequestHeaders(input: string | URL | Request, init: RequestInit | undefined): Headers {
|
|
254
|
+
if (init?.headers) return new Headers(init.headers);
|
|
255
|
+
return input instanceof Request ? new Headers(input.headers) : new Headers();
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
async function snapshotRequestBody(
|
|
259
|
+
input: string | URL | Request,
|
|
260
|
+
init: RequestInit | undefined,
|
|
261
|
+
contentType: string | null,
|
|
262
|
+
): Promise<RequestDebugBody | undefined> {
|
|
263
|
+
if (init?.body !== undefined && init.body !== null) return snapshotBodyInit(init.body, contentType);
|
|
264
|
+
if (input instanceof Request && input.body) {
|
|
265
|
+
return snapshotBytes(new Uint8Array(await input.clone().arrayBuffer()), contentType);
|
|
266
|
+
}
|
|
267
|
+
return undefined;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
async function snapshotBodyInit(body: RequestBodyInit, contentType: string | null): Promise<RequestDebugBody> {
|
|
271
|
+
if (typeof body === "string") return snapshotText(body, contentType);
|
|
272
|
+
if (body instanceof URLSearchParams) return { bodyText: body.toString() };
|
|
273
|
+
if (body instanceof FormData) return { bodyUnavailable: "FormData" };
|
|
274
|
+
if (body instanceof Blob) return snapshotBytes(new Uint8Array(await body.arrayBuffer()), body.type || contentType);
|
|
275
|
+
if (body instanceof ArrayBuffer) return snapshotBytes(new Uint8Array(body), contentType);
|
|
276
|
+
if (ArrayBuffer.isView(body)) {
|
|
277
|
+
return snapshotBytes(new Uint8Array(body.buffer, body.byteOffset, body.byteLength), contentType);
|
|
278
|
+
}
|
|
279
|
+
if (body instanceof ReadableStream) return { bodyUnavailable: "ReadableStream" };
|
|
280
|
+
return { bodyText: String(body) };
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
function snapshotBytes(bytes: Uint8Array, contentType: string | null): RequestDebugBody {
|
|
284
|
+
try {
|
|
285
|
+
return snapshotText(utf8Decoder.decode(bytes), contentType);
|
|
286
|
+
} catch {
|
|
287
|
+
return { bodyBase64: Buffer.from(bytes).toString("base64") };
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
function snapshotText(text: string, contentType: string | null): RequestDebugBody {
|
|
292
|
+
if (isJsonContentType(contentType) || looksLikeJson(text)) {
|
|
293
|
+
try {
|
|
294
|
+
return { body: JSON.parse(text) };
|
|
295
|
+
} catch {
|
|
296
|
+
// Fall through to bodyText: malformed JSON is still useful as raw text.
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
return { bodyText: text };
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
function isJsonContentType(contentType: string | null): boolean {
|
|
303
|
+
if (!contentType) return false;
|
|
304
|
+
const lower = contentType.toLowerCase();
|
|
305
|
+
return lower.includes("application/json") || lower.includes("+json");
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
function looksLikeJson(text: string): boolean {
|
|
309
|
+
const trimmed = text.trimStart();
|
|
310
|
+
return trimmed.startsWith("{") || trimmed.startsWith("[");
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
function formatResponseHeaderBlock(statusLine: string, headers?: RequestDebugHeaders): string {
|
|
314
|
+
const lines = [statusLine];
|
|
315
|
+
const record = headersToRecord(headers);
|
|
316
|
+
if (record) {
|
|
317
|
+
for (const name in record) {
|
|
318
|
+
const value = record[name];
|
|
319
|
+
if (Array.isArray(value)) {
|
|
320
|
+
for (const item of value) lines.push(`${name}: ${item}`);
|
|
321
|
+
} else {
|
|
322
|
+
lines.push(`${name}: ${value}`);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
return `${lines.join("\r\n")}\r\n\r\n`;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
function headersToRecord(headers: RequestDebugHeaders): Record<string, string | string[]> | undefined {
|
|
330
|
+
if (!headers) return undefined;
|
|
331
|
+
const record: Record<string, string | string[]> = {};
|
|
332
|
+
let hasHeaders = false;
|
|
333
|
+
if (headers instanceof Headers) {
|
|
334
|
+
headers.forEach((value, key) => {
|
|
335
|
+
hasHeaders = true;
|
|
336
|
+
record[key] = value;
|
|
337
|
+
});
|
|
338
|
+
} else {
|
|
339
|
+
for (const key in headers) {
|
|
340
|
+
const value = headers[key];
|
|
341
|
+
if (value === undefined || value === null) continue;
|
|
342
|
+
hasHeaders = true;
|
|
343
|
+
record[key] = Array.isArray(value) ? value.map(String) : String(value);
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
return hasHeaders ? record : undefined;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
function isFileExistsError(error: unknown): boolean {
|
|
350
|
+
return typeof error === "object" && error !== null && (error as { code?: unknown }).code === "EEXIST";
|
|
351
|
+
}
|