@aitne/shared 0.1.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/LICENSE +21 -0
- package/dist/advisor-models.d.ts +34 -0
- package/dist/advisor-models.d.ts.map +1 -0
- package/dist/advisor-models.js +39 -0
- package/dist/advisor-models.js.map +1 -0
- package/dist/agent-identity.d.ts +11 -0
- package/dist/agent-identity.d.ts.map +1 -0
- package/dist/agent-identity.js +29 -0
- package/dist/agent-identity.js.map +1 -0
- package/dist/alerts.d.ts +44 -0
- package/dist/alerts.d.ts.map +1 -0
- package/dist/alerts.js +12 -0
- package/dist/alerts.js.map +1 -0
- package/dist/backend-api-key-config.d.ts +337 -0
- package/dist/backend-api-key-config.d.ts.map +1 -0
- package/dist/backend-api-key-config.js +682 -0
- package/dist/backend-api-key-config.js.map +1 -0
- package/dist/backend.d.ts +93 -0
- package/dist/backend.d.ts.map +1 -0
- package/dist/backend.js +22 -0
- package/dist/backend.js.map +1 -0
- package/dist/branding.d.ts +96 -0
- package/dist/branding.d.ts.map +1 -0
- package/dist/branding.js +102 -0
- package/dist/branding.js.map +1 -0
- package/dist/chat-session-scope.d.ts +14 -0
- package/dist/chat-session-scope.d.ts.map +1 -0
- package/dist/chat-session-scope.js +18 -0
- package/dist/chat-session-scope.js.map +1 -0
- package/dist/date-utils.d.ts +80 -0
- package/dist/date-utils.d.ts.map +1 -0
- package/dist/date-utils.js +187 -0
- package/dist/date-utils.js.map +1 -0
- package/dist/docs-frontmatter.d.ts +51 -0
- package/dist/docs-frontmatter.d.ts.map +1 -0
- package/dist/docs-frontmatter.js +184 -0
- package/dist/docs-frontmatter.js.map +1 -0
- package/dist/docs-schema.d.ts +79 -0
- package/dist/docs-schema.d.ts.map +1 -0
- package/dist/docs-schema.js +135 -0
- package/dist/docs-schema.js.map +1 -0
- package/dist/editable-config-keys.d.ts +14 -0
- package/dist/editable-config-keys.d.ts.map +1 -0
- package/dist/editable-config-keys.js +157 -0
- package/dist/editable-config-keys.js.map +1 -0
- package/dist/exec-with-stdin.d.ts +14 -0
- package/dist/exec-with-stdin.d.ts.map +1 -0
- package/dist/exec-with-stdin.js +35 -0
- package/dist/exec-with-stdin.js.map +1 -0
- package/dist/index.d.ts +37 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +49 -0
- package/dist/index.js.map +1 -0
- package/dist/integrations-snapshot.d.ts +183 -0
- package/dist/integrations-snapshot.d.ts.map +1 -0
- package/dist/integrations-snapshot.js +757 -0
- package/dist/integrations-snapshot.js.map +1 -0
- package/dist/integrations.d.ts +675 -0
- package/dist/integrations.d.ts.map +1 -0
- package/dist/integrations.js +1656 -0
- package/dist/integrations.js.map +1 -0
- package/dist/keychain-helper-client.d.ts +31 -0
- package/dist/keychain-helper-client.d.ts.map +1 -0
- package/dist/keychain-helper-client.js +105 -0
- package/dist/keychain-helper-client.js.map +1 -0
- package/dist/log-entry.d.ts +14 -0
- package/dist/log-entry.d.ts.map +1 -0
- package/dist/log-entry.js +2 -0
- package/dist/log-entry.js.map +1 -0
- package/dist/management-domains.d.ts +369 -0
- package/dist/management-domains.d.ts.map +1 -0
- package/dist/management-domains.js +499 -0
- package/dist/management-domains.js.map +1 -0
- package/dist/process-key.d.ts +67 -0
- package/dist/process-key.d.ts.map +1 -0
- package/dist/process-key.js +366 -0
- package/dist/process-key.js.map +1 -0
- package/dist/schemas.d.ts +267 -0
- package/dist/schemas.d.ts.map +1 -0
- package/dist/schemas.js +271 -0
- package/dist/schemas.js.map +1 -0
- package/dist/secret-client-factory.d.ts +16 -0
- package/dist/secret-client-factory.d.ts.map +1 -0
- package/dist/secret-client-factory.js +111 -0
- package/dist/secret-client-factory.js.map +1 -0
- package/dist/secret-client-file.d.ts +51 -0
- package/dist/secret-client-file.d.ts.map +1 -0
- package/dist/secret-client-file.js +160 -0
- package/dist/secret-client-file.js.map +1 -0
- package/dist/secret-client-linux.d.ts +26 -0
- package/dist/secret-client-linux.d.ts.map +1 -0
- package/dist/secret-client-linux.js +63 -0
- package/dist/secret-client-linux.js.map +1 -0
- package/dist/secret-client-windows.d.ts +37 -0
- package/dist/secret-client-windows.d.ts.map +1 -0
- package/dist/secret-client-windows.js +82 -0
- package/dist/secret-client-windows.js.map +1 -0
- package/dist/secret-redaction.d.ts +3 -0
- package/dist/secret-redaction.d.ts.map +1 -0
- package/dist/secret-redaction.js +31 -0
- package/dist/secret-redaction.js.map +1 -0
- package/dist/skill-curation/decision-language.d.ts +6 -0
- package/dist/skill-curation/decision-language.d.ts.map +1 -0
- package/dist/skill-curation/decision-language.js +38 -0
- package/dist/skill-curation/decision-language.js.map +1 -0
- package/dist/skill-curation/schemas.d.ts +461 -0
- package/dist/skill-curation/schemas.d.ts.map +1 -0
- package/dist/skill-curation/schemas.js +211 -0
- package/dist/skill-curation/schemas.js.map +1 -0
- package/dist/types.d.ts +204 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +54 -0
- package/dist/types.js.map +1 -0
- package/package.json +50 -0
|
@@ -0,0 +1,682 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-backend provider auth configuration.
|
|
3
|
+
*
|
|
4
|
+
* Direct API keys (Anthropic / OpenAI / Google) are the long-standing path,
|
|
5
|
+
* but Claude Code's SDK also supports cloud-hosted Anthropic deployments,
|
|
6
|
+
* and Gemini CLI supports Vertex AI:
|
|
7
|
+
*
|
|
8
|
+
* Claude Code:
|
|
9
|
+
* - Amazon Bedrock β CLAUDE_CODE_USE_BEDROCK=1 + AWS creds (access key /
|
|
10
|
+
* bearer token / profile) + AWS_REGION
|
|
11
|
+
* - Google Vertex AI β CLAUDE_CODE_USE_VERTEX=1 + ANTHROPIC_VERTEX_PROJECT_ID +
|
|
12
|
+
* CLOUD_ML_REGION (creds via Application Default
|
|
13
|
+
* Credentials chain or GOOGLE_APPLICATION_CREDENTIALS file)
|
|
14
|
+
* - Microsoft Foundry β CLAUDE_CODE_USE_FOUNDRY=1 + ANTHROPIC_FOUNDRY_RESOURCE
|
|
15
|
+
* (or ANTHROPIC_FOUNDRY_BASE_URL) + optional
|
|
16
|
+
* ANTHROPIC_FOUNDRY_API_KEY (Entra ID auto-fallback)
|
|
17
|
+
*
|
|
18
|
+
* Gemini CLI:
|
|
19
|
+
* - Vertex AI β GOOGLE_GENAI_USE_VERTEXAI=true + GOOGLE_CLOUD_PROJECT +
|
|
20
|
+
* GOOGLE_CLOUD_LOCATION (auth via ADC / service account
|
|
21
|
+
* file / Vertex API key)
|
|
22
|
+
*
|
|
23
|
+
* Codex CLI's Azure OpenAI mode requires a `~/.codex/config.toml` file and is
|
|
24
|
+
* NOT exposed through this surface β env-var mirroring is insufficient to
|
|
25
|
+
* configure it. Codex stays direct-API-key only here.
|
|
26
|
+
*
|
|
27
|
+
* The daemon stores the chosen provider + its credentials as a single JSON
|
|
28
|
+
* blob in the OS keychain (`backend.<id>.api_key`). At startup and on every
|
|
29
|
+
* UI mutation the daemon mirrors the active provider's env vars into
|
|
30
|
+
* `process.env`, so the unchanged Claude SDK / Codex CLI / Gemini CLI
|
|
31
|
+
* subprocesses pick them up via the same inherited-env path.
|
|
32
|
+
*
|
|
33
|
+
* Backwards compatibility: legacy entries written before this feature were
|
|
34
|
+
* raw strings (the API key itself). `parseBackendApiKeyConfig` accepts both
|
|
35
|
+
* the new JSON shape and the legacy raw-string form.
|
|
36
|
+
*
|
|
37
|
+
* Env-var spec sources (verified 2026-05):
|
|
38
|
+
* - https://code.claude.com/docs/en/amazon-bedrock
|
|
39
|
+
* - https://code.claude.com/docs/en/google-vertex-ai
|
|
40
|
+
* - https://code.claude.com/docs/en/microsoft-foundry
|
|
41
|
+
* - https://geminicli.com/docs/get-started/authentication/
|
|
42
|
+
*/
|
|
43
|
+
import { z } from "zod";
|
|
44
|
+
// ββ Provider IDs per backend βββββββββββββββββββββββββββββββββββββββββ
|
|
45
|
+
export const CLAUDE_API_KEY_PROVIDERS = [
|
|
46
|
+
"anthropic",
|
|
47
|
+
"bedrock",
|
|
48
|
+
"vertex",
|
|
49
|
+
"foundry",
|
|
50
|
+
];
|
|
51
|
+
export const CODEX_API_KEY_PROVIDERS = ["openai", "azure-openai"];
|
|
52
|
+
export const GEMINI_API_KEY_PROVIDERS = ["google", "gemini-vertex"];
|
|
53
|
+
export const API_KEY_PROVIDERS_BY_BACKEND = {
|
|
54
|
+
claude: CLAUDE_API_KEY_PROVIDERS,
|
|
55
|
+
codex: CODEX_API_KEY_PROVIDERS,
|
|
56
|
+
gemini: GEMINI_API_KEY_PROVIDERS,
|
|
57
|
+
};
|
|
58
|
+
export function defaultApiKeyProvider(backendId) {
|
|
59
|
+
return API_KEY_PROVIDERS_BY_BACKEND[backendId][0];
|
|
60
|
+
}
|
|
61
|
+
export function isApiKeyProviderForBackend(backendId, provider) {
|
|
62
|
+
return API_KEY_PROVIDERS_BY_BACKEND[backendId].includes(provider);
|
|
63
|
+
}
|
|
64
|
+
// ββ Discriminated config union βββββββββββββββββββββββββββββββββββββββ
|
|
65
|
+
export const anthropicApiKeyConfigSchema = z.object({
|
|
66
|
+
provider: z.literal("anthropic"),
|
|
67
|
+
apiKey: z.string().min(1),
|
|
68
|
+
});
|
|
69
|
+
/**
|
|
70
|
+
* Bedrock supports three documented auth paths:
|
|
71
|
+
* - `access_key` β AWS_ACCESS_KEY_ID + AWS_SECRET_ACCESS_KEY (+ optional
|
|
72
|
+
* AWS_SESSION_TOKEN for STS / Identity Center)
|
|
73
|
+
* - `bearer_token` β AWS_BEARER_TOKEN_BEDROCK (Bedrock API key β simplest)
|
|
74
|
+
* - `profile` β AWS_PROFILE (delegates to ~/.aws/config / ~/.aws/credentials)
|
|
75
|
+
*
|
|
76
|
+
* AWS_REGION is required by Claude Code regardless of the auth mode (the
|
|
77
|
+
* docs explicitly say `Claude Code does not read from the .aws config
|
|
78
|
+
* file for this setting`). `bedrockBaseUrl` is an optional override for
|
|
79
|
+
* VPC endpoints / gateways.
|
|
80
|
+
*/
|
|
81
|
+
const bedrockAccessKeySchema = z.object({
|
|
82
|
+
authMode: z.literal("access_key"),
|
|
83
|
+
awsAccessKeyId: z.string().min(1),
|
|
84
|
+
awsSecretAccessKey: z.string().min(1),
|
|
85
|
+
awsSessionToken: z.string().min(1).optional(),
|
|
86
|
+
});
|
|
87
|
+
const bedrockBearerTokenSchema = z.object({
|
|
88
|
+
authMode: z.literal("bearer_token"),
|
|
89
|
+
awsBearerTokenBedrock: z.string().min(1),
|
|
90
|
+
});
|
|
91
|
+
const bedrockProfileSchema = z.object({
|
|
92
|
+
authMode: z.literal("profile"),
|
|
93
|
+
awsProfile: z.string().min(1),
|
|
94
|
+
});
|
|
95
|
+
/**
|
|
96
|
+
* Per-provider model pinning fields. Setting any of these mirrors to the
|
|
97
|
+
* `ANTHROPIC_DEFAULT_OPUS_MODEL` / `ANTHROPIC_DEFAULT_SONNET_MODEL` /
|
|
98
|
+
* `ANTHROPIC_DEFAULT_HAIKU_MODEL` env vars so the `opus`/`sonnet`/`haiku`
|
|
99
|
+
* aliases resolve to the cloud-native model ID instead of the latest
|
|
100
|
+
* version (which may not be enabled in the customer's account, causing
|
|
101
|
+
* a 404 or fallback at startup). The Anthropic docs explicitly call this
|
|
102
|
+
* out as required for production deployments.
|
|
103
|
+
*
|
|
104
|
+
* The field shape is shared across Bedrock / Vertex / Foundry β only the
|
|
105
|
+
* recommended *defaults* differ per cloud (see
|
|
106
|
+
* `RECOMMENDED_PINNED_MODELS_BY_PROVIDER`).
|
|
107
|
+
*/
|
|
108
|
+
const pinnedModelsSchema = z.object({
|
|
109
|
+
defaultOpusModel: z.string().min(1).optional(),
|
|
110
|
+
defaultSonnetModel: z.string().min(1).optional(),
|
|
111
|
+
defaultHaikuModel: z.string().min(1).optional(),
|
|
112
|
+
});
|
|
113
|
+
/**
|
|
114
|
+
* Recommended model IDs per cloud. The dashboard pre-fills the pinning
|
|
115
|
+
* fields with these values, but the operator can edit. Update these
|
|
116
|
+
* alongside Anthropic's docs when a new model rolls out across all clouds.
|
|
117
|
+
*/
|
|
118
|
+
export const RECOMMENDED_PINNED_MODELS_BY_PROVIDER = {
|
|
119
|
+
bedrock: {
|
|
120
|
+
defaultOpusModel: "us.anthropic.claude-opus-4-7",
|
|
121
|
+
defaultSonnetModel: "us.anthropic.claude-sonnet-4-6",
|
|
122
|
+
defaultHaikuModel: "us.anthropic.claude-haiku-4-5-20251001-v1:0",
|
|
123
|
+
},
|
|
124
|
+
vertex: {
|
|
125
|
+
defaultOpusModel: "claude-opus-4-7",
|
|
126
|
+
defaultSonnetModel: "claude-sonnet-4-6",
|
|
127
|
+
defaultHaikuModel: "claude-haiku-4-5@20251001",
|
|
128
|
+
},
|
|
129
|
+
foundry: {
|
|
130
|
+
defaultOpusModel: "claude-opus-4-7",
|
|
131
|
+
defaultSonnetModel: "claude-sonnet-4-6",
|
|
132
|
+
defaultHaikuModel: "claude-haiku-4-5",
|
|
133
|
+
},
|
|
134
|
+
};
|
|
135
|
+
/**
|
|
136
|
+
* Bedrock-specific extras: Mantle endpoint flag + optional overrides.
|
|
137
|
+
* Mantle is an alternate Bedrock endpoint that uses the native Anthropic
|
|
138
|
+
* API shape; setting both `CLAUDE_CODE_USE_BEDROCK=1` and
|
|
139
|
+
* `CLAUDE_CODE_USE_MANTLE=1` is supported (Claude Code routes per-model).
|
|
140
|
+
*/
|
|
141
|
+
const bedrockExtrasSchema = z.object({
|
|
142
|
+
bedrockBaseUrl: z.string().url().optional(),
|
|
143
|
+
// Mantle endpoint
|
|
144
|
+
useMantle: z.boolean().optional(),
|
|
145
|
+
mantleBaseUrl: z.string().url().optional(),
|
|
146
|
+
// Skip client-side auth (for gateways that inject AWS creds server-side)
|
|
147
|
+
skipMantleAuth: z.boolean().optional(),
|
|
148
|
+
// Per-model region override for the Haiku-class small/fast model
|
|
149
|
+
smallFastModelAwsRegion: z.string().min(1).optional(),
|
|
150
|
+
});
|
|
151
|
+
export const bedrockApiKeyConfigSchema = z
|
|
152
|
+
.discriminatedUnion("authMode", [
|
|
153
|
+
bedrockAccessKeySchema,
|
|
154
|
+
bedrockBearerTokenSchema,
|
|
155
|
+
bedrockProfileSchema,
|
|
156
|
+
])
|
|
157
|
+
.and(z.object({
|
|
158
|
+
provider: z.literal("bedrock"),
|
|
159
|
+
awsRegion: z.string().min(1),
|
|
160
|
+
}))
|
|
161
|
+
.and(bedrockExtrasSchema)
|
|
162
|
+
.and(pinnedModelsSchema);
|
|
163
|
+
export const vertexApiKeyConfigSchema = z
|
|
164
|
+
.object({
|
|
165
|
+
provider: z.literal("vertex"),
|
|
166
|
+
projectId: z.string().min(1),
|
|
167
|
+
region: z.string().min(1),
|
|
168
|
+
// Optional file path for a service-account key file. Mirrors to
|
|
169
|
+
// GOOGLE_APPLICATION_CREDENTIALS. When omitted, the SDK uses Application
|
|
170
|
+
// Default Credentials (e.g. `gcloud auth application-default login`).
|
|
171
|
+
// The previous `serviceAccountJson` (inline JSON) field was removed β
|
|
172
|
+
// Anthropic's SDK does NOT read inline JSON; the env var is a file path.
|
|
173
|
+
credentialsFile: z.string().min(1).optional(),
|
|
174
|
+
vertexBaseUrl: z.string().url().optional(),
|
|
175
|
+
})
|
|
176
|
+
.and(pinnedModelsSchema);
|
|
177
|
+
/**
|
|
178
|
+
* Foundry needs *one* of `resource` (the Azure resource name; daemon
|
|
179
|
+
* routes to https://<resource>.services.ai.azure.com/anthropic) OR
|
|
180
|
+
* `baseUrl` (the full URL). API key is **optional** β when omitted,
|
|
181
|
+
* Claude Code uses the Azure DefaultAzureCredential chain (e.g. `az
|
|
182
|
+
* login` / managed identity).
|
|
183
|
+
*/
|
|
184
|
+
export const foundryApiKeyConfigSchema = z
|
|
185
|
+
.object({
|
|
186
|
+
provider: z.literal("foundry"),
|
|
187
|
+
resource: z.string().min(1).optional(),
|
|
188
|
+
baseUrl: z.string().url().optional(),
|
|
189
|
+
apiKey: z.string().min(1).optional(),
|
|
190
|
+
})
|
|
191
|
+
.and(pinnedModelsSchema)
|
|
192
|
+
.refine((v) => Boolean(v.resource) !== Boolean(v.baseUrl), {
|
|
193
|
+
message: "Foundry config requires exactly one of `resource` or `baseUrl`.",
|
|
194
|
+
});
|
|
195
|
+
export const openaiApiKeyConfigSchema = z.object({
|
|
196
|
+
provider: z.literal("openai"),
|
|
197
|
+
apiKey: z.string().min(1),
|
|
198
|
+
});
|
|
199
|
+
/**
|
|
200
|
+
* Codex CLI on Azure OpenAI. Codex CLI requires a `[model_providers.azure]`
|
|
201
|
+
* block in `config.toml` β env vars alone are insufficient. The daemon
|
|
202
|
+
* works around this by writing a managed `config.toml` to
|
|
203
|
+
* `<PA_DATA_DIR>/codex-home/config.toml` and pointing `CODEX_HOME` at
|
|
204
|
+
* that directory for spawned codex subprocesses, leaving the operator's
|
|
205
|
+
* personal `~/.codex/` configuration untouched.
|
|
206
|
+
*
|
|
207
|
+
* Required: `resource` (Azure resource name) and `apiKey` (mirrored to
|
|
208
|
+
* `AZURE_OPENAI_API_KEY`). Optional: `apiVersion` (defaults to the latest
|
|
209
|
+
* preview version Codex docs recommend) and `deploymentName` β when set,
|
|
210
|
+
* Codex's `model` setting is pinned to this deployment.
|
|
211
|
+
*
|
|
212
|
+
* **Known limitation: `--model` flag override.** The daemon's CodexCore
|
|
213
|
+
* spawns Codex with `--model <id>` where `<id>` is the per-process
|
|
214
|
+
* model from the registry (e.g. `gpt-5-codex`). On Azure, Codex treats
|
|
215
|
+
* the model argument as the *deployment name*, so the operator MUST
|
|
216
|
+
* name their Azure deployment to match the model IDs the daemon's
|
|
217
|
+
* routing uses. Renaming the deployment in Azure is the supported fix;
|
|
218
|
+
* a future round may plumb the active provider into CodexCore so the
|
|
219
|
+
* daemon can rewrite `--model` automatically.
|
|
220
|
+
*/
|
|
221
|
+
export const DEFAULT_AZURE_OPENAI_API_VERSION = "2025-04-01-preview";
|
|
222
|
+
export const azureOpenAiApiKeyConfigSchema = z.object({
|
|
223
|
+
provider: z.literal("azure-openai"),
|
|
224
|
+
resource: z.string().min(1),
|
|
225
|
+
apiKey: z.string().min(1),
|
|
226
|
+
apiVersion: z.string().min(1).optional(),
|
|
227
|
+
deploymentName: z.string().min(1).optional(),
|
|
228
|
+
});
|
|
229
|
+
export const googleApiKeyConfigSchema = z.object({
|
|
230
|
+
provider: z.literal("google"),
|
|
231
|
+
apiKey: z.string().min(1),
|
|
232
|
+
});
|
|
233
|
+
/**
|
|
234
|
+
* Gemini CLI on Vertex AI. Three auth paths:
|
|
235
|
+
* - `adc` β Application Default Credentials (gcloud)
|
|
236
|
+
* - `service_account` β GOOGLE_APPLICATION_CREDENTIALS file path
|
|
237
|
+
* - `api_key` β GOOGLE_API_KEY (Vertex API key, distinct from
|
|
238
|
+
* the direct `google` provider's GEMINI_API_KEY)
|
|
239
|
+
*
|
|
240
|
+
* `GOOGLE_CLOUD_PROJECT` + `GOOGLE_CLOUD_LOCATION` are required for all
|
|
241
|
+
* three modes. `GOOGLE_GENAI_USE_VERTEXAI=true` flips the SDK to Vertex.
|
|
242
|
+
*/
|
|
243
|
+
const geminiVertexAdcSchema = z.object({ authMode: z.literal("adc") });
|
|
244
|
+
const geminiVertexServiceAccountSchema = z.object({
|
|
245
|
+
authMode: z.literal("service_account"),
|
|
246
|
+
credentialsFile: z.string().min(1),
|
|
247
|
+
});
|
|
248
|
+
const geminiVertexApiKeySchema = z.object({
|
|
249
|
+
authMode: z.literal("api_key"),
|
|
250
|
+
apiKey: z.string().min(1),
|
|
251
|
+
});
|
|
252
|
+
export const geminiVertexApiKeyConfigSchema = z
|
|
253
|
+
.discriminatedUnion("authMode", [
|
|
254
|
+
geminiVertexAdcSchema,
|
|
255
|
+
geminiVertexServiceAccountSchema,
|
|
256
|
+
geminiVertexApiKeySchema,
|
|
257
|
+
])
|
|
258
|
+
.and(z.object({
|
|
259
|
+
provider: z.literal("gemini-vertex"),
|
|
260
|
+
projectId: z.string().min(1),
|
|
261
|
+
location: z.string().min(1),
|
|
262
|
+
}));
|
|
263
|
+
export const backendApiKeyConfigSchema = z.union([
|
|
264
|
+
anthropicApiKeyConfigSchema,
|
|
265
|
+
bedrockApiKeyConfigSchema,
|
|
266
|
+
vertexApiKeyConfigSchema,
|
|
267
|
+
foundryApiKeyConfigSchema,
|
|
268
|
+
openaiApiKeyConfigSchema,
|
|
269
|
+
azureOpenAiApiKeyConfigSchema,
|
|
270
|
+
googleApiKeyConfigSchema,
|
|
271
|
+
geminiVertexApiKeyConfigSchema,
|
|
272
|
+
]);
|
|
273
|
+
// ββ Env var management βββββββββββββββββββββββββββββββββββββββββββββββ
|
|
274
|
+
/**
|
|
275
|
+
* Every env var the daemon may set or clear when mirroring auth state for a
|
|
276
|
+
* backend. Returned as a stable list so `backend-api-key-env.ts` can snapshot
|
|
277
|
+
* the operator's shell values once at startup and restore them on UI clear.
|
|
278
|
+
*
|
|
279
|
+
* The list is the **superset** across providers: switching from Anthropic to
|
|
280
|
+
* Bedrock must clear `ANTHROPIC_API_KEY` and set `CLAUDE_CODE_USE_BEDROCK=1`
|
|
281
|
+
* + AWS_*, so both must appear here even though they are never set together.
|
|
282
|
+
*/
|
|
283
|
+
export function getManagedApiKeyEnvVars(backendId) {
|
|
284
|
+
switch (backendId) {
|
|
285
|
+
case "claude":
|
|
286
|
+
return [
|
|
287
|
+
"ANTHROPIC_API_KEY",
|
|
288
|
+
// Bedrock β covers all three auth modes
|
|
289
|
+
"CLAUDE_CODE_USE_BEDROCK",
|
|
290
|
+
"AWS_REGION",
|
|
291
|
+
"AWS_ACCESS_KEY_ID",
|
|
292
|
+
"AWS_SECRET_ACCESS_KEY",
|
|
293
|
+
"AWS_SESSION_TOKEN",
|
|
294
|
+
"AWS_BEARER_TOKEN_BEDROCK",
|
|
295
|
+
"AWS_PROFILE",
|
|
296
|
+
"ANTHROPIC_BEDROCK_BASE_URL",
|
|
297
|
+
// Bedrock Mantle endpoint
|
|
298
|
+
"CLAUDE_CODE_USE_MANTLE",
|
|
299
|
+
"ANTHROPIC_BEDROCK_MANTLE_BASE_URL",
|
|
300
|
+
"CLAUDE_CODE_SKIP_MANTLE_AUTH",
|
|
301
|
+
"ANTHROPIC_SMALL_FAST_MODEL_AWS_REGION",
|
|
302
|
+
// Vertex
|
|
303
|
+
"CLAUDE_CODE_USE_VERTEX",
|
|
304
|
+
"ANTHROPIC_VERTEX_PROJECT_ID",
|
|
305
|
+
"CLOUD_ML_REGION",
|
|
306
|
+
"GOOGLE_APPLICATION_CREDENTIALS",
|
|
307
|
+
"ANTHROPIC_VERTEX_BASE_URL",
|
|
308
|
+
// Foundry
|
|
309
|
+
"CLAUDE_CODE_USE_FOUNDRY",
|
|
310
|
+
"ANTHROPIC_FOUNDRY_RESOURCE",
|
|
311
|
+
"ANTHROPIC_FOUNDRY_BASE_URL",
|
|
312
|
+
"ANTHROPIC_FOUNDRY_API_KEY",
|
|
313
|
+
// Cloud-only model pinning (shared across Bedrock / Vertex / Foundry)
|
|
314
|
+
"ANTHROPIC_DEFAULT_OPUS_MODEL",
|
|
315
|
+
"ANTHROPIC_DEFAULT_SONNET_MODEL",
|
|
316
|
+
"ANTHROPIC_DEFAULT_HAIKU_MODEL",
|
|
317
|
+
];
|
|
318
|
+
case "codex":
|
|
319
|
+
return [
|
|
320
|
+
"OPENAI_API_KEY",
|
|
321
|
+
// Azure OpenAI β daemon points CODEX_HOME at a managed config.toml
|
|
322
|
+
// dir so the user's `~/.codex/` config is untouched.
|
|
323
|
+
"AZURE_OPENAI_API_KEY",
|
|
324
|
+
"CODEX_HOME",
|
|
325
|
+
];
|
|
326
|
+
case "gemini":
|
|
327
|
+
return [
|
|
328
|
+
"GEMINI_API_KEY",
|
|
329
|
+
"GOOGLE_API_KEY",
|
|
330
|
+
// Vertex AI on Gemini CLI
|
|
331
|
+
"GOOGLE_GENAI_USE_VERTEXAI",
|
|
332
|
+
"GOOGLE_CLOUD_PROJECT",
|
|
333
|
+
"GOOGLE_CLOUD_LOCATION",
|
|
334
|
+
"GOOGLE_APPLICATION_CREDENTIALS",
|
|
335
|
+
];
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
/**
|
|
339
|
+
* Map a partial set of pinned model fields to their `ANTHROPIC_DEFAULT_*_MODEL`
|
|
340
|
+
* env vars. Skips any field the operator left unset so the cloud's built-in
|
|
341
|
+
* default takes over (matching the docs' fallback semantics).
|
|
342
|
+
*/
|
|
343
|
+
function pinnedModelEnvAssignments(config) {
|
|
344
|
+
const out = {};
|
|
345
|
+
if (config.defaultOpusModel) {
|
|
346
|
+
out.ANTHROPIC_DEFAULT_OPUS_MODEL = config.defaultOpusModel;
|
|
347
|
+
}
|
|
348
|
+
if (config.defaultSonnetModel) {
|
|
349
|
+
out.ANTHROPIC_DEFAULT_SONNET_MODEL = config.defaultSonnetModel;
|
|
350
|
+
}
|
|
351
|
+
if (config.defaultHaikuModel) {
|
|
352
|
+
out.ANTHROPIC_DEFAULT_HAIKU_MODEL = config.defaultHaikuModel;
|
|
353
|
+
}
|
|
354
|
+
return out;
|
|
355
|
+
}
|
|
356
|
+
/**
|
|
357
|
+
* Build the `config.toml` Codex CLI consumes when the daemon points
|
|
358
|
+
* `CODEX_HOME` at the managed directory.
|
|
359
|
+
*
|
|
360
|
+
* Per OpenAI Codex docs (verified 2026-05), the wire_api must be
|
|
361
|
+
* `responses` and the API version must be supplied as a `query_params`
|
|
362
|
+
* entry. Codex resolves the API key by reading `env_key` against
|
|
363
|
+
* `process.env`.
|
|
364
|
+
*
|
|
365
|
+
* **TOML layout matters.** `model_provider` (and `model`, when set) MUST
|
|
366
|
+
* appear *before* the `[model_providers.azure]` table β once a `[section]`
|
|
367
|
+
* header opens, all subsequent keys belong to that section until the next
|
|
368
|
+
* header. Emitting `model_provider = "azure"` after the section header
|
|
369
|
+
* would silently nest it as `model_providers.azure.model_provider`,
|
|
370
|
+
* leaving the top-level `model_provider` unset and Codex routing through
|
|
371
|
+
* the OpenAI default. Tested with `smol-toml`.
|
|
372
|
+
*/
|
|
373
|
+
export function buildCodexAzureConfigToml(config) {
|
|
374
|
+
const apiVersion = config.apiVersion ?? DEFAULT_AZURE_OPENAI_API_VERSION;
|
|
375
|
+
const baseUrl = `https://${config.resource}.openai.azure.com/openai/v1`;
|
|
376
|
+
const lines = [
|
|
377
|
+
"# Generated by aitne β do not edit by hand.",
|
|
378
|
+
"# Source: dashboard provider-auth panel.",
|
|
379
|
+
"",
|
|
380
|
+
// Top-level keys FIRST. Keep these before any [section] header.
|
|
381
|
+
`model_provider = "azure"`,
|
|
382
|
+
];
|
|
383
|
+
if (config.deploymentName) {
|
|
384
|
+
// Pin the active model to the operator's deployment name. Note:
|
|
385
|
+
// when the daemon's CodexCore invokes codex with `--model <id>`,
|
|
386
|
+
// the CLI flag overrides this. The operator should name their
|
|
387
|
+
// Azure deployment to match the model IDs the daemon's process
|
|
388
|
+
// routing maps to (see /settings/models).
|
|
389
|
+
lines.push(`model = "${config.deploymentName}"`);
|
|
390
|
+
}
|
|
391
|
+
lines.push("", "[model_providers.azure]", `name = "Azure OpenAI"`, `base_url = "${baseUrl}"`, `env_key = "AZURE_OPENAI_API_KEY"`, `query_params = { api-version = "${apiVersion}" }`, `wire_api = "responses"`, "");
|
|
392
|
+
return lines.join("\n");
|
|
393
|
+
}
|
|
394
|
+
/**
|
|
395
|
+
* Resolve which env vars to write for a given provider config. Returns a map
|
|
396
|
+
* of env-var name β value. Env vars omitted from the map should be cleared
|
|
397
|
+
* (or restored to their captured shell value) by the caller.
|
|
398
|
+
*
|
|
399
|
+
* Note on `GOOGLE_APPLICATION_CREDENTIALS`: this is the Google standard env
|
|
400
|
+
* var name and expects a **file path** (not inline JSON). The earlier
|
|
401
|
+
* iteration of this code wrote `GOOGLE_APPLICATION_CREDENTIALS_JSON` with
|
|
402
|
+
* inline JSON, which the Anthropic SDK and gcloud SDK both ignore. Operators
|
|
403
|
+
* who want service-account-based auth supply the file path; operators who
|
|
404
|
+
* want ADC leave it blank and use `gcloud auth application-default login`.
|
|
405
|
+
*
|
|
406
|
+
* Note on `CODEX_HOME` (Azure OpenAI): this map does NOT include it. The
|
|
407
|
+
* daemon owns the managed config.toml directory path and adds `CODEX_HOME`
|
|
408
|
+
* separately when materializing the assignment β see the daemon-side
|
|
409
|
+
* `materializeCodexAzureConfig` helper.
|
|
410
|
+
*/
|
|
411
|
+
export function getApiKeyEnvAssignments(config) {
|
|
412
|
+
switch (config.provider) {
|
|
413
|
+
case "anthropic":
|
|
414
|
+
return { ANTHROPIC_API_KEY: config.apiKey };
|
|
415
|
+
case "bedrock": {
|
|
416
|
+
const base = {
|
|
417
|
+
CLAUDE_CODE_USE_BEDROCK: "1",
|
|
418
|
+
AWS_REGION: config.awsRegion,
|
|
419
|
+
...(config.bedrockBaseUrl
|
|
420
|
+
? { ANTHROPIC_BEDROCK_BASE_URL: config.bedrockBaseUrl }
|
|
421
|
+
: {}),
|
|
422
|
+
// Mantle endpoint (set alongside CLAUDE_CODE_USE_BEDROCK β they're
|
|
423
|
+
// not mutually exclusive: setting both lets Claude Code route per
|
|
424
|
+
// model, sending Mantle-format IDs to Mantle and Invoke-API IDs
|
|
425
|
+
// to standard Bedrock).
|
|
426
|
+
...(config.useMantle ? { CLAUDE_CODE_USE_MANTLE: "1" } : {}),
|
|
427
|
+
...(config.mantleBaseUrl
|
|
428
|
+
? { ANTHROPIC_BEDROCK_MANTLE_BASE_URL: config.mantleBaseUrl }
|
|
429
|
+
: {}),
|
|
430
|
+
...(config.skipMantleAuth ? { CLAUDE_CODE_SKIP_MANTLE_AUTH: "1" } : {}),
|
|
431
|
+
...(config.smallFastModelAwsRegion
|
|
432
|
+
? {
|
|
433
|
+
ANTHROPIC_SMALL_FAST_MODEL_AWS_REGION: config.smallFastModelAwsRegion,
|
|
434
|
+
}
|
|
435
|
+
: {}),
|
|
436
|
+
...pinnedModelEnvAssignments(config),
|
|
437
|
+
};
|
|
438
|
+
switch (config.authMode) {
|
|
439
|
+
case "access_key":
|
|
440
|
+
return {
|
|
441
|
+
...base,
|
|
442
|
+
AWS_ACCESS_KEY_ID: config.awsAccessKeyId,
|
|
443
|
+
AWS_SECRET_ACCESS_KEY: config.awsSecretAccessKey,
|
|
444
|
+
...(config.awsSessionToken
|
|
445
|
+
? { AWS_SESSION_TOKEN: config.awsSessionToken }
|
|
446
|
+
: {}),
|
|
447
|
+
};
|
|
448
|
+
case "bearer_token":
|
|
449
|
+
return {
|
|
450
|
+
...base,
|
|
451
|
+
AWS_BEARER_TOKEN_BEDROCK: config.awsBearerTokenBedrock,
|
|
452
|
+
};
|
|
453
|
+
case "profile":
|
|
454
|
+
return { ...base, AWS_PROFILE: config.awsProfile };
|
|
455
|
+
/* v8 ignore next 6 */
|
|
456
|
+
}
|
|
457
|
+
// Exhaustiveness guard β TS verifies all authMode branches are covered.
|
|
458
|
+
const _exhaustive = config;
|
|
459
|
+
void _exhaustive;
|
|
460
|
+
return base;
|
|
461
|
+
}
|
|
462
|
+
case "vertex":
|
|
463
|
+
return {
|
|
464
|
+
CLAUDE_CODE_USE_VERTEX: "1",
|
|
465
|
+
ANTHROPIC_VERTEX_PROJECT_ID: config.projectId,
|
|
466
|
+
CLOUD_ML_REGION: config.region,
|
|
467
|
+
...(config.credentialsFile
|
|
468
|
+
? { GOOGLE_APPLICATION_CREDENTIALS: config.credentialsFile }
|
|
469
|
+
: {}),
|
|
470
|
+
...(config.vertexBaseUrl
|
|
471
|
+
? { ANTHROPIC_VERTEX_BASE_URL: config.vertexBaseUrl }
|
|
472
|
+
: {}),
|
|
473
|
+
...pinnedModelEnvAssignments(config),
|
|
474
|
+
};
|
|
475
|
+
case "foundry":
|
|
476
|
+
return {
|
|
477
|
+
CLAUDE_CODE_USE_FOUNDRY: "1",
|
|
478
|
+
...(config.resource
|
|
479
|
+
? { ANTHROPIC_FOUNDRY_RESOURCE: config.resource }
|
|
480
|
+
: {}),
|
|
481
|
+
...(config.baseUrl
|
|
482
|
+
? { ANTHROPIC_FOUNDRY_BASE_URL: config.baseUrl }
|
|
483
|
+
: {}),
|
|
484
|
+
...(config.apiKey
|
|
485
|
+
? { ANTHROPIC_FOUNDRY_API_KEY: config.apiKey }
|
|
486
|
+
: {}),
|
|
487
|
+
...pinnedModelEnvAssignments(config),
|
|
488
|
+
};
|
|
489
|
+
case "openai":
|
|
490
|
+
return { OPENAI_API_KEY: config.apiKey };
|
|
491
|
+
case "azure-openai":
|
|
492
|
+
// CODEX_HOME is owned by the daemon (it points at the managed
|
|
493
|
+
// config.toml directory). The daemon's Codex Azure materializer
|
|
494
|
+
// computes the path and adds CODEX_HOME alongside this map; the
|
|
495
|
+
// pure shared layer just provides the env vars Codex CLI itself
|
|
496
|
+
// needs at runtime to fetch credentials.
|
|
497
|
+
return {
|
|
498
|
+
AZURE_OPENAI_API_KEY: config.apiKey,
|
|
499
|
+
};
|
|
500
|
+
case "google":
|
|
501
|
+
// Direct Gemini API β populates BOTH aliases so the SDK reads a
|
|
502
|
+
// consistent value. Notably this does NOT set the Vertex flag.
|
|
503
|
+
return {
|
|
504
|
+
GEMINI_API_KEY: config.apiKey,
|
|
505
|
+
GOOGLE_API_KEY: config.apiKey,
|
|
506
|
+
};
|
|
507
|
+
case "gemini-vertex": {
|
|
508
|
+
const base = {
|
|
509
|
+
GOOGLE_GENAI_USE_VERTEXAI: "true",
|
|
510
|
+
GOOGLE_CLOUD_PROJECT: config.projectId,
|
|
511
|
+
GOOGLE_CLOUD_LOCATION: config.location,
|
|
512
|
+
};
|
|
513
|
+
switch (config.authMode) {
|
|
514
|
+
case "adc":
|
|
515
|
+
return base;
|
|
516
|
+
case "service_account":
|
|
517
|
+
return {
|
|
518
|
+
...base,
|
|
519
|
+
GOOGLE_APPLICATION_CREDENTIALS: config.credentialsFile,
|
|
520
|
+
};
|
|
521
|
+
case "api_key":
|
|
522
|
+
// Vertex API-key auth uses GOOGLE_API_KEY (not GEMINI_API_KEY).
|
|
523
|
+
return { ...base, GOOGLE_API_KEY: config.apiKey };
|
|
524
|
+
/* v8 ignore next 6 */
|
|
525
|
+
}
|
|
526
|
+
// Exhaustiveness guard β TS verifies all authMode branches are covered.
|
|
527
|
+
const _exhaustive = config;
|
|
528
|
+
void _exhaustive;
|
|
529
|
+
return base;
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
// ββ Parse / serialize ββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
534
|
+
/**
|
|
535
|
+
* Decode the raw keychain string for a backend into a typed config.
|
|
536
|
+
*
|
|
537
|
+
* Three accepted forms (highest priority first):
|
|
538
|
+
* 1. JSON-encoded `BackendApiKeyConfig` β the new format.
|
|
539
|
+
* 2. Legacy raw API key (non-JSON string) β promoted to the backend's
|
|
540
|
+
* default direct provider (anthropic / openai / google).
|
|
541
|
+
* 3. `null` / blank β no config.
|
|
542
|
+
*/
|
|
543
|
+
export function parseBackendApiKeyConfig(backendId, raw) {
|
|
544
|
+
if (!raw)
|
|
545
|
+
return null;
|
|
546
|
+
const trimmed = raw.trim();
|
|
547
|
+
if (!trimmed)
|
|
548
|
+
return null;
|
|
549
|
+
// New format: JSON-encoded discriminated union.
|
|
550
|
+
if (trimmed.startsWith("{")) {
|
|
551
|
+
try {
|
|
552
|
+
const parsed = backendApiKeyConfigSchema.safeParse(JSON.parse(trimmed));
|
|
553
|
+
if (parsed.success
|
|
554
|
+
&& isApiKeyProviderForBackend(backendId, parsed.data.provider)) {
|
|
555
|
+
return parsed.data;
|
|
556
|
+
}
|
|
557
|
+
return null;
|
|
558
|
+
}
|
|
559
|
+
catch {
|
|
560
|
+
return null;
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
// Legacy raw API key β promote to the default direct provider for the
|
|
564
|
+
// backend. This keeps existing keychain entries working byte-for-byte.
|
|
565
|
+
switch (backendId) {
|
|
566
|
+
case "claude":
|
|
567
|
+
return { provider: "anthropic", apiKey: trimmed };
|
|
568
|
+
case "codex":
|
|
569
|
+
return { provider: "openai", apiKey: trimmed };
|
|
570
|
+
case "gemini":
|
|
571
|
+
return { provider: "google", apiKey: trimmed };
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
/** Encode a typed config back into the JSON form stored in the keychain. */
|
|
575
|
+
export function serializeBackendApiKeyConfig(config) {
|
|
576
|
+
return JSON.stringify(config);
|
|
577
|
+
}
|
|
578
|
+
// ββ Format validation ββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
579
|
+
const ANTHROPIC_KEY_PATTERN = /^sk-ant-[A-Za-z0-9_-]{20,}$/;
|
|
580
|
+
const OPENAI_KEY_PATTERN = /^sk-[A-Za-z0-9_-]{30,}$/;
|
|
581
|
+
const GEMINI_KEY_PATTERN = /^AIza[0-9A-Za-z_-]{35}$/;
|
|
582
|
+
export function isPlausibleAnthropicApiKey(value) {
|
|
583
|
+
return ANTHROPIC_KEY_PATTERN.test(value.trim());
|
|
584
|
+
}
|
|
585
|
+
export function isPlausibleOpenAiApiKey(value) {
|
|
586
|
+
const trimmed = value.trim();
|
|
587
|
+
if (/^sk-ant-/.test(trimmed))
|
|
588
|
+
return false;
|
|
589
|
+
return OPENAI_KEY_PATTERN.test(trimmed);
|
|
590
|
+
}
|
|
591
|
+
export function isPlausibleGeminiApiKey(value) {
|
|
592
|
+
return GEMINI_KEY_PATTERN.test(value.trim());
|
|
593
|
+
}
|
|
594
|
+
/**
|
|
595
|
+
* Best-effort format check on a populated config. Returns null when the
|
|
596
|
+
* shape looks plausible, or a human-readable hint when something obvious
|
|
597
|
+
* is wrong. Server-side probes are still authoritative β this catches
|
|
598
|
+
* cheap typos before the keychain write.
|
|
599
|
+
*/
|
|
600
|
+
export function validateBackendApiKeyConfigFormat(backendId, config) {
|
|
601
|
+
if (!isApiKeyProviderForBackend(backendId, config.provider)) {
|
|
602
|
+
return `Provider "${config.provider}" is not valid for the ${backendId} backend.`;
|
|
603
|
+
}
|
|
604
|
+
switch (config.provider) {
|
|
605
|
+
case "anthropic":
|
|
606
|
+
if (!isPlausibleAnthropicApiKey(config.apiKey)) {
|
|
607
|
+
return "Expected an Anthropic API key beginning with `sk-ant-β¦`.";
|
|
608
|
+
}
|
|
609
|
+
return null;
|
|
610
|
+
case "openai":
|
|
611
|
+
if (!isPlausibleOpenAiApiKey(config.apiKey)) {
|
|
612
|
+
return "Expected an OpenAI API key beginning with `sk-β¦` (not `sk-ant-β¦`).";
|
|
613
|
+
}
|
|
614
|
+
return null;
|
|
615
|
+
case "azure-openai":
|
|
616
|
+
if (!config.resource.trim() || /\s/.test(config.resource)) {
|
|
617
|
+
return "Azure resource name looks malformed (no spaces; e.g. `my-foundry-resource`).";
|
|
618
|
+
}
|
|
619
|
+
if (!config.apiKey.trim()) {
|
|
620
|
+
return "Azure OpenAI API key is required.";
|
|
621
|
+
}
|
|
622
|
+
return null;
|
|
623
|
+
case "google":
|
|
624
|
+
if (!isPlausibleGeminiApiKey(config.apiKey)) {
|
|
625
|
+
return "Expected a Google API key beginning with `AIzaβ¦` (39 chars total).";
|
|
626
|
+
}
|
|
627
|
+
return null;
|
|
628
|
+
case "bedrock": {
|
|
629
|
+
if (config.awsRegion.length < 2 || /\s/.test(config.awsRegion)) {
|
|
630
|
+
return "AWS region looks malformed (e.g. expected `us-east-1`).";
|
|
631
|
+
}
|
|
632
|
+
if (config.authMode === "access_key") {
|
|
633
|
+
if (!/^[A-Z0-9]{16,}$/.test(config.awsAccessKeyId)) {
|
|
634
|
+
return "AWS access key id should be uppercase alphanumeric (β₯ 16 chars, e.g. `AKIAβ¦` or `ASIAβ¦`).";
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
return null;
|
|
638
|
+
}
|
|
639
|
+
case "vertex":
|
|
640
|
+
if (config.projectId.length < 4 || /\s/.test(config.projectId)) {
|
|
641
|
+
return "GCP project id looks malformed.";
|
|
642
|
+
}
|
|
643
|
+
if (config.region.length < 2 || /\s/.test(config.region)) {
|
|
644
|
+
return "Vertex region looks malformed (e.g. `us-east5`, `global`, or `eu`).";
|
|
645
|
+
}
|
|
646
|
+
return null;
|
|
647
|
+
case "foundry": {
|
|
648
|
+
if (!config.resource && !config.baseUrl) {
|
|
649
|
+
return "Foundry config requires either a resource name or a base URL.";
|
|
650
|
+
}
|
|
651
|
+
if (config.resource && config.baseUrl) {
|
|
652
|
+
return "Foundry config: set EITHER `resource` OR `baseUrl`, not both.";
|
|
653
|
+
}
|
|
654
|
+
if (config.baseUrl) {
|
|
655
|
+
try {
|
|
656
|
+
const url = new URL(config.baseUrl);
|
|
657
|
+
if (url.protocol !== "https:" && url.protocol !== "http:") {
|
|
658
|
+
return "Foundry base URL must be an http(s) URL.";
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
catch {
|
|
662
|
+
return "Foundry base URL is not a valid URL.";
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
return null;
|
|
666
|
+
}
|
|
667
|
+
case "gemini-vertex": {
|
|
668
|
+
if (config.projectId.length < 4 || /\s/.test(config.projectId)) {
|
|
669
|
+
return "GCP project id looks malformed.";
|
|
670
|
+
}
|
|
671
|
+
if (config.location.length < 2 || /\s/.test(config.location)) {
|
|
672
|
+
return "GCP location looks malformed (e.g. `us-central1`).";
|
|
673
|
+
}
|
|
674
|
+
if (config.authMode === "api_key"
|
|
675
|
+
&& !isPlausibleGeminiApiKey(config.apiKey)) {
|
|
676
|
+
return "Vertex API key should start with `AIzaβ¦` (39 chars total).";
|
|
677
|
+
}
|
|
678
|
+
return null;
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
//# sourceMappingURL=backend-api-key-config.js.map
|