@hyav/pi-provider 0.1.4 → 0.1.6

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.
@@ -1,5 +1,5 @@
1
1
  import type { PreflightAdapter } from "@hyav/pi-provider";
2
- import { definePreflightExtension, ProviderDataError, parseRetryAfter } from "@hyav/pi-provider";
2
+ import { definePreflightExtension, hasBaseUrlOrigin, ProviderDataError, parseRetryAfter } from "@hyav/pi-provider";
3
3
  import { OPENROUTER_KEY_URL } from "../status/openrouter.ts";
4
4
 
5
5
  export const OPENROUTER_MODELS_URL = "https://openrouter.ai/api/v1/models";
@@ -79,6 +79,7 @@ export const openRouterPreflightAdapter: PreflightAdapter = {
79
79
  name: "OpenRouter",
80
80
  cacheTtlMs: 30_000,
81
81
  requestTimeoutMs: 8_000,
82
+ supportsModel: (model) => hasBaseUrlOrigin(model.baseUrl, OPENROUTER_MODELS_URL),
82
83
  async fetch(context) {
83
84
  const modelIds = await collectModelIds(context);
84
85
  const apiKey = await context.getApiKey();
@@ -37,11 +37,6 @@ export function createVercelAIGatewayPreflightAdapter(requestTimeoutMs: number):
37
37
  cacheTtlMs: 30_000,
38
38
  requestTimeoutMs,
39
39
  async fetch(context): Promise<PreflightSnapshot> {
40
- const key = await context.getApiKey();
41
- if (!key || key === "proxy-managed") {
42
- return { passed: false, checks: ["auth"], updatedAt: context.now() };
43
- }
44
-
45
40
  const response = await context.fetch(VERCEL_MODELS_URL, {
46
41
  headers: {
47
42
  Accept: "application/json",
@@ -67,7 +62,7 @@ export function createVercelAIGatewayPreflightAdapter(requestTimeoutMs: number):
67
62
 
68
63
  return {
69
64
  passed: parseVercelModelIds(payload).has(context.model.id),
70
- checks: ["endpoint", "auth", "catalog"],
65
+ checks: ["endpoint", "catalog"],
71
66
  updatedAt: context.now(),
72
67
  httpStatus: response.status,
73
68
  };
package/preflight/xai.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import type { PreflightAdapter } from "@hyav/pi-provider";
2
- import { definePreflightExtension, ProviderDataError, parseRetryAfter } from "@hyav/pi-provider";
2
+ import { definePreflightExtension, hasBaseUrlOrigin, ProviderDataError, parseRetryAfter } from "@hyav/pi-provider";
3
3
  import { XAI_MODELS_URL } from "../status/xai.ts";
4
4
 
5
5
  function isRecord(value: unknown): value is Record<string, unknown> {
@@ -12,6 +12,7 @@ export const xaiPreflightAdapter: PreflightAdapter = {
12
12
  name: "xAI",
13
13
  cacheTtlMs: 30_000,
14
14
  requestTimeoutMs: 8_000,
15
+ supportsModel: (model) => hasBaseUrlOrigin(model.baseUrl, XAI_MODELS_URL),
15
16
  async fetch(context) {
16
17
  const apiKey = await context.getApiKey();
17
18
  const authHeaders: Record<string, string> = {
@@ -9,6 +9,7 @@ import type {
9
9
  import {
10
10
  defineProviderExtension,
11
11
  isProviderDataError,
12
+ MAX_PROVIDER_MODEL_COUNT,
12
13
  normalizeProviderModels,
13
14
  ProviderDataError,
14
15
  withDeadline,
@@ -242,6 +243,7 @@ function parseCurrentHyperModel(value: unknown): ProviderModelDraft | undefined
242
243
 
243
244
  function parseCurrentHyperModels(payload: Record<string, unknown>): ProviderModelDraft[] | undefined {
244
245
  if (!Array.isArray(payload.models)) return undefined;
246
+ if (payload.models.length > MAX_PROVIDER_MODEL_COUNT) return [];
245
247
  const models: ProviderModelDraft[] = [];
246
248
  const seenIds = new Set<string>();
247
249
  for (const value of payload.models) {
@@ -256,7 +258,7 @@ function parseCurrentHyperModels(payload: Record<string, unknown>): ProviderMode
256
258
  }
257
259
 
258
260
  function parseLegacyHyperModels(payload: Record<string, unknown>): ProviderModelDraft[] {
259
- if (!Array.isArray(payload.data)) return [];
261
+ if (!Array.isArray(payload.data) || payload.data.length > MAX_PROVIDER_MODEL_COUNT) return [];
260
262
  const models: ProviderModelDraft[] = [];
261
263
  const seenIds = new Set<string>();
262
264
 
@@ -356,6 +358,7 @@ async function discoverHyperModels(
356
358
  if (models.length === 0) {
357
359
  throw new ProviderDataError("Charm Hyper model discovery returned no valid models", "badjson");
358
360
  }
361
+ normalizeProviderModels(models);
359
362
  return models;
360
363
  },
361
364
  timeoutMs,
@@ -1,5 +1,5 @@
1
1
  import type { StatusAdapter, StatusEntry, StatusSnapshot } from "@hyav/pi-provider";
2
- import { defineStatusExtension, ProviderDataError, parseRetryAfter } from "@hyav/pi-provider";
2
+ import { defineStatusExtension, hasBaseUrlOrigin, ProviderDataError, parseRetryAfter } from "@hyav/pi-provider";
3
3
 
4
4
  /**
5
5
  * Anthropic usage endpoint for subscription quotas (Claude Pro/Max) and extra
@@ -170,6 +170,10 @@ export function createAnthropicStatusAdapter(
170
170
  name: "Anthropic",
171
171
  cacheTtlMs: 60_000,
172
172
  requestTimeoutMs,
173
+ supportsModel: (model) =>
174
+ usageUrl === "" ||
175
+ usageUrl !== DEFAULT_ANTHROPIC_USAGE_URL ||
176
+ hasBaseUrlOrigin(model.baseUrl, "https://api.anthropic.com"),
173
177
  async fetch(context): Promise<StatusSnapshot> {
174
178
  const key = await context.getApiKey();
175
179
  if (!key || key === "proxy-managed") {
@@ -1,5 +1,5 @@
1
1
  import type { StatusAdapter, StatusSnapshot } from "@hyav/pi-provider";
2
- import { defineStatusExtension, ProviderDataError, parseRetryAfter } from "@hyav/pi-provider";
2
+ import { defineStatusExtension, hasBaseUrlOrigin, ProviderDataError, parseRetryAfter } from "@hyav/pi-provider";
3
3
  import { hyperJsonHeaders } from "../providers/charm-hyper/constants.ts";
4
4
 
5
5
  const CREDITS_URL = "https://hyper.charm.land/v1/credits";
@@ -23,6 +23,7 @@ export const hyperStatusAdapter: StatusAdapter = {
23
23
  name: "Charm Hyper",
24
24
  cacheTtlMs: 60_000,
25
25
  requestTimeoutMs: 8_000,
26
+ supportsModel: (model) => hasBaseUrlOrigin(model.baseUrl, CREDITS_URL),
26
27
  async fetch(context): Promise<StatusSnapshot> {
27
28
  const key = await context.getApiKey();
28
29
  const headers = new Headers(hyperJsonHeaders({ "Accept-Encoding": "identity" }));
@@ -1,5 +1,5 @@
1
1
  import type { StatusAdapter, StatusSnapshot } from "@hyav/pi-provider";
2
- import { defineStatusExtension, ProviderDataError, parseRetryAfter } from "@hyav/pi-provider";
2
+ import { defineStatusExtension, hasBaseUrlOrigin, ProviderDataError, parseRetryAfter } from "@hyav/pi-provider";
3
3
 
4
4
  export const DEEPSEEK_BALANCE_URL = "https://api.deepseek.com/user/balance";
5
5
 
@@ -41,6 +41,7 @@ export const deepSeekStatusAdapter: StatusAdapter = {
41
41
  name: "DeepSeek",
42
42
  cacheTtlMs: 30_000,
43
43
  requestTimeoutMs: 8_000,
44
+ supportsModel: (model) => hasBaseUrlOrigin(model.baseUrl, DEEPSEEK_BALANCE_URL),
44
45
  async fetch(context): Promise<StatusSnapshot> {
45
46
  const key = await context.getApiKey();
46
47
  if (!key || key === "proxy-managed") {
@@ -1,12 +1,21 @@
1
1
  import type { StatusAdapter, StatusEntry, StatusSnapshot } from "@hyav/pi-provider";
2
- import { defineStatusExtension, ProviderDataError, parseRetryAfter } from "@hyav/pi-provider";
2
+ import {
3
+ appendBaseUrlPath,
4
+ authDefinesHeader,
5
+ defineStatusExtension,
6
+ getContextAuth,
7
+ mergeDiagnosticHeaders,
8
+ ProviderDataError,
9
+ parseRetryAfter,
10
+ } from "@hyav/pi-provider";
3
11
 
4
12
  /**
5
13
  * GitHub Copilot Individual usage. These endpoints are not part of public
6
14
  * GitHub documentation, so payload shapes are parsed defensively and a 404
7
15
  * degrades to a single explanatory entry instead of an error state.
8
16
  */
9
- export const COPILOT_USAGE_URL = "https://api.individual.githubcopilot.com/usage";
17
+ export const COPILOT_BASE_URL = "https://api.individual.githubcopilot.com";
18
+ export const COPILOT_USAGE_URL = `${COPILOT_BASE_URL}/usage`;
10
19
 
11
20
  function isRecord(value: unknown): value is Record<string, unknown> {
12
21
  return value !== null && typeof value === "object" && !Array.isArray(value);
@@ -109,17 +118,21 @@ export const githubCopilotStatusAdapter: StatusAdapter = {
109
118
  cacheTtlMs: 60_000,
110
119
  requestTimeoutMs: 8_000,
111
120
  async fetch(context): Promise<StatusSnapshot> {
112
- const key = await context.getApiKey();
121
+ const auth = await getContextAuth(context);
122
+ const key = auth.apiKey;
113
123
  if (!key || key === "proxy-managed") {
114
124
  throw new ProviderDataError("GitHub Copilot status requires Copilot OAuth", "auth");
115
125
  }
116
- const response = await context.fetch(COPILOT_USAGE_URL, {
117
- headers: {
118
- Accept: "application/json",
119
- "Accept-Encoding": "identity",
120
- Authorization: `Bearer ${key}`,
121
- "User-Agent": "@hyav/pi-provider",
122
- },
126
+ const url = appendBaseUrlPath(context.model?.baseUrl ?? auth.baseUrl, "usage", COPILOT_BASE_URL);
127
+ if (!url) throw new ProviderDataError("GitHub Copilot usage endpoint is unavailable", "unsupported");
128
+ const headers = mergeDiagnosticHeaders(auth, {
129
+ Accept: "application/json",
130
+ "Accept-Encoding": "identity",
131
+ "User-Agent": "@hyav/pi-provider",
132
+ });
133
+ if (!authDefinesHeader(auth, "Authorization")) headers.set("Authorization", `Bearer ${key}`);
134
+ const response = await context.fetch(url, {
135
+ headers,
123
136
  signal: context.signal,
124
137
  });
125
138
  if (response.status === 404) {
package/status/groq.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import type { StatusAdapter, StatusEntry, StatusSnapshot } from "@hyav/pi-provider";
2
- import { defineStatusExtension, ProviderDataError, parseRetryAfter } from "@hyav/pi-provider";
2
+ import { defineStatusExtension, hasBaseUrlOrigin, ProviderDataError, parseRetryAfter } from "@hyav/pi-provider";
3
3
  import { parseRateLimitWindows } from "../core/ratelimit-headers.ts";
4
4
 
5
5
  export const GROQ_MODELS_URL = "https://api.groq.com/openai/v1/models";
@@ -29,6 +29,7 @@ export const groqStatusAdapter: StatusAdapter = {
29
29
  name: "Groq",
30
30
  cacheTtlMs: 60_000,
31
31
  requestTimeoutMs: 8_000,
32
+ supportsModel: (model) => hasBaseUrlOrigin(model.baseUrl, GROQ_MODELS_URL),
32
33
  async fetch(context): Promise<StatusSnapshot> {
33
34
  const key = await context.getApiKey();
34
35
  if (!key || key === "proxy-managed") {
@@ -1,5 +1,5 @@
1
1
  import type { StatusAdapter, StatusEntry, StatusSnapshot } from "@hyav/pi-provider";
2
- import { defineStatusExtension, ProviderDataError, parseRetryAfter } from "@hyav/pi-provider";
2
+ import { defineStatusExtension, hasBaseUrlOrigin, ProviderDataError, parseRetryAfter } from "@hyav/pi-provider";
3
3
 
4
4
  export const HF_WHOAMI_URL = "https://huggingface.co/api/whoami-v2";
5
5
 
@@ -44,6 +44,7 @@ export const huggingFaceStatusAdapter: StatusAdapter = {
44
44
  name: "Hugging Face",
45
45
  cacheTtlMs: 60_000,
46
46
  requestTimeoutMs: 8_000,
47
+ supportsModel: (model) => hasBaseUrlOrigin(model.baseUrl, "https://router.huggingface.co"),
47
48
  async fetch(context): Promise<StatusSnapshot> {
48
49
  const key = await context.getApiKey();
49
50
  if (!key || key === "proxy-managed") {
@@ -1,5 +1,5 @@
1
1
  import type { StatusAdapter, StatusEntry, StatusSnapshot } from "@hyav/pi-provider";
2
- import { defineStatusExtension, ProviderDataError, parseRetryAfter } from "@hyav/pi-provider";
2
+ import { defineStatusExtension, hasBaseUrlOrigin, ProviderDataError, parseRetryAfter } from "@hyav/pi-provider";
3
3
 
4
4
  /**
5
5
  * Moonshot (Kimi) balance checks. International and China platforms keep
@@ -65,6 +65,7 @@ export function createMoonshotStatusAdapter(config: MoonshotStatusConfig, reques
65
65
  name: config.name,
66
66
  cacheTtlMs: 60_000,
67
67
  requestTimeoutMs,
68
+ supportsModel: (model) => hasBaseUrlOrigin(model.baseUrl, config.balanceUrl),
68
69
  async fetch(context): Promise<StatusSnapshot> {
69
70
  const key = await context.getApiKey();
70
71
  if (!key || key === "proxy-managed") {
@@ -1,5 +1,5 @@
1
1
  import type { StatusAdapter, StatusEntry, StatusSnapshot } from "@hyav/pi-provider";
2
- import { defineStatusExtension, ProviderDataError, parseRetryAfter } from "@hyav/pi-provider";
2
+ import { defineStatusExtension, hasBaseUrlOrigin, ProviderDataError, parseRetryAfter } from "@hyav/pi-provider";
3
3
 
4
4
  export const CODEX_USAGE_URL = "https://chatgpt.com/backend-api/wham/usage";
5
5
  const ACCOUNT_ID_CLAIM = "https://api.openai.com/auth";
@@ -170,6 +170,7 @@ export const openAICodexStatusAdapter: StatusAdapter = {
170
170
  name: "OpenAI Codex",
171
171
  cacheTtlMs: 60_000,
172
172
  requestTimeoutMs: 8_000,
173
+ supportsModel: (model) => hasBaseUrlOrigin(model.baseUrl, CODEX_USAGE_URL),
173
174
  async fetch(context): Promise<StatusSnapshot> {
174
175
  const key = await context.getApiKey();
175
176
  if (!key || key === "proxy-managed") {
@@ -1,5 +1,5 @@
1
1
  import type { StatusAdapter, StatusEntry, StatusSnapshot } from "@hyav/pi-provider";
2
- import { defineStatusExtension, ProviderDataError, parseRetryAfter } from "@hyav/pi-provider";
2
+ import { defineStatusExtension, hasBaseUrlOrigin, ProviderDataError, parseRetryAfter } from "@hyav/pi-provider";
3
3
 
4
4
  export const OPENCODE_GO_USAGE_URL = "https://opencode.ai/zen/go/v1/usage";
5
5
 
@@ -70,6 +70,7 @@ export const openCodeGoStatusAdapter: StatusAdapter = {
70
70
  name: "OpenCode Go",
71
71
  cacheTtlMs: 60_000,
72
72
  requestTimeoutMs: 8_000,
73
+ supportsModel: (model) => hasBaseUrlOrigin(model.baseUrl, OPENCODE_GO_USAGE_URL),
73
74
  async fetch(context): Promise<StatusSnapshot> {
74
75
  const key = await context.getApiKey();
75
76
  if (!key || key === "proxy-managed") {
@@ -1,5 +1,5 @@
1
1
  import type { StatusAdapter, StatusEntry, StatusSnapshot } from "@hyav/pi-provider";
2
- import { defineStatusExtension, ProviderDataError, parseRetryAfter } from "@hyav/pi-provider";
2
+ import { defineStatusExtension, hasBaseUrlOrigin, ProviderDataError, parseRetryAfter } from "@hyav/pi-provider";
3
3
 
4
4
  export const OPENROUTER_KEY_URL = "https://openrouter.ai/api/v1/auth/key";
5
5
  export const OPENROUTER_CREDITS_URL = "https://openrouter.ai/api/v1/credits";
@@ -109,6 +109,7 @@ export const openRouterStatusAdapter: StatusAdapter = {
109
109
  name: "OpenRouter",
110
110
  cacheTtlMs: 60_000,
111
111
  requestTimeoutMs: 8_000,
112
+ supportsModel: (model) => hasBaseUrlOrigin(model.baseUrl, OPENROUTER_KEY_URL),
112
113
  async fetch(context): Promise<StatusSnapshot> {
113
114
  const key = await context.getApiKey();
114
115
  if (!key || key === "proxy-managed") {
@@ -1,5 +1,5 @@
1
1
  import type { StatusAdapter, StatusSnapshot } from "@hyav/pi-provider";
2
- import { defineStatusExtension, ProviderDataError, parseRetryAfter } from "@hyav/pi-provider";
2
+ import { defineStatusExtension, hasBaseUrlOrigin, ProviderDataError, parseRetryAfter } from "@hyav/pi-provider";
3
3
  import { VERCEL_PROVIDER_ID } from "./vercel-ai-gateway/constants.ts";
4
4
 
5
5
  export const VERCEL_CREDITS_URL = "https://ai-gateway.vercel.sh/v1/credits";
@@ -41,6 +41,7 @@ export function createVercelAIGatewayStatusAdapter(requestTimeoutMs: number): St
41
41
  name: "Vercel AI Gateway",
42
42
  cacheTtlMs: 30_000,
43
43
  requestTimeoutMs,
44
+ supportsModel: (model) => hasBaseUrlOrigin(model.baseUrl, VERCEL_CREDITS_URL),
44
45
  async fetch(context): Promise<StatusSnapshot> {
45
46
  const key = await context.getApiKey();
46
47
  if (!key || key === "proxy-managed") {
package/status/xai.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import type { StatusAdapter, StatusEntry, StatusSnapshot } from "@hyav/pi-provider";
2
- import { defineStatusExtension, ProviderDataError, parseRetryAfter } from "@hyav/pi-provider";
2
+ import { defineStatusExtension, hasBaseUrlOrigin, ProviderDataError, parseRetryAfter } from "@hyav/pi-provider";
3
3
  import { parseRateLimitWindows } from "../core/ratelimit-headers.ts";
4
4
 
5
5
  export const XAI_MODELS_URL = "https://api.x.ai/v1/models";
@@ -21,6 +21,7 @@ export const xaiStatusAdapter: StatusAdapter = {
21
21
  name: "xAI",
22
22
  cacheTtlMs: 60_000,
23
23
  requestTimeoutMs: 8_000,
24
+ supportsModel: (model) => hasBaseUrlOrigin(model.baseUrl, XAI_MODELS_URL),
24
25
  async fetch(context): Promise<StatusSnapshot> {
25
26
  const key = await context.getApiKey();
26
27
  if (!key || key === "proxy-managed") {