@oh-my-pi/pi-catalog 18.0.8 → 18.0.9
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 +12 -0
- package/dist/types/identity/family.d.ts +15 -11
- package/dist/types/wire/cloudflare-ai-gateway.d.ts +18 -0
- package/package.json +4 -4
- package/src/identity/family.ts +43 -24
- package/src/models.json +50 -33
- package/src/provider-models/openai-compat.ts +7 -6
- package/src/variant-collapse.ts +1 -1
- package/src/wire/cloudflare-ai-gateway.ts +44 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [18.0.9] - 2026-08-28
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- Added Z.AI GLM-5.3-Flash to the bundled GLM Coding Plan catalog, with a 1M-token context window, 131,072-token output limit, native image input, and low/high/max thinking levels. It is now available through model discovery and the model picker.
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
|
|
13
|
+
- Fixed Google Antigravity Gemini 3.7 Flash tiered models failing with HTTP 400 errors at minimal or disabled thinking levels; those tiers now use the compatible Gemini 3.7 Flash model routing.
|
|
14
|
+
- Fixed OpenCode Go and Zen GLM-5.3 Flash models exposing an unsupported extra-high reasoning level; they now use the supported low/high/max reasoning levels.
|
|
15
|
+
- Fixed Cloudflare AI Gateway credential handling so stored credentials and routing metadata are parsed and used correctly.
|
|
16
|
+
|
|
5
17
|
## [18.0.8] - 2026-08-27
|
|
6
18
|
|
|
7
19
|
### Fixed
|
|
@@ -145,24 +145,28 @@ export declare const supportsCodexReasoningSummary: (modelId: string) => boolean
|
|
|
145
145
|
export declare const isOpenAISamplingRestrictedModelId: (modelId: string) => boolean;
|
|
146
146
|
/**
|
|
147
147
|
* Reasoning-capable GLM coding SKUs: glm-4.5 and up on the base / `-air` /
|
|
148
|
-
* `-turbo` lines
|
|
149
|
-
* `-
|
|
150
|
-
* keeps newly-bumped integers (`glm-5.
|
|
151
|
-
* allowlist.
|
|
148
|
+
* `-turbo` lines, plus `-flash` from GLM-5.3-Flash on. Excludes the vision
|
|
149
|
+
* (`…v`) shape, the non-reasoning `-flashx`/`-preview` variants, and pre-4.5
|
|
150
|
+
* ids. Matching the family keeps newly-bumped integers (`glm-5.4`, `glm-6`, …)
|
|
151
|
+
* covered without a per-id allowlist.
|
|
152
152
|
*/
|
|
153
153
|
export declare const isReasoningGlmModelId: (modelId: string) => boolean;
|
|
154
154
|
/** GLM-5.2+ coding SKUs accept `reasoning_effort` in addition to binary thinking. */
|
|
155
155
|
export declare const isGlm52ReasoningEffortModelId: (modelId: string) => boolean;
|
|
156
156
|
/**
|
|
157
|
-
* GLM-5.3+
|
|
158
|
-
* host-specific),
|
|
159
|
-
* ladder on every host, and thinking can
|
|
160
|
-
* `thinking.type` must always be `enabled`. Matching
|
|
161
|
-
* bumps (`glm-5.4`, `glm-6`, …) covered while excluding
|
|
162
|
-
* shape and the non-reasoning `-
|
|
157
|
+
* GLM-5.3+ reasoning-effort SKUs. Unlike GLM-5.2 (whose dialect is
|
|
158
|
+
* host-specific), the base, `-air`, `-turbo`, and `-flash` variants expose a
|
|
159
|
+
* uniform wire-exact `low`/`high`/`max` ladder on every host, and thinking can
|
|
160
|
+
* no longer be disabled — `thinking.type` must always be `enabled`. Matching
|
|
161
|
+
* the family keeps future bumps (`glm-5.4`, `glm-6`, …) covered while excluding
|
|
162
|
+
* the vision (`…v`) shape and the non-reasoning `-flashx`/`-preview` variants.
|
|
163
163
|
*/
|
|
164
164
|
export declare const isGlm53ReasoningEffortModelId: (modelId: string) => boolean;
|
|
165
|
-
/**
|
|
165
|
+
/**
|
|
166
|
+
* GLM SKUs that accept image input: the `v` that attaches to the version
|
|
167
|
+
* (`glm-4v`, `glm-4.5v`) and the natively multimodal `-flash` line from
|
|
168
|
+
* GLM-5.3-Flash on, whose id carries no `v` marker.
|
|
169
|
+
*/
|
|
166
170
|
export declare const isGlmVisionModelId: (modelId: string) => boolean;
|
|
167
171
|
/**
|
|
168
172
|
* Coarse vendor-lineage token for "are two models the same family?" checks
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/** Provider-native AI Gateway prefix before the upstream route segment. */
|
|
2
|
+
export declare const CLOUDFLARE_AI_GATEWAY_BASE_URL = "https://gateway.ai.cloudflare.com/v1/<account>/<gateway>";
|
|
3
|
+
/** Anthropic Messages passthrough endpoint. */
|
|
4
|
+
export declare const CLOUDFLARE_AI_GATEWAY_ANTHROPIC_BASE_URL = "https://gateway.ai.cloudflare.com/v1/<account>/<gateway>/anthropic";
|
|
5
|
+
/** OpenAI Responses passthrough endpoint. */
|
|
6
|
+
export declare const CLOUDFLARE_AI_GATEWAY_OPENAI_BASE_URL = "https://gateway.ai.cloudflare.com/v1/<account>/<gateway>/openai";
|
|
7
|
+
/** OpenAI-compatible Workers AI endpoint. */
|
|
8
|
+
export declare const CLOUDFLARE_AI_GATEWAY_COMPAT_BASE_URL = "https://gateway.ai.cloudflare.com/v1/<account>/<gateway>/compat";
|
|
9
|
+
/** Opaque API-key credential payload persisted by Cloudflare AI Gateway login. */
|
|
10
|
+
export interface CloudflareAiGatewayCredential {
|
|
11
|
+
token: string;
|
|
12
|
+
accountId?: string;
|
|
13
|
+
gatewayId?: string;
|
|
14
|
+
}
|
|
15
|
+
/** Parse both structured login credentials and legacy plain gateway tokens. */
|
|
16
|
+
export declare function parseCloudflareAiGatewayCredential(value: string): CloudflareAiGatewayCredential | null;
|
|
17
|
+
/** Serialize the gateway token with the routing identifiers collected during login. */
|
|
18
|
+
export declare function serializeCloudflareAiGatewayCredential(token: string, accountId: string, gatewayId: string): string;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@oh-my-pi/pi-catalog",
|
|
4
|
-
"version": "18.0.
|
|
4
|
+
"version": "18.0.9",
|
|
5
5
|
"description": "Model catalog for omp: bundled model database, provider discovery descriptors, model identity, classification, and equivalence",
|
|
6
6
|
"homepage": "https://omp.sh",
|
|
7
7
|
"author": "Stencil Labs, Inc.",
|
|
@@ -34,11 +34,11 @@
|
|
|
34
34
|
"gen:proto": "bun scripts/generate-protocols.ts"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@oh-my-pi/omptype": "18.0.
|
|
38
|
-
"@oh-my-pi/pi-utils": "18.0.
|
|
37
|
+
"@oh-my-pi/omptype": "18.0.9",
|
|
38
|
+
"@oh-my-pi/pi-utils": "18.0.9"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
|
-
"@oh-my-pi/pi-ai": "18.0.
|
|
41
|
+
"@oh-my-pi/pi-ai": "18.0.9",
|
|
42
42
|
"@types/bun": "^1.3.14"
|
|
43
43
|
},
|
|
44
44
|
"engines": {
|
package/src/identity/family.ts
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
import {
|
|
11
11
|
bareModelId,
|
|
12
|
+
type GlmModel,
|
|
12
13
|
isAnthropicAdaptiveGenAtLeast,
|
|
13
14
|
isFableOrMythos,
|
|
14
15
|
parseAnthropicModel,
|
|
@@ -283,19 +284,35 @@ export const isOpenAISamplingRestrictedModelId = memo((modelId: string): boolean
|
|
|
283
284
|
return isOpenAIWireGen5Plus(modelId) || O_SERIES_REASONING_RE.test(bare);
|
|
284
285
|
});
|
|
285
286
|
|
|
287
|
+
/**
|
|
288
|
+
* GLM SKU shapes that carry the reasoning ladders. `base`/`air`/`turbo` have
|
|
289
|
+
* always been reasoning lines; `flash` joins at GLM-5.3-Flash, the first flash
|
|
290
|
+
* SKU whose thinking is mandatory (pre-5.3 `-flash` SKUs are non-reasoning).
|
|
291
|
+
* `flashx` and `preview` stay out.
|
|
292
|
+
*/
|
|
293
|
+
function isGlmReasoningVariant(glm: GlmModel): boolean {
|
|
294
|
+
switch (glm.variant) {
|
|
295
|
+
case "base":
|
|
296
|
+
case "air":
|
|
297
|
+
case "turbo":
|
|
298
|
+
return true;
|
|
299
|
+
case "flash":
|
|
300
|
+
return semverGte(glm.version, "5.3");
|
|
301
|
+
default:
|
|
302
|
+
return false;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
286
306
|
/**
|
|
287
307
|
* Reasoning-capable GLM coding SKUs: glm-4.5 and up on the base / `-air` /
|
|
288
|
-
* `-turbo` lines
|
|
289
|
-
* `-
|
|
290
|
-
* keeps newly-bumped integers (`glm-5.
|
|
291
|
-
* allowlist.
|
|
308
|
+
* `-turbo` lines, plus `-flash` from GLM-5.3-Flash on. Excludes the vision
|
|
309
|
+
* (`…v`) shape, the non-reasoning `-flashx`/`-preview` variants, and pre-4.5
|
|
310
|
+
* ids. Matching the family keeps newly-bumped integers (`glm-5.4`, `glm-6`, …)
|
|
311
|
+
* covered without a per-id allowlist.
|
|
292
312
|
*/
|
|
293
313
|
export const isReasoningGlmModelId = memo((modelId: string): boolean => {
|
|
294
314
|
const glm = parseGlmModel(bareModelId(modelId));
|
|
295
|
-
if (!glm || glm.vision) {
|
|
296
|
-
return false;
|
|
297
|
-
}
|
|
298
|
-
if (glm.variant !== "base" && glm.variant !== "air" && glm.variant !== "turbo") {
|
|
315
|
+
if (!glm || glm.vision || !isGlmReasoningVariant(glm)) {
|
|
299
316
|
return false;
|
|
300
317
|
}
|
|
301
318
|
return semverGte(glm.version, "4.5");
|
|
@@ -304,37 +321,39 @@ export const isReasoningGlmModelId = memo((modelId: string): boolean => {
|
|
|
304
321
|
/** GLM-5.2+ coding SKUs accept `reasoning_effort` in addition to binary thinking. */
|
|
305
322
|
export const isGlm52ReasoningEffortModelId = memo((modelId: string): boolean => {
|
|
306
323
|
const glm = parseGlmModel(bareModelId(modelId));
|
|
307
|
-
if (!glm || glm.vision) {
|
|
308
|
-
return false;
|
|
309
|
-
}
|
|
310
|
-
if (glm.variant !== "base" && glm.variant !== "air" && glm.variant !== "turbo") {
|
|
324
|
+
if (!glm || glm.vision || !isGlmReasoningVariant(glm)) {
|
|
311
325
|
return false;
|
|
312
326
|
}
|
|
313
327
|
return semverGte(glm.version, "5.2");
|
|
314
328
|
});
|
|
315
329
|
|
|
316
330
|
/**
|
|
317
|
-
* GLM-5.3+
|
|
318
|
-
* host-specific),
|
|
319
|
-
* ladder on every host, and thinking can
|
|
320
|
-
* `thinking.type` must always be `enabled`. Matching
|
|
321
|
-
* bumps (`glm-5.4`, `glm-6`, …) covered while excluding
|
|
322
|
-
* shape and the non-reasoning `-
|
|
331
|
+
* GLM-5.3+ reasoning-effort SKUs. Unlike GLM-5.2 (whose dialect is
|
|
332
|
+
* host-specific), the base, `-air`, `-turbo`, and `-flash` variants expose a
|
|
333
|
+
* uniform wire-exact `low`/`high`/`max` ladder on every host, and thinking can
|
|
334
|
+
* no longer be disabled — `thinking.type` must always be `enabled`. Matching
|
|
335
|
+
* the family keeps future bumps (`glm-5.4`, `glm-6`, …) covered while excluding
|
|
336
|
+
* the vision (`…v`) shape and the non-reasoning `-flashx`/`-preview` variants.
|
|
323
337
|
*/
|
|
324
338
|
export const isGlm53ReasoningEffortModelId = memo((modelId: string): boolean => {
|
|
325
339
|
const glm = parseGlmModel(bareModelId(modelId));
|
|
326
|
-
if (!glm || glm.vision) {
|
|
327
|
-
return false;
|
|
328
|
-
}
|
|
329
|
-
if (glm.variant !== "base" && glm.variant !== "air" && glm.variant !== "turbo") {
|
|
340
|
+
if (!glm || glm.vision || !isGlmReasoningVariant(glm)) {
|
|
330
341
|
return false;
|
|
331
342
|
}
|
|
332
343
|
return semverGte(glm.version, "5.3");
|
|
333
344
|
});
|
|
334
345
|
|
|
335
|
-
/**
|
|
346
|
+
/**
|
|
347
|
+
* GLM SKUs that accept image input: the `v` that attaches to the version
|
|
348
|
+
* (`glm-4v`, `glm-4.5v`) and the natively multimodal `-flash` line from
|
|
349
|
+
* GLM-5.3-Flash on, whose id carries no `v` marker.
|
|
350
|
+
*/
|
|
336
351
|
export const isGlmVisionModelId = memo((modelId: string): boolean => {
|
|
337
|
-
|
|
352
|
+
const glm = parseGlmModel(bareModelId(modelId));
|
|
353
|
+
if (!glm) {
|
|
354
|
+
return false;
|
|
355
|
+
}
|
|
356
|
+
return glm.vision || (glm.variant === "flash" && semverGte(glm.version, "5.3"));
|
|
338
357
|
});
|
|
339
358
|
|
|
340
359
|
/**
|
package/src/models.json
CHANGED
|
@@ -64887,38 +64887,6 @@
|
|
|
64887
64887
|
"requiresGlyphTokenization": false,
|
|
64888
64888
|
"supportsComputerUse": false
|
|
64889
64889
|
},
|
|
64890
|
-
"gemini-3.7-flash-tiered": {
|
|
64891
|
-
"id": "gemini-3.7-flash-tiered",
|
|
64892
|
-
"name": "gemini-3.7-flash-tiered",
|
|
64893
|
-
"api": "google-gemini-cli",
|
|
64894
|
-
"provider": "google-antigravity",
|
|
64895
|
-
"baseUrl": "https://daily-cloudcode-pa.googleapis.com",
|
|
64896
|
-
"reasoning": true,
|
|
64897
|
-
"input": [
|
|
64898
|
-
"text",
|
|
64899
|
-
"image"
|
|
64900
|
-
],
|
|
64901
|
-
"cost": {
|
|
64902
|
-
"input": 0,
|
|
64903
|
-
"output": 0,
|
|
64904
|
-
"cacheRead": 0,
|
|
64905
|
-
"cacheWrite": 0
|
|
64906
|
-
},
|
|
64907
|
-
"contextWindow": 1048576,
|
|
64908
|
-
"maxTokens": 65536,
|
|
64909
|
-
"thinking": {
|
|
64910
|
-
"mode": "google-level",
|
|
64911
|
-
"efforts": [
|
|
64912
|
-
"minimal",
|
|
64913
|
-
"low",
|
|
64914
|
-
"medium",
|
|
64915
|
-
"high"
|
|
64916
|
-
],
|
|
64917
|
-
"requiresEffort": true
|
|
64918
|
-
},
|
|
64919
|
-
"requiresGlyphTokenization": false,
|
|
64920
|
-
"supportsComputerUse": false
|
|
64921
|
-
},
|
|
64922
64890
|
"gpt-oss-120b": {
|
|
64923
64891
|
"id": "gpt-oss-120b",
|
|
64924
64892
|
"name": "GPT OSS 120B",
|
|
@@ -294589,6 +294557,55 @@
|
|
|
294589
294557
|
},
|
|
294590
294558
|
"supportsComputerUseConfig": false
|
|
294591
294559
|
},
|
|
294560
|
+
"glm-5.3-flash": {
|
|
294561
|
+
"id": "glm-5.3-flash",
|
|
294562
|
+
"name": "GLM-5.3-Flash",
|
|
294563
|
+
"api": "anthropic-messages",
|
|
294564
|
+
"provider": "zai",
|
|
294565
|
+
"baseUrl": "https://api.z.ai/api/anthropic",
|
|
294566
|
+
"reasoning": true,
|
|
294567
|
+
"input": [
|
|
294568
|
+
"text",
|
|
294569
|
+
"image"
|
|
294570
|
+
],
|
|
294571
|
+
"cost": {
|
|
294572
|
+
"input": 0.15,
|
|
294573
|
+
"output": 0.5,
|
|
294574
|
+
"cacheRead": 0.03,
|
|
294575
|
+
"cacheWrite": 0
|
|
294576
|
+
},
|
|
294577
|
+
"contextWindow": 1000000,
|
|
294578
|
+
"maxTokens": 131072,
|
|
294579
|
+
"thinking": {
|
|
294580
|
+
"mode": "anthropic-budget-effort",
|
|
294581
|
+
"efforts": [
|
|
294582
|
+
"low",
|
|
294583
|
+
"high",
|
|
294584
|
+
"max"
|
|
294585
|
+
],
|
|
294586
|
+
"defaultLevel": "max",
|
|
294587
|
+
"requiresEffort": true
|
|
294588
|
+
},
|
|
294589
|
+
"requiresGlyphTokenization": false,
|
|
294590
|
+
"tokenizer": "glm5",
|
|
294591
|
+
"supportsComputerUse": false,
|
|
294592
|
+
"compat": {
|
|
294593
|
+
"officialEndpoint": false,
|
|
294594
|
+
"signingEndpoint": false,
|
|
294595
|
+
"disableStrictTools": false,
|
|
294596
|
+
"disableAdaptiveThinking": false,
|
|
294597
|
+
"allowAnthropicHeaderOverrides": false,
|
|
294598
|
+
"supportsEagerToolInputStreaming": false,
|
|
294599
|
+
"supportsLongCacheRetention": false,
|
|
294600
|
+
"supportsMidConversationSystem": false,
|
|
294601
|
+
"supportsForcedToolChoice": true,
|
|
294602
|
+
"supportsSamplingParams": true,
|
|
294603
|
+
"requiresToolResultId": true,
|
|
294604
|
+
"requiresThinkingEnabled": false,
|
|
294605
|
+
"replayUnsignedThinking": true,
|
|
294606
|
+
"escapeBuiltinToolNames": false
|
|
294607
|
+
}
|
|
294608
|
+
},
|
|
294592
294609
|
"glm-5v-turbo": {
|
|
294593
294610
|
"id": "glm-5v-turbo",
|
|
294594
294611
|
"name": "GLM-5V-Turbo",
|
|
@@ -312261,4 +312278,4 @@
|
|
|
312261
312278
|
}
|
|
312262
312279
|
}
|
|
312263
312280
|
}
|
|
312264
|
-
}
|
|
312281
|
+
}
|
|
@@ -2823,6 +2823,9 @@ const OPENCODE_GO_API_ID_OVERRIDES: Readonly<Record<string, Api>> = {
|
|
|
2823
2823
|
"qwen3.5-plus": "openai-completions",
|
|
2824
2824
|
"qwen3.6-plus": "openai-completions",
|
|
2825
2825
|
};
|
|
2826
|
+
// Runtime-discovered rows cached before model-identity corrections retain
|
|
2827
|
+
// stale capability metadata until the authoritative catalog TTL expires.
|
|
2828
|
+
const OPENCODE_CACHE_MIGRATION_MODEL_IDS = ["glm-5.3-flash"] as const;
|
|
2826
2829
|
|
|
2827
2830
|
// Billing-variant suffixes the OpenCode gateways append to a base model id
|
|
2828
2831
|
// without changing its transport (`deepseek-v4-flash-free`,
|
|
@@ -2883,12 +2886,10 @@ function openCodeModelManagerOptions(
|
|
|
2883
2886
|
providerId,
|
|
2884
2887
|
cacheProviderId: resolveModelCacheProviderId(providerId, { apiKey, baseUrl: discoveryBaseUrl }),
|
|
2885
2888
|
dynamicModelsAuthoritative: true,
|
|
2886
|
-
//
|
|
2887
|
-
// before a
|
|
2888
|
-
//
|
|
2889
|
-
|
|
2890
|
-
// by the 2h cache TTL instead.
|
|
2891
|
-
dropCachedModelIdsOnStaticMismatch: Object.keys(apiOverrides),
|
|
2889
|
+
// Per-id route pins and capability migrations are cache identity:
|
|
2890
|
+
// without this, rows cached before a correction keep the stale route or
|
|
2891
|
+
// thinking surface until TTL expiry (#8957, #9960).
|
|
2892
|
+
dropCachedModelIdsOnStaticMismatch: [...Object.keys(apiOverrides), ...OPENCODE_CACHE_MIGRATION_MODEL_IDS],
|
|
2892
2893
|
modelsDev: {
|
|
2893
2894
|
fetch: () => fetchRevalidatedWellKnownModelsWithTimeout(config?.fetch),
|
|
2894
2895
|
map: payload => {
|
package/src/variant-collapse.ts
CHANGED
|
@@ -307,7 +307,7 @@ function geminiLevelFlashFamily(version: "3.6" | "3.7", ...additionalMembers: st
|
|
|
307
307
|
}
|
|
308
308
|
|
|
309
309
|
const GEMINI_36_FLASH_FAMILY = geminiLevelFlashFamily("3.6", "gemini-3.6-flash-tiered");
|
|
310
|
-
const GEMINI_37_FLASH_FAMILY = geminiLevelFlashFamily("3.7");
|
|
310
|
+
const GEMINI_37_FLASH_FAMILY = geminiLevelFlashFamily("3.7", "gemini-3.7-flash-tiered");
|
|
311
311
|
|
|
312
312
|
function geminiProFamily(mode: "budget" | "google-level"): EffortVariantFamily {
|
|
313
313
|
const budget = mode === "budget";
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { isRecord } from "../utils";
|
|
2
|
+
|
|
3
|
+
/** Provider-native AI Gateway prefix before the upstream route segment. */
|
|
4
|
+
export const CLOUDFLARE_AI_GATEWAY_BASE_URL = "https://gateway.ai.cloudflare.com/v1/<account>/<gateway>";
|
|
5
|
+
/** Anthropic Messages passthrough endpoint. */
|
|
6
|
+
export const CLOUDFLARE_AI_GATEWAY_ANTHROPIC_BASE_URL = `${CLOUDFLARE_AI_GATEWAY_BASE_URL}/anthropic`;
|
|
7
|
+
/** OpenAI Responses passthrough endpoint. */
|
|
8
|
+
export const CLOUDFLARE_AI_GATEWAY_OPENAI_BASE_URL = `${CLOUDFLARE_AI_GATEWAY_BASE_URL}/openai`;
|
|
9
|
+
/** OpenAI-compatible Workers AI endpoint. */
|
|
10
|
+
export const CLOUDFLARE_AI_GATEWAY_COMPAT_BASE_URL = `${CLOUDFLARE_AI_GATEWAY_BASE_URL}/compat`;
|
|
11
|
+
|
|
12
|
+
/** Opaque API-key credential payload persisted by Cloudflare AI Gateway login. */
|
|
13
|
+
export interface CloudflareAiGatewayCredential {
|
|
14
|
+
token: string;
|
|
15
|
+
accountId?: string;
|
|
16
|
+
gatewayId?: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** Parse both structured login credentials and legacy plain gateway tokens. */
|
|
20
|
+
export function parseCloudflareAiGatewayCredential(value: string): CloudflareAiGatewayCredential | null {
|
|
21
|
+
const trimmed = value.trim();
|
|
22
|
+
if (!trimmed) return null;
|
|
23
|
+
if (!trimmed.startsWith("{")) return { token: trimmed };
|
|
24
|
+
try {
|
|
25
|
+
const parsed: unknown = JSON.parse(trimmed);
|
|
26
|
+
if (!isRecord(parsed)) return null;
|
|
27
|
+
if (typeof parsed.token !== "string" || !parsed.token.trim()) return null;
|
|
28
|
+
if (parsed.accountId !== undefined && typeof parsed.accountId !== "string") return null;
|
|
29
|
+
if (parsed.gatewayId !== undefined && typeof parsed.gatewayId !== "string") return null;
|
|
30
|
+
const credential: CloudflareAiGatewayCredential = { token: parsed.token.trim() };
|
|
31
|
+
const accountId = parsed.accountId?.trim();
|
|
32
|
+
const gatewayId = parsed.gatewayId?.trim();
|
|
33
|
+
if (accountId) credential.accountId = accountId;
|
|
34
|
+
if (gatewayId) credential.gatewayId = gatewayId;
|
|
35
|
+
return credential;
|
|
36
|
+
} catch {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** Serialize the gateway token with the routing identifiers collected during login. */
|
|
42
|
+
export function serializeCloudflareAiGatewayCredential(token: string, accountId: string, gatewayId: string): string {
|
|
43
|
+
return JSON.stringify({ token: token.trim(), accountId: accountId.trim(), gatewayId: gatewayId.trim() });
|
|
44
|
+
}
|