@oh-my-pi/pi-catalog 18.4.2 → 18.4.4

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.
@@ -377,8 +377,17 @@ export type CompiledAuthValidation =
377
377
  maxTokensField?: "max_tokens" | "max_completion_tokens";
378
378
  maxTokens?: number;
379
379
  optional?: boolean;
380
+ /** With `optional`: a 403 also trusts the key; only a 401 rejects it. */
381
+ trustForbidden?: boolean;
382
+ }
383
+ | {
384
+ kind: "anthropic-messages";
385
+ label?: string;
386
+ baseUrl: string;
387
+ model: string;
388
+ optional?: boolean;
389
+ trustForbidden?: boolean;
380
390
  }
381
- | { kind: "anthropic-messages"; label?: string; baseUrl: string; model: string; optional?: boolean }
382
391
  | {
383
392
  kind: "models-endpoint";
384
393
  label?: string;
@@ -388,6 +397,7 @@ export type CompiledAuthValidation =
388
397
  /** Hook returning extra request headers (may throw a configuration error). */
389
398
  headersHook?: string;
390
399
  optional?: boolean;
400
+ trustForbidden?: boolean;
391
401
  };
392
402
 
393
403
  /** Paste-an-API-key login: optional browser hint, prompt, optional validation. */
@@ -71,6 +71,7 @@ const codexModelEntrySchema = type({
71
71
  "use_responses_lite?": "unknown",
72
72
  "tool_mode?": "unknown",
73
73
  "available_access_programs?": "unknown",
74
+ "service_tiers?": "unknown",
74
75
  });
75
76
 
76
77
  const codexModelsResponseSchema = type({
@@ -303,6 +304,8 @@ interface ParsedCodexModelEntry {
303
304
  useResponsesLite: boolean;
304
305
  toolMode: boolean;
305
306
  priority: number;
307
+ /** Advertised tier ids; `undefined` when the entry has no `service_tiers` array. */
308
+ serviceTiers: string[] | undefined;
306
309
  }
307
310
 
308
311
  function parseCodexModelEntry(entry: unknown): ParsedCodexModelEntry | null {
@@ -332,6 +335,17 @@ function parseCodexModelEntry(entry: unknown): ParsedCodexModelEntry | null {
332
335
  }
333
336
  }
334
337
 
338
+ // codex-rs `ModelServiceTier { id, name, description }`; only the id reaches the wire.
339
+ // An explicit empty array is kept: it means the model offers no optional tier.
340
+ let serviceTiers: string[] | undefined;
341
+ if (Array.isArray(payload.service_tiers)) {
342
+ serviceTiers = [];
343
+ for (const tier of payload.service_tiers) {
344
+ const id = tier !== null && typeof tier === "object" && "id" in tier ? toNonEmptyString(tier.id) : null;
345
+ if (id && !serviceTiers.includes(id)) serviceTiers.push(id);
346
+ }
347
+ }
348
+
335
349
  return {
336
350
  slug,
337
351
  cyberPrograms,
@@ -344,6 +358,7 @@ function parseCodexModelEntry(entry: unknown): ParsedCodexModelEntry | null {
344
358
  useResponsesLite: toBoolean(payload.use_responses_lite) === true,
345
359
  toolMode: payload.tool_mode === "code_mode_only",
346
360
  priority: toFiniteNumber(payload.priority) ?? Number.MAX_SAFE_INTEGER,
361
+ serviceTiers,
347
362
  };
348
363
  }
349
364
 
@@ -408,6 +423,7 @@ function buildNormalizedCodexModel(
408
423
  ...(parsed.preferWebsockets ? { preferWebsockets: true } : {}),
409
424
  ...(parsed.useResponsesLite ? { useResponsesLite: true } : {}),
410
425
  ...(parsed.toolMode ? { toolMode: "code_mode_only" as const } : {}),
426
+ ...(parsed.serviceTiers !== undefined ? { serviceTiers: parsed.serviceTiers } : {}),
411
427
  ...(parsed.priority !== Number.MAX_SAFE_INTEGER ? { priority: parsed.priority } : {}),
412
428
  },
413
429
  };
package/src/hosts.ts CHANGED
@@ -169,6 +169,53 @@ export function isAzureDeploymentsUrl(baseUrl: string): boolean {
169
169
  return baseUrl.includes("/deployments/");
170
170
  }
171
171
 
172
+ // Bedrock inference endpoints, per AWS's endpoint and PrivateLink docs:
173
+ // - bedrock-runtime: `bedrock-runtime.<region>.amazonaws.com`, FIPS `bedrock-runtime-fips.<region>.amazonaws.com`
174
+ // - bedrock-mantle: `bedrock-mantle.<region>.api.aws`; the bundled provider keeps a `{region}`
175
+ // template until request preparation fills it in
176
+ // - PrivateLink endpoint-specific names for either service, Regional or zonal:
177
+ // `<vpce-id>[-<az>].<service>.<region>.vpce.amazonaws.com`
178
+ // With private DNS enabled, a VPC endpoint answers on the public names above.
179
+ const BEDROCK_PUBLIC_HOST =
180
+ /^(?:(?<runtime>bedrock-runtime(?:-fips)?)\.[a-z0-9-]+\.amazonaws\.com|bedrock-mantle\.(?:[a-z0-9-]+|\{region\})\.api\.aws)$/;
181
+ const BEDROCK_PRIVATELINK_HOST =
182
+ /^vpce-[a-z0-9-]+\.(?:(?<runtime>bedrock-runtime(?:-fips)?)|bedrock-mantle)\.[a-z0-9-]+\.vpce\.amazonaws\.com$/;
183
+
184
+ function hasPathPrefix(pathname: string, prefix: string): boolean {
185
+ return pathname === prefix || pathname.startsWith(`${prefix}/`);
186
+ }
187
+
188
+ /**
189
+ * Amazon Bedrock API route on a bedrock-runtime or bedrock-mantle endpoint
190
+ * (public, FIPS, or PrivateLink hostname):
191
+ * - `anthropic`: the Anthropic Messages API under `/anthropic`.
192
+ * - `openai`: the OpenAI-compatible APIs under `/openai`, plus Mantle's
193
+ * documented `/v1` base (`https://bedrock-mantle.<region>.api.aws/v1`).
194
+ *
195
+ * Hostnames are parsed strictly so proxies that embed these hosts in a path
196
+ * do not match.
197
+ */
198
+ export function isBedrockRouteUrl(baseUrl: string | undefined, route: "openai" | "anthropic"): boolean {
199
+ if (!baseUrl) return false;
200
+ let url: URL;
201
+ try {
202
+ url = new URL(baseUrl);
203
+ } catch {
204
+ return false;
205
+ }
206
+ if (url.protocol !== "https:") return false;
207
+ const host = BEDROCK_PUBLIC_HOST.exec(url.hostname) ?? BEDROCK_PRIVATELINK_HOST.exec(url.hostname);
208
+ if (!host) return false;
209
+ if (route === "anthropic") return hasPathPrefix(url.pathname, "/anthropic");
210
+ const isMantle = host.groups?.runtime === undefined;
211
+ return hasPathPrefix(url.pathname, "/openai") || (isMantle && hasPathPrefix(url.pathname, "/v1"));
212
+ }
213
+
214
+ /** Amazon Bedrock's OpenAI-compatible routes; see {@link isBedrockRouteUrl}. */
215
+ export function isBedrockOpenAIUrl(baseUrl: string | undefined): boolean {
216
+ return isBedrockRouteUrl(baseUrl, "openai");
217
+ }
218
+
172
219
  /** Alibaba DashScope consumer `compatible-mode` endpoint (rejects multimodal arrays for some text-only SKUs). */
173
220
  export function isDashscopeCompatibleModeUrl(baseUrl: string): boolean {
174
221
  const normalized = baseUrl.toLowerCase();