@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,772 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AWS credential resolution for the Bedrock provider.
|
|
3
|
+
*
|
|
4
|
+
* Chain (first hit wins):
|
|
5
|
+
* 1. Static credentials from the environment
|
|
6
|
+
* (`AWS_ACCESS_KEY_ID` + `AWS_SECRET_ACCESS_KEY` [+ `AWS_SESSION_TOKEN`]).
|
|
7
|
+
* 2. Web identity (`AWS_WEB_IDENTITY_TOKEN_FILE` + `AWS_ROLE_ARN`).
|
|
8
|
+
* 3. Profile in `~/.aws/credentials` (and `~/.aws/config` for SSO):
|
|
9
|
+
* - static keys, SSO, or `credential_process`.
|
|
10
|
+
* 4. ECS/container credentials from `AWS_CONTAINER_CREDENTIALS_*`.
|
|
11
|
+
* 5. EC2 IMDSv2 when metadata is enabled.
|
|
12
|
+
*
|
|
13
|
+
* Resolved credentials are cached process-wide per profile and refreshed
|
|
14
|
+
* 60 s before `Expiration` to absorb clock skew.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import * as fs from "node:fs";
|
|
18
|
+
import * as os from "node:os";
|
|
19
|
+
import * as path from "node:path";
|
|
20
|
+
import { $env, isEnoent, logger } from "@linxiraos/pi-utils";
|
|
21
|
+
import * as AIError from "../error";
|
|
22
|
+
import type { FetchImpl } from "../types";
|
|
23
|
+
import { raceWithSignal } from "../utils/abort";
|
|
24
|
+
import {
|
|
25
|
+
type AwsIniFile,
|
|
26
|
+
parseAwsIni,
|
|
27
|
+
resolveAwsProfile,
|
|
28
|
+
resolveAwsRegion,
|
|
29
|
+
shouldLoadAwsSharedConfig,
|
|
30
|
+
} from "../utils/aws-profile";
|
|
31
|
+
import { isLocalOrMetadataHost } from "../utils/proxy";
|
|
32
|
+
import type { AwsCredentials } from "./aws-sigv4";
|
|
33
|
+
|
|
34
|
+
export interface ResolvedCredentials extends AwsCredentials {
|
|
35
|
+
/** Absolute expiration timestamp in ms. `undefined` for non-expiring static creds. */
|
|
36
|
+
expiresAt?: number;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface CredentialResolveOptions {
|
|
40
|
+
/** Named profile from `~/.aws/credentials` / `~/.aws/config`. */
|
|
41
|
+
profile?: string;
|
|
42
|
+
/** Falls back to env (`AWS_REGION` / `AWS_DEFAULT_REGION`) and finally `us-east-1`. */
|
|
43
|
+
region?: string;
|
|
44
|
+
signal?: AbortSignal;
|
|
45
|
+
fetch?: FetchImpl;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const REFRESH_SKEW_MS = 60_000;
|
|
49
|
+
/**
|
|
50
|
+
* TTL for file-sourced credentials that carry a session token but no expiry.
|
|
51
|
+
* Tools like aws-vault/saml2aws rewrite ~/.aws/credentials with short-lived STS
|
|
52
|
+
* session keys; caching them forever serves stale creds after rotation.
|
|
53
|
+
*/
|
|
54
|
+
const FILE_SESSION_CREDS_TTL_MS = 5 * 60_000;
|
|
55
|
+
/**
|
|
56
|
+
* Bound for the detached (signal-free) shared resolution: a hung
|
|
57
|
+
* credential_process/SSO/IMDS fetch must not pin the inflight slot forever.
|
|
58
|
+
*/
|
|
59
|
+
const SHARED_RESOLVE_TIMEOUT_MS = 30_000;
|
|
60
|
+
|
|
61
|
+
function requireDynamicCredentialExpiration(
|
|
62
|
+
value: string | undefined,
|
|
63
|
+
source: "AWS web identity" | "AWS container credential",
|
|
64
|
+
kind: "web-identity" | "container",
|
|
65
|
+
): number {
|
|
66
|
+
const expiresAt = value ? Date.parse(value) : Number.NaN;
|
|
67
|
+
if (Number.isFinite(expiresAt)) return expiresAt;
|
|
68
|
+
throw new AIError.AwsCredentialsError(`${source} response has a missing or invalid Expiration.`, kind);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** Credential-process expiry is optional; missing/malformed values disable caching. */
|
|
72
|
+
function dynamicCredentialExpiration(value: string | undefined): number {
|
|
73
|
+
if (!value) return Date.now();
|
|
74
|
+
const expiresAt = Date.parse(value);
|
|
75
|
+
return Number.isFinite(expiresAt) ? expiresAt : Date.now();
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
interface CacheEntry {
|
|
79
|
+
creds: ResolvedCredentials;
|
|
80
|
+
expiresAt: number;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const cache: Map<string, CacheEntry> = new Map();
|
|
84
|
+
const inflight: Map<string, Promise<ResolvedCredentials>> = new Map();
|
|
85
|
+
|
|
86
|
+
function credentialCacheKey(profile: string, region: string, loadSharedConfig: boolean): string {
|
|
87
|
+
return `${profile}\x00${region}\x00${loadSharedConfig ? "config" : "credentials"}`;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export async function resolveAwsCredentials(opts: CredentialResolveOptions = {}): Promise<ResolvedCredentials> {
|
|
91
|
+
const profile = resolveAwsProfile(opts.profile);
|
|
92
|
+
const region = resolveAwsRegion(opts.region, opts.profile);
|
|
93
|
+
const loadSharedConfig = shouldLoadAwsSharedConfig(opts.profile);
|
|
94
|
+
const cacheKey = credentialCacheKey(profile, region, loadSharedConfig);
|
|
95
|
+
|
|
96
|
+
const hit = cache.get(cacheKey);
|
|
97
|
+
if (hit && hit.expiresAt - REFRESH_SKEW_MS > Date.now()) return hit.creds;
|
|
98
|
+
|
|
99
|
+
// Single-flight: N concurrent cold calls must not each spawn credential_process/SSO/IMDS fetches.
|
|
100
|
+
// The shared resolution is deliberately detached from any caller's signal — aborting one
|
|
101
|
+
// request must not fail every waiter — and bounded by its own timeout instead; each caller
|
|
102
|
+
// races its own signal against the shared promise.
|
|
103
|
+
const existing = inflight.get(cacheKey);
|
|
104
|
+
if (existing) return raceWithSignal(existing, opts.signal);
|
|
105
|
+
|
|
106
|
+
const fetchImpl = opts.fetch ?? (globalThis.fetch as FetchImpl);
|
|
107
|
+
const promise = (async () => {
|
|
108
|
+
try {
|
|
109
|
+
const creds = await resolveFresh(
|
|
110
|
+
profile,
|
|
111
|
+
region,
|
|
112
|
+
loadSharedConfig,
|
|
113
|
+
AbortSignal.timeout(SHARED_RESOLVE_TIMEOUT_MS),
|
|
114
|
+
fetchImpl,
|
|
115
|
+
);
|
|
116
|
+
cache.set(cacheKey, { creds, expiresAt: creds.expiresAt ?? Number.POSITIVE_INFINITY });
|
|
117
|
+
return creds;
|
|
118
|
+
} finally {
|
|
119
|
+
inflight.delete(cacheKey);
|
|
120
|
+
}
|
|
121
|
+
})();
|
|
122
|
+
inflight.set(cacheKey, promise);
|
|
123
|
+
return raceWithSignal(promise, opts.signal);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
async function resolveFresh(
|
|
127
|
+
profile: string,
|
|
128
|
+
region: string,
|
|
129
|
+
loadSharedConfig: boolean,
|
|
130
|
+
signal?: AbortSignal,
|
|
131
|
+
fetchImpl: FetchImpl = globalThis.fetch as FetchImpl,
|
|
132
|
+
): Promise<ResolvedCredentials> {
|
|
133
|
+
// 1. Environment first — matches the AWS SDK chain order.
|
|
134
|
+
const envCreds = readEnvCredentials();
|
|
135
|
+
if (envCreds) return envCreds;
|
|
136
|
+
|
|
137
|
+
// 2. Web identity.
|
|
138
|
+
const webIdentityCreds = await readWebIdentityCredentials(region, signal, fetchImpl);
|
|
139
|
+
if (webIdentityCreds) return webIdentityCreds;
|
|
140
|
+
|
|
141
|
+
// 3. Profile (static, SSO, or credential_process).
|
|
142
|
+
const profileCreds = await readProfileCredentials(profile, region, loadSharedConfig, signal, fetchImpl);
|
|
143
|
+
if (profileCreds) return profileCreds;
|
|
144
|
+
|
|
145
|
+
// 4. ECS/container credentials.
|
|
146
|
+
const containerCreds = await readContainerCredentials(signal, fetchImpl);
|
|
147
|
+
if (containerCreds) return containerCreds;
|
|
148
|
+
|
|
149
|
+
// 5. EC2 IMDSv2.
|
|
150
|
+
if ($env.AWS_EC2_METADATA_DISABLED?.toLowerCase() !== "true") {
|
|
151
|
+
const imdsCreds = await readImdsCredentials(signal, fetchImpl);
|
|
152
|
+
if (imdsCreds) return imdsCreds;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
throw new AIError.AwsCredentialsError(
|
|
156
|
+
`Unable to resolve AWS credentials. Configure static environment keys, web identity, ` +
|
|
157
|
+
`an AWS profile, ECS credentials, or an EC2 instance role.`,
|
|
158
|
+
"resolution",
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function readEnvCredentials(): ResolvedCredentials | undefined {
|
|
163
|
+
const ak = $env.AWS_ACCESS_KEY_ID;
|
|
164
|
+
const sk = $env.AWS_SECRET_ACCESS_KEY;
|
|
165
|
+
if (!ak || !sk) return undefined;
|
|
166
|
+
const token = $env.AWS_SESSION_TOKEN;
|
|
167
|
+
return token
|
|
168
|
+
? { accessKeyId: ak, secretAccessKey: sk, sessionToken: token }
|
|
169
|
+
: { accessKeyId: ak, secretAccessKey: sk };
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
async function readIniFile(p: string): Promise<AwsIniFile | undefined> {
|
|
173
|
+
try {
|
|
174
|
+
const text = await fs.promises.readFile(p, "utf8");
|
|
175
|
+
return parseAwsIni(text);
|
|
176
|
+
} catch (err) {
|
|
177
|
+
if (isEnoent(err)) return undefined;
|
|
178
|
+
throw err;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// ---------- Profile / SSO ----------
|
|
183
|
+
|
|
184
|
+
async function readProfileCredentials(
|
|
185
|
+
profile: string,
|
|
186
|
+
region: string,
|
|
187
|
+
loadSharedConfig: boolean,
|
|
188
|
+
signal: AbortSignal | undefined,
|
|
189
|
+
fetchImpl: FetchImpl,
|
|
190
|
+
): Promise<ResolvedCredentials | undefined> {
|
|
191
|
+
const home = os.homedir();
|
|
192
|
+
const credentialsPath = $env.AWS_SHARED_CREDENTIALS_FILE || path.join(home, ".aws", "credentials");
|
|
193
|
+
const configPath = $env.AWS_CONFIG_FILE || path.join(home, ".aws", "config");
|
|
194
|
+
|
|
195
|
+
const credentialsIni = await readIniFile(credentialsPath);
|
|
196
|
+
const configIni = loadSharedConfig ? await readIniFile(configPath) : undefined;
|
|
197
|
+
|
|
198
|
+
// Static credentials live in ~/.aws/credentials; SSO config lives in
|
|
199
|
+
// ~/.aws/config under `[profile foo]`. Merge into a single view.
|
|
200
|
+
const merged: Record<string, string> = { ...(configIni?.[profile] ?? {}), ...(credentialsIni?.[profile] ?? {}) };
|
|
201
|
+
if (Object.keys(merged).length === 0) return undefined;
|
|
202
|
+
|
|
203
|
+
if (merged.aws_access_key_id && merged.aws_secret_access_key) {
|
|
204
|
+
const out: ResolvedCredentials = {
|
|
205
|
+
accessKeyId: merged.aws_access_key_id,
|
|
206
|
+
secretAccessKey: merged.aws_secret_access_key,
|
|
207
|
+
};
|
|
208
|
+
if (merged.aws_session_token) {
|
|
209
|
+
out.sessionToken = merged.aws_session_token;
|
|
210
|
+
// Session-token creds in the credentials file are short-lived STS keys that
|
|
211
|
+
// external tools rotate in place; cap the cache so rotations are picked up.
|
|
212
|
+
out.expiresAt = Date.now() + FILE_SESSION_CREDS_TTL_MS;
|
|
213
|
+
}
|
|
214
|
+
return out;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
if (merged.sso_account_id && merged.sso_role_name) {
|
|
218
|
+
return readSsoCredentials(merged, configIni, region, signal, fetchImpl);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
if (merged.credential_process) {
|
|
222
|
+
return readCredentialProcess(profile, merged.credential_process, signal);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
return undefined;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
interface SsoCachedToken {
|
|
229
|
+
accessToken?: string;
|
|
230
|
+
expiresAt?: string;
|
|
231
|
+
startUrl?: string;
|
|
232
|
+
region?: string;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
async function readSsoCredentials(
|
|
236
|
+
profileCfg: Record<string, string>,
|
|
237
|
+
configIni: AwsIniFile | undefined,
|
|
238
|
+
defaultRegion: string,
|
|
239
|
+
signal: AbortSignal | undefined,
|
|
240
|
+
fetchImpl: FetchImpl,
|
|
241
|
+
): Promise<ResolvedCredentials | undefined> {
|
|
242
|
+
// Two SSO profile shapes:
|
|
243
|
+
// - legacy: `sso_start_url` + `sso_region` directly on the profile
|
|
244
|
+
// - sso-session: `sso_session = my-session` references a `[sso-session my-session]` block
|
|
245
|
+
let startUrl = profileCfg.sso_start_url;
|
|
246
|
+
let ssoRegion = profileCfg.sso_region;
|
|
247
|
+
const sessionName = profileCfg.sso_session;
|
|
248
|
+
if (sessionName && configIni) {
|
|
249
|
+
const session = configIni[`sso-session:${sessionName}`];
|
|
250
|
+
if (session) {
|
|
251
|
+
startUrl = startUrl || session.sso_start_url;
|
|
252
|
+
ssoRegion = ssoRegion || session.sso_region;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
if (!startUrl || !ssoRegion) return undefined;
|
|
256
|
+
|
|
257
|
+
const token = await loadSsoCachedToken(startUrl, sessionName);
|
|
258
|
+
if (!token?.accessToken) {
|
|
259
|
+
throw new AIError.AwsCredentialsError(
|
|
260
|
+
`AWS SSO token for ${startUrl} not found in ~/.aws/sso/cache. Run 'aws sso login' first.`,
|
|
261
|
+
"sso-token-missing",
|
|
262
|
+
);
|
|
263
|
+
}
|
|
264
|
+
const expiresAt = token.expiresAt ? Date.parse(token.expiresAt) : Number.POSITIVE_INFINITY;
|
|
265
|
+
if (Number.isFinite(expiresAt) && expiresAt <= Date.now()) {
|
|
266
|
+
throw new AIError.AwsCredentialsError(
|
|
267
|
+
`AWS SSO token for ${startUrl} has expired. Run 'aws sso login' to refresh.`,
|
|
268
|
+
"sso-token-expired",
|
|
269
|
+
);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
const url =
|
|
273
|
+
`https://portal.sso.${ssoRegion}.amazonaws.com/federation/credentials` +
|
|
274
|
+
`?account_id=${encodeURIComponent(profileCfg.sso_account_id)}` +
|
|
275
|
+
`&role_name=${encodeURIComponent(profileCfg.sso_role_name)}`;
|
|
276
|
+
const response = await fetchImpl(url, {
|
|
277
|
+
method: "GET",
|
|
278
|
+
headers: { "x-amz-sso_bearer_token": token.accessToken },
|
|
279
|
+
signal,
|
|
280
|
+
});
|
|
281
|
+
if (!response.ok) {
|
|
282
|
+
const body = await response.text().catch(() => "");
|
|
283
|
+
throw new AIError.AwsCredentialsError(
|
|
284
|
+
`AWS SSO GetRoleCredentials failed: ${response.status} ${body.slice(0, 200)}`,
|
|
285
|
+
"sso-role",
|
|
286
|
+
);
|
|
287
|
+
}
|
|
288
|
+
const json = (await response.json()) as {
|
|
289
|
+
roleCredentials?: { accessKeyId: string; secretAccessKey: string; sessionToken: string; expiration: number };
|
|
290
|
+
};
|
|
291
|
+
const role = json.roleCredentials;
|
|
292
|
+
if (!role)
|
|
293
|
+
throw new AIError.AwsCredentialsError(
|
|
294
|
+
"AWS SSO GetRoleCredentials: missing roleCredentials in response",
|
|
295
|
+
"sso-role",
|
|
296
|
+
);
|
|
297
|
+
|
|
298
|
+
// region is honored at the caller; we only consume defaultRegion to keep the
|
|
299
|
+
// param wired for symmetry with other resolution paths.
|
|
300
|
+
void defaultRegion;
|
|
301
|
+
|
|
302
|
+
return {
|
|
303
|
+
accessKeyId: role.accessKeyId,
|
|
304
|
+
secretAccessKey: role.secretAccessKey,
|
|
305
|
+
sessionToken: role.sessionToken,
|
|
306
|
+
expiresAt: role.expiration,
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
async function loadSsoCachedToken(
|
|
311
|
+
startUrl: string,
|
|
312
|
+
sessionName: string | undefined,
|
|
313
|
+
): Promise<SsoCachedToken | undefined> {
|
|
314
|
+
const cacheDir = path.join(os.homedir(), ".aws", "sso", "cache");
|
|
315
|
+
let entries: string[];
|
|
316
|
+
try {
|
|
317
|
+
entries = await fs.promises.readdir(cacheDir);
|
|
318
|
+
} catch (err) {
|
|
319
|
+
if (isEnoent(err)) return undefined;
|
|
320
|
+
throw err;
|
|
321
|
+
}
|
|
322
|
+
// Prefer the deterministic hash for legacy `sso_start_url` profiles or the
|
|
323
|
+
// session name for the newer `sso-session` shape; otherwise scan.
|
|
324
|
+
const candidates: string[] = [];
|
|
325
|
+
const hash = await sha1Hex(sessionName || startUrl);
|
|
326
|
+
candidates.push(`${hash}.json`);
|
|
327
|
+
for (const entry of entries) {
|
|
328
|
+
if (entry.endsWith(".json") && !candidates.includes(entry)) candidates.push(entry);
|
|
329
|
+
}
|
|
330
|
+
for (const file of candidates) {
|
|
331
|
+
if (!entries.includes(file)) continue;
|
|
332
|
+
try {
|
|
333
|
+
const text = await fs.promises.readFile(path.join(cacheDir, file), "utf8");
|
|
334
|
+
const parsed = JSON.parse(text) as SsoCachedToken;
|
|
335
|
+
if (parsed.startUrl === startUrl || (sessionName && file === `${hash}.json`)) {
|
|
336
|
+
return parsed;
|
|
337
|
+
}
|
|
338
|
+
} catch (err) {
|
|
339
|
+
logger.debug("aws-credentials: failed to read SSO cache", { file, err: String(err) });
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
return undefined;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
async function sha1Hex(input: string): Promise<string> {
|
|
346
|
+
const digest = await globalThis.crypto.subtle.digest("SHA-1", new TextEncoder().encode(input));
|
|
347
|
+
const bytes = new Uint8Array(digest);
|
|
348
|
+
let out = "";
|
|
349
|
+
for (let i = 0; i < bytes.length; i++) out += bytes[i].toString(16).padStart(2, "0");
|
|
350
|
+
return out;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
// ---------- credential_process ----------
|
|
354
|
+
|
|
355
|
+
/** JSON envelope emitted by an external credential process. Matches the
|
|
356
|
+
* AWS CLI / SDK contract documented at
|
|
357
|
+
* https://docs.aws.amazon.com/sdkref/latest/guide/feature-process-credentials.html */
|
|
358
|
+
interface CredentialProcessEnvelope {
|
|
359
|
+
Version?: number;
|
|
360
|
+
AccessKeyId?: string;
|
|
361
|
+
SecretAccessKey?: string;
|
|
362
|
+
SessionToken?: string;
|
|
363
|
+
Expiration?: string;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
async function readCredentialProcess(
|
|
367
|
+
profile: string,
|
|
368
|
+
command: string,
|
|
369
|
+
signal: AbortSignal | undefined,
|
|
370
|
+
): Promise<ResolvedCredentials> {
|
|
371
|
+
const argv = buildCredentialProcessArgv(profile, command);
|
|
372
|
+
const child = Bun.spawn(argv, {
|
|
373
|
+
stdin: "ignore",
|
|
374
|
+
stdout: "pipe",
|
|
375
|
+
stderr: "pipe",
|
|
376
|
+
windowsHide: true,
|
|
377
|
+
signal,
|
|
378
|
+
});
|
|
379
|
+
const [stdout, stderr, exitCode] = await Promise.all([
|
|
380
|
+
new Response(child.stdout).text(),
|
|
381
|
+
new Response(child.stderr).text(),
|
|
382
|
+
child.exited,
|
|
383
|
+
]);
|
|
384
|
+
if (exitCode !== 0) {
|
|
385
|
+
const tail = stderr.trim().slice(-512) || stdout.trim().slice(-512) || "(no output)";
|
|
386
|
+
throw new AIError.AwsCredentialsError(
|
|
387
|
+
`AWS credential_process for profile '${profile}' exited ${exitCode}: ${tail}`,
|
|
388
|
+
"credential-process",
|
|
389
|
+
);
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
let parsed: CredentialProcessEnvelope;
|
|
393
|
+
try {
|
|
394
|
+
parsed = JSON.parse(stdout) as CredentialProcessEnvelope;
|
|
395
|
+
} catch (err) {
|
|
396
|
+
throw new AIError.AwsCredentialsError(
|
|
397
|
+
`AWS credential_process for profile '${profile}' did not emit valid JSON: ${String(err)}`,
|
|
398
|
+
"credential-process",
|
|
399
|
+
{ cause: err },
|
|
400
|
+
);
|
|
401
|
+
}
|
|
402
|
+
if (parsed.Version !== 1) {
|
|
403
|
+
throw new AIError.AwsCredentialsError(
|
|
404
|
+
`AWS credential_process for profile '${profile}' returned unsupported Version ${parsed.Version ?? "<missing>"}; expected 1.`,
|
|
405
|
+
"credential-process",
|
|
406
|
+
);
|
|
407
|
+
}
|
|
408
|
+
if (!parsed.AccessKeyId || !parsed.SecretAccessKey) {
|
|
409
|
+
throw new AIError.AwsCredentialsError(
|
|
410
|
+
`AWS credential_process for profile '${profile}' returned envelope without AccessKeyId/SecretAccessKey.`,
|
|
411
|
+
"credential-process",
|
|
412
|
+
);
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
const out: ResolvedCredentials = {
|
|
416
|
+
accessKeyId: parsed.AccessKeyId,
|
|
417
|
+
secretAccessKey: parsed.SecretAccessKey,
|
|
418
|
+
};
|
|
419
|
+
if (parsed.SessionToken) {
|
|
420
|
+
out.sessionToken = parsed.SessionToken;
|
|
421
|
+
out.expiresAt = dynamicCredentialExpiration(parsed.Expiration);
|
|
422
|
+
} else if (parsed.Expiration) {
|
|
423
|
+
out.expiresAt = dynamicCredentialExpiration(parsed.Expiration);
|
|
424
|
+
}
|
|
425
|
+
return out;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
/** Resolve the argv for `Bun.spawn`. On Windows we route `.cmd`/`.bat` helpers
|
|
429
|
+
* through `cmd.exe /c` because direct execution refuses batch files (mirrors
|
|
430
|
+
* Node's `execFile` policy and avoids surprise no-ops). */
|
|
431
|
+
function buildCredentialProcessArgv(profile: string, command: string): string[] {
|
|
432
|
+
const tokens = tokenizeCredentialProcessCommand(command);
|
|
433
|
+
if (tokens.length === 0) {
|
|
434
|
+
throw new AIError.AwsCredentialsError(
|
|
435
|
+
`AWS credential_process for profile '${profile}' is empty.`,
|
|
436
|
+
"credential-process",
|
|
437
|
+
);
|
|
438
|
+
}
|
|
439
|
+
if (process.platform === "win32" && isBatchScript(tokens[0])) {
|
|
440
|
+
return ["cmd.exe", "/d", "/s", "/c", command];
|
|
441
|
+
}
|
|
442
|
+
return tokens;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
function isBatchScript(executable: string): boolean {
|
|
446
|
+
const lower = executable.toLowerCase();
|
|
447
|
+
return lower.endsWith(".cmd") || lower.endsWith(".bat");
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
/** POSIX-shell-style tokenizer used by the AWS CLI for `credential_process`.
|
|
451
|
+
*
|
|
452
|
+
* Outside quotes a backslash escapes the next character. Inside single quotes
|
|
453
|
+
* everything is literal (no escapes, cannot contain `'`). Inside double quotes
|
|
454
|
+
* a backslash only escapes `$`, `` ` ``, `"`, and `\` — every other backslash
|
|
455
|
+
* is preserved verbatim, which is what makes Windows paths like
|
|
456
|
+
* `"C:\Program Files\tool\auth.exe"` survive tokenization. */
|
|
457
|
+
export function tokenizeCredentialProcessCommand(cmd: string): string[] {
|
|
458
|
+
const tokens: string[] = [];
|
|
459
|
+
let current = "";
|
|
460
|
+
let hasToken = false;
|
|
461
|
+
let mode: "normal" | "single" | "double" = "normal";
|
|
462
|
+
for (let i = 0; i < cmd.length; i++) {
|
|
463
|
+
const ch = cmd[i];
|
|
464
|
+
if (mode === "normal") {
|
|
465
|
+
if (ch === "'") {
|
|
466
|
+
mode = "single";
|
|
467
|
+
hasToken = true;
|
|
468
|
+
continue;
|
|
469
|
+
}
|
|
470
|
+
if (ch === '"') {
|
|
471
|
+
mode = "double";
|
|
472
|
+
hasToken = true;
|
|
473
|
+
continue;
|
|
474
|
+
}
|
|
475
|
+
if (ch === "\\" && i + 1 < cmd.length) {
|
|
476
|
+
current += cmd[++i];
|
|
477
|
+
hasToken = true;
|
|
478
|
+
continue;
|
|
479
|
+
}
|
|
480
|
+
if (ch === " " || ch === "\t" || ch === "\n" || ch === "\r") {
|
|
481
|
+
if (hasToken) {
|
|
482
|
+
tokens.push(current);
|
|
483
|
+
current = "";
|
|
484
|
+
hasToken = false;
|
|
485
|
+
}
|
|
486
|
+
continue;
|
|
487
|
+
}
|
|
488
|
+
current += ch;
|
|
489
|
+
hasToken = true;
|
|
490
|
+
continue;
|
|
491
|
+
}
|
|
492
|
+
if (mode === "single") {
|
|
493
|
+
if (ch === "'") {
|
|
494
|
+
mode = "normal";
|
|
495
|
+
continue;
|
|
496
|
+
}
|
|
497
|
+
current += ch;
|
|
498
|
+
continue;
|
|
499
|
+
}
|
|
500
|
+
// double-quote
|
|
501
|
+
if (ch === '"') {
|
|
502
|
+
mode = "normal";
|
|
503
|
+
continue;
|
|
504
|
+
}
|
|
505
|
+
if (ch === "\\" && i + 1 < cmd.length) {
|
|
506
|
+
const next = cmd[i + 1];
|
|
507
|
+
if (next === "$" || next === "`" || next === '"' || next === "\\") {
|
|
508
|
+
current += next;
|
|
509
|
+
i++;
|
|
510
|
+
continue;
|
|
511
|
+
}
|
|
512
|
+
// Preserve literal backslash for Windows paths.
|
|
513
|
+
current += ch;
|
|
514
|
+
continue;
|
|
515
|
+
}
|
|
516
|
+
current += ch;
|
|
517
|
+
}
|
|
518
|
+
if (mode !== "normal") {
|
|
519
|
+
throw new AIError.AwsCredentialsError(
|
|
520
|
+
"AWS credential_process command has an unterminated quote.",
|
|
521
|
+
"credential-process",
|
|
522
|
+
);
|
|
523
|
+
}
|
|
524
|
+
if (hasToken) tokens.push(current);
|
|
525
|
+
return tokens;
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
// ---------- Web identity ----------
|
|
529
|
+
|
|
530
|
+
function xmlTag(xml: string, tag: string): string | undefined {
|
|
531
|
+
const value = new RegExp(`<${tag}>([\\s\\S]*?)</${tag}>`).exec(xml)?.[1];
|
|
532
|
+
if (!value) return undefined;
|
|
533
|
+
return value
|
|
534
|
+
.replaceAll("&", "&")
|
|
535
|
+
.replaceAll("<", "<")
|
|
536
|
+
.replaceAll(">", ">")
|
|
537
|
+
.replaceAll(""", '"')
|
|
538
|
+
.replaceAll("'", "'");
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
function stsEndpoint(region: string): string {
|
|
542
|
+
const dnsSuffix = region.startsWith("cn-") ? "amazonaws.com.cn" : "amazonaws.com";
|
|
543
|
+
return `https://sts.${region}.${dnsSuffix}/`;
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
async function readWebIdentityCredentials(
|
|
547
|
+
region: string,
|
|
548
|
+
signal: AbortSignal | undefined,
|
|
549
|
+
fetchImpl: FetchImpl,
|
|
550
|
+
): Promise<ResolvedCredentials | undefined> {
|
|
551
|
+
const tokenFile = $env.AWS_WEB_IDENTITY_TOKEN_FILE;
|
|
552
|
+
const roleArn = $env.AWS_ROLE_ARN;
|
|
553
|
+
if (!tokenFile || !roleArn) return undefined;
|
|
554
|
+
let token: string;
|
|
555
|
+
try {
|
|
556
|
+
token = (await Bun.file(tokenFile).text()).trim();
|
|
557
|
+
} catch (err) {
|
|
558
|
+
throw new AIError.AwsCredentialsError(
|
|
559
|
+
`Unable to read AWS web identity token file: ${String(err)}`,
|
|
560
|
+
"web-identity",
|
|
561
|
+
{
|
|
562
|
+
cause: err,
|
|
563
|
+
},
|
|
564
|
+
);
|
|
565
|
+
}
|
|
566
|
+
if (!token) {
|
|
567
|
+
throw new AIError.AwsCredentialsError("AWS web identity token file is empty.", "web-identity");
|
|
568
|
+
}
|
|
569
|
+
const body = new URLSearchParams({
|
|
570
|
+
Action: "AssumeRoleWithWebIdentity",
|
|
571
|
+
Version: "2011-06-15",
|
|
572
|
+
RoleArn: roleArn,
|
|
573
|
+
RoleSessionName: $env.AWS_ROLE_SESSION_NAME || `omp-${process.pid}`,
|
|
574
|
+
WebIdentityToken: token,
|
|
575
|
+
});
|
|
576
|
+
const response = await fetchImpl(stsEndpoint(region), {
|
|
577
|
+
method: "POST",
|
|
578
|
+
headers: { "content-type": "application/x-www-form-urlencoded" },
|
|
579
|
+
body: body.toString(),
|
|
580
|
+
signal,
|
|
581
|
+
});
|
|
582
|
+
const xml = await response.text();
|
|
583
|
+
if (!response.ok) {
|
|
584
|
+
throw new AIError.AwsCredentialsError(
|
|
585
|
+
`AWS AssumeRoleWithWebIdentity failed: ${response.status} ${xmlTag(xml, "Message") ?? xml.slice(0, 200)}`,
|
|
586
|
+
"web-identity",
|
|
587
|
+
);
|
|
588
|
+
}
|
|
589
|
+
const accessKeyId = xmlTag(xml, "AccessKeyId");
|
|
590
|
+
const secretAccessKey = xmlTag(xml, "SecretAccessKey");
|
|
591
|
+
const sessionToken = xmlTag(xml, "SessionToken");
|
|
592
|
+
if (!accessKeyId || !secretAccessKey || !sessionToken) {
|
|
593
|
+
throw new AIError.AwsCredentialsError(
|
|
594
|
+
"AWS AssumeRoleWithWebIdentity response is missing credentials.",
|
|
595
|
+
"web-identity",
|
|
596
|
+
);
|
|
597
|
+
}
|
|
598
|
+
const expiresAt = requireDynamicCredentialExpiration(xmlTag(xml, "Expiration"), "AWS web identity", "web-identity");
|
|
599
|
+
return {
|
|
600
|
+
accessKeyId,
|
|
601
|
+
secretAccessKey,
|
|
602
|
+
sessionToken,
|
|
603
|
+
expiresAt,
|
|
604
|
+
};
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
// ---------- ECS/container credentials ----------
|
|
608
|
+
|
|
609
|
+
interface ContainerCredentialResponse {
|
|
610
|
+
AccessKeyId?: string;
|
|
611
|
+
SecretAccessKey?: string;
|
|
612
|
+
Token?: string;
|
|
613
|
+
Expiration?: string;
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
const ECS_TASK_CREDENTIALS_BASE_URL = new URL("http://169.254.170.2/");
|
|
617
|
+
|
|
618
|
+
async function readContainerCredentials(
|
|
619
|
+
signal: AbortSignal | undefined,
|
|
620
|
+
fetchImpl: FetchImpl,
|
|
621
|
+
): Promise<ResolvedCredentials | undefined> {
|
|
622
|
+
const relativeUri = $env.AWS_CONTAINER_CREDENTIALS_RELATIVE_URI;
|
|
623
|
+
const fullUri = $env.AWS_CONTAINER_CREDENTIALS_FULL_URI;
|
|
624
|
+
if (!relativeUri && !fullUri) return undefined;
|
|
625
|
+
let endpoint: URL;
|
|
626
|
+
if (relativeUri) {
|
|
627
|
+
if (!relativeUri.startsWith("/") || relativeUri.startsWith("//")) {
|
|
628
|
+
throw new AIError.AwsCredentialsError(
|
|
629
|
+
"AWS_CONTAINER_CREDENTIALS_RELATIVE_URI must be a single-host absolute path.",
|
|
630
|
+
"container",
|
|
631
|
+
);
|
|
632
|
+
}
|
|
633
|
+
endpoint = new URL(relativeUri.slice(1), ECS_TASK_CREDENTIALS_BASE_URL);
|
|
634
|
+
} else {
|
|
635
|
+
try {
|
|
636
|
+
endpoint = new URL(fullUri as string);
|
|
637
|
+
} catch (err) {
|
|
638
|
+
throw new AIError.AwsCredentialsError(
|
|
639
|
+
`AWS_CONTAINER_CREDENTIALS_FULL_URI is invalid: ${String(err)}`,
|
|
640
|
+
"container",
|
|
641
|
+
{ cause: err },
|
|
642
|
+
);
|
|
643
|
+
}
|
|
644
|
+
if (endpoint.protocol !== "https:" && !isLocalOrMetadataHost(endpoint.hostname)) {
|
|
645
|
+
throw new AIError.AwsCredentialsError(
|
|
646
|
+
"AWS_CONTAINER_CREDENTIALS_FULL_URI must use HTTPS or a local metadata host.",
|
|
647
|
+
"container",
|
|
648
|
+
);
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
let authorization = $env.AWS_CONTAINER_AUTHORIZATION_TOKEN;
|
|
652
|
+
const authorizationTokenFile = $env.AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE;
|
|
653
|
+
if (!authorization && authorizationTokenFile) {
|
|
654
|
+
try {
|
|
655
|
+
authorization = (await Bun.file(authorizationTokenFile).text()).trim();
|
|
656
|
+
} catch (err) {
|
|
657
|
+
throw new AIError.AwsCredentialsError(
|
|
658
|
+
`Unable to read AWS container authorization token file: ${String(err)}`,
|
|
659
|
+
"container",
|
|
660
|
+
{ cause: err },
|
|
661
|
+
);
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
const response = await fetchImpl(endpoint, {
|
|
665
|
+
headers: authorization ? { authorization } : undefined,
|
|
666
|
+
signal,
|
|
667
|
+
});
|
|
668
|
+
if (!response.ok) {
|
|
669
|
+
const body = await response.text().catch(() => "");
|
|
670
|
+
throw new AIError.AwsCredentialsError(
|
|
671
|
+
`AWS container credential endpoint failed: ${response.status} ${body.slice(0, 200)}`,
|
|
672
|
+
"container",
|
|
673
|
+
);
|
|
674
|
+
}
|
|
675
|
+
const body = (await response.json()) as ContainerCredentialResponse;
|
|
676
|
+
if (!body.AccessKeyId || !body.SecretAccessKey || !body.Token) {
|
|
677
|
+
throw new AIError.AwsCredentialsError(
|
|
678
|
+
"AWS container credential response is missing AccessKeyId/SecretAccessKey/Token.",
|
|
679
|
+
"container",
|
|
680
|
+
);
|
|
681
|
+
}
|
|
682
|
+
return {
|
|
683
|
+
accessKeyId: body.AccessKeyId,
|
|
684
|
+
secretAccessKey: body.SecretAccessKey,
|
|
685
|
+
sessionToken: body.Token,
|
|
686
|
+
expiresAt: requireDynamicCredentialExpiration(body.Expiration, "AWS container credential", "container"),
|
|
687
|
+
};
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
// ---------- IMDSv2 ----------
|
|
691
|
+
|
|
692
|
+
const IMDS_IPV4_BASE_URL = "http://169.254.169.254/";
|
|
693
|
+
const IMDS_IPV6_BASE_URL = "http://[fd00:ec2::254]/";
|
|
694
|
+
const IMDS_TIMEOUT_MS = 1000;
|
|
695
|
+
|
|
696
|
+
function imdsRequestSignal(parentSignal: AbortSignal | undefined): AbortSignal {
|
|
697
|
+
const timeout = AbortSignal.timeout(IMDS_TIMEOUT_MS);
|
|
698
|
+
return parentSignal ? AbortSignal.any([parentSignal, timeout]) : timeout;
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
function imdsBaseUrl(): URL {
|
|
702
|
+
const mode = $env.AWS_EC2_METADATA_SERVICE_ENDPOINT_MODE?.toLowerCase();
|
|
703
|
+
const fallback = mode === "ipv6" ? IMDS_IPV6_BASE_URL : IMDS_IPV4_BASE_URL;
|
|
704
|
+
const endpoint = new URL($env.AWS_EC2_METADATA_SERVICE_ENDPOINT || fallback);
|
|
705
|
+
if (!endpoint.pathname.endsWith("/")) endpoint.pathname += "/";
|
|
706
|
+
return endpoint;
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
async function readImdsCredentials(
|
|
710
|
+
parentSignal: AbortSignal | undefined,
|
|
711
|
+
fetchImpl: FetchImpl,
|
|
712
|
+
): Promise<ResolvedCredentials | undefined> {
|
|
713
|
+
try {
|
|
714
|
+
const endpoint = imdsBaseUrl();
|
|
715
|
+
const tokenRes = await fetchImpl(new URL("latest/api/token", endpoint), {
|
|
716
|
+
method: "PUT",
|
|
717
|
+
headers: { "x-aws-ec2-metadata-token-ttl-seconds": "21600" },
|
|
718
|
+
signal: imdsRequestSignal(parentSignal),
|
|
719
|
+
});
|
|
720
|
+
if (!tokenRes.ok) return undefined;
|
|
721
|
+
const token = await tokenRes.text();
|
|
722
|
+
|
|
723
|
+
const roleRes = await fetchImpl(new URL("latest/meta-data/iam/security-credentials/", endpoint), {
|
|
724
|
+
headers: { "x-aws-ec2-metadata-token": token },
|
|
725
|
+
signal: imdsRequestSignal(parentSignal),
|
|
726
|
+
});
|
|
727
|
+
if (!roleRes.ok) return undefined;
|
|
728
|
+
const role = (await roleRes.text()).trim();
|
|
729
|
+
if (!role) return undefined;
|
|
730
|
+
|
|
731
|
+
const credsRes = await fetchImpl(
|
|
732
|
+
new URL(`latest/meta-data/iam/security-credentials/${encodeURIComponent(role)}`, endpoint),
|
|
733
|
+
{
|
|
734
|
+
headers: { "x-aws-ec2-metadata-token": token },
|
|
735
|
+
signal: imdsRequestSignal(parentSignal),
|
|
736
|
+
},
|
|
737
|
+
);
|
|
738
|
+
if (!credsRes.ok) return undefined;
|
|
739
|
+
const body = (await credsRes.json()) as {
|
|
740
|
+
AccessKeyId?: string;
|
|
741
|
+
SecretAccessKey?: string;
|
|
742
|
+
Token?: string;
|
|
743
|
+
Expiration?: string;
|
|
744
|
+
};
|
|
745
|
+
if (!body.AccessKeyId || !body.SecretAccessKey || !body.Token || !body.Expiration) return undefined;
|
|
746
|
+
const expiresAt = Date.parse(body.Expiration);
|
|
747
|
+
if (!Number.isFinite(expiresAt)) return undefined;
|
|
748
|
+
return {
|
|
749
|
+
accessKeyId: body.AccessKeyId,
|
|
750
|
+
secretAccessKey: body.SecretAccessKey,
|
|
751
|
+
sessionToken: body.Token,
|
|
752
|
+
expiresAt,
|
|
753
|
+
};
|
|
754
|
+
} catch {
|
|
755
|
+
return undefined;
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
/** Test/diagnostic helper — drops cached credentials. */
|
|
760
|
+
export function clearAwsCredentialCache(): void {
|
|
761
|
+
cache.clear();
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
/**
|
|
765
|
+
* Drop the cache entry for one profile/region. Called by the Bedrock provider on
|
|
766
|
+
* 401/403 responses so stale credentials are re-resolved instead of served until restart.
|
|
767
|
+
*/
|
|
768
|
+
export function invalidateAwsCredentialCache(opts: { profile?: string; region?: string } = {}): void {
|
|
769
|
+
const profile = resolveAwsProfile(opts.profile);
|
|
770
|
+
const region = resolveAwsRegion(opts.region, opts.profile);
|
|
771
|
+
cache.delete(credentialCacheKey(profile, region, shouldLoadAwsSharedConfig(opts.profile)));
|
|
772
|
+
}
|