@agentproto/model-intel 0.2.0 → 0.3.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/dist/index.d.ts +36 -1
- package/dist/index.mjs +42 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -105,6 +105,41 @@ declare class QuotaReaderRegistry {
|
|
|
105
105
|
read(profile: QuotaProfile, ctx?: QuotaReadContext): Promise<QuotaStatus | undefined>;
|
|
106
106
|
}
|
|
107
107
|
|
|
108
|
+
/**
|
|
109
|
+
* Catalog watch domain: pure diff of two catalog snapshots.
|
|
110
|
+
* No I/O — mirrors the pure-evaluator pattern of `build.ts`.
|
|
111
|
+
*/
|
|
112
|
+
interface CatalogSnapshotEntry {
|
|
113
|
+
/** Catalog model id, e.g. "claude-sonnet-5-5". The snapshot's own key. */
|
|
114
|
+
id: string;
|
|
115
|
+
inputPer1M: number;
|
|
116
|
+
outputPer1M: number;
|
|
117
|
+
/** Derived: true when inputPer1M === 0 && outputPer1M === 0. */
|
|
118
|
+
free: boolean;
|
|
119
|
+
}
|
|
120
|
+
/** Keyed by CatalogSnapshotEntry.id. */
|
|
121
|
+
type CatalogSnapshot = Record<string, CatalogSnapshotEntry>;
|
|
122
|
+
interface CatalogPriceChange {
|
|
123
|
+
id: string;
|
|
124
|
+
prev: Pick<CatalogSnapshotEntry, "inputPer1M" | "outputPer1M">;
|
|
125
|
+
next: Pick<CatalogSnapshotEntry, "inputPer1M" | "outputPer1M">;
|
|
126
|
+
}
|
|
127
|
+
interface CatalogDiff {
|
|
128
|
+
/** ids present in `next`, absent from `prev`. */
|
|
129
|
+
appeared: string[];
|
|
130
|
+
/** ids present in `prev`, absent from `next`. */
|
|
131
|
+
removed: string[];
|
|
132
|
+
/** same id in both, inputPer1M or outputPer1M differs. Price-only — an id
|
|
133
|
+
* in `appeared`/`removed` never also appears here. */
|
|
134
|
+
priceChanged: CatalogPriceChange[];
|
|
135
|
+
/** id was free in prev, not free in next (and still present in both —
|
|
136
|
+
* already counted in priceChanged too if the price itself differs). */
|
|
137
|
+
freeToPaid: string[];
|
|
138
|
+
/** id was not free in prev, free in next. */
|
|
139
|
+
paidToFree: string[];
|
|
140
|
+
}
|
|
141
|
+
declare function diffCatalog(prev: CatalogSnapshot, next: CatalogSnapshot): CatalogDiff;
|
|
142
|
+
|
|
108
143
|
interface AnthropicHeadersInput {
|
|
109
144
|
profileRef: string;
|
|
110
145
|
headers: Headers | Record<string, string | undefined>;
|
|
@@ -182,4 +217,4 @@ declare function createOpenRouterQuotaReader(opts?: OpenRouterQuotaReaderOptions
|
|
|
182
217
|
declare const SPEC_NAME: "quota/v1";
|
|
183
218
|
declare const SPEC_VERSION: "1.0.0-alpha";
|
|
184
219
|
|
|
185
|
-
export { type BuildQuotaStatusInput, type CodexCredits, type CodexRateLimitWindow, type CodexRateLimits, type CodexRateLimitsInput, QUOTA_BASES, type QuotaBasis, type QuotaCredits, type QuotaProfile, type QuotaReadContext, type QuotaReader, QuotaReaderRegistry, type QuotaStatus, type QuotaWindow, SPEC_NAME, SPEC_VERSION, buildQuotaStatus, createMoonshotQuotaReader, createOpenRouterQuotaReader, defineQuotaReader, quotaCreditsSchema, quotaFromAnthropicHeaders, quotaFromCodexRateLimits, quotaStatusSchema, quotaWindowSchema, remainingPctFromCounts, remainingPctFromUsed };
|
|
220
|
+
export { type BuildQuotaStatusInput, type CatalogDiff, type CatalogPriceChange, type CatalogSnapshot, type CatalogSnapshotEntry, type CodexCredits, type CodexRateLimitWindow, type CodexRateLimits, type CodexRateLimitsInput, QUOTA_BASES, type QuotaBasis, type QuotaCredits, type QuotaProfile, type QuotaReadContext, type QuotaReader, QuotaReaderRegistry, type QuotaStatus, type QuotaWindow, SPEC_NAME, SPEC_VERSION, buildQuotaStatus, createMoonshotQuotaReader, createOpenRouterQuotaReader, defineQuotaReader, diffCatalog, quotaCreditsSchema, quotaFromAnthropicHeaders, quotaFromCodexRateLimits, quotaStatusSchema, quotaWindowSchema, remainingPctFromCounts, remainingPctFromUsed };
|
package/dist/index.mjs
CHANGED
|
@@ -107,6 +107,47 @@ function safeSupports(reader, profile) {
|
|
|
107
107
|
}
|
|
108
108
|
}
|
|
109
109
|
|
|
110
|
+
// src/catalog-diff.ts
|
|
111
|
+
function isFree(entry) {
|
|
112
|
+
return entry.inputPer1M === 0 && entry.outputPer1M === 0;
|
|
113
|
+
}
|
|
114
|
+
function diffCatalog(prev, next) {
|
|
115
|
+
const appeared = [];
|
|
116
|
+
const removed = [];
|
|
117
|
+
const priceChanged = [];
|
|
118
|
+
const freeToPaid = [];
|
|
119
|
+
const paidToFree = [];
|
|
120
|
+
for (const id of Object.keys(next)) {
|
|
121
|
+
if (!(id in prev)) appeared.push(id);
|
|
122
|
+
}
|
|
123
|
+
for (const id of Object.keys(prev)) {
|
|
124
|
+
if (!(id in next)) removed.push(id);
|
|
125
|
+
}
|
|
126
|
+
for (const id of Object.keys(next)) {
|
|
127
|
+
const before = prev[id];
|
|
128
|
+
const after = next[id];
|
|
129
|
+
if (!before || !after) continue;
|
|
130
|
+
if (before.inputPer1M !== after.inputPer1M || before.outputPer1M !== after.outputPer1M) {
|
|
131
|
+
priceChanged.push({
|
|
132
|
+
id,
|
|
133
|
+
prev: { inputPer1M: before.inputPer1M, outputPer1M: before.outputPer1M },
|
|
134
|
+
next: { inputPer1M: after.inputPer1M, outputPer1M: after.outputPer1M }
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
const wasFree = isFree(before);
|
|
138
|
+
const isNowFree = isFree(after);
|
|
139
|
+
if (wasFree && !isNowFree) freeToPaid.push(id);
|
|
140
|
+
if (!wasFree && isNowFree) paidToFree.push(id);
|
|
141
|
+
}
|
|
142
|
+
return {
|
|
143
|
+
appeared: appeared.sort(),
|
|
144
|
+
removed: removed.sort(),
|
|
145
|
+
priceChanged: priceChanged.sort((a, b) => a.id < b.id ? -1 : a.id > b.id ? 1 : 0),
|
|
146
|
+
freeToPaid: freeToPaid.sort(),
|
|
147
|
+
paidToFree: paidToFree.sort()
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
|
|
110
151
|
// src/readers/anthropic.ts
|
|
111
152
|
var DIMENSIONS = [
|
|
112
153
|
{ id: "requests-1m", header: "requests", unit: "requests" },
|
|
@@ -350,6 +391,6 @@ function createOpenRouterQuotaReader(opts) {
|
|
|
350
391
|
var SPEC_NAME = "quota/v1";
|
|
351
392
|
var SPEC_VERSION = "1.0.0-alpha";
|
|
352
393
|
|
|
353
|
-
export { QUOTA_BASES, QuotaReaderRegistry, SPEC_NAME, SPEC_VERSION, buildQuotaStatus, createMoonshotQuotaReader, createOpenRouterQuotaReader, defineQuotaReader, quotaCreditsSchema, quotaFromAnthropicHeaders, quotaFromCodexRateLimits, quotaStatusSchema, quotaWindowSchema, remainingPctFromCounts, remainingPctFromUsed };
|
|
394
|
+
export { QUOTA_BASES, QuotaReaderRegistry, SPEC_NAME, SPEC_VERSION, buildQuotaStatus, createMoonshotQuotaReader, createOpenRouterQuotaReader, defineQuotaReader, diffCatalog, quotaCreditsSchema, quotaFromAnthropicHeaders, quotaFromCodexRateLimits, quotaStatusSchema, quotaWindowSchema, remainingPctFromCounts, remainingPctFromUsed };
|
|
354
395
|
//# sourceMappingURL=index.mjs.map
|
|
355
396
|
//# sourceMappingURL=index.mjs.map
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/types.ts","../src/build.ts","../src/registry.ts","../src/readers/anthropic.ts","../src/readers/codex.ts","../src/readers/moonshot.ts","../src/readers/openrouter.ts","../src/index.ts"],"names":["remainingPct","z","DEFAULT_BASE_URL","DEFAULT_TIMEOUT_MS"],"mappings":"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","file":"index.mjs","sourcesContent":["import { z } from \"zod\"\n\n/**\n * Where a quota figure came from. `provider` is a documented provider\n * surface; `observed` is derived from this host's own traffic; `estimated`\n * is a local computation; `probe` is an undocumented surface reached through\n * a connector and MUST NOT be emitted by core readers.\n */\nexport const QUOTA_BASES = [\"provider\", \"probe\", \"observed\", \"estimated\"] as const\nexport type QuotaBasis = (typeof QUOTA_BASES)[number]\n\nconst isoTimestamp = z.string().refine((s) => !Number.isNaN(Date.parse(s)), {\n message: \"expected an ISO-8601 timestamp\",\n})\n\nexport const quotaWindowSchema = z.object({\n /** Open string: \"5h\", \"7d\", \"month\", \"minute\", \"day\", \"primary\", \"key_limit\", ... */\n id: z.string().min(1),\n /** 0-100. Required: a window that cannot state it is not emitted. */\n remainingPct: z.number().min(0).max(100),\n used: z.number().optional(),\n limit: z.number().optional(),\n remaining: z.number().optional(),\n unit: z.string().optional(),\n resetsAt: isoTimestamp.optional(),\n})\nexport type QuotaWindow = z.infer<typeof quotaWindowSchema>\n\nexport const quotaCreditsSchema = z.object({\n balanceUsd: z.number().optional(),\n unlimited: z.boolean().optional(),\n currency: z.string().min(1),\n})\nexport type QuotaCredits = z.infer<typeof quotaCreditsSchema>\n\nexport const quotaStatusSchema = z.object({\n profileRef: z.string().min(1),\n asOf: isoTimestamp,\n basis: z.enum(QUOTA_BASES),\n source: z.string().optional(),\n windows: z.array(quotaWindowSchema),\n credits: quotaCreditsSchema.optional(),\n})\nexport type QuotaStatus = z.infer<typeof quotaStatusSchema>\n\n/** The minimal auth-profile projection a reader needs. */\nexport interface QuotaProfile {\n profileRef: string\n endpoint: string\n method: \"oauth-bearer\" | \"api-key\"\n credentialRef?: string\n}\n\nexport interface QuotaReadContext {\n signal?: AbortSignal\n /** Resolve a profile's opaque credentialRef to a bearer secret. */\n resolveCredential?(ref: string): Promise<string | undefined>\n fetchImpl?: typeof fetch\n now?: () => Date\n}\n\n/**\n * Reads quota for a profile. MUST NOT throw: any failure or unknown value\n * resolves to `undefined`. Never fabricates a figure.\n */\nexport interface QuotaReader {\n readonly id: string\n supports(profile: QuotaProfile): boolean\n read(profile: QuotaProfile, ctx: QuotaReadContext): Promise<QuotaStatus | undefined>\n}\n","import type { QuotaBasis, QuotaCredits, QuotaStatus, QuotaWindow } from \"./types.js\"\nimport { quotaWindowSchema } from \"./types.js\"\n\n/** remainingPct from a used percentage, or undefined when it is not a finite 0-100 number. */\nexport function remainingPctFromUsed(usedPct: number | null | undefined): number | undefined {\n if (usedPct === null || usedPct === undefined || !Number.isFinite(usedPct)) return undefined\n if (usedPct < 0 || usedPct > 100) return undefined\n return 100 - usedPct\n}\n\n/** remainingPct from remaining/limit counts, or undefined when limit is not a positive finite number. */\nexport function remainingPctFromCounts(\n remaining: number | null | undefined,\n limit: number | null | undefined,\n): number | undefined {\n if (remaining === null || remaining === undefined || limit === null || limit === undefined) {\n return undefined\n }\n if (!Number.isFinite(remaining) || !Number.isFinite(limit) || limit <= 0) return undefined\n if (remaining < 0 || remaining > limit) return undefined\n return (remaining / limit) * 100\n}\n\nexport interface BuildQuotaStatusInput {\n profileRef: string\n basis: QuotaBasis\n source?: string\n now?: () => Date\n /** Candidate windows; any that fail validation are dropped, never repaired. */\n windows?: readonly unknown[]\n credits?: QuotaCredits\n}\n\n/**\n * Assemble a QuotaStatus. Invalid windows are omitted. Returns `undefined`\n * when there is nothing to report (no valid window and no credits).\n */\nexport function buildQuotaStatus(input: BuildQuotaStatusInput): QuotaStatus | undefined {\n const windows: QuotaWindow[] = []\n for (const candidate of input.windows ?? []) {\n const parsed = quotaWindowSchema.safeParse(candidate)\n if (parsed.success) windows.push(parsed.data)\n }\n if (windows.length === 0 && input.credits === undefined) return undefined\n return {\n profileRef: input.profileRef,\n asOf: (input.now ?? (() => new Date()))().toISOString(),\n basis: input.basis,\n ...(input.source !== undefined ? { source: input.source } : {}),\n windows,\n ...(input.credits !== undefined ? { credits: input.credits } : {}),\n }\n}\n","import type { QuotaProfile, QuotaReadContext, QuotaReader, QuotaStatus } from \"./types.js\"\n\n/** Identity helper that fixes the QuotaReader contract at definition time (AIP-43 handle). */\nexport function defineQuotaReader(reader: QuotaReader): QuotaReader {\n return reader\n}\n\nexport class QuotaReaderRegistry {\n private readonly readers = new Map<string, QuotaReader>()\n\n register(reader: QuotaReader): this {\n if (this.readers.has(reader.id)) {\n throw new Error(`quota reader already registered: ${reader.id}`)\n }\n this.readers.set(reader.id, reader)\n return this\n }\n\n list(): QuotaReader[] {\n return [...this.readers.values()]\n }\n\n /** First registered reader that supports the profile, in registration order. */\n find(profile: QuotaProfile): QuotaReader | undefined {\n for (const reader of this.readers.values()) {\n if (safeSupports(reader, profile)) return reader\n }\n return undefined\n }\n\n /** Read through the first supporting reader. Never throws; unknown -> undefined. */\n async read(profile: QuotaProfile, ctx: QuotaReadContext = {}): Promise<QuotaStatus | undefined> {\n const reader = this.find(profile)\n if (!reader) return undefined\n try {\n return await reader.read(profile, ctx)\n } catch {\n return undefined\n }\n }\n}\n\nfunction safeSupports(reader: QuotaReader, profile: QuotaProfile): boolean {\n try {\n return reader.supports(profile)\n } catch {\n return false\n }\n}\n","import { buildQuotaStatus, remainingPctFromCounts } from \"../build.js\"\nimport type { QuotaStatus } from \"../types.js\"\n\nexport interface AnthropicHeadersInput {\n profileRef: string\n headers: Headers | Record<string, string | undefined>\n now?: () => Date\n}\n\ninterface Dimension {\n id: string\n header: string\n unit: string\n}\n\nconst DIMENSIONS: readonly Dimension[] = [\n { id: \"requests-1m\", header: \"requests\", unit: \"requests\" },\n { id: \"tokens-1m\", header: \"tokens\", unit: \"tokens\" },\n { id: \"input-tokens-1m\", header: \"input-tokens\", unit: \"tokens\" },\n { id: \"output-tokens-1m\", header: \"output-tokens\", unit: \"tokens\" },\n]\n\n/** Read a header case-insensitively from a `Headers` or a plain record. */\nfunction headerGet(\n headers: Headers | Record<string, string | undefined>,\n name: string,\n): string | undefined {\n if (headers instanceof Headers) {\n return headers.get(name) ?? undefined\n }\n const lower = name.toLowerCase()\n for (const [key, value] of Object.entries(headers)) {\n if (key.toLowerCase() === lower) return value\n }\n return undefined\n}\n\n/** Parse a numeric rate-limit header; empty/garbage → undefined. */\nfunction parseCount(raw: string | undefined): number | undefined {\n if (raw === undefined) return undefined\n const trimmed = raw.trim()\n if (!trimmed) return undefined\n const n = Number(trimmed)\n return Number.isFinite(n) ? n : undefined\n}\n\n/** Normalize an RFC 3339 reset header to an ISO string; unparseable → undefined. */\nfunction parseReset(raw: string | undefined): string | undefined {\n if (raw === undefined) return undefined\n const trimmed = raw.trim()\n if (!trimmed) return undefined\n const date = new Date(trimmed)\n return Number.isNaN(date.getTime()) ? undefined : date.toISOString()\n}\n\n/**\n * PURE: build a QuotaStatus from Anthropic's DOCUMENTED rate-limit headers\n * `anthropic-ratelimit-{requests,tokens,input-tokens,output-tokens}-{limit,remaining,reset}`.\n * The undocumented `anthropic-ratelimit-unified-*` family is never read.\n * A dimension with a missing/non-numeric limit or remaining is skipped.\n */\nexport function quotaFromAnthropicHeaders(input: AnthropicHeadersInput): QuotaStatus | undefined {\n const windows: Record<string, unknown>[] = []\n for (const dimension of DIMENSIONS) {\n const prefix = `anthropic-ratelimit-${dimension.header}`\n const limit = parseCount(headerGet(input.headers, `${prefix}-limit`))\n const remaining = parseCount(headerGet(input.headers, `${prefix}-remaining`))\n if (limit === undefined || remaining === undefined) continue\n const remainingPct = remainingPctFromCounts(remaining, limit)\n if (remainingPct === undefined) continue\n const resetsAt = parseReset(headerGet(input.headers, `${prefix}-reset`))\n windows.push({\n id: dimension.id,\n remainingPct,\n limit,\n remaining,\n unit: dimension.unit,\n ...(resetsAt !== undefined ? { resetsAt } : {}),\n })\n }\n return buildQuotaStatus({\n profileRef: input.profileRef,\n basis: \"provider\",\n source: \"anthropic:ratelimit-headers\",\n now: input.now,\n windows,\n })\n}\n","import { buildQuotaStatus } from \"../build.js\"\nimport type { QuotaCredits, QuotaStatus, QuotaWindow } from \"../types.js\"\n\n/** One side of a Codex `rate_limits` payload. */\nexport interface CodexRateLimitWindow {\n used_percent: number\n window_minutes?: number\n /** Epoch seconds. */\n reset_at?: number\n}\n\nexport interface CodexRateLimits {\n primary?: CodexRateLimitWindow\n secondary?: CodexRateLimitWindow\n}\n\nexport interface CodexCredits {\n has_credits?: boolean\n unlimited?: boolean\n balance?: number\n}\n\nexport interface CodexRateLimitsInput {\n profileRef: string\n rateLimits: CodexRateLimits\n credits?: CodexCredits\n now?: () => Date\n}\n\n/** remainingPct clamped to 0..100; a used percentage >100 maps to 0, kept. */\nfunction remainingPct(used: number): number {\n return Math.max(0, Math.min(100, 100 - used))\n}\n\nfunction windowFromSide(id: string, side: CodexRateLimitWindow | undefined): QuotaWindow | undefined {\n if (!side || typeof side.used_percent !== \"number\" || !Number.isFinite(side.used_percent)) {\n return undefined\n }\n const window: QuotaWindow = { id, remainingPct: remainingPct(side.used_percent) }\n if (typeof side.reset_at === \"number\" && Number.isFinite(side.reset_at)) {\n const date = new Date(side.reset_at * 1000)\n if (!Number.isNaN(date.getTime())) window.resetsAt = date.toISOString()\n }\n return window\n}\n\n/**\n * Pure parser for a Codex `rate_limits` payload: each present side becomes one\n * window and optional credits become a credits block. Never throws; a missing\n * or non-numeric field is dropped, a valid value (0 included) is kept.\n */\nexport function quotaFromCodexRateLimits(\n input: CodexRateLimitsInput,\n): QuotaStatus | undefined {\n try {\n const windows: QuotaWindow[] = []\n const primary = windowFromSide(\"primary\", input.rateLimits?.primary)\n if (primary) windows.push(primary)\n const secondary = windowFromSide(\"secondary\", input.rateLimits?.secondary)\n if (secondary) windows.push(secondary)\n\n let credits: QuotaCredits | undefined\n const raw = input.credits\n if (raw) {\n const hasBalance = typeof raw.balance === \"number\" && Number.isFinite(raw.balance)\n if (raw.unlimited === true || hasBalance) {\n credits = { currency: \"USD\" }\n if (raw.unlimited === true) credits.unlimited = true\n if (hasBalance) credits.balanceUsd = raw.balance\n }\n }\n\n return buildQuotaStatus({\n profileRef: input.profileRef,\n basis: \"provider\",\n source: \"codex:rate_limits\",\n now: input.now,\n windows,\n ...(credits !== undefined ? { credits } : {}),\n })\n } catch {\n return undefined\n }\n}\n","import { z } from \"zod\"\nimport { buildQuotaStatus } from \"../build.js\"\nimport { defineQuotaReader } from \"../registry.js\"\nimport type { QuotaProfile, QuotaReadContext, QuotaReader, QuotaStatus } from \"../types.js\"\n\nconst DEFAULT_BASE_URL = \"https://api.moonshot.ai\"\nconst DEFAULT_TIMEOUT_MS = 4000\n\nconst MOONSHOT_BALANCE_SCHEMA = z\n .object({\n data: z\n .object({\n available_balance: z.number(),\n })\n .passthrough(),\n })\n .passthrough()\n\nexport interface MoonshotQuotaReaderOptions {\n baseUrl?: string\n timeoutMs?: number\n}\n\n/** GET `${baseUrl}/v1/users/me/balance` and parse the body, never throwing. */\nasync function getBalanceJson(\n baseUrl: string,\n secret: string,\n ctx: QuotaReadContext,\n timeoutMs: number,\n): Promise<unknown | undefined> {\n const controller = new AbortController()\n const timer = setTimeout(() => controller.abort(), timeoutMs)\n const onAbort = () => controller.abort()\n ctx.signal?.addEventListener(\"abort\", onAbort)\n try {\n const res = await (ctx.fetchImpl ?? fetch)(`${baseUrl}/v1/users/me/balance`, {\n method: \"GET\",\n headers: { authorization: `Bearer ${secret}` },\n signal: controller.signal,\n })\n if (!res.ok) return undefined\n return await res.json()\n } catch {\n return undefined\n } finally {\n clearTimeout(timer)\n ctx.signal?.removeEventListener(\"abort\", onAbort)\n }\n}\n\n/**\n * Moonshot balance reader: `GET /v1/users/me/balance` exposes the account's\n * available USD balance, but no cap, so it is reported as credits-only (no\n * window). The whole read never throws; a balance of 0 or below is real data\n * and is kept as is.\n */\nexport function createMoonshotQuotaReader(opts?: MoonshotQuotaReaderOptions): QuotaReader {\n const baseUrl = opts?.baseUrl ?? DEFAULT_BASE_URL\n const timeoutMs = opts?.timeoutMs ?? DEFAULT_TIMEOUT_MS\n\n return defineQuotaReader({\n id: \"moonshot-balance\",\n supports: (profile: QuotaProfile): boolean =>\n profile.endpoint === \"moonshot\" && profile.method === \"api-key\",\n read: async (\n profile: QuotaProfile,\n ctx: QuotaReadContext,\n ): Promise<QuotaStatus | undefined> => {\n try {\n if (!profile.credentialRef) return undefined\n if (!ctx.resolveCredential) return undefined\n const secret = await ctx.resolveCredential(profile.credentialRef).catch(() => undefined)\n if (!secret) return undefined\n\n const body = await getBalanceJson(baseUrl, secret, ctx, timeoutMs)\n if (body === undefined) return undefined\n const parsed = MOONSHOT_BALANCE_SCHEMA.safeParse(body)\n if (!parsed.success) return undefined\n\n const balance = parsed.data.data.available_balance\n if (!Number.isFinite(balance)) return undefined\n\n return buildQuotaStatus({\n profileRef: profile.profileRef,\n basis: \"provider\",\n source: \"moonshot:/users/me/balance\",\n now: ctx.now,\n windows: [],\n credits: { balanceUsd: balance, currency: \"USD\" },\n })\n } catch {\n return undefined\n }\n },\n })\n}\n","import { z } from \"zod\"\nimport { buildQuotaStatus, remainingPctFromCounts } from \"../build.js\"\nimport { defineQuotaReader } from \"../registry.js\"\nimport type { QuotaCredits, QuotaProfile, QuotaReadContext, QuotaReader, QuotaStatus, QuotaWindow } from \"../types.js\"\n\nconst DEFAULT_BASE_URL = \"https://openrouter.ai/api/v1\"\nconst DEFAULT_TIMEOUT_MS = 4000\n\nconst OPENROUTER_KEY_SCHEMA = z\n .object({\n data: z\n .object({\n limit: z.number().nullable().optional(),\n limit_remaining: z.number().nullable().optional(),\n })\n .passthrough(),\n })\n .passthrough()\n\nexport interface OpenRouterQuotaReaderOptions {\n baseUrl?: string\n timeoutMs?: number\n}\n\n/** GET `${baseUrl}/key` and parse the body, never throwing. */\nasync function getKeyJson(\n baseUrl: string,\n secret: string,\n ctx: QuotaReadContext,\n timeoutMs: number,\n): Promise<unknown | undefined> {\n const controller = new AbortController()\n const timer = setTimeout(() => controller.abort(), timeoutMs)\n const onAbort = () => controller.abort()\n ctx.signal?.addEventListener(\"abort\", onAbort)\n try {\n const res = await (ctx.fetchImpl ?? fetch)(`${baseUrl}/key`, {\n method: \"GET\",\n headers: { authorization: `Bearer ${secret}` },\n signal: controller.signal,\n })\n if (!res.ok) return undefined\n return await res.json()\n } catch {\n return undefined\n } finally {\n clearTimeout(timer)\n ctx.signal?.removeEventListener(\"abort\", onAbort)\n }\n}\n\n/**\n * OpenRouter quota reader: `GET /key` exposes the key's USD cap and remaining\n * headroom. The whole read is wrapped so it never throws; an uncapped key\n * (`limit_remaining` null/absent) yields no window and no credits.\n */\nexport function createOpenRouterQuotaReader(opts?: OpenRouterQuotaReaderOptions): QuotaReader {\n const baseUrl = opts?.baseUrl ?? DEFAULT_BASE_URL\n const timeoutMs = opts?.timeoutMs ?? DEFAULT_TIMEOUT_MS\n\n return defineQuotaReader({\n id: \"openrouter-key\",\n supports: (profile: QuotaProfile): boolean =>\n profile.endpoint === \"openrouter\" && profile.method === \"api-key\",\n read: async (\n profile: QuotaProfile,\n ctx: QuotaReadContext,\n ): Promise<QuotaStatus | undefined> => {\n try {\n if (!profile.credentialRef) return undefined\n if (!ctx.resolveCredential) return undefined\n const secret = await ctx.resolveCredential(profile.credentialRef).catch(() => undefined)\n if (!secret) return undefined\n\n const body = await getKeyJson(baseUrl, secret, ctx, timeoutMs)\n if (body === undefined) return undefined\n const parsed = OPENROUTER_KEY_SCHEMA.safeParse(body)\n if (!parsed.success) return undefined\n\n const { limit, limit_remaining } = parsed.data.data\n const windows: QuotaWindow[] = []\n if (\n typeof limit === \"number\" &&\n Number.isFinite(limit) &&\n typeof limit_remaining === \"number\" &&\n Number.isFinite(limit_remaining)\n ) {\n const remainingPct = remainingPctFromCounts(limit_remaining, limit)\n if (remainingPct !== undefined) {\n windows.push({\n id: \"key_limit\",\n remainingPct,\n remaining: limit_remaining,\n limit,\n unit: \"USD\",\n })\n }\n }\n let credits: QuotaCredits | undefined\n if (typeof limit_remaining === \"number\" && Number.isFinite(limit_remaining)) {\n credits = { balanceUsd: limit_remaining, currency: \"USD\" }\n }\n\n return buildQuotaStatus({\n profileRef: profile.profileRef,\n basis: \"provider\",\n source: \"openrouter:/key\",\n now: ctx.now,\n windows,\n ...(credits !== undefined ? { credits } : {}),\n })\n } catch {\n return undefined\n }\n },\n })\n}\n","/**\n * @agentproto/model-intel — model intelligence primitives.\n *\n * Unified, provider-neutral quota status (AIP-67): one QuotaStatus shape with a\n * mandatory `basis`, a never-throw QuotaReader contract, and a reader registry.\n * Unknown is omitted, never fabricated.\n *\n * @see https://agentproto.sh/docs/aip-67\n */\n\nexport const SPEC_NAME = \"quota/v1\" as const\nexport const SPEC_VERSION = \"1.0.0-alpha\" as const\n\nexport {\n QUOTA_BASES,\n quotaCreditsSchema,\n quotaStatusSchema,\n quotaWindowSchema,\n} from \"./types.js\"\nexport type {\n QuotaBasis,\n QuotaCredits,\n QuotaProfile,\n QuotaReadContext,\n QuotaReader,\n QuotaStatus,\n QuotaWindow,\n} from \"./types.js\"\nexport {\n buildQuotaStatus,\n remainingPctFromCounts,\n remainingPctFromUsed,\n} from \"./build.js\"\nexport type { BuildQuotaStatusInput } from \"./build.js\"\nexport { QuotaReaderRegistry, defineQuotaReader } from \"./registry.js\"\nexport { quotaFromAnthropicHeaders } from \"./readers/anthropic.js\"\nexport { quotaFromCodexRateLimits } from \"./readers/codex.js\"\nexport type {\n CodexCredits,\n CodexRateLimits,\n CodexRateLimitsInput,\n CodexRateLimitWindow,\n} from \"./readers/codex.js\"\nexport { createMoonshotQuotaReader } from \"./readers/moonshot.js\"\nexport { createOpenRouterQuotaReader } from \"./readers/openrouter.js\"\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/types.ts","../src/build.ts","../src/registry.ts","../src/catalog-diff.ts","../src/readers/anthropic.ts","../src/readers/codex.ts","../src/readers/moonshot.ts","../src/readers/openrouter.ts","../src/index.ts"],"names":["remainingPct","z","DEFAULT_BASE_URL","DEFAULT_TIMEOUT_MS"],"mappings":"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","file":"index.mjs","sourcesContent":["import { z } from \"zod\"\n\n/**\n * Where a quota figure came from. `provider` is a documented provider\n * surface; `observed` is derived from this host's own traffic; `estimated`\n * is a local computation; `probe` is an undocumented surface reached through\n * a connector and MUST NOT be emitted by core readers.\n */\nexport const QUOTA_BASES = [\"provider\", \"probe\", \"observed\", \"estimated\"] as const\nexport type QuotaBasis = (typeof QUOTA_BASES)[number]\n\nconst isoTimestamp = z.string().refine((s) => !Number.isNaN(Date.parse(s)), {\n message: \"expected an ISO-8601 timestamp\",\n})\n\nexport const quotaWindowSchema = z.object({\n /** Open string: \"5h\", \"7d\", \"month\", \"minute\", \"day\", \"primary\", \"key_limit\", ... */\n id: z.string().min(1),\n /** 0-100. Required: a window that cannot state it is not emitted. */\n remainingPct: z.number().min(0).max(100),\n used: z.number().optional(),\n limit: z.number().optional(),\n remaining: z.number().optional(),\n unit: z.string().optional(),\n resetsAt: isoTimestamp.optional(),\n})\nexport type QuotaWindow = z.infer<typeof quotaWindowSchema>\n\nexport const quotaCreditsSchema = z.object({\n balanceUsd: z.number().optional(),\n unlimited: z.boolean().optional(),\n currency: z.string().min(1),\n})\nexport type QuotaCredits = z.infer<typeof quotaCreditsSchema>\n\nexport const quotaStatusSchema = z.object({\n profileRef: z.string().min(1),\n asOf: isoTimestamp,\n basis: z.enum(QUOTA_BASES),\n source: z.string().optional(),\n windows: z.array(quotaWindowSchema),\n credits: quotaCreditsSchema.optional(),\n})\nexport type QuotaStatus = z.infer<typeof quotaStatusSchema>\n\n/** The minimal auth-profile projection a reader needs. */\nexport interface QuotaProfile {\n profileRef: string\n endpoint: string\n method: \"oauth-bearer\" | \"api-key\"\n credentialRef?: string\n}\n\nexport interface QuotaReadContext {\n signal?: AbortSignal\n /** Resolve a profile's opaque credentialRef to a bearer secret. */\n resolveCredential?(ref: string): Promise<string | undefined>\n fetchImpl?: typeof fetch\n now?: () => Date\n}\n\n/**\n * Reads quota for a profile. MUST NOT throw: any failure or unknown value\n * resolves to `undefined`. Never fabricates a figure.\n */\nexport interface QuotaReader {\n readonly id: string\n supports(profile: QuotaProfile): boolean\n read(profile: QuotaProfile, ctx: QuotaReadContext): Promise<QuotaStatus | undefined>\n}\n","import type { QuotaBasis, QuotaCredits, QuotaStatus, QuotaWindow } from \"./types.js\"\nimport { quotaWindowSchema } from \"./types.js\"\n\n/** remainingPct from a used percentage, or undefined when it is not a finite 0-100 number. */\nexport function remainingPctFromUsed(usedPct: number | null | undefined): number | undefined {\n if (usedPct === null || usedPct === undefined || !Number.isFinite(usedPct)) return undefined\n if (usedPct < 0 || usedPct > 100) return undefined\n return 100 - usedPct\n}\n\n/** remainingPct from remaining/limit counts, or undefined when limit is not a positive finite number. */\nexport function remainingPctFromCounts(\n remaining: number | null | undefined,\n limit: number | null | undefined,\n): number | undefined {\n if (remaining === null || remaining === undefined || limit === null || limit === undefined) {\n return undefined\n }\n if (!Number.isFinite(remaining) || !Number.isFinite(limit) || limit <= 0) return undefined\n if (remaining < 0 || remaining > limit) return undefined\n return (remaining / limit) * 100\n}\n\nexport interface BuildQuotaStatusInput {\n profileRef: string\n basis: QuotaBasis\n source?: string\n now?: () => Date\n /** Candidate windows; any that fail validation are dropped, never repaired. */\n windows?: readonly unknown[]\n credits?: QuotaCredits\n}\n\n/**\n * Assemble a QuotaStatus. Invalid windows are omitted. Returns `undefined`\n * when there is nothing to report (no valid window and no credits).\n */\nexport function buildQuotaStatus(input: BuildQuotaStatusInput): QuotaStatus | undefined {\n const windows: QuotaWindow[] = []\n for (const candidate of input.windows ?? []) {\n const parsed = quotaWindowSchema.safeParse(candidate)\n if (parsed.success) windows.push(parsed.data)\n }\n if (windows.length === 0 && input.credits === undefined) return undefined\n return {\n profileRef: input.profileRef,\n asOf: (input.now ?? (() => new Date()))().toISOString(),\n basis: input.basis,\n ...(input.source !== undefined ? { source: input.source } : {}),\n windows,\n ...(input.credits !== undefined ? { credits: input.credits } : {}),\n }\n}\n","import type { QuotaProfile, QuotaReadContext, QuotaReader, QuotaStatus } from \"./types.js\"\n\n/** Identity helper that fixes the QuotaReader contract at definition time (AIP-43 handle). */\nexport function defineQuotaReader(reader: QuotaReader): QuotaReader {\n return reader\n}\n\nexport class QuotaReaderRegistry {\n private readonly readers = new Map<string, QuotaReader>()\n\n register(reader: QuotaReader): this {\n if (this.readers.has(reader.id)) {\n throw new Error(`quota reader already registered: ${reader.id}`)\n }\n this.readers.set(reader.id, reader)\n return this\n }\n\n list(): QuotaReader[] {\n return [...this.readers.values()]\n }\n\n /** First registered reader that supports the profile, in registration order. */\n find(profile: QuotaProfile): QuotaReader | undefined {\n for (const reader of this.readers.values()) {\n if (safeSupports(reader, profile)) return reader\n }\n return undefined\n }\n\n /** Read through the first supporting reader. Never throws; unknown -> undefined. */\n async read(profile: QuotaProfile, ctx: QuotaReadContext = {}): Promise<QuotaStatus | undefined> {\n const reader = this.find(profile)\n if (!reader) return undefined\n try {\n return await reader.read(profile, ctx)\n } catch {\n return undefined\n }\n }\n}\n\nfunction safeSupports(reader: QuotaReader, profile: QuotaProfile): boolean {\n try {\n return reader.supports(profile)\n } catch {\n return false\n }\n}\n","/**\n * Catalog watch domain: pure diff of two catalog snapshots.\n * No I/O — mirrors the pure-evaluator pattern of `build.ts`.\n */\n\nexport interface CatalogSnapshotEntry {\n /** Catalog model id, e.g. \"claude-sonnet-5-5\". The snapshot's own key. */\n id: string\n inputPer1M: number\n outputPer1M: number\n /** Derived: true when inputPer1M === 0 && outputPer1M === 0. */\n free: boolean\n}\n\n/** Keyed by CatalogSnapshotEntry.id. */\nexport type CatalogSnapshot = Record<string, CatalogSnapshotEntry>\n\nexport interface CatalogPriceChange {\n id: string\n prev: Pick<CatalogSnapshotEntry, \"inputPer1M\" | \"outputPer1M\">\n next: Pick<CatalogSnapshotEntry, \"inputPer1M\" | \"outputPer1M\">\n}\n\nexport interface CatalogDiff {\n /** ids present in `next`, absent from `prev`. */\n appeared: string[]\n /** ids present in `prev`, absent from `next`. */\n removed: string[]\n /** same id in both, inputPer1M or outputPer1M differs. Price-only — an id\n * in `appeared`/`removed` never also appears here. */\n priceChanged: CatalogPriceChange[]\n /** id was free in prev, not free in next (and still present in both —\n * already counted in priceChanged too if the price itself differs). */\n freeToPaid: string[]\n /** id was not free in prev, free in next. */\n paidToFree: string[]\n}\n\nfunction isFree(entry: CatalogSnapshotEntry): boolean {\n return entry.inputPer1M === 0 && entry.outputPer1M === 0\n}\n\nexport function diffCatalog(prev: CatalogSnapshot, next: CatalogSnapshot): CatalogDiff {\n const appeared: string[] = []\n const removed: string[] = []\n const priceChanged: CatalogPriceChange[] = []\n const freeToPaid: string[] = []\n const paidToFree: string[] = []\n\n for (const id of Object.keys(next)) {\n if (!(id in prev)) appeared.push(id)\n }\n for (const id of Object.keys(prev)) {\n if (!(id in next)) removed.push(id)\n }\n\n for (const id of Object.keys(next)) {\n const before = prev[id]\n // Object.keys(next) guarantees `after` exists; the guard satisfies noUncheckedIndexedAccess.\n const after = next[id]\n if (!before || !after) continue\n if (before.inputPer1M !== after.inputPer1M || before.outputPer1M !== after.outputPer1M) {\n priceChanged.push({\n id,\n prev: { inputPer1M: before.inputPer1M, outputPer1M: before.outputPer1M },\n next: { inputPer1M: after.inputPer1M, outputPer1M: after.outputPer1M },\n })\n }\n const wasFree = isFree(before)\n const isNowFree = isFree(after)\n if (wasFree && !isNowFree) freeToPaid.push(id)\n if (!wasFree && isNowFree) paidToFree.push(id)\n }\n\n return {\n appeared: appeared.sort(),\n removed: removed.sort(),\n priceChanged: priceChanged.sort((a, b) => (a.id < b.id ? -1 : a.id > b.id ? 1 : 0)),\n freeToPaid: freeToPaid.sort(),\n paidToFree: paidToFree.sort(),\n }\n}\n","import { buildQuotaStatus, remainingPctFromCounts } from \"../build.js\"\nimport type { QuotaStatus } from \"../types.js\"\n\nexport interface AnthropicHeadersInput {\n profileRef: string\n headers: Headers | Record<string, string | undefined>\n now?: () => Date\n}\n\ninterface Dimension {\n id: string\n header: string\n unit: string\n}\n\nconst DIMENSIONS: readonly Dimension[] = [\n { id: \"requests-1m\", header: \"requests\", unit: \"requests\" },\n { id: \"tokens-1m\", header: \"tokens\", unit: \"tokens\" },\n { id: \"input-tokens-1m\", header: \"input-tokens\", unit: \"tokens\" },\n { id: \"output-tokens-1m\", header: \"output-tokens\", unit: \"tokens\" },\n]\n\n/** Read a header case-insensitively from a `Headers` or a plain record. */\nfunction headerGet(\n headers: Headers | Record<string, string | undefined>,\n name: string,\n): string | undefined {\n if (headers instanceof Headers) {\n return headers.get(name) ?? undefined\n }\n const lower = name.toLowerCase()\n for (const [key, value] of Object.entries(headers)) {\n if (key.toLowerCase() === lower) return value\n }\n return undefined\n}\n\n/** Parse a numeric rate-limit header; empty/garbage → undefined. */\nfunction parseCount(raw: string | undefined): number | undefined {\n if (raw === undefined) return undefined\n const trimmed = raw.trim()\n if (!trimmed) return undefined\n const n = Number(trimmed)\n return Number.isFinite(n) ? n : undefined\n}\n\n/** Normalize an RFC 3339 reset header to an ISO string; unparseable → undefined. */\nfunction parseReset(raw: string | undefined): string | undefined {\n if (raw === undefined) return undefined\n const trimmed = raw.trim()\n if (!trimmed) return undefined\n const date = new Date(trimmed)\n return Number.isNaN(date.getTime()) ? undefined : date.toISOString()\n}\n\n/**\n * PURE: build a QuotaStatus from Anthropic's DOCUMENTED rate-limit headers\n * `anthropic-ratelimit-{requests,tokens,input-tokens,output-tokens}-{limit,remaining,reset}`.\n * The undocumented `anthropic-ratelimit-unified-*` family is never read.\n * A dimension with a missing/non-numeric limit or remaining is skipped.\n */\nexport function quotaFromAnthropicHeaders(input: AnthropicHeadersInput): QuotaStatus | undefined {\n const windows: Record<string, unknown>[] = []\n for (const dimension of DIMENSIONS) {\n const prefix = `anthropic-ratelimit-${dimension.header}`\n const limit = parseCount(headerGet(input.headers, `${prefix}-limit`))\n const remaining = parseCount(headerGet(input.headers, `${prefix}-remaining`))\n if (limit === undefined || remaining === undefined) continue\n const remainingPct = remainingPctFromCounts(remaining, limit)\n if (remainingPct === undefined) continue\n const resetsAt = parseReset(headerGet(input.headers, `${prefix}-reset`))\n windows.push({\n id: dimension.id,\n remainingPct,\n limit,\n remaining,\n unit: dimension.unit,\n ...(resetsAt !== undefined ? { resetsAt } : {}),\n })\n }\n return buildQuotaStatus({\n profileRef: input.profileRef,\n basis: \"provider\",\n source: \"anthropic:ratelimit-headers\",\n now: input.now,\n windows,\n })\n}\n","import { buildQuotaStatus } from \"../build.js\"\nimport type { QuotaCredits, QuotaStatus, QuotaWindow } from \"../types.js\"\n\n/** One side of a Codex `rate_limits` payload. */\nexport interface CodexRateLimitWindow {\n used_percent: number\n window_minutes?: number\n /** Epoch seconds. */\n reset_at?: number\n}\n\nexport interface CodexRateLimits {\n primary?: CodexRateLimitWindow\n secondary?: CodexRateLimitWindow\n}\n\nexport interface CodexCredits {\n has_credits?: boolean\n unlimited?: boolean\n balance?: number\n}\n\nexport interface CodexRateLimitsInput {\n profileRef: string\n rateLimits: CodexRateLimits\n credits?: CodexCredits\n now?: () => Date\n}\n\n/** remainingPct clamped to 0..100; a used percentage >100 maps to 0, kept. */\nfunction remainingPct(used: number): number {\n return Math.max(0, Math.min(100, 100 - used))\n}\n\nfunction windowFromSide(id: string, side: CodexRateLimitWindow | undefined): QuotaWindow | undefined {\n if (!side || typeof side.used_percent !== \"number\" || !Number.isFinite(side.used_percent)) {\n return undefined\n }\n const window: QuotaWindow = { id, remainingPct: remainingPct(side.used_percent) }\n if (typeof side.reset_at === \"number\" && Number.isFinite(side.reset_at)) {\n const date = new Date(side.reset_at * 1000)\n if (!Number.isNaN(date.getTime())) window.resetsAt = date.toISOString()\n }\n return window\n}\n\n/**\n * Pure parser for a Codex `rate_limits` payload: each present side becomes one\n * window and optional credits become a credits block. Never throws; a missing\n * or non-numeric field is dropped, a valid value (0 included) is kept.\n */\nexport function quotaFromCodexRateLimits(\n input: CodexRateLimitsInput,\n): QuotaStatus | undefined {\n try {\n const windows: QuotaWindow[] = []\n const primary = windowFromSide(\"primary\", input.rateLimits?.primary)\n if (primary) windows.push(primary)\n const secondary = windowFromSide(\"secondary\", input.rateLimits?.secondary)\n if (secondary) windows.push(secondary)\n\n let credits: QuotaCredits | undefined\n const raw = input.credits\n if (raw) {\n const hasBalance = typeof raw.balance === \"number\" && Number.isFinite(raw.balance)\n if (raw.unlimited === true || hasBalance) {\n credits = { currency: \"USD\" }\n if (raw.unlimited === true) credits.unlimited = true\n if (hasBalance) credits.balanceUsd = raw.balance\n }\n }\n\n return buildQuotaStatus({\n profileRef: input.profileRef,\n basis: \"provider\",\n source: \"codex:rate_limits\",\n now: input.now,\n windows,\n ...(credits !== undefined ? { credits } : {}),\n })\n } catch {\n return undefined\n }\n}\n","import { z } from \"zod\"\nimport { buildQuotaStatus } from \"../build.js\"\nimport { defineQuotaReader } from \"../registry.js\"\nimport type { QuotaProfile, QuotaReadContext, QuotaReader, QuotaStatus } from \"../types.js\"\n\nconst DEFAULT_BASE_URL = \"https://api.moonshot.ai\"\nconst DEFAULT_TIMEOUT_MS = 4000\n\nconst MOONSHOT_BALANCE_SCHEMA = z\n .object({\n data: z\n .object({\n available_balance: z.number(),\n })\n .passthrough(),\n })\n .passthrough()\n\nexport interface MoonshotQuotaReaderOptions {\n baseUrl?: string\n timeoutMs?: number\n}\n\n/** GET `${baseUrl}/v1/users/me/balance` and parse the body, never throwing. */\nasync function getBalanceJson(\n baseUrl: string,\n secret: string,\n ctx: QuotaReadContext,\n timeoutMs: number,\n): Promise<unknown | undefined> {\n const controller = new AbortController()\n const timer = setTimeout(() => controller.abort(), timeoutMs)\n const onAbort = () => controller.abort()\n ctx.signal?.addEventListener(\"abort\", onAbort)\n try {\n const res = await (ctx.fetchImpl ?? fetch)(`${baseUrl}/v1/users/me/balance`, {\n method: \"GET\",\n headers: { authorization: `Bearer ${secret}` },\n signal: controller.signal,\n })\n if (!res.ok) return undefined\n return await res.json()\n } catch {\n return undefined\n } finally {\n clearTimeout(timer)\n ctx.signal?.removeEventListener(\"abort\", onAbort)\n }\n}\n\n/**\n * Moonshot balance reader: `GET /v1/users/me/balance` exposes the account's\n * available USD balance, but no cap, so it is reported as credits-only (no\n * window). The whole read never throws; a balance of 0 or below is real data\n * and is kept as is.\n */\nexport function createMoonshotQuotaReader(opts?: MoonshotQuotaReaderOptions): QuotaReader {\n const baseUrl = opts?.baseUrl ?? DEFAULT_BASE_URL\n const timeoutMs = opts?.timeoutMs ?? DEFAULT_TIMEOUT_MS\n\n return defineQuotaReader({\n id: \"moonshot-balance\",\n supports: (profile: QuotaProfile): boolean =>\n profile.endpoint === \"moonshot\" && profile.method === \"api-key\",\n read: async (\n profile: QuotaProfile,\n ctx: QuotaReadContext,\n ): Promise<QuotaStatus | undefined> => {\n try {\n if (!profile.credentialRef) return undefined\n if (!ctx.resolveCredential) return undefined\n const secret = await ctx.resolveCredential(profile.credentialRef).catch(() => undefined)\n if (!secret) return undefined\n\n const body = await getBalanceJson(baseUrl, secret, ctx, timeoutMs)\n if (body === undefined) return undefined\n const parsed = MOONSHOT_BALANCE_SCHEMA.safeParse(body)\n if (!parsed.success) return undefined\n\n const balance = parsed.data.data.available_balance\n if (!Number.isFinite(balance)) return undefined\n\n return buildQuotaStatus({\n profileRef: profile.profileRef,\n basis: \"provider\",\n source: \"moonshot:/users/me/balance\",\n now: ctx.now,\n windows: [],\n credits: { balanceUsd: balance, currency: \"USD\" },\n })\n } catch {\n return undefined\n }\n },\n })\n}\n","import { z } from \"zod\"\nimport { buildQuotaStatus, remainingPctFromCounts } from \"../build.js\"\nimport { defineQuotaReader } from \"../registry.js\"\nimport type { QuotaCredits, QuotaProfile, QuotaReadContext, QuotaReader, QuotaStatus, QuotaWindow } from \"../types.js\"\n\nconst DEFAULT_BASE_URL = \"https://openrouter.ai/api/v1\"\nconst DEFAULT_TIMEOUT_MS = 4000\n\nconst OPENROUTER_KEY_SCHEMA = z\n .object({\n data: z\n .object({\n limit: z.number().nullable().optional(),\n limit_remaining: z.number().nullable().optional(),\n })\n .passthrough(),\n })\n .passthrough()\n\nexport interface OpenRouterQuotaReaderOptions {\n baseUrl?: string\n timeoutMs?: number\n}\n\n/** GET `${baseUrl}/key` and parse the body, never throwing. */\nasync function getKeyJson(\n baseUrl: string,\n secret: string,\n ctx: QuotaReadContext,\n timeoutMs: number,\n): Promise<unknown | undefined> {\n const controller = new AbortController()\n const timer = setTimeout(() => controller.abort(), timeoutMs)\n const onAbort = () => controller.abort()\n ctx.signal?.addEventListener(\"abort\", onAbort)\n try {\n const res = await (ctx.fetchImpl ?? fetch)(`${baseUrl}/key`, {\n method: \"GET\",\n headers: { authorization: `Bearer ${secret}` },\n signal: controller.signal,\n })\n if (!res.ok) return undefined\n return await res.json()\n } catch {\n return undefined\n } finally {\n clearTimeout(timer)\n ctx.signal?.removeEventListener(\"abort\", onAbort)\n }\n}\n\n/**\n * OpenRouter quota reader: `GET /key` exposes the key's USD cap and remaining\n * headroom. The whole read is wrapped so it never throws; an uncapped key\n * (`limit_remaining` null/absent) yields no window and no credits.\n */\nexport function createOpenRouterQuotaReader(opts?: OpenRouterQuotaReaderOptions): QuotaReader {\n const baseUrl = opts?.baseUrl ?? DEFAULT_BASE_URL\n const timeoutMs = opts?.timeoutMs ?? DEFAULT_TIMEOUT_MS\n\n return defineQuotaReader({\n id: \"openrouter-key\",\n supports: (profile: QuotaProfile): boolean =>\n profile.endpoint === \"openrouter\" && profile.method === \"api-key\",\n read: async (\n profile: QuotaProfile,\n ctx: QuotaReadContext,\n ): Promise<QuotaStatus | undefined> => {\n try {\n if (!profile.credentialRef) return undefined\n if (!ctx.resolveCredential) return undefined\n const secret = await ctx.resolveCredential(profile.credentialRef).catch(() => undefined)\n if (!secret) return undefined\n\n const body = await getKeyJson(baseUrl, secret, ctx, timeoutMs)\n if (body === undefined) return undefined\n const parsed = OPENROUTER_KEY_SCHEMA.safeParse(body)\n if (!parsed.success) return undefined\n\n const { limit, limit_remaining } = parsed.data.data\n const windows: QuotaWindow[] = []\n if (\n typeof limit === \"number\" &&\n Number.isFinite(limit) &&\n typeof limit_remaining === \"number\" &&\n Number.isFinite(limit_remaining)\n ) {\n const remainingPct = remainingPctFromCounts(limit_remaining, limit)\n if (remainingPct !== undefined) {\n windows.push({\n id: \"key_limit\",\n remainingPct,\n remaining: limit_remaining,\n limit,\n unit: \"USD\",\n })\n }\n }\n let credits: QuotaCredits | undefined\n if (typeof limit_remaining === \"number\" && Number.isFinite(limit_remaining)) {\n credits = { balanceUsd: limit_remaining, currency: \"USD\" }\n }\n\n return buildQuotaStatus({\n profileRef: profile.profileRef,\n basis: \"provider\",\n source: \"openrouter:/key\",\n now: ctx.now,\n windows,\n ...(credits !== undefined ? { credits } : {}),\n })\n } catch {\n return undefined\n }\n },\n })\n}\n","/**\n * @agentproto/model-intel — model intelligence primitives.\n *\n * Unified, provider-neutral quota status (AIP-67): one QuotaStatus shape with a\n * mandatory `basis`, a never-throw QuotaReader contract, and a reader registry.\n * Unknown is omitted, never fabricated.\n *\n * @see https://agentproto.sh/docs/aip-67\n */\n\nexport const SPEC_NAME = \"quota/v1\" as const\nexport const SPEC_VERSION = \"1.0.0-alpha\" as const\n\nexport {\n QUOTA_BASES,\n quotaCreditsSchema,\n quotaStatusSchema,\n quotaWindowSchema,\n} from \"./types.js\"\nexport type {\n QuotaBasis,\n QuotaCredits,\n QuotaProfile,\n QuotaReadContext,\n QuotaReader,\n QuotaStatus,\n QuotaWindow,\n} from \"./types.js\"\nexport {\n buildQuotaStatus,\n remainingPctFromCounts,\n remainingPctFromUsed,\n} from \"./build.js\"\nexport type { BuildQuotaStatusInput } from \"./build.js\"\nexport { QuotaReaderRegistry, defineQuotaReader } from \"./registry.js\"\n\n/**\n * Catalog watch domain: pure diff of two priced catalog snapshots.\n */\nexport { diffCatalog } from \"./catalog-diff.js\"\nexport type {\n CatalogDiff,\n CatalogPriceChange,\n CatalogSnapshot,\n CatalogSnapshotEntry,\n} from \"./catalog-diff.js\"\nexport { quotaFromAnthropicHeaders } from \"./readers/anthropic.js\"\nexport { quotaFromCodexRateLimits } from \"./readers/codex.js\"\nexport type {\n CodexCredits,\n CodexRateLimits,\n CodexRateLimitsInput,\n CodexRateLimitWindow,\n} from \"./readers/codex.js\"\nexport { createMoonshotQuotaReader } from \"./readers/moonshot.js\"\nexport { createOpenRouterQuotaReader } from \"./readers/openrouter.js\"\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agentproto/model-intel",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "@agentproto/model-intel — model intelligence primitives. Unified, provider-neutral quota status (AIP-67): a QuotaStatus shape with a mandatory basis, a never-throw QuotaReader contract, and a reader registry. Unknown is omitted, never fabricated.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agentproto",
|