@gajae-code/ai 0.1.1
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 +2644 -0
- package/README.md +1181 -0
- package/dist/types/api-registry.d.ts +30 -0
- package/dist/types/auth-broker/client.d.ts +66 -0
- package/dist/types/auth-broker/index.d.ts +5 -0
- package/dist/types/auth-broker/refresher.d.ts +25 -0
- package/dist/types/auth-broker/remote-store.d.ts +96 -0
- package/dist/types/auth-broker/server.d.ts +32 -0
- package/dist/types/auth-broker/types.d.ts +105 -0
- package/dist/types/auth-broker/wire-schemas.d.ts +412 -0
- package/dist/types/auth-gateway/http.d.ts +39 -0
- package/dist/types/auth-gateway/index.d.ts +3 -0
- package/dist/types/auth-gateway/server.d.ts +17 -0
- package/dist/types/auth-gateway/types.d.ts +115 -0
- package/dist/types/auth-storage.d.ts +641 -0
- package/dist/types/cli.d.ts +2 -0
- package/dist/types/index.d.ts +49 -0
- package/dist/types/model-cache.d.ts +17 -0
- package/dist/types/model-manager.d.ts +62 -0
- package/dist/types/model-thinking.d.ts +71 -0
- package/dist/types/models.d.ts +12 -0
- package/dist/types/provider-details.d.ts +24 -0
- package/dist/types/provider-models/bundled-references.d.ts +4 -0
- package/dist/types/provider-models/descriptors.d.ts +48 -0
- package/dist/types/provider-models/google.d.ts +20 -0
- package/dist/types/provider-models/index.d.ts +5 -0
- package/dist/types/provider-models/ollama.d.ts +7 -0
- package/dist/types/provider-models/openai-compat.d.ts +237 -0
- package/dist/types/provider-models/special.d.ts +16 -0
- package/dist/types/providers/amazon-bedrock.d.ts +36 -0
- package/dist/types/providers/anthropic-messages-server-schema.d.ts +450 -0
- package/dist/types/providers/anthropic-messages-server.d.ts +17 -0
- package/dist/types/providers/anthropic.d.ts +188 -0
- package/dist/types/providers/aws-credentials.d.ts +43 -0
- package/dist/types/providers/aws-eventstream.d.ts +38 -0
- package/dist/types/providers/aws-sigv4.d.ts +55 -0
- package/dist/types/providers/azure-openai-responses.d.ts +15 -0
- package/dist/types/providers/cursor/gen/agent_pb.d.ts +13022 -0
- package/dist/types/providers/cursor.d.ts +42 -0
- package/dist/types/providers/error-message.d.ts +27 -0
- package/dist/types/providers/github-copilot-headers.d.ts +40 -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 +72 -0
- package/dist/types/providers/google-gemini-headers.d.ts +18 -0
- package/dist/types/providers/google-shared.d.ts +163 -0
- package/dist/types/providers/google-types.d.ts +138 -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 +175 -0
- package/dist/types/providers/ollama.d.ts +6 -0
- package/dist/types/providers/openai-anthropic-shim.d.ts +31 -0
- package/dist/types/providers/openai-chat-server-schema.d.ts +814 -0
- package/dist/types/providers/openai-chat-server.d.ts +16 -0
- package/dist/types/providers/openai-codex/constants.d.ts +26 -0
- package/dist/types/providers/openai-codex/request-transformer.d.ts +49 -0
- package/dist/types/providers/openai-codex/response-handler.d.ts +17 -0
- package/dist/types/providers/openai-codex-responses.d.ts +67 -0
- package/dist/types/providers/openai-completions-compat.d.ts +25 -0
- package/dist/types/providers/openai-completions.d.ts +33 -0
- package/dist/types/providers/openai-responses-server-schema.d.ts +392 -0
- package/dist/types/providers/openai-responses-server.d.ts +17 -0
- package/dist/types/providers/openai-responses-shared.d.ts +89 -0
- package/dist/types/providers/openai-responses.d.ts +32 -0
- package/dist/types/providers/pi-native-client.d.ts +13 -0
- package/dist/types/providers/pi-native-server.d.ts +68 -0
- package/dist/types/providers/register-builtins.d.ts +31 -0
- package/dist/types/providers/synthetic.d.ts +26 -0
- package/dist/types/providers/transform-messages.d.ts +12 -0
- package/dist/types/providers/vision-guard.d.ts +8 -0
- package/dist/types/rate-limit-utils.d.ts +19 -0
- package/dist/types/stream.d.ts +24 -0
- package/dist/types/types.d.ts +746 -0
- package/dist/types/usage/claude.d.ts +3 -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 +2 -0
- package/dist/types/usage/kimi.d.ts +2 -0
- package/dist/types/usage/minimax-code.d.ts +2 -0
- package/dist/types/usage/openai-codex.d.ts +3 -0
- package/dist/types/usage/shared.d.ts +1 -0
- package/dist/types/usage/zai.d.ts +2 -0
- package/dist/types/usage.d.ts +258 -0
- package/dist/types/utils/abort.d.ts +19 -0
- package/dist/types/utils/anthropic-auth.d.ts +31 -0
- package/dist/types/utils/discovery/antigravity.d.ts +61 -0
- package/dist/types/utils/discovery/codex.d.ts +38 -0
- package/dist/types/utils/discovery/cursor.d.ts +23 -0
- package/dist/types/utils/discovery/gemini.d.ts +25 -0
- package/dist/types/utils/discovery/index.d.ts +4 -0
- package/dist/types/utils/discovery/openai-compatible.d.ts +72 -0
- package/dist/types/utils/event-stream.d.ts +28 -0
- package/dist/types/utils/fireworks-model-id.d.ts +10 -0
- package/dist/types/utils/foundry.d.ts +1 -0
- package/dist/types/utils/h2-fetch.d.ts +22 -0
- package/dist/types/utils/http-inspector.d.ts +31 -0
- package/dist/types/utils/idle-iterator.d.ts +67 -0
- package/dist/types/utils/json-parse.d.ts +10 -0
- package/dist/types/utils/oauth/alibaba-coding-plan.d.ts +18 -0
- package/dist/types/utils/oauth/anthropic.d.ts +22 -0
- package/dist/types/utils/oauth/api-key-login.d.ts +35 -0
- package/dist/types/utils/oauth/api-key-validation.d.ts +27 -0
- package/dist/types/utils/oauth/callback-server.d.ts +57 -0
- package/dist/types/utils/oauth/cerebras.d.ts +1 -0
- package/dist/types/utils/oauth/cloudflare-ai-gateway.d.ts +18 -0
- package/dist/types/utils/oauth/cursor.d.ts +15 -0
- package/dist/types/utils/oauth/deepseek.d.ts +10 -0
- package/dist/types/utils/oauth/firepass.d.ts +1 -0
- package/dist/types/utils/oauth/fireworks.d.ts +1 -0
- package/dist/types/utils/oauth/github-copilot.d.ts +38 -0
- package/dist/types/utils/oauth/gitlab-duo.d.ts +3 -0
- package/dist/types/utils/oauth/google-antigravity.d.ts +11 -0
- package/dist/types/utils/oauth/google-gemini-cli.d.ts +10 -0
- package/dist/types/utils/oauth/google-oauth-shared.d.ts +28 -0
- package/dist/types/utils/oauth/huggingface.d.ts +19 -0
- package/dist/types/utils/oauth/index.d.ts +38 -0
- package/dist/types/utils/oauth/kagi.d.ts +17 -0
- package/dist/types/utils/oauth/kilo.d.ts +5 -0
- package/dist/types/utils/oauth/kimi.d.ts +21 -0
- package/dist/types/utils/oauth/litellm.d.ts +18 -0
- package/dist/types/utils/oauth/lm-studio.d.ts +17 -0
- package/dist/types/utils/oauth/minimax-code.d.ts +28 -0
- package/dist/types/utils/oauth/moonshot.d.ts +1 -0
- package/dist/types/utils/oauth/nanogpt.d.ts +1 -0
- package/dist/types/utils/oauth/nvidia.d.ts +18 -0
- package/dist/types/utils/oauth/ollama-cloud.d.ts +2 -0
- package/dist/types/utils/oauth/ollama.d.ts +18 -0
- package/dist/types/utils/oauth/openai-codex.d.ts +21 -0
- package/dist/types/utils/oauth/opencode.d.ts +18 -0
- package/dist/types/utils/oauth/parallel.d.ts +17 -0
- package/dist/types/utils/oauth/perplexity.d.ts +9 -0
- package/dist/types/utils/oauth/pkce.d.ts +8 -0
- package/dist/types/utils/oauth/qianfan.d.ts +17 -0
- package/dist/types/utils/oauth/qwen-portal.d.ts +19 -0
- package/dist/types/utils/oauth/synthetic.d.ts +1 -0
- package/dist/types/utils/oauth/tavily.d.ts +17 -0
- package/dist/types/utils/oauth/together.d.ts +1 -0
- package/dist/types/utils/oauth/types.d.ts +44 -0
- package/dist/types/utils/oauth/venice.d.ts +18 -0
- package/dist/types/utils/oauth/vercel-ai-gateway.d.ts +18 -0
- package/dist/types/utils/oauth/vllm.d.ts +16 -0
- package/dist/types/utils/oauth/xiaomi.d.ts +19 -0
- package/dist/types/utils/oauth/zai.d.ts +18 -0
- package/dist/types/utils/oauth/zenmux.d.ts +1 -0
- package/dist/types/utils/overflow.d.ts +54 -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/retry-after.d.ts +3 -0
- package/dist/types/utils/retry.d.ts +26 -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 +49 -0
- package/dist/types/utils/schema/index.d.ts +13 -0
- package/dist/types/utils/schema/json-schema-validator.d.ts +12 -0
- package/dist/types/utils/schema/meta-validator.d.ts +2 -0
- package/dist/types/utils/schema/normalize.d.ts +93 -0
- package/dist/types/utils/schema/spill.d.ts +8 -0
- package/dist/types/utils/schema/stamps.d.ts +25 -0
- package/dist/types/utils/schema/types.d.ts +4 -0
- package/dist/types/utils/schema/wire.d.ts +54 -0
- package/dist/types/utils/schema/zod-decontaminate.d.ts +31 -0
- package/dist/types/utils/sse-debug.d.ts +10 -0
- package/dist/types/utils/tool-call-healing.d.ts +71 -0
- package/dist/types/utils/tool-choice.d.ts +50 -0
- package/dist/types/utils/validation.d.ts +17 -0
- package/dist/types/utils.d.ts +28 -0
- package/package.json +146 -0
- package/src/api-registry.ts +96 -0
- package/src/auth-broker/client.ts +358 -0
- package/src/auth-broker/index.ts +5 -0
- package/src/auth-broker/refresher.ts +127 -0
- package/src/auth-broker/remote-store.ts +623 -0
- package/src/auth-broker/server.ts +644 -0
- package/src/auth-broker/types.ts +127 -0
- package/src/auth-broker/wire-schemas.ts +200 -0
- package/src/auth-gateway/http.ts +194 -0
- package/src/auth-gateway/index.ts +3 -0
- package/src/auth-gateway/server.ts +717 -0
- package/src/auth-gateway/types.ts +134 -0
- package/src/auth-storage.ts +4104 -0
- package/src/cli.ts +262 -0
- package/src/index.ts +54 -0
- package/src/model-cache.ts +129 -0
- package/src/model-manager.ts +450 -0
- package/src/model-thinking.ts +691 -0
- package/src/models.json +73853 -0
- package/src/models.json.d.ts +9 -0
- package/src/models.ts +56 -0
- package/src/prompts/turn-aborted-guidance.md +4 -0
- package/src/provider-details.ts +90 -0
- package/src/provider-models/bundled-references.ts +38 -0
- package/src/provider-models/descriptors.ts +308 -0
- package/src/provider-models/google.ts +91 -0
- package/src/provider-models/index.ts +5 -0
- package/src/provider-models/ollama.ts +153 -0
- package/src/provider-models/openai-compat.ts +2275 -0
- package/src/provider-models/special.ts +67 -0
- package/src/providers/amazon-bedrock.ts +849 -0
- package/src/providers/anthropic-messages-server-schema.ts +229 -0
- package/src/providers/anthropic-messages-server.ts +677 -0
- package/src/providers/anthropic.ts +2696 -0
- package/src/providers/aws-credentials.ts +501 -0
- package/src/providers/aws-eventstream.ts +185 -0
- package/src/providers/aws-sigv4.ts +218 -0
- package/src/providers/azure-openai-responses.ts +337 -0
- package/src/providers/cursor/gen/agent_pb.ts +15274 -0
- package/src/providers/cursor/proto/agent.proto +3526 -0
- package/src/providers/cursor/proto/buf.gen.yaml +6 -0
- package/src/providers/cursor/proto/buf.yaml +17 -0
- package/src/providers/cursor.ts +2561 -0
- package/src/providers/error-message.ts +21 -0
- package/src/providers/github-copilot-headers.ts +140 -0
- package/src/providers/gitlab-duo.ts +372 -0
- package/src/providers/google-auth.ts +252 -0
- package/src/providers/google-gemini-cli.ts +795 -0
- package/src/providers/google-gemini-headers.ts +41 -0
- package/src/providers/google-shared.ts +902 -0
- package/src/providers/google-types.ts +167 -0
- package/src/providers/google-vertex.ts +88 -0
- package/src/providers/google.ts +41 -0
- package/src/providers/grammar.ts +70 -0
- package/src/providers/kimi.ts +52 -0
- package/src/providers/mock.ts +500 -0
- package/src/providers/ollama.ts +544 -0
- package/src/providers/openai-anthropic-shim.ts +138 -0
- package/src/providers/openai-chat-server-schema.ts +243 -0
- package/src/providers/openai-chat-server.ts +628 -0
- package/src/providers/openai-codex/constants.ts +43 -0
- package/src/providers/openai-codex/request-transformer.ts +161 -0
- package/src/providers/openai-codex/response-handler.ts +81 -0
- package/src/providers/openai-codex-responses.ts +2598 -0
- package/src/providers/openai-completions-compat.ts +279 -0
- package/src/providers/openai-completions.ts +1853 -0
- package/src/providers/openai-responses-server-schema.ts +290 -0
- package/src/providers/openai-responses-server.ts +1183 -0
- package/src/providers/openai-responses-shared.ts +800 -0
- package/src/providers/openai-responses.ts +621 -0
- package/src/providers/pi-native-client.ts +228 -0
- package/src/providers/pi-native-server.ts +210 -0
- package/src/providers/register-builtins.ts +412 -0
- package/src/providers/synthetic.ts +50 -0
- package/src/providers/transform-messages.ts +309 -0
- package/src/providers/vision-guard.ts +31 -0
- package/src/rate-limit-utils.ts +84 -0
- package/src/stream.ts +895 -0
- package/src/types.ts +884 -0
- package/src/usage/claude.ts +431 -0
- package/src/usage/gemini.ts +250 -0
- package/src/usage/github-copilot.ts +421 -0
- package/src/usage/google-antigravity.ts +201 -0
- package/src/usage/kimi.ts +271 -0
- package/src/usage/minimax-code.ts +31 -0
- package/src/usage/openai-codex.ts +503 -0
- package/src/usage/shared.ts +10 -0
- package/src/usage/zai.ts +247 -0
- package/src/usage.ts +183 -0
- package/src/utils/abort.ts +51 -0
- package/src/utils/anthropic-auth.ts +87 -0
- package/src/utils/discovery/antigravity.ts +261 -0
- package/src/utils/discovery/codex.ts +371 -0
- package/src/utils/discovery/cursor.ts +306 -0
- package/src/utils/discovery/gemini.ts +248 -0
- package/src/utils/discovery/index.ts +4 -0
- package/src/utils/discovery/openai-compatible.ts +224 -0
- package/src/utils/event-stream.ts +142 -0
- package/src/utils/fireworks-model-id.ts +30 -0
- package/src/utils/foundry.ts +8 -0
- package/src/utils/h2-fetch.ts +60 -0
- package/src/utils/http-inspector.ts +176 -0
- package/src/utils/idle-iterator.ts +250 -0
- package/src/utils/json-parse.ts +148 -0
- package/src/utils/oauth/alibaba-coding-plan.ts +59 -0
- package/src/utils/oauth/anthropic.ts +200 -0
- package/src/utils/oauth/api-key-login.ts +87 -0
- package/src/utils/oauth/api-key-validation.ts +92 -0
- package/src/utils/oauth/callback-server.ts +276 -0
- package/src/utils/oauth/cerebras.ts +16 -0
- package/src/utils/oauth/cloudflare-ai-gateway.ts +48 -0
- package/src/utils/oauth/cursor.ts +157 -0
- package/src/utils/oauth/deepseek.ts +53 -0
- package/src/utils/oauth/firepass.ts +24 -0
- package/src/utils/oauth/fireworks.ts +15 -0
- package/src/utils/oauth/github-copilot.ts +362 -0
- package/src/utils/oauth/gitlab-duo.ts +123 -0
- package/src/utils/oauth/google-antigravity.ts +200 -0
- package/src/utils/oauth/google-gemini-cli.ts +256 -0
- package/src/utils/oauth/google-oauth-shared.ts +110 -0
- package/src/utils/oauth/huggingface.ts +62 -0
- package/src/utils/oauth/index.ts +444 -0
- package/src/utils/oauth/kagi.ts +47 -0
- package/src/utils/oauth/kilo.ts +87 -0
- package/src/utils/oauth/kimi.ts +254 -0
- package/src/utils/oauth/litellm.ts +47 -0
- package/src/utils/oauth/lm-studio.ts +38 -0
- package/src/utils/oauth/minimax-code.ts +78 -0
- package/src/utils/oauth/moonshot.ts +16 -0
- package/src/utils/oauth/nanogpt.ts +15 -0
- package/src/utils/oauth/nvidia.ts +70 -0
- package/src/utils/oauth/oauth.html +199 -0
- package/src/utils/oauth/ollama-cloud.ts +28 -0
- package/src/utils/oauth/ollama.ts +47 -0
- package/src/utils/oauth/openai-codex.ts +299 -0
- package/src/utils/oauth/opencode.ts +49 -0
- package/src/utils/oauth/parallel.ts +46 -0
- package/src/utils/oauth/perplexity.ts +206 -0
- package/src/utils/oauth/pkce.ts +18 -0
- package/src/utils/oauth/qianfan.ts +58 -0
- package/src/utils/oauth/qwen-portal.ts +60 -0
- package/src/utils/oauth/synthetic.ts +16 -0
- package/src/utils/oauth/tavily.ts +46 -0
- package/src/utils/oauth/together.ts +16 -0
- package/src/utils/oauth/types.ts +94 -0
- package/src/utils/oauth/venice.ts +59 -0
- package/src/utils/oauth/vercel-ai-gateway.ts +47 -0
- package/src/utils/oauth/vllm.ts +40 -0
- package/src/utils/oauth/xiaomi.ts +137 -0
- package/src/utils/oauth/zai.ts +60 -0
- package/src/utils/oauth/zenmux.ts +15 -0
- package/src/utils/overflow.ts +137 -0
- package/src/utils/parse-bind.ts +54 -0
- package/src/utils/provider-response.ts +30 -0
- package/src/utils/retry-after.ts +110 -0
- package/src/utils/retry.ts +54 -0
- package/src/utils/schema/CONSTRAINTS.md +164 -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 +190 -0
- package/src/utils/schema/index.ts +13 -0
- package/src/utils/schema/json-schema-validator.ts +577 -0
- package/src/utils/schema/meta-validator.ts +167 -0
- package/src/utils/schema/normalize.ts +1588 -0
- package/src/utils/schema/spill.ts +43 -0
- package/src/utils/schema/stamps.ts +97 -0
- package/src/utils/schema/types.ts +11 -0
- package/src/utils/schema/wire.ts +213 -0
- package/src/utils/schema/zod-decontaminate.ts +331 -0
- package/src/utils/sse-debug.ts +289 -0
- package/src/utils/tool-call-healing.ts +271 -0
- package/src/utils/tool-choice.ts +99 -0
- package/src/utils/validation.ts +1019 -0
- package/src/utils.ts +166 -0
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Application Default Credentials (ADC) resolution for Vertex AI.
|
|
3
|
+
*
|
|
4
|
+
* Replaces `google-auth-library` with a direct WebCrypto + REST implementation.
|
|
5
|
+
* Sources, in priority order:
|
|
6
|
+
* 1. `GOOGLE_APPLICATION_CREDENTIALS` env → file with `type: "service_account"` (RS256 JWT exchange)
|
|
7
|
+
* or `type: "authorized_user"` (refresh-token exchange).
|
|
8
|
+
* 2. `~/.config/gcloud/application_default_credentials.json` (user ADC, same authorized_user flow).
|
|
9
|
+
* 3. GCE / Cloud Run metadata server (`metadata.google.internal`).
|
|
10
|
+
*
|
|
11
|
+
* Tokens are cached per source key and refreshed `GOOGLE_VERTEX_REFRESH_SKEW_MS` before expiry
|
|
12
|
+
* (default 60s). Concurrent callers waiting on a refresh share the same in-flight promise.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { Buffer } from "node:buffer";
|
|
16
|
+
import * as os from "node:os";
|
|
17
|
+
import * as path from "node:path";
|
|
18
|
+
import { $envpos, isEnoent, logger } from "@gajae-code/utils";
|
|
19
|
+
import type { FetchImpl } from "../types";
|
|
20
|
+
|
|
21
|
+
const OAUTH_TOKEN_URL = "https://oauth2.googleapis.com/token";
|
|
22
|
+
const METADATA_TOKEN_URL = "http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token";
|
|
23
|
+
const CLOUD_PLATFORM_SCOPE = "https://www.googleapis.com/auth/cloud-platform";
|
|
24
|
+
const JWT_BEARER_GRANT = "urn:ietf:params:oauth:grant-type:jwt-bearer";
|
|
25
|
+
|
|
26
|
+
interface CachedToken {
|
|
27
|
+
token: string;
|
|
28
|
+
expiresAtMs: number;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
interface ServiceAccountCredentials {
|
|
32
|
+
type: "service_account";
|
|
33
|
+
client_email: string;
|
|
34
|
+
private_key: string;
|
|
35
|
+
private_key_id?: string;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
interface AuthorizedUserCredentials {
|
|
39
|
+
type: "authorized_user";
|
|
40
|
+
client_id: string;
|
|
41
|
+
client_secret: string;
|
|
42
|
+
refresh_token: string;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
type AdcFileCredentials = ServiceAccountCredentials | AuthorizedUserCredentials;
|
|
46
|
+
|
|
47
|
+
interface TokenResponse {
|
|
48
|
+
access_token: string;
|
|
49
|
+
expires_in: number;
|
|
50
|
+
token_type?: string;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const tokenCache = new Map<string, CachedToken>();
|
|
54
|
+
const inflight = new Map<string, Promise<string>>();
|
|
55
|
+
|
|
56
|
+
function getRefreshSkewMs(): number {
|
|
57
|
+
return $envpos("GOOGLE_VERTEX_REFRESH_SKEW_MS", 60_000);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function userAdcPath(): string {
|
|
61
|
+
return path.join(os.homedir(), ".config", "gcloud", "application_default_credentials.json");
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
async function readJsonFile<T>(filePath: string): Promise<T | undefined> {
|
|
65
|
+
try {
|
|
66
|
+
return (await Bun.file(filePath).json()) as T;
|
|
67
|
+
} catch (err) {
|
|
68
|
+
if (isEnoent(err)) return undefined;
|
|
69
|
+
throw err;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
async function loadAdcCredentials(): Promise<{ source: string; creds: AdcFileCredentials } | undefined> {
|
|
74
|
+
const gacPath = Bun.env.GOOGLE_APPLICATION_CREDENTIALS;
|
|
75
|
+
if (gacPath) {
|
|
76
|
+
const creds = await readJsonFile<AdcFileCredentials>(gacPath);
|
|
77
|
+
if (!creds) {
|
|
78
|
+
throw new Error(`GOOGLE_APPLICATION_CREDENTIALS points to a missing file: ${gacPath}`);
|
|
79
|
+
}
|
|
80
|
+
return { source: `gac:${gacPath}`, creds };
|
|
81
|
+
}
|
|
82
|
+
const userPath = userAdcPath();
|
|
83
|
+
const creds = await readJsonFile<AdcFileCredentials>(userPath);
|
|
84
|
+
if (creds) return { source: `user:${userPath}`, creds };
|
|
85
|
+
return undefined;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function base64UrlEncode(bytes: Uint8Array | string): string {
|
|
89
|
+
const buf = typeof bytes === "string" ? Buffer.from(bytes, "utf8") : bytes;
|
|
90
|
+
return Buffer.from(buf.buffer, buf.byteOffset, buf.byteLength).toString("base64url");
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function pemToPkcs8(pem: string): Uint8Array<ArrayBuffer> {
|
|
94
|
+
const body = pem
|
|
95
|
+
.replace(/-----BEGIN [^-]+-----/g, "")
|
|
96
|
+
.replace(/-----END [^-]+-----/g, "")
|
|
97
|
+
.replace(/\s+/g, "");
|
|
98
|
+
if (!body) throw new Error("Invalid PEM: empty body");
|
|
99
|
+
return Uint8Array.fromBase64(body);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
async function signJwtRs256(claims: Record<string, unknown>, privateKeyPem: string, keyId?: string): Promise<string> {
|
|
103
|
+
const header: Record<string, unknown> = { alg: "RS256", typ: "JWT" };
|
|
104
|
+
if (keyId) header.kid = keyId;
|
|
105
|
+
const payload = `${base64UrlEncode(JSON.stringify(header))}.${base64UrlEncode(JSON.stringify(claims))}`;
|
|
106
|
+
|
|
107
|
+
const key = await globalThis.crypto.subtle.importKey(
|
|
108
|
+
"pkcs8",
|
|
109
|
+
pemToPkcs8(privateKeyPem),
|
|
110
|
+
{ name: "RSASSA-PKCS1-v1_5", hash: "SHA-256" },
|
|
111
|
+
false,
|
|
112
|
+
["sign"],
|
|
113
|
+
);
|
|
114
|
+
const signature = new Uint8Array(
|
|
115
|
+
await globalThis.crypto.subtle.sign("RSASSA-PKCS1-v1_5", key, new TextEncoder().encode(payload)),
|
|
116
|
+
);
|
|
117
|
+
return `${payload}.${base64UrlEncode(signature)}`;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
async function exchangeJwtForToken(
|
|
121
|
+
creds: ServiceAccountCredentials,
|
|
122
|
+
signal: AbortSignal | undefined,
|
|
123
|
+
fetchImpl: FetchImpl,
|
|
124
|
+
): Promise<TokenResponse> {
|
|
125
|
+
const now = Math.floor(Date.now() / 1000);
|
|
126
|
+
const assertion = await signJwtRs256(
|
|
127
|
+
{
|
|
128
|
+
iss: creds.client_email,
|
|
129
|
+
scope: CLOUD_PLATFORM_SCOPE,
|
|
130
|
+
aud: OAUTH_TOKEN_URL,
|
|
131
|
+
exp: now + 3600,
|
|
132
|
+
iat: now,
|
|
133
|
+
},
|
|
134
|
+
creds.private_key,
|
|
135
|
+
creds.private_key_id,
|
|
136
|
+
);
|
|
137
|
+
const body = new URLSearchParams({ grant_type: JWT_BEARER_GRANT, assertion });
|
|
138
|
+
return postForToken(OAUTH_TOKEN_URL, body, signal, fetchImpl);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
async function exchangeRefreshToken(
|
|
142
|
+
creds: AuthorizedUserCredentials,
|
|
143
|
+
signal: AbortSignal | undefined,
|
|
144
|
+
fetchImpl: FetchImpl,
|
|
145
|
+
): Promise<TokenResponse> {
|
|
146
|
+
const body = new URLSearchParams({
|
|
147
|
+
client_id: creds.client_id,
|
|
148
|
+
client_secret: creds.client_secret,
|
|
149
|
+
refresh_token: creds.refresh_token,
|
|
150
|
+
grant_type: "refresh_token",
|
|
151
|
+
});
|
|
152
|
+
return postForToken(OAUTH_TOKEN_URL, body, signal, fetchImpl);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
async function fetchMetadataToken(
|
|
156
|
+
signal: AbortSignal | undefined,
|
|
157
|
+
fetchImpl: FetchImpl,
|
|
158
|
+
): Promise<TokenResponse | undefined> {
|
|
159
|
+
const timeout = AbortSignal.timeout(2000);
|
|
160
|
+
const combined = signal ? AbortSignal.any([signal, timeout]) : timeout;
|
|
161
|
+
try {
|
|
162
|
+
const response = await fetchImpl(METADATA_TOKEN_URL, {
|
|
163
|
+
method: "GET",
|
|
164
|
+
headers: { "Metadata-Flavor": "Google" },
|
|
165
|
+
signal: combined,
|
|
166
|
+
});
|
|
167
|
+
if (!response.ok) return undefined;
|
|
168
|
+
return (await response.json()) as TokenResponse;
|
|
169
|
+
} catch {
|
|
170
|
+
return undefined;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
async function postForToken(
|
|
175
|
+
url: string,
|
|
176
|
+
body: URLSearchParams,
|
|
177
|
+
signal: AbortSignal | undefined,
|
|
178
|
+
fetchImpl: FetchImpl,
|
|
179
|
+
): Promise<TokenResponse> {
|
|
180
|
+
const response = await fetchImpl(url, {
|
|
181
|
+
method: "POST",
|
|
182
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
183
|
+
body: body.toString(),
|
|
184
|
+
signal,
|
|
185
|
+
});
|
|
186
|
+
if (!response.ok) {
|
|
187
|
+
const detail = await response.text().catch(() => "");
|
|
188
|
+
throw new Error(`Google OAuth token exchange failed (${response.status}): ${detail}`);
|
|
189
|
+
}
|
|
190
|
+
return (await response.json()) as TokenResponse;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
async function resolveAccessTokenUncached(
|
|
194
|
+
signal: AbortSignal | undefined,
|
|
195
|
+
fetchImpl: FetchImpl,
|
|
196
|
+
): Promise<{ source: string; token: TokenResponse }> {
|
|
197
|
+
const adc = await loadAdcCredentials();
|
|
198
|
+
if (adc) {
|
|
199
|
+
const token =
|
|
200
|
+
adc.creds.type === "service_account"
|
|
201
|
+
? await exchangeJwtForToken(adc.creds, signal, fetchImpl)
|
|
202
|
+
: await exchangeRefreshToken(adc.creds, signal, fetchImpl);
|
|
203
|
+
return { source: adc.source, token };
|
|
204
|
+
}
|
|
205
|
+
const metadata = await fetchMetadataToken(signal, fetchImpl);
|
|
206
|
+
if (metadata) return { source: "metadata", token: metadata };
|
|
207
|
+
throw new Error(
|
|
208
|
+
"Vertex AI requires Application Default Credentials. Set GOOGLE_APPLICATION_CREDENTIALS, run `gcloud auth application-default login`, or run on a GCE/Cloud Run instance with a service account.",
|
|
209
|
+
);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Returns a Bearer access token suitable for the `Authorization` header on Vertex AI calls.
|
|
214
|
+
* The token is cached in module scope and refreshed `GOOGLE_VERTEX_REFRESH_SKEW_MS` ms before it expires.
|
|
215
|
+
*/
|
|
216
|
+
export async function getVertexAccessToken(options?: { signal?: AbortSignal; fetch?: FetchImpl }): Promise<string> {
|
|
217
|
+
const fetchImpl = options?.fetch ?? globalThis.fetch.bind(globalThis);
|
|
218
|
+
const skew = getRefreshSkewMs();
|
|
219
|
+
const now = Date.now();
|
|
220
|
+
|
|
221
|
+
// Best-effort cache key probe: we don't know the source until we resolve, but cached entries
|
|
222
|
+
// are keyed by their resolved source. Try every cached source first.
|
|
223
|
+
for (const [source, cached] of tokenCache) {
|
|
224
|
+
if (cached.expiresAtMs - skew > now) return cached.token;
|
|
225
|
+
// expired entry — drop and re-resolve
|
|
226
|
+
tokenCache.delete(source);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
const cacheKey = "vertex-adc";
|
|
230
|
+
const existing = inflight.get(cacheKey);
|
|
231
|
+
if (existing) return existing;
|
|
232
|
+
|
|
233
|
+
const promise = (async () => {
|
|
234
|
+
try {
|
|
235
|
+
const { source, token } = await resolveAccessTokenUncached(options?.signal, fetchImpl);
|
|
236
|
+
const expiresAtMs = Date.now() + Math.max(0, token.expires_in * 1000);
|
|
237
|
+
tokenCache.set(source, { token: token.access_token, expiresAtMs });
|
|
238
|
+
logger.debug("vertex.adc acquired access token", { source, expiresInSec: token.expires_in });
|
|
239
|
+
return token.access_token;
|
|
240
|
+
} finally {
|
|
241
|
+
inflight.delete(cacheKey);
|
|
242
|
+
}
|
|
243
|
+
})();
|
|
244
|
+
inflight.set(cacheKey, promise);
|
|
245
|
+
return promise;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/** Test seam: clears every cached token. */
|
|
249
|
+
export function __resetVertexTokenCache(): void {
|
|
250
|
+
tokenCache.clear();
|
|
251
|
+
inflight.clear();
|
|
252
|
+
}
|