@llmnative/react 2.3.0 → 3.0.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.
Files changed (61) hide show
  1. package/README.md +3 -2
  2. package/dist/index.js +20 -21
  3. package/dist/index.js.map +1 -1
  4. package/dist/index.mjs +6768 -6811
  5. package/dist/index.mjs.map +1 -1
  6. package/dist/modelIdentity-CIXNPtTA.mjs +624 -0
  7. package/dist/modelIdentity-CIXNPtTA.mjs.map +1 -0
  8. package/dist/modelIdentity-DX62i7g2.js +3 -0
  9. package/dist/modelIdentity-DX62i7g2.js.map +1 -0
  10. package/dist/server-B430eH0y.mjs +352 -0
  11. package/dist/server-B430eH0y.mjs.map +1 -0
  12. package/dist/server-CDheNgME.js +2 -0
  13. package/dist/server-CDheNgME.js.map +1 -0
  14. package/dist/server.js +1 -1
  15. package/dist/server.mjs +3 -3
  16. package/dist/types/src/App.d.ts +6 -4
  17. package/dist/types/src/I18n.d.ts +2 -0
  18. package/dist/types/src/components/widgets/grid-core/GridCore.d.ts +1 -1
  19. package/dist/types/src/components/widgets/grid-core/types.d.ts +2 -0
  20. package/dist/types/src/components/widgets/grid-core/useGridDBRecords.d.ts +6 -2
  21. package/dist/types/src/index.d.ts +21 -8
  22. package/dist/types/src/libs/index.d.ts +0 -1
  23. package/dist/types/src/providers/ProviderRegistryContext.d.ts +7 -5
  24. package/dist/types/src/providers/ai/server.d.ts +20 -0
  25. package/dist/types/src/providers/api/ApiProvider.d.ts +1 -1
  26. package/dist/types/src/providers/data/DataProvider.d.ts +29 -0
  27. package/dist/types/src/providers/data/firebase.d.ts +2 -2
  28. package/dist/types/src/providers/data/firestore.d.ts +2 -2
  29. package/dist/types/src/providers/data/query.d.ts +14 -0
  30. package/dist/types/src/providers/data/useDataSubscription.d.ts +20 -0
  31. package/dist/types/src/providers/email/EmailError.d.ts +27 -0
  32. package/dist/types/src/providers/email/EmailProviderContext.d.ts +13 -7
  33. package/dist/types/src/providers/email/EmailRead.d.ts +48 -0
  34. package/dist/types/src/providers/email/EmailSend.d.ts +54 -0
  35. package/dist/types/src/providers/email/brevo/BrevoEmailSender.d.ts +21 -0
  36. package/dist/types/src/providers/email/definitions.d.ts +4 -2
  37. package/dist/types/src/providers/email/gmail/GmailEmailReader.d.ts +37 -0
  38. package/dist/types/src/providers/email/gmail/GmailEmailSender.d.ts +21 -0
  39. package/dist/types/src/providers/email/gmail/browser.d.ts +10 -0
  40. package/dist/types/src/providers/email/gmail/gmailApi.d.ts +53 -0
  41. package/dist/types/src/providers/email/gmail/gmailOAuth.d.ts +25 -0
  42. package/dist/types/src/providers/email/imap/ImapEmailReader.d.ts +70 -0
  43. package/dist/types/src/providers/email/resend/ResendEmailSender.d.ts +20 -0
  44. package/dist/types/src/providers/manifest.d.ts +7 -5
  45. package/dist/vite.js +1 -1
  46. package/dist/vite.mjs +1 -1
  47. package/llms-full.txt +1690 -1625
  48. package/llms.txt +83 -82
  49. package/package.json +18 -1
  50. package/dist/modelIdentity-CGWliGDU.mjs +0 -183
  51. package/dist/modelIdentity-CGWliGDU.mjs.map +0 -1
  52. package/dist/modelIdentity-CgRMrC5H.js +0 -2
  53. package/dist/modelIdentity-CgRMrC5H.js.map +0 -1
  54. package/dist/server-CCbjPJEz.js +0 -2
  55. package/dist/server-CCbjPJEz.js.map +0 -1
  56. package/dist/server-CiWwr3Zg.mjs +0 -107
  57. package/dist/server-CiWwr3Zg.mjs.map +0 -1
  58. package/dist/types/src/libs/email.d.ts +0 -16
  59. package/dist/types/src/providers/email/EmailProvider.d.ts +0 -10
  60. package/dist/types/src/providers/email/google/GmailEmailProvider.d.ts +0 -7
  61. package/dist/types/src/providers/email/google/email.d.ts +0 -13
@@ -0,0 +1 @@
1
+ {"version":3,"file":"modelIdentity-CIXNPtTA.mjs","names":["pending","getMissingKeys","keys","value","config","key","prefix","createConfigurationState","missingKeys","provider","getProviderConfigurationState","fallbackName","configured","SERVER_CREDENTIAL_PREFIX","SCOPE_PATTERN","PLACEHOLDER_PATTERN","serverCredential","scope","findServerCredentials","text","placeholder","containsServerCredential","AI_PROVIDER_HOSTS","trimSlashes","path","isAIProviderTarget","connection","base","basePath","target","MODELS_DEV_URL","CACHE_KEY","CACHE_TTL_MS","MODELS_DEV_PROVIDER_KEYS","pending$1","readCache","raw","cached","writeCache","buildPricingIndex","wanted","index","models","catalog","prices","modelId","entry","input","output","loadPricingIndex","response","error","candidateIds","lower","model","undated","lookupModelsDevPricing","providerId","id","hit","withModelsDevPricing","m","pricing","EmailProviderError","message","options","code","emailErrorCode","status","callEmailApi","http","url","init","cause","toEmailAddress","recipient","toEmailAddresses","recipients","address","formatEmailAddress","name","email","prepareEmail","from","params","defaultFrom","sender","fromRequired","to","cc","bcc","GMAIL_API","GOOGLE_TOKEN_URL","GMAIL_SCOPES","PROVIDER","gmailConfigurationState","auth","gmailAccessToken","grant","bytesToBase64","binary","i","bytes","base64","base64Url","fromBase64Url","data","char","CRLF","headerText","headerAddress","partHeaders","type","partBody","body","gmailRawMessage","headers","mail","line","boundary","section","content","partOf","payload","mime","child","found","textOf","toEmailMessage","header","h","arrived","GmailEmailSender","state","token","sent","GMAIL_CONSENT_STEPS","EMAIL_SEND_PROVIDER_DEFINITIONS","EMAIL_READ_PROVIDER_DEFINITIONS","RESEND_EMAIL_URL","ResendEmailSender","list","addresses","BREVO_EMAIL_URL","brevoAddress","BrevoEmailSender","VARIANT_WORDS","DATE_WORD","TRAILING_GROUP","MODE_GROUP","canonicalModelName","rest","modes","group","words","mode","instability","byStability","left","right","buildModelIdentityIndex","providers","providerName","entries","identity","cost","resolveModel","best","modelOffers","pending","loaded","loadModelIdentityIndex","fetchFn"],"sources":["../src/providers/ProviderConfiguration.ts","../src/providers/ai/serverCredentials.ts","../src/providers/ai/modelsDevPricing.ts","../src/providers/email/EmailError.ts","../src/providers/email/EmailSend.ts","../src/providers/email/gmail/gmailApi.ts","../src/providers/email/gmail/GmailEmailSender.ts","../src/providers/email/definitions.ts","../src/providers/email/resend/ResendEmailSender.ts","../src/providers/email/brevo/BrevoEmailSender.ts","../src/providers/ai/modelIdentity.ts"],"sourcesContent":["export interface ProviderConfigurationState {\r\n configured: boolean;\r\n reason?: string;\r\n missingKeys?: string[];\r\n}\r\n\r\nexport interface ProviderConfigurable {\r\n isConfigured?(): boolean;\r\n getConfigurationState?(): ProviderConfigurationState;\r\n /**\r\n * Optional teardown hook. Called by setProvider() before an adapter is replaced\r\n * in the registry — e.g. unsubscribe realtime listeners, sign out, close connections.\r\n * Never called on unmount; only on explicit replacement.\r\n */\r\n dispose?(): Promise<void> | void;\r\n}\r\n\r\nexport const getMissingKeys = (\r\n config: Record<string, unknown> | undefined,\r\n keys: string[],\r\n prefix = ''\r\n): string[] => keys\r\n .filter((key) => {\r\n const value = config?.[key];\r\n return value === undefined || value === null || value === '';\r\n })\r\n .map((key) => `${prefix}${key}`);\r\n\r\nexport const createConfigurationState = (\r\n provider: string,\r\n missingKeys: string[]\r\n): ProviderConfigurationState => ({\r\n configured: missingKeys.length === 0,\r\n missingKeys,\r\n reason: missingKeys.length\r\n ? `${provider} is not configured. Missing: ${missingKeys.join(', ')}.`\r\n : undefined,\r\n});\r\n\r\nexport const getProviderConfigurationState = (\r\n provider: ProviderConfigurable | null | undefined,\r\n fallbackName = 'Provider'\r\n): ProviderConfigurationState => {\r\n if (!provider) {\r\n return {\r\n configured: false,\r\n reason: `${fallbackName} is not registered.`,\r\n };\r\n }\r\n\r\n if (provider.getConfigurationState) {\r\n return provider.getConfigurationState();\r\n }\r\n\r\n if (provider.isConfigured) {\r\n const configured = provider.isConfigured();\r\n return {\r\n configured,\r\n reason: configured ? undefined : `${fallbackName} is not configured.`,\r\n };\r\n }\r\n\r\n return { configured: true };\r\n};\r\n","// Server-held AI credentials (CR-102). An app that cannot hold provider keys in the browser builds\n// its adapters with a placeholder where the key goes; its proxy swaps the placeholder for the real\n// key, only towards a host of that provider. Pure module: no React, no config — the browser\n// registry, proxyFetch and the Node entry (`@llmnative/react/server`) all share it.\n\nexport const SERVER_CREDENTIAL_PREFIX = 'llmnative-server-credential';\n\nconst SCOPE_PATTERN = /^[A-Za-z0-9_-]+$/;\nconst PLACEHOLDER_PATTERN = /llmnative-server-credential:([A-Za-z0-9_-]+):([a-z-]+)/g;\n\n/** A provider connected on the server: its id, and what its URLs are built from. Never the key. */\nexport type ServerAIConnection = {\n id: string;\n /** Cloudflare: the account in every Workers AI URL. */\n accountId?: string;\n /** openai-compatible: the endpoint. */\n baseUrl?: string;\n};\n\n/** The placeholder an adapter writes where the key goes: `llmnative-server-credential:<scope>:<provider>`. */\nexport const serverCredential = (scope: string, provider: string): string => {\n if (!SCOPE_PATTERN.test(scope)) throw new Error(`Server credential scope \"${scope}\" may only contain letters, digits, \"-\" and \"_\".`);\n return `${SERVER_CREDENTIAL_PREFIX}:${scope}:${provider}`;\n};\n\n/** Every placeholder in `text`, as scope and provider. */\nexport const findServerCredentials = (text: string): Array<{ placeholder: string; scope: string; provider: string }> =>\n [...text.matchAll(PLACEHOLDER_PATTERN)].map(([placeholder, scope, provider]) => ({ placeholder, scope, provider }));\n\nexport const containsServerCredential = (text: string): boolean => text.includes(`${SERVER_CREDENTIAL_PREFIX}:`);\n\n/** The fixed API host of each built-in provider (openai-compatible and Cloudflare depend on the connection). */\nexport const AI_PROVIDER_HOSTS: Readonly<Record<string, string>> = {\n openai: 'api.openai.com',\n openrouter: 'openrouter.ai',\n opencode: 'opencode.ai',\n 'opencode-go': 'opencode.ai',\n deepseek: 'api.deepseek.com',\n gemini: 'generativelanguage.googleapis.com',\n anthropic: 'api.anthropic.com',\n mistral: 'api.mistral.ai',\n glm: 'open.bigmodel.cn',\n groq: 'api.groq.com',\n cloudflare: 'api.cloudflare.com',\n};\n\nconst trimSlashes = (path: string) => path.replace(/\\/+$/, '');\n\n/** Whether `target` is an API URL of the provider of `connection`: where its key may go. */\nexport const isAIProviderTarget = (connection: ServerAIConnection, target: URL): boolean => {\n if (connection.id === 'openai-compatible') {\n if (!connection.baseUrl) return false;\n const base = new URL(connection.baseUrl);\n const basePath = trimSlashes(base.pathname);\n return target.origin === base.origin && (target.pathname === basePath || target.pathname.startsWith(`${basePath}/`));\n }\n if (target.protocol !== 'https:' || target.host !== AI_PROVIDER_HOSTS[connection.id]) return false;\n // Cloudflare: one host for every account, so the key only goes to the account it belongs to.\n if (connection.id === 'cloudflare') {\n return Boolean(connection.accountId) && target.pathname.startsWith(`/client/v4/accounts/${encodeURIComponent(connection.accountId!.trim())}/`);\n }\n return true;\n};\n","import type { AIModelDescriptor, AIModelPricing } from './AIProvider';\r\n\r\n/** Public, CORS-open catalog (`access-control-allow-origin: *`) of models with their token\r\n * prices, in USD per 1M tokens — the same unit AIModelPricing uses. */\r\nexport const MODELS_DEV_URL = 'https://models.dev/api.json';\r\n\r\nconst CACHE_KEY = 'ai.pricing.modelsdev';\r\nconst CACHE_TTL_MS = 1000 * 60 * 60 * 24;\r\n\r\n/** Built-in provider id → models.dev provider keys, in lookup order. `openai-compatible` has\r\n * no entry: its endpoint is arbitrary, so no catalog can know its prices. */\r\nconst MODELS_DEV_PROVIDER_KEYS: Record<string, string[]> = {\r\n openai: ['openai'],\r\n openrouter: ['openrouter'],\r\n opencode: ['opencode'],\r\n 'opencode-go': ['opencode-go'],\r\n deepseek: ['deepseek'],\r\n gemini: ['google'],\r\n anthropic: ['anthropic'],\r\n mistral: ['mistral'],\r\n // The GLM adapter targets open.bigmodel.cn (zhipuai); `zai` is the international twin.\r\n glm: ['zhipuai', 'zai'],\r\n cloudflare: ['cloudflare-workers-ai'],\r\n groq: ['groq'],\r\n};\r\n\r\n/** Compact cache shape: providerKey → modelId → [input, output]. The raw catalog is ~5 MB,\r\n * this keeps only the providers above (~700 entries). */\r\ntype PricingIndex = Record<string, Record<string, [number, number]>>;\r\n\r\ntype ModelsDevCatalog = Record<string, { models?: Record<string, { cost?: { input?: unknown; output?: unknown } }> }>;\r\n\r\nlet pending: Promise<PricingIndex> | null = null;\r\n\r\nconst readCache = (): PricingIndex | null => {\r\n if (typeof localStorage === 'undefined') return null;\r\n try {\r\n const raw = localStorage.getItem(CACHE_KEY);\r\n if (!raw) return null;\r\n const cached = JSON.parse(raw) as { fetchedAt?: number; index?: PricingIndex };\r\n if (!cached.fetchedAt || !cached.index || Date.now() - cached.fetchedAt > CACHE_TTL_MS) return null;\r\n return cached.index;\r\n } catch {\r\n return null;\r\n }\r\n};\r\n\r\nconst writeCache = (index: PricingIndex) => {\r\n if (typeof localStorage === 'undefined') return;\r\n try {\r\n localStorage.setItem(CACHE_KEY, JSON.stringify({ fetchedAt: Date.now(), index }));\r\n } catch {\r\n // Ignore quota/storage failures: pricing still works in-memory for this session.\r\n }\r\n};\r\n\r\nexport const buildPricingIndex = (catalog: ModelsDevCatalog): PricingIndex => {\r\n const wanted = new Set(Object.values(MODELS_DEV_PROVIDER_KEYS).flat());\r\n const index: PricingIndex = {};\r\n for (const key of wanted) {\r\n const models = catalog[key]?.models ?? {};\r\n const prices: Record<string, [number, number]> = {};\r\n for (const [modelId, entry] of Object.entries(models)) {\r\n const input = entry?.cost?.input;\r\n const output = entry?.cost?.output;\r\n if (typeof input === 'number' && typeof output === 'number') prices[modelId] = [input, output];\r\n }\r\n index[key] = prices;\r\n }\r\n return index;\r\n};\r\n\r\n/** One fetch per session at most (in-flight promise shared by every provider), then the\r\n * localStorage copy for 24h. A failed fetch resolves to an empty index — every model then\r\n * shows \"price not found\" — and is not cached, so the next catalog load retries. */\r\nconst loadPricingIndex = (): Promise<PricingIndex> => {\r\n const cached = readCache();\r\n if (cached) return Promise.resolve(cached);\r\n if (!pending) {\r\n pending = fetch(MODELS_DEV_URL)\r\n .then((response) => (response.ok ? response.json() : Promise.reject(new Error(`HTTP ${response.status}`))))\r\n .then((catalog: ModelsDevCatalog) => {\r\n const index = buildPricingIndex(catalog);\r\n writeCache(index);\r\n return index;\r\n })\r\n .catch((error) => {\r\n console.warn('models.dev pricing unavailable', error);\r\n return {};\r\n })\r\n .finally(() => { pending = null; });\r\n }\r\n return pending;\r\n};\r\n\r\n/** Provider ids often carry a release date models.dev lists only under the undated alias\r\n * (e.g. `gpt-4o-mini-2024-07-18`); exact match wins, the stripped id is the fallback. */\r\nconst candidateIds = (model: string): string[] => {\r\n const lower = model.toLowerCase();\r\n const undated = lower.replace(/-(\\d{8}|\\d{4}-\\d{2}-\\d{2})$/, '');\r\n return [...new Set([model, lower, undated])];\r\n};\r\n\r\nexport const lookupModelsDevPricing = (index: PricingIndex, providerId: string, model: string): AIModelPricing | undefined => {\r\n for (const key of MODELS_DEV_PROVIDER_KEYS[providerId] ?? []) {\r\n const prices = index[key];\r\n if (!prices) continue;\r\n for (const id of candidateIds(model)) {\r\n const hit = prices[id];\r\n if (hit) return { input: hit[0], output: hit[1], currency: 'USD', source: 'models.dev' };\r\n }\r\n }\r\n return undefined;\r\n};\r\n\r\n/** Fills `pricing` from models.dev on the models the provider listing left without one.\r\n * Models are never dropped: a model with no match simply keeps `pricing` undefined. */\r\nexport const withModelsDevPricing = async (providerId: string, models: AIModelDescriptor[]): Promise<AIModelDescriptor[]> => {\r\n if (!MODELS_DEV_PROVIDER_KEYS[providerId] || models.every((m) => m.pricing)) return models;\r\n const index = await loadPricingIndex();\r\n return models.map((m) => {\r\n if (m.pricing) return m;\r\n const pricing = lookupModelsDevPricing(index, providerId, m.model);\r\n return pricing ? { ...m, pricing } : m;\r\n });\r\n};\r\n\r\n/** Test-only: drops the in-flight promise between cases. */\r\nexport const resetModelsDevPricingForTests = () => { pending = null; };\r\n","// The one error every email adapter throws (CR-130): a code the app can act on, the provider and the HTTP\n// status. The message never carries a key, a token or the body of the provider's answer.\n\nexport type EmailProviderErrorCode =\n /** The adapter misses a setting (key, sender, client, library). */\n | 'not-configured'\n /** The message itself is not valid: no recipient, no body. */\n | 'invalid-message'\n /** The provider refused the credentials (401/403, revoked token). */\n | 'unauthorized'\n /** The provider refused the message (400/422: sender not verified, address not valid). */\n | 'rejected'\n | 'rate-limited'\n /** The provider or the network did not answer. */\n | 'unavailable'\n | 'unknown';\n\nexport class EmailProviderError extends Error {\n readonly code: EmailProviderErrorCode;\n readonly provider: string;\n readonly status?: number;\n\n constructor(code: EmailProviderErrorCode, provider: string, message: string, options: { status?: number; cause?: unknown } = {}) {\n super(message, options.cause === undefined ? undefined : { cause: options.cause });\n this.name = 'EmailProviderError';\n this.code = code;\n this.provider = provider;\n this.status = options.status;\n }\n}\n\n/** The code of an HTTP status of a provider. */\nexport const emailErrorCode = (status: number): EmailProviderErrorCode => {\n if (status === 401 || status === 403) return 'unauthorized';\n if (status === 429) return 'rate-limited';\n if (status === 400 || status === 404 || status === 409 || status === 422) return 'rejected';\n if (status >= 500) return 'unavailable';\n return 'unknown';\n};\n\n/**\n * A JSON call to a provider: the answer, or an `EmailProviderError` with the status and the path only.\n * A network failure is `unavailable`.\n */\nexport async function callEmailApi<T>(\n provider: string,\n http: typeof fetch,\n url: string,\n init: RequestInit,\n): Promise<T> {\n let response: Response;\n try {\n response = await http(url, init);\n } catch (cause) {\n throw new EmailProviderError('unavailable', provider, `${provider} did not answer`, { cause });\n }\n if (!response.ok) {\n throw new EmailProviderError(emailErrorCode(response.status), provider, `${provider} answered ${response.status} on ${new URL(url).pathname}`, { status: response.status });\n }\n const text = await response.text();\n return (text ? JSON.parse(text) : {}) as T;\n}\n","import type { ProviderConfigurable } from '../ProviderConfiguration';\r\nimport { EmailProviderError } from './EmailError';\r\n\r\n// The category `emailSend` (CR-130): one contract to send a message, whatever provider sends it.\r\n\r\n/** An address, with the name shown to who reads it. */\r\nexport type EmailAddress = { email: string; name?: string };\r\n\r\n/** An address as text (`ada@example.com`) or with its name. */\r\nexport type EmailRecipient = string | EmailAddress;\r\n\r\nexport interface EmailSendParams {\r\n /** The sender; without it, the adapter's own `from`. */\r\n from?: EmailRecipient;\r\n to: EmailRecipient | EmailRecipient[];\r\n cc?: EmailRecipient | EmailRecipient[];\r\n bcc?: EmailRecipient | EmailRecipient[];\r\n replyTo?: EmailRecipient;\r\n subject: string;\r\n /** The body as HTML, as text, or both (the reader's client picks). At least one. */\r\n html?: string;\r\n text?: string;\r\n /** Gmail: the conversation the message joins. The other providers ignore it. */\r\n threadId?: string;\r\n}\r\n\r\nexport interface EmailSendResult {\r\n /** The id the provider gave the message. */\r\n id: string;\r\n threadId?: string;\r\n}\r\n\r\nexport interface EmailSendAdapter extends ProviderConfigurable {\r\n send(params: EmailSendParams): Promise<EmailSendResult>;\r\n}\r\n\r\n/** An address as `{ email, name? }`. */\r\nexport const toEmailAddress = (recipient: EmailRecipient): EmailAddress =>\r\n typeof recipient === 'string' ? { email: recipient.trim() } : { email: recipient.email.trim(), ...(recipient.name ? { name: recipient.name } : {}) };\r\n\r\n/** One or more addresses as a list, without the empty ones. */\r\nexport const toEmailAddresses = (recipients: EmailRecipient | EmailRecipient[] | undefined): EmailAddress[] =>\r\n (recipients === undefined ? [] : Array.isArray(recipients) ? recipients : [recipients])\r\n .map(toEmailAddress)\r\n .filter((address) => address.email !== '');\r\n\r\n/** An address as a header shows it: `Ada <ada@example.com>`, or the bare address. */\r\nexport const formatEmailAddress = ({ email, name }: EmailAddress): string =>\r\n name ? `${/[\",<>@;:]/.test(name) ? JSON.stringify(name) : name} <${email}>` : email;\r\n\r\n/**\r\n * The message an adapter sends: sender and recipients as addresses, at least one recipient and one body.\r\n * Throws `invalid-message` or (no sender, when the provider needs one) `not-configured`.\r\n */\r\nexport function prepareEmail(provider: string, params: EmailSendParams, defaultFrom: EmailRecipient | undefined, { fromRequired = true } = {}) {\r\n const from = params.from ?? defaultFrom;\r\n const sender = from ? toEmailAddress(from) : undefined;\r\n if (fromRequired && !sender?.email) throw new EmailProviderError('not-configured', provider, `${provider} has no sender`);\r\n const to = toEmailAddresses(params.to);\r\n const cc = toEmailAddresses(params.cc);\r\n const bcc = toEmailAddresses(params.bcc);\r\n if (to.length + cc.length + bcc.length === 0) throw new EmailProviderError('invalid-message', provider, 'The message has no recipient');\r\n if (!params.html && !params.text) throw new EmailProviderError('invalid-message', provider, 'The message has no body');\r\n return {\r\n from: sender?.email ? sender : undefined,\r\n to, cc, bcc,\r\n replyTo: params.replyTo ? toEmailAddress(params.replyTo) : undefined,\r\n subject: params.subject,\r\n html: params.html || undefined,\r\n text: params.text || undefined,\r\n threadId: params.threadId,\r\n };\r\n}\r\n\r\nexport type PreparedEmail = ReturnType<typeof prepareEmail>;\r\n","import { createConfigurationState, getMissingKeys, type ProviderConfigurationState } from '../../ProviderConfiguration';\r\nimport { callEmailApi, EmailProviderError } from '../EmailError';\r\nimport { formatEmailAddress, type PreparedEmail } from '../EmailSend';\r\nimport type { EmailMessage } from '../EmailRead';\r\n\r\n// The Gmail REST API as the Gmail adapters use it (CR-130): only `fetch`, `TextEncoder` and `btoa`/`atob`,\r\n// so the same code runs in a browser and on a server.\r\n\r\nexport const GMAIL_API = 'https://gmail.googleapis.com/gmail/v1/users/me';\r\nexport const GOOGLE_TOKEN_URL = 'https://oauth2.googleapis.com/token';\r\n\r\n/** The scopes of Gmail: read the mailbox, send from it. */\r\nexport const GMAIL_SCOPES = {\r\n read: 'https://www.googleapis.com/auth/gmail.readonly',\r\n send: 'https://www.googleapis.com/auth/gmail.send',\r\n} as const;\r\n\r\n/**\r\n * How a Gmail adapter gets its access token: a function (the browser's Google sign-in), or the OAuth client\r\n * of the app and the refresh token of the mailbox (a server).\r\n */\r\nexport type GmailAuth =\r\n | { accessToken: () => Promise<string> }\r\n | { clientId: string; clientSecret: string; refreshToken: string };\r\n\r\nexport const PROVIDER = 'Gmail';\r\n\r\n/** Whether the auth has what it needs. */\r\nexport function gmailConfigurationState(name: string, auth: GmailAuth | undefined): ProviderConfigurationState {\r\n if (auth && 'accessToken' in auth) return createConfigurationState(name, typeof auth.accessToken === 'function' ? [] : ['accessToken']);\r\n return createConfigurationState(name, getMissingKeys(auth as Record<string, unknown> | undefined, ['clientId', 'clientSecret', 'refreshToken']));\r\n}\r\n\r\n/** A fresh access token: from the function, or exchanging the refresh token. */\r\nexport async function gmailAccessToken(auth: GmailAuth, http: typeof fetch): Promise<string> {\r\n if ('accessToken' in auth) return auth.accessToken();\r\n const grant = await callEmailApi<{ access_token?: string }>(PROVIDER, http, GOOGLE_TOKEN_URL, {\r\n method: 'POST',\r\n headers: { 'content-type': 'application/x-www-form-urlencoded' },\r\n body: new URLSearchParams({\r\n client_id: auth.clientId,\r\n client_secret: auth.clientSecret,\r\n refresh_token: auth.refreshToken,\r\n grant_type: 'refresh_token',\r\n }),\r\n });\r\n if (!grant.access_token) throw new EmailProviderError('unauthorized', PROVIDER, 'Google returned no access token');\r\n return grant.access_token;\r\n}\r\n\r\n// ── Base64 ───────────────────────────────────────────────────────────────────\r\n\r\nconst bytesToBase64 = (bytes: Uint8Array): string => {\r\n let binary = '';\r\n for (let i = 0; i < bytes.length; i += 0x8000) binary += String.fromCharCode(...bytes.subarray(i, i + 0x8000));\r\n return btoa(binary);\r\n};\r\n\r\nconst base64 = (text: string): string => bytesToBase64(new TextEncoder().encode(text));\r\n\r\nexport const base64Url = (text: string): string => base64(text).replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '');\r\n\r\nexport const fromBase64Url = (data: string): string => {\r\n const binary = atob(data.replace(/-/g, '+').replace(/_/g, '/'));\r\n return new TextDecoder().decode(Uint8Array.from(binary, (char) => char.charCodeAt(0)));\r\n};\r\n\r\n// ── The message Gmail sends ───────────────────────────────────────────────────\r\n\r\nconst CRLF = '\\r\\n';\r\n\r\n/** A header value in UTF-8 when it is not plain ASCII (RFC 2047). */\r\nconst headerText = (text: string): string => (/^[\\x20-\\x7e]*$/.test(text) ? text : `=?UTF-8?B?${base64(text)}?=`);\r\n\r\nconst headerAddress = (address: { email: string; name?: string }): string =>\r\n address.name ? `${headerText(address.name)} <${address.email}>` : formatEmailAddress(address);\r\n\r\n/** The headers of a body part in UTF-8 and base64. */\r\nconst partHeaders = (type: string): string[] => [`Content-Type: ${type}; charset=UTF-8`, 'Content-Transfer-Encoding: base64'];\r\n\r\n/** A body in base64, 76 characters a line. */\r\nconst partBody = (body: string): string => (base64(body).match(/.{1,76}/g) ?? []).join(CRLF);\r\n\r\n/** The raw message (RFC 2822) in base64url, as `messages.send` takes it. */\r\nexport function gmailRawMessage(mail: PreparedEmail): string {\r\n const headers = [\r\n mail.from ? `From: ${headerAddress(mail.from)}` : null,\r\n mail.to.length ? `To: ${mail.to.map(headerAddress).join(', ')}` : null,\r\n mail.cc.length ? `Cc: ${mail.cc.map(headerAddress).join(', ')}` : null,\r\n mail.bcc.length ? `Bcc: ${mail.bcc.map(headerAddress).join(', ')}` : null,\r\n mail.replyTo ? `Reply-To: ${headerAddress(mail.replyTo)}` : null,\r\n `Subject: ${headerText(mail.subject)}`,\r\n 'MIME-Version: 1.0',\r\n ].filter((line): line is string => line !== null);\r\n let body: string;\r\n if (mail.html && mail.text) {\r\n const boundary = `llmnative-${Math.random().toString(36).slice(2)}`;\r\n headers.push(`Content-Type: multipart/alternative; boundary=\"${boundary}\"`);\r\n const section = (type: string, content: string) => [`--${boundary}`, ...partHeaders(type), '', partBody(content)].join(CRLF);\r\n body = [section('text/plain', mail.text), section('text/html', mail.html), `--${boundary}--`].join(CRLF);\r\n } else {\r\n headers.push(...partHeaders(mail.html ? 'text/html' : 'text/plain'));\r\n body = partBody(mail.html ?? mail.text ?? '');\r\n }\r\n return base64Url([...headers, '', body].join(CRLF));\r\n}\r\n\r\n// ── The messages Gmail reads ──────────────────────────────────────────────────\r\n\r\ntype GmailPart = { mimeType?: string; body?: { data?: string }; parts?: GmailPart[] };\r\n\r\n/** The first part of the tree with this MIME type and a body, decoded. */\r\nfunction partOf(payload: GmailPart | undefined, mime: string): string | undefined {\r\n if (!payload) return undefined;\r\n if (payload.mimeType === mime && payload.body?.data) return fromBase64Url(payload.body.data);\r\n for (const child of payload.parts ?? []) {\r\n const found = partOf(child, mime);\r\n if (found !== undefined) return found;\r\n }\r\n return undefined;\r\n}\r\n\r\n/** The text of a message: its text part wherever it is, else its HTML part without tags. */\r\nfunction textOf(payload: GmailPart | undefined): string {\r\n return partOf(payload, 'text/plain')\r\n ?? partOf(payload, 'text/html')?.replace(/<[^>]*>/g, ' ').replace(/\\s+/g, ' ').trim()\r\n ?? '';\r\n}\r\n\r\nexport type GmailApiMessage = {\r\n id: string;\r\n threadId?: string;\r\n labelIds?: string[];\r\n snippet?: string;\r\n internalDate?: string;\r\n payload?: GmailPart & { headers?: { name: string; value: string }[] };\r\n};\r\n\r\n/** A message of the API (`format=full`) as an `EmailMessage`, with the cursor after it. */\r\nexport function toEmailMessage(message: GmailApiMessage, cursor: string): EmailMessage {\r\n const header = (name: string) => String(message.payload?.headers?.find((h) => h.name.toLowerCase() === name)?.value ?? '');\r\n const arrived = message.internalDate ? new Date(Number(message.internalDate)) : new Date(header('date'));\r\n return {\r\n id: String(message.id),\r\n threadId: String(message.threadId ?? message.id),\r\n from: header('from'),\r\n to: header('to'),\r\n subject: header('subject'),\r\n date: (Number.isNaN(arrived.getTime()) ? new Date() : arrived).toISOString(),\r\n text: (textOf(message.payload) || String(message.snippet ?? '')).trim(),\r\n direction: (message.labelIds ?? []).includes('SENT') ? 'outbound' : 'inbound',\r\n cursor,\r\n };\r\n}\r\n","import type { ProviderConfigurationState } from '../../ProviderConfiguration';\nimport { callEmailApi, EmailProviderError } from '../EmailError';\nimport { prepareEmail, type EmailRecipient, type EmailSendAdapter, type EmailSendParams, type EmailSendResult } from '../EmailSend';\nimport { GMAIL_API, gmailAccessToken, gmailConfigurationState, gmailRawMessage, PROVIDER, type GmailAuth } from './gmailApi';\n\nexport type GmailEmailSenderConfig = {\n auth: GmailAuth;\n /** The sender shown; without it Gmail uses the address of the account (an alias must be set in Gmail). */\n from?: EmailRecipient;\n fetch?: typeof fetch;\n};\n\n/**\n * Sends from a Gmail mailbox (CR-130): with the browser's Google sign-in (`auth.accessToken`), or on a server\n * with the app's OAuth client and the mailbox's refresh token (scope `GMAIL_SCOPES.send`). `threadId` keeps a\n * reply in its conversation.\n */\nexport class GmailEmailSender implements EmailSendAdapter {\n constructor(protected readonly config: GmailEmailSenderConfig) {}\n\n getConfigurationState(): ProviderConfigurationState {\n return gmailConfigurationState('GmailEmailSender', this.config.auth);\n }\n\n isConfigured(): boolean {\n return this.getConfigurationState().configured;\n }\n\n async send(params: EmailSendParams): Promise<EmailSendResult> {\n const state = this.getConfigurationState();\n if (!state.configured) throw new EmailProviderError('not-configured', PROVIDER, state.reason ?? 'Gmail is not configured');\n const mail = prepareEmail(PROVIDER, params, this.config.from, { fromRequired: false });\n const http = this.config.fetch ?? globalThis.fetch;\n const token = await gmailAccessToken(this.config.auth, http);\n const sent = await callEmailApi<{ id?: string; threadId?: string }>(PROVIDER, http, `${GMAIL_API}/messages/send`, {\n method: 'POST',\n headers: { authorization: `Bearer ${token}`, 'content-type': 'application/json' },\n body: JSON.stringify({ raw: gmailRawMessage(mail), ...(mail.threadId ? { threadId: mail.threadId } : {}) }),\n });\n return { id: String(sent.id ?? ''), ...(sent.threadId ? { threadId: String(sent.threadId) } : {}) };\n }\n}\n","// Metadata for the email providers a \"connect\" UI can offer (CR-130): what each is called, the credentials it\n// needs and where to get them — separate from the runtime contracts (EmailSendAdapter, EmailReadAdapter).\n// The providers with an adapter in the framework: Resend, Brevo, Gmail (send); Gmail, IMAP (read).\nimport type { ProviderDescriptor } from '../ProviderDescriptor';\n\nexport type EmailSendProviderId = 'resend' | 'brevo' | 'gmail' | 'sendgrid' | 'mailgun' | 'postmark' | 'ses' | 'smtp';\nexport type EmailReadProviderId = 'gmail' | 'imap';\n\nconst GMAIL_CONSENT_STEPS = [\n 'Google Cloud console → APIs & Services → enable the Gmail API.',\n 'OAuth consent screen: add the Gmail scopes the app asks for.',\n 'Credentials → Create OAuth client ID → Web application, with the redirect URI of the app\\'s server.',\n 'In the app, connect the mailbox: Google asks the owner for consent.',\n];\n\nexport const EMAIL_SEND_PROVIDER_DEFINITIONS: ProviderDescriptor[] = [\n {\n id: 'resend',\n label: 'Resend',\n description: 'Developer-first transactional email API.',\n credentialFields: [\n { key: 'apiKey', label: 'API Key', type: 'password', placeholder: 're_...' },\n { key: 'from', label: 'Sender', type: 'text', placeholder: 'App <noreply@yourdomain.com>' },\n ],\n credentialsUrl: 'https://resend.com/api-keys',\n credentialsHint: 'Resend dashboard → API Keys → Create API Key. The sender\\'s domain must be verified under Domains.',\n },\n {\n id: 'brevo',\n label: 'Brevo',\n description: 'Transactional and marketing email, EU-based.',\n credentialFields: [\n { key: 'apiKey', label: 'API Key', type: 'password', placeholder: 'xkeysib-...' },\n { key: 'from', label: 'Sender', type: 'text', placeholder: 'App <noreply@yourdomain.com>' },\n ],\n credentialsUrl: 'https://app.brevo.com/settings/keys/api',\n credentialsHint: 'Brevo → SMTP & API → API Keys → Generate a new API key. The sender must be verified under Senders, Domains & Dedicated IPs.',\n },\n {\n id: 'gmail',\n label: 'Gmail',\n description: 'Send from a Gmail or Google Workspace mailbox, with the owner\\'s consent.',\n credentialFields: [],\n credentialsUrl: 'https://console.cloud.google.com/apis/credentials',\n credentialsSteps: GMAIL_CONSENT_STEPS,\n },\n {\n id: 'sendgrid',\n label: 'SendGrid',\n description: 'Scalable email delivery by Twilio.',\n credentialFields: [{ key: 'apiKey', label: 'API Key', type: 'password', placeholder: 'SG...' }],\n credentialsUrl: 'https://app.sendgrid.com/settings/api_keys',\n credentialsHint: 'SendGrid dashboard → Settings → API Keys → Create API Key.',\n },\n {\n id: 'mailgun',\n label: 'Mailgun',\n description: 'Email API with analytics and webhooks.',\n credentialFields: [\n { key: 'apiKey', label: 'API Key', type: 'password' },\n { key: 'domain', label: 'Mailgun domain', type: 'text', placeholder: 'mg.yourdomain.com' },\n ],\n credentialsUrl: 'https://app.mailgun.com/settings/api_security/api_keys',\n credentialsHint: 'Mailgun dashboard → Security → API Keys. The domain is the sending domain you verified under Sending → Domains.',\n },\n {\n id: 'postmark',\n label: 'Postmark',\n description: 'High-deliverability transactional email.',\n credentialFields: [{ key: 'serverToken', label: 'Server API Token', type: 'password' }],\n credentialsUrl: 'https://account.postmarkapp.com/servers',\n credentialsHint: 'Postmark → your Server → API Tokens tab (each server has its own token).',\n },\n {\n id: 'ses',\n label: 'AWS SES',\n description: 'Amazon Simple Email Service.',\n credentialFields: [\n { key: 'accessKeyId', label: 'Access Key ID', type: 'text' },\n { key: 'secretAccessKey', label: 'Secret Access Key', type: 'password' },\n { key: 'region', label: 'Region', type: 'text', placeholder: 'us-east-1' },\n ],\n credentialsUrl: 'https://console.aws.amazon.com/iam/home#/security_credentials',\n credentialsHint: 'AWS IAM → Security credentials → Access keys, for a user with SES send permissions.',\n },\n {\n id: 'smtp',\n label: 'SMTP',\n description: 'Any SMTP-compatible mail server.',\n credentialFields: [\n { key: 'host', label: 'Host', type: 'text', placeholder: 'smtp.example.com' },\n { key: 'port', label: 'Port', type: 'text', placeholder: '587' },\n { key: 'username', label: 'Username', type: 'text' },\n { key: 'password', label: 'Password', type: 'password' },\n ],\n credentialsHint: 'Ask your mail host or hosting provider for the SMTP host, port, and credentials.',\n },\n];\n\nexport const EMAIL_READ_PROVIDER_DEFINITIONS: ProviderDescriptor[] = [\n {\n id: 'gmail',\n label: 'Gmail',\n description: 'New mails arrive at once, through a Pub/Sub topic of Google Cloud.',\n credentialFields: [],\n credentialsUrl: 'https://console.cloud.google.com/cloudpubsub/topic/list',\n credentialsSteps: [\n ...GMAIL_CONSENT_STEPS,\n 'Pub/Sub: create a topic and give roles/pubsub.publisher on it to gmail-api-push@system.gserviceaccount.com.',\n ],\n },\n {\n id: 'imap',\n label: 'IMAP',\n description: 'Any other mailbox, read every few minutes.',\n credentialFields: [\n { key: 'host', label: 'IMAP server', type: 'text', placeholder: 'imap.example.com' },\n { key: 'port', label: 'Port', type: 'text', placeholder: '993' },\n { key: 'user', label: 'User', type: 'text', placeholder: 'name@example.com' },\n { key: 'password', label: 'Password', type: 'password' },\n ],\n credentialsHint: 'The IMAP settings of your mail provider. Use an app password where the provider has them.',\n },\n];\n","import { createConfigurationState, getMissingKeys, type ProviderConfigurationState } from '../../ProviderConfiguration';\nimport { callEmailApi, EmailProviderError } from '../EmailError';\nimport { formatEmailAddress, prepareEmail, type EmailRecipient, type EmailSendAdapter, type EmailSendParams, type EmailSendResult } from '../EmailSend';\n\nexport const RESEND_EMAIL_URL = 'https://api.resend.com/emails';\n\nexport type ResendEmailSenderConfig = {\n apiKey: string;\n /** The default sender, on a domain verified in Resend. */\n from?: EmailRecipient;\n /** The `fetch` to use (a proxy, a test). */\n fetch?: typeof fetch;\n};\n\n/**\n * Sends with Resend (CR-130). The key is a secret: use it on a server, not in a browser bundle.\n */\nexport class ResendEmailSender implements EmailSendAdapter {\n constructor(private readonly config: ResendEmailSenderConfig) {}\n\n getConfigurationState(): ProviderConfigurationState {\n return createConfigurationState('ResendEmailSender', getMissingKeys(this.config as Record<string, unknown>, ['apiKey']));\n }\n\n isConfigured(): boolean {\n return this.getConfigurationState().configured;\n }\n\n async send(params: EmailSendParams): Promise<EmailSendResult> {\n if (!this.isConfigured()) throw new EmailProviderError('not-configured', 'Resend', 'Resend has no API key');\n const mail = prepareEmail('Resend', params, this.config.from);\n const list = (addresses: typeof mail.to) => (addresses.length ? addresses.map(formatEmailAddress) : undefined);\n const sent = await callEmailApi<{ id?: string }>('Resend', this.config.fetch ?? globalThis.fetch, RESEND_EMAIL_URL, {\n method: 'POST',\n headers: { authorization: `Bearer ${this.config.apiKey}`, 'content-type': 'application/json' },\n body: JSON.stringify({\n from: formatEmailAddress(mail.from!),\n to: list(mail.to),\n cc: list(mail.cc),\n bcc: list(mail.bcc),\n reply_to: mail.replyTo ? formatEmailAddress(mail.replyTo) : undefined,\n subject: mail.subject,\n html: mail.html,\n text: mail.text,\n }),\n });\n return { id: String(sent.id ?? '') };\n }\n}\n","import { createConfigurationState, getMissingKeys, type ProviderConfigurationState } from '../../ProviderConfiguration';\nimport { callEmailApi, EmailProviderError } from '../EmailError';\nimport { prepareEmail, type EmailAddress, type EmailRecipient, type EmailSendAdapter, type EmailSendParams, type EmailSendResult } from '../EmailSend';\n\nexport const BREVO_EMAIL_URL = 'https://api.brevo.com/v3/smtp/email';\n\nexport type BrevoEmailSenderConfig = {\n apiKey: string;\n /** The default sender, a sender or domain verified in Brevo. */\n from?: EmailRecipient;\n /** The `fetch` to use (a proxy, a test). */\n fetch?: typeof fetch;\n};\n\n/** An address as Brevo takes it. */\nconst brevoAddress = ({ email, name }: EmailAddress) => (name ? { email, name } : { email });\n\n/**\n * Sends with Brevo's transactional API (CR-130). The key is a secret: use it on a server, not in a\n * browser bundle.\n */\nexport class BrevoEmailSender implements EmailSendAdapter {\n constructor(private readonly config: BrevoEmailSenderConfig) {}\n\n getConfigurationState(): ProviderConfigurationState {\n return createConfigurationState('BrevoEmailSender', getMissingKeys(this.config as Record<string, unknown>, ['apiKey']));\n }\n\n isConfigured(): boolean {\n return this.getConfigurationState().configured;\n }\n\n async send(params: EmailSendParams): Promise<EmailSendResult> {\n if (!this.isConfigured()) throw new EmailProviderError('not-configured', 'Brevo', 'Brevo has no API key');\n const mail = prepareEmail('Brevo', params, this.config.from);\n const list = (addresses: EmailAddress[]) => (addresses.length ? addresses.map(brevoAddress) : undefined);\n const sent = await callEmailApi<{ messageId?: string }>('Brevo', this.config.fetch ?? globalThis.fetch, BREVO_EMAIL_URL, {\n method: 'POST',\n headers: { 'api-key': this.config.apiKey, 'content-type': 'application/json', accept: 'application/json' },\n body: JSON.stringify({\n sender: brevoAddress(mail.from!),\n to: list(mail.to),\n cc: list(mail.cc),\n bcc: list(mail.bcc),\n replyTo: mail.replyTo ? brevoAddress(mail.replyTo) : undefined,\n subject: mail.subject,\n htmlContent: mail.html,\n textContent: mail.text,\n }),\n });\n return { id: String(sent.messageId ?? '') };\n }\n}\n","// Provider-agnostic identity of AI models (CR-103). models.dev names a model the same way on every\r\n// provider once release dates and variants are removed: \"DeepSeek V4.1 Flash\" is\r\n// `deepseek/deepseek-v4.1-flash` on OpenRouter, `deepseek-flash` on DeepSeek and\r\n// `@cf/deepseek-ai/deepseek-v4.1-flash-1017` on Cloudflare. Pure module: no React, no browser API.\r\n\r\nimport { MODELS_DEV_URL } from './modelsDevPricing';\r\n\r\n/** Words that name a variant of a model, not another model. */\r\nconst VARIANT_WORDS = new Set(['free', 'latest']);\r\n/** A trailing release date: MMDD, YYYYMMDD or YYYY-MM-DD. */\r\nconst DATE_WORD = /^(\\d{4}|\\d{8}|\\d{4}-\\d{2}-\\d{2})$/;\r\n/** A trailing group in parentheses: who hosts it, a region or a variant (\"(Alibaba Cloud)\", \"(EU)\", \"(Latest)\"). */\r\nconst TRAILING_GROUP = /\\s*\\(([^()]*)\\)\\s*$/;\r\n/** Groups that name another way of running the model, not another host: they stay in the name. */\r\nconst MODE_GROUP = /^(thinking|reasoning|non-reasoning|fast)$/i;\r\n\r\n/**\r\n * The key of a model name on any provider: lower case, without the host, region, release date, Free\r\n * or Latest that some providers add; a mode (\"(Thinking)\") stays.\r\n */\r\nexport const canonicalModelName = (name: string): string => {\r\n let rest = name.trim();\r\n const modes: string[] = [];\r\n for (let group = TRAILING_GROUP.exec(rest); group; group = TRAILING_GROUP.exec(rest)) {\r\n if (MODE_GROUP.test(group[1].trim())) modes.unshift(group[1].trim());\r\n rest = rest.slice(0, group.index);\r\n }\r\n const words = rest.toLowerCase().replace(/[()[\\]]/g, ' ').split(/\\s+/).filter(Boolean);\r\n while (words.length > 1 && (VARIANT_WORDS.has(words[words.length - 1]) || DATE_WORD.test(words[words.length - 1]))) words.pop();\r\n return [...words, ...modes.map((mode) => mode.toLowerCase())].join(' ');\r\n};\r\n\r\n/** One model of a provider, as models.dev lists it, with its price there when models.dev has one (CR-119). */\r\nexport type ModelIdentityEntry = { id: string; name: string; cost?: ModelCost };\r\n\r\n/** What a model costs on a provider: USD per 1M tokens. */\r\nexport type ModelCost = { input: number; output: number };\r\n\r\nexport type ModelIdentity = {\r\n /** canonicalModelName() of its name. */\r\n key: string;\r\n /** The name to show: the one of a stable id, without date or variant when there is one. */\r\n name: string;\r\n family?: string;\r\n /** models.dev provider key (the ids opencode uses) → the ids of the model there. */\r\n providers: Record<string, ModelIdentityEntry[]>;\r\n};\r\n\r\nexport type ModelIdentityIndex = {\r\n models: Record<string, ModelIdentity>;\r\n /** models.dev provider key → its name. */\r\n providers: Record<string, string>;\r\n};\r\n\r\ntype ModelsDevCatalog = Record<string, { name?: string; models?: Record<string, { name?: string; family?: string; cost?: { input?: unknown; output?: unknown } }> }>;\r\n\r\n/** How far an id is from the stable one: `~` aliases, free tiers, Latest and dated ids come last. */\r\nconst instability = ({ id, name }: ModelIdentityEntry): number => {\r\n const words = name.toLowerCase().split(/\\s+/);\r\n return (id.startsWith('~') ? 8 : 0)\r\n + (words.includes('free') ? 4 : 0)\r\n + (words.includes('latest') ? 2 : 0)\r\n + (DATE_WORD.test(words[words.length - 1] ?? '') ? 1 : 0)\r\n // A host or region in parentheses: the plain name of the same provider goes first.\r\n + (TRAILING_GROUP.test(name) ? 1 : 0);\r\n};\r\n\r\nconst byStability = (left: ModelIdentityEntry, right: ModelIdentityEntry) =>\r\n instability(left) - instability(right) || left.id.length - right.id.length || left.id.localeCompare(right.id);\r\n\r\n/** One entry per canonical model, with its ids on every provider of the models.dev catalog. */\r\nexport function buildModelIdentityIndex(catalog: ModelsDevCatalog): ModelIdentityIndex {\r\n const models: Record<string, ModelIdentity> = {};\r\n const providers: Record<string, string> = {};\r\n for (const [provider, { name: providerName, models: entries = {} }] of Object.entries(catalog)) {\r\n providers[provider] = providerName || provider;\r\n for (const [id, entry] of Object.entries(entries)) {\r\n const name = entry?.name?.trim() || id;\r\n const key = canonicalModelName(name);\r\n const identity = models[key] ??= { key, name, providers: {} };\r\n if (!identity.family && entry?.family) identity.family = entry.family;\r\n const input = entry?.cost?.input;\r\n const output = entry?.cost?.output;\r\n const cost = typeof input === 'number' && typeof output === 'number' ? { input, output } : undefined;\r\n (identity.providers[provider] ??= []).push({ id, name, ...(cost ? { cost } : {}) });\r\n // The name to show is the one of the most stable id seen so far.\r\n if (instability({ id, name }) < instability({ id: '', name: identity.name })) identity.name = name;\r\n }\r\n }\r\n for (const identity of Object.values(models)) {\r\n for (const entries of Object.values(identity.providers)) entries.sort(byStability);\r\n }\r\n return { models, providers };\r\n}\r\n\r\nexport type ResolvedModel = {\r\n /** models.dev provider key. */\r\n provider: string;\r\n /** The id of the model on that provider. */\r\n model: string;\r\n /** `provider/model`: the form opencode reads. */\r\n ref: string;\r\n};\r\n\r\n/**\r\n * The id of `model` (a name or a canonical key) on the first of `providers` that serves it, the most\r\n * stable id of that provider; nothing when none of them does.\r\n */\r\nexport function resolveModel(index: ModelIdentityIndex, model: string, providers: readonly string[]): ResolvedModel | undefined {\r\n const identity = index.models[canonicalModelName(model)];\r\n if (!identity) return undefined;\r\n for (const provider of providers) {\r\n const best = identity.providers[provider]?.[0];\r\n if (best) return { provider, model: best.id, ref: `${provider}/${best.id}` };\r\n }\r\n return undefined;\r\n}\r\n\r\n/** A provider that serves a model: its most stable id there, and what it costs there when known. */\r\nexport type ModelOffer = ResolvedModel & { cost?: ModelCost };\r\n\r\n/**\r\n * Every provider that serves `model` (a name or a canonical key), each with its most stable id and its\r\n * price: of `providers`, in their order, when given; else of the whole catalog, by provider key (CR-119).\r\n */\r\nexport function modelOffers(index: ModelIdentityIndex, model: string, providers?: readonly string[]): ModelOffer[] {\r\n const identity = index.models[canonicalModelName(model)];\r\n if (!identity) return [];\r\n const keys = providers ?? Object.keys(identity.providers).sort();\r\n return keys.flatMap((provider) => {\r\n const best = identity.providers[provider]?.[0];\r\n return best ? [{ provider, model: best.id, ref: `${provider}/${best.id}`, ...(best.cost ? { cost: best.cost } : {}) }] : [];\r\n });\r\n}\r\n\r\nlet pending: Promise<ModelIdentityIndex> | null = null;\r\nlet loaded: ModelIdentityIndex | null = null;\r\n\r\n/** The models.dev index, fetched once per session (the catalog is several MB: not cached on disk). */\r\nexport function loadModelIdentityIndex(fetchFn: typeof fetch = globalThis.fetch): Promise<ModelIdentityIndex> {\r\n if (loaded) return Promise.resolve(loaded);\r\n pending ??= fetchFn(MODELS_DEV_URL)\r\n .then((response) => (response.ok ? response.json() : Promise.reject(new Error(`models.dev: HTTP ${response.status}`))))\r\n .then((catalog: ModelsDevCatalog) => (loaded = buildModelIdentityIndex(catalog)))\r\n .finally(() => { pending = null; });\r\n return pending;\r\n}\r\n"],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ var e=(e,t,n=``)=>t.filter(t=>{let n=e?.[t];return n==null||n===``}).map(e=>`${n}${e}`),t=(e,t)=>({configured:t.length===0,missingKeys:t,reason:t.length?`${e} is not configured. Missing: ${t.join(`, `)}.`:void 0}),n=(e,t=`Provider`)=>{if(!e)return{configured:!1,reason:`${t} is not registered.`};if(e.getConfigurationState)return e.getConfigurationState();if(e.isConfigured){let n=e.isConfigured();return{configured:n,reason:n?void 0:`${t} is not configured.`}}return{configured:!0}},r=`llmnative-server-credential`,i=/^[A-Za-z0-9_-]+$/,a=/llmnative-server-credential:([A-Za-z0-9_-]+):([a-z-]+)/g,o=(e,t)=>{if(!i.test(e))throw Error(`Server credential scope "${e}" may only contain letters, digits, "-" and "_".`);return`${r}:${e}:${t}`},s=e=>[...e.matchAll(a)].map(([e,t,n])=>({placeholder:e,scope:t,provider:n})),c=e=>e.includes(`${r}:`),l={openai:`api.openai.com`,openrouter:`openrouter.ai`,opencode:`opencode.ai`,"opencode-go":`opencode.ai`,deepseek:`api.deepseek.com`,gemini:`generativelanguage.googleapis.com`,anthropic:`api.anthropic.com`,mistral:`api.mistral.ai`,glm:`open.bigmodel.cn`,groq:`api.groq.com`,cloudflare:`api.cloudflare.com`},u=e=>e.replace(/\/+$/,``),ee=(e,t)=>{if(e.id===`openai-compatible`){if(!e.baseUrl)return!1;let n=new URL(e.baseUrl),r=u(n.pathname);return t.origin===n.origin&&(t.pathname===r||t.pathname.startsWith(`${r}/`))}return t.protocol!==`https:`||t.host!==l[e.id]?!1:e.id!==`cloudflare`||!!e.accountId&&t.pathname.startsWith(`/client/v4/accounts/${encodeURIComponent(e.accountId.trim())}/`)},d=`https://models.dev/api.json`,f=`ai.pricing.modelsdev`,te=864e5,p={openai:[`openai`],openrouter:[`openrouter`],opencode:[`opencode`],"opencode-go":[`opencode-go`],deepseek:[`deepseek`],gemini:[`google`],anthropic:[`anthropic`],mistral:[`mistral`],glm:[`zhipuai`,`zai`],cloudflare:[`cloudflare-workers-ai`],groq:[`groq`]},m=null,ne=()=>{if(typeof localStorage>`u`)return null;try{let e=localStorage.getItem(f);if(!e)return null;let t=JSON.parse(e);return!t.fetchedAt||!t.index||Date.now()-t.fetchedAt>te?null:t.index}catch{return null}},re=e=>{if(typeof localStorage<`u`)try{localStorage.setItem(f,JSON.stringify({fetchedAt:Date.now(),index:e}))}catch{}},ie=e=>{let t=new Set(Object.values(p).flat()),n={};for(let r of t){let t=e[r]?.models??{},i={};for(let[e,n]of Object.entries(t)){let t=n?.cost?.input,r=n?.cost?.output;typeof t==`number`&&typeof r==`number`&&(i[e]=[t,r])}n[r]=i}return n},h=()=>{let e=ne();return e?Promise.resolve(e):(m||=fetch(d).then(e=>e.ok?e.json():Promise.reject(Error(`HTTP ${e.status}`))).then(e=>{let t=ie(e);return re(t),t}).catch(e=>(console.warn(`models.dev pricing unavailable`,e),{})).finally(()=>{m=null}),m)},ae=e=>{let t=e.toLowerCase(),n=t.replace(/-(\d{8}|\d{4}-\d{2}-\d{2})$/,``);return[...new Set([e,t,n])]},oe=(e,t,n)=>{for(let r of p[t]??[]){let t=e[r];if(t)for(let e of ae(n)){let n=t[e];if(n)return{input:n[0],output:n[1],currency:`USD`,source:`models.dev`}}}},g=async(e,t)=>{if(!p[e]||t.every(e=>e.pricing))return t;let n=await h();return t.map(t=>{if(t.pricing)return t;let r=oe(n,e,t.model);return r?{...t,pricing:r}:t})},_=class extends Error{code;provider;status;constructor(e,t,n,r={}){super(n,r.cause===void 0?void 0:{cause:r.cause}),this.name=`EmailProviderError`,this.code=e,this.provider=t,this.status=r.status}},v=e=>e===401||e===403?`unauthorized`:e===429?`rate-limited`:e===400||e===404||e===409||e===422?`rejected`:e>=500?`unavailable`:`unknown`;async function y(e,t,n,r){let i;try{i=await t(n,r)}catch(t){throw new _(`unavailable`,e,`${e} did not answer`,{cause:t})}if(!i.ok)throw new _(v(i.status),e,`${e} answered ${i.status} on ${new URL(n).pathname}`,{status:i.status});let a=await i.text();return a?JSON.parse(a):{}}var b=e=>typeof e==`string`?{email:e.trim()}:{email:e.email.trim(),...e.name?{name:e.name}:{}},x=e=>(e===void 0?[]:Array.isArray(e)?e:[e]).map(b).filter(e=>e.email!==``),S=({email:e,name:t})=>t?`${/[",<>@;:]/.test(t)?JSON.stringify(t):t} <${e}>`:e;function C(e,t,n,{fromRequired:r=!0}={}){let i=t.from??n,a=i?b(i):void 0;if(r&&!a?.email)throw new _(`not-configured`,e,`${e} has no sender`);let o=x(t.to),s=x(t.cc),c=x(t.bcc);if(o.length+s.length+c.length===0)throw new _(`invalid-message`,e,`The message has no recipient`);if(!t.html&&!t.text)throw new _(`invalid-message`,e,`The message has no body`);return{from:a?.email?a:void 0,to:o,cc:s,bcc:c,replyTo:t.replyTo?b(t.replyTo):void 0,subject:t.subject,html:t.html||void 0,text:t.text||void 0,threadId:t.threadId}}var w=`https://gmail.googleapis.com/gmail/v1/users/me`,T=`https://oauth2.googleapis.com/token`,E={read:`https://www.googleapis.com/auth/gmail.readonly`,send:`https://www.googleapis.com/auth/gmail.send`},D=`Gmail`;function O(n,r){return r&&`accessToken`in r?t(n,typeof r.accessToken==`function`?[]:[`accessToken`]):t(n,e(r,[`clientId`,`clientSecret`,`refreshToken`]))}async function k(e,t){if(`accessToken`in e)return e.accessToken();let n=await y(D,t,T,{method:`POST`,headers:{"content-type":`application/x-www-form-urlencoded`},body:new URLSearchParams({client_id:e.clientId,client_secret:e.clientSecret,refresh_token:e.refreshToken,grant_type:`refresh_token`})});if(!n.access_token)throw new _(`unauthorized`,D,`Google returned no access token`);return n.access_token}var se=e=>{let t=``;for(let n=0;n<e.length;n+=32768)t+=String.fromCharCode(...e.subarray(n,n+32768));return btoa(t)},A=e=>se(new TextEncoder().encode(e)),j=e=>A(e).replace(/\+/g,`-`).replace(/\//g,`_`).replace(/=+$/,``),M=e=>{let t=atob(e.replace(/-/g,`+`).replace(/_/g,`/`));return new TextDecoder().decode(Uint8Array.from(t,e=>e.charCodeAt(0)))},N=`\r
2
+ `,P=e=>/^[\x20-\x7e]*$/.test(e)?e:`=?UTF-8?B?${A(e)}?=`,F=e=>e.name?`${P(e.name)} <${e.email}>`:S(e),I=e=>[`Content-Type: ${e}; charset=UTF-8`,`Content-Transfer-Encoding: base64`],L=e=>(A(e).match(/.{1,76}/g)??[]).join(N);function R(e){let t=[e.from?`From: ${F(e.from)}`:null,e.to.length?`To: ${e.to.map(F).join(`, `)}`:null,e.cc.length?`Cc: ${e.cc.map(F).join(`, `)}`:null,e.bcc.length?`Bcc: ${e.bcc.map(F).join(`, `)}`:null,e.replyTo?`Reply-To: ${F(e.replyTo)}`:null,`Subject: ${P(e.subject)}`,`MIME-Version: 1.0`].filter(e=>e!==null),n;if(e.html&&e.text){let r=`llmnative-${Math.random().toString(36).slice(2)}`;t.push(`Content-Type: multipart/alternative; boundary="${r}"`);let i=(e,t)=>[`--${r}`,...I(e),``,L(t)].join(N);n=[i(`text/plain`,e.text),i(`text/html`,e.html),`--${r}--`].join(N)}else t.push(...I(e.html?`text/html`:`text/plain`)),n=L(e.html??e.text??``);return j([...t,``,n].join(N))}function z(e,t){if(e){if(e.mimeType===t&&e.body?.data)return M(e.body.data);for(let n of e.parts??[]){let e=z(n,t);if(e!==void 0)return e}}}function B(e){return z(e,`text/plain`)??z(e,`text/html`)?.replace(/<[^>]*>/g,` `).replace(/\s+/g,` `).trim()??``}function V(e,t){let n=t=>String(e.payload?.headers?.find(e=>e.name.toLowerCase()===t)?.value??``),r=e.internalDate?new Date(Number(e.internalDate)):new Date(n(`date`));return{id:String(e.id),threadId:String(e.threadId??e.id),from:n(`from`),to:n(`to`),subject:n(`subject`),date:(Number.isNaN(r.getTime())?new Date:r).toISOString(),text:(B(e.payload)||String(e.snippet??``)).trim(),direction:(e.labelIds??[]).includes(`SENT`)?`outbound`:`inbound`,cursor:t}}var H=class{config;constructor(e){this.config=e}getConfigurationState(){return O(`GmailEmailSender`,this.config.auth)}isConfigured(){return this.getConfigurationState().configured}async send(e){let t=this.getConfigurationState();if(!t.configured)throw new _(`not-configured`,D,t.reason??`Gmail is not configured`);let n=C(D,e,this.config.from,{fromRequired:!1}),r=this.config.fetch??globalThis.fetch,i=await k(this.config.auth,r),a=await y(D,r,`${w}/messages/send`,{method:`POST`,headers:{authorization:`Bearer ${i}`,"content-type":`application/json`},body:JSON.stringify({raw:R(n),...n.threadId?{threadId:n.threadId}:{}})});return{id:String(a.id??``),...a.threadId?{threadId:String(a.threadId)}:{}}}},U=[`Google Cloud console → APIs & Services → enable the Gmail API.`,`OAuth consent screen: add the Gmail scopes the app asks for.`,`Credentials → Create OAuth client ID → Web application, with the redirect URI of the app's server.`,`In the app, connect the mailbox: Google asks the owner for consent.`],W=[{id:`resend`,label:`Resend`,description:`Developer-first transactional email API.`,credentialFields:[{key:`apiKey`,label:`API Key`,type:`password`,placeholder:`re_...`},{key:`from`,label:`Sender`,type:`text`,placeholder:`App <noreply@yourdomain.com>`}],credentialsUrl:`https://resend.com/api-keys`,credentialsHint:`Resend dashboard → API Keys → Create API Key. The sender's domain must be verified under Domains.`},{id:`brevo`,label:`Brevo`,description:`Transactional and marketing email, EU-based.`,credentialFields:[{key:`apiKey`,label:`API Key`,type:`password`,placeholder:`xkeysib-...`},{key:`from`,label:`Sender`,type:`text`,placeholder:`App <noreply@yourdomain.com>`}],credentialsUrl:`https://app.brevo.com/settings/keys/api`,credentialsHint:`Brevo → SMTP & API → API Keys → Generate a new API key. The sender must be verified under Senders, Domains & Dedicated IPs.`},{id:`gmail`,label:`Gmail`,description:`Send from a Gmail or Google Workspace mailbox, with the owner's consent.`,credentialFields:[],credentialsUrl:`https://console.cloud.google.com/apis/credentials`,credentialsSteps:U},{id:`sendgrid`,label:`SendGrid`,description:`Scalable email delivery by Twilio.`,credentialFields:[{key:`apiKey`,label:`API Key`,type:`password`,placeholder:`SG...`}],credentialsUrl:`https://app.sendgrid.com/settings/api_keys`,credentialsHint:`SendGrid dashboard → Settings → API Keys → Create API Key.`},{id:`mailgun`,label:`Mailgun`,description:`Email API with analytics and webhooks.`,credentialFields:[{key:`apiKey`,label:`API Key`,type:`password`},{key:`domain`,label:`Mailgun domain`,type:`text`,placeholder:`mg.yourdomain.com`}],credentialsUrl:`https://app.mailgun.com/settings/api_security/api_keys`,credentialsHint:`Mailgun dashboard → Security → API Keys. The domain is the sending domain you verified under Sending → Domains.`},{id:`postmark`,label:`Postmark`,description:`High-deliverability transactional email.`,credentialFields:[{key:`serverToken`,label:`Server API Token`,type:`password`}],credentialsUrl:`https://account.postmarkapp.com/servers`,credentialsHint:`Postmark → your Server → API Tokens tab (each server has its own token).`},{id:`ses`,label:`AWS SES`,description:`Amazon Simple Email Service.`,credentialFields:[{key:`accessKeyId`,label:`Access Key ID`,type:`text`},{key:`secretAccessKey`,label:`Secret Access Key`,type:`password`},{key:`region`,label:`Region`,type:`text`,placeholder:`us-east-1`}],credentialsUrl:`https://console.aws.amazon.com/iam/home#/security_credentials`,credentialsHint:`AWS IAM → Security credentials → Access keys, for a user with SES send permissions.`},{id:`smtp`,label:`SMTP`,description:`Any SMTP-compatible mail server.`,credentialFields:[{key:`host`,label:`Host`,type:`text`,placeholder:`smtp.example.com`},{key:`port`,label:`Port`,type:`text`,placeholder:`587`},{key:`username`,label:`Username`,type:`text`},{key:`password`,label:`Password`,type:`password`}],credentialsHint:`Ask your mail host or hosting provider for the SMTP host, port, and credentials.`}],G=[{id:`gmail`,label:`Gmail`,description:`New mails arrive at once, through a Pub/Sub topic of Google Cloud.`,credentialFields:[],credentialsUrl:`https://console.cloud.google.com/cloudpubsub/topic/list`,credentialsSteps:[...U,`Pub/Sub: create a topic and give roles/pubsub.publisher on it to gmail-api-push@system.gserviceaccount.com.`]},{id:`imap`,label:`IMAP`,description:`Any other mailbox, read every few minutes.`,credentialFields:[{key:`host`,label:`IMAP server`,type:`text`,placeholder:`imap.example.com`},{key:`port`,label:`Port`,type:`text`,placeholder:`993`},{key:`user`,label:`User`,type:`text`,placeholder:`name@example.com`},{key:`password`,label:`Password`,type:`password`}],credentialsHint:`The IMAP settings of your mail provider. Use an app password where the provider has them.`}],ce=`https://api.resend.com/emails`,le=class{config;constructor(e){this.config=e}getConfigurationState(){return t(`ResendEmailSender`,e(this.config,[`apiKey`]))}isConfigured(){return this.getConfigurationState().configured}async send(e){if(!this.isConfigured())throw new _(`not-configured`,`Resend`,`Resend has no API key`);let t=C(`Resend`,e,this.config.from),n=e=>e.length?e.map(S):void 0,r=await y(`Resend`,this.config.fetch??globalThis.fetch,ce,{method:`POST`,headers:{authorization:`Bearer ${this.config.apiKey}`,"content-type":`application/json`},body:JSON.stringify({from:S(t.from),to:n(t.to),cc:n(t.cc),bcc:n(t.bcc),reply_to:t.replyTo?S(t.replyTo):void 0,subject:t.subject,html:t.html,text:t.text})});return{id:String(r.id??``)}}},ue=`https://api.brevo.com/v3/smtp/email`,K=({email:e,name:t})=>t?{email:e,name:t}:{email:e},de=class{config;constructor(e){this.config=e}getConfigurationState(){return t(`BrevoEmailSender`,e(this.config,[`apiKey`]))}isConfigured(){return this.getConfigurationState().configured}async send(e){if(!this.isConfigured())throw new _(`not-configured`,`Brevo`,`Brevo has no API key`);let t=C(`Brevo`,e,this.config.from),n=e=>e.length?e.map(K):void 0,r=await y(`Brevo`,this.config.fetch??globalThis.fetch,ue,{method:`POST`,headers:{"api-key":this.config.apiKey,"content-type":`application/json`,accept:`application/json`},body:JSON.stringify({sender:K(t.from),to:n(t.to),cc:n(t.cc),bcc:n(t.bcc),replyTo:t.replyTo?K(t.replyTo):void 0,subject:t.subject,htmlContent:t.html,textContent:t.text})});return{id:String(r.messageId??``)}}},fe=new Set([`free`,`latest`]),q=/^(\d{4}|\d{8}|\d{4}-\d{2}-\d{2})$/,J=/\s*\(([^()]*)\)\s*$/,pe=/^(thinking|reasoning|non-reasoning|fast)$/i,Y=e=>{let t=e.trim(),n=[];for(let e=J.exec(t);e;e=J.exec(t))pe.test(e[1].trim())&&n.unshift(e[1].trim()),t=t.slice(0,e.index);let r=t.toLowerCase().replace(/[()[\]]/g,` `).split(/\s+/).filter(Boolean);for(;r.length>1&&(fe.has(r[r.length-1])||q.test(r[r.length-1]));)r.pop();return[...r,...n.map(e=>e.toLowerCase())].join(` `)},X=({id:e,name:t})=>{let n=t.toLowerCase().split(/\s+/);return(e.startsWith(`~`)?8:0)+(n.includes(`free`)?4:0)+(n.includes(`latest`)?2:0)+ +!!q.test(n[n.length-1]??``)+ +!!J.test(t)},me=(e,t)=>X(e)-X(t)||e.id.length-t.id.length||e.id.localeCompare(t.id);function Z(e){let t={},n={};for(let[r,{name:i,models:a={}}]of Object.entries(e)){n[r]=i||r;for(let[e,n]of Object.entries(a)){let i=n?.name?.trim()||e,a=Y(i),o=t[a]??={key:a,name:i,providers:{}};!o.family&&n?.family&&(o.family=n.family);let s=n?.cost?.input,c=n?.cost?.output,l=typeof s==`number`&&typeof c==`number`?{input:s,output:c}:void 0;(o.providers[r]??=[]).push({id:e,name:i,...l?{cost:l}:{}}),X({id:e,name:i})<X({id:``,name:o.name})&&(o.name=i)}}for(let e of Object.values(t))for(let t of Object.values(e.providers))t.sort(me);return{models:t,providers:n}}function he(e,t,n){let r=e.models[Y(t)];if(r)for(let e of n){let t=r.providers[e]?.[0];if(t)return{provider:e,model:t.id,ref:`${e}/${t.id}`}}}function ge(e,t,n){let r=e.models[Y(t)];return r?(n??Object.keys(r.providers).sort()).flatMap(e=>{let t=r.providers[e]?.[0];return t?[{provider:e,model:t.id,ref:`${e}/${t.id}`,...t.cost?{cost:t.cost}:{}}]:[]}):[]}var Q=null,$=null;function _e(e=globalThis.fetch){return $?Promise.resolve($):(Q??=e(d).then(e=>e.ok?e.json():Promise.reject(Error(`models.dev: HTTP ${e.status}`))).then(e=>$=Z(e)).finally(()=>{Q=null}),Q)}Object.defineProperty(exports,"A",{enumerable:!0,get:function(){return n}}),Object.defineProperty(exports,"C",{enumerable:!0,get:function(){return r}}),Object.defineProperty(exports,"D",{enumerable:!0,get:function(){return o}}),Object.defineProperty(exports,"E",{enumerable:!0,get:function(){return ee}}),Object.defineProperty(exports,"O",{enumerable:!0,get:function(){return t}}),Object.defineProperty(exports,"S",{enumerable:!0,get:function(){return l}}),Object.defineProperty(exports,"T",{enumerable:!0,get:function(){return s}}),Object.defineProperty(exports,"_",{enumerable:!0,get:function(){return O}}),Object.defineProperty(exports,"a",{enumerable:!0,get:function(){return he}}),Object.defineProperty(exports,"b",{enumerable:!0,get:function(){return y}}),Object.defineProperty(exports,"c",{enumerable:!0,get:function(){return G}}),Object.defineProperty(exports,"d",{enumerable:!0,get:function(){return w}}),Object.defineProperty(exports,"f",{enumerable:!0,get:function(){return E}}),Object.defineProperty(exports,"g",{enumerable:!0,get:function(){return k}}),Object.defineProperty(exports,"h",{enumerable:!0,get:function(){return M}}),Object.defineProperty(exports,"i",{enumerable:!0,get:function(){return ge}}),Object.defineProperty(exports,"k",{enumerable:!0,get:function(){return e}}),Object.defineProperty(exports,"l",{enumerable:!0,get:function(){return W}}),Object.defineProperty(exports,"m",{enumerable:!0,get:function(){return D}}),Object.defineProperty(exports,"n",{enumerable:!0,get:function(){return Y}}),Object.defineProperty(exports,"o",{enumerable:!0,get:function(){return de}}),Object.defineProperty(exports,"p",{enumerable:!0,get:function(){return T}}),Object.defineProperty(exports,"r",{enumerable:!0,get:function(){return _e}}),Object.defineProperty(exports,"s",{enumerable:!0,get:function(){return le}}),Object.defineProperty(exports,"t",{enumerable:!0,get:function(){return Z}}),Object.defineProperty(exports,"u",{enumerable:!0,get:function(){return H}}),Object.defineProperty(exports,"v",{enumerable:!0,get:function(){return V}}),Object.defineProperty(exports,"w",{enumerable:!0,get:function(){return c}}),Object.defineProperty(exports,"x",{enumerable:!0,get:function(){return g}}),Object.defineProperty(exports,"y",{enumerable:!0,get:function(){return _}});
3
+ //# sourceMappingURL=modelIdentity-DX62i7g2.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"modelIdentity-DX62i7g2.js","names":["pending","getMissingKeys","keys","value","config","key","prefix","createConfigurationState","missingKeys","provider","getProviderConfigurationState","fallbackName","configured","SERVER_CREDENTIAL_PREFIX","SCOPE_PATTERN","PLACEHOLDER_PATTERN","serverCredential","scope","findServerCredentials","text","placeholder","containsServerCredential","AI_PROVIDER_HOSTS","trimSlashes","path","isAIProviderTarget","connection","base","basePath","target","MODELS_DEV_URL","CACHE_KEY","CACHE_TTL_MS","MODELS_DEV_PROVIDER_KEYS","pending$1","readCache","raw","cached","writeCache","buildPricingIndex","wanted","index","models","catalog","prices","modelId","entry","input","output","loadPricingIndex","response","error","candidateIds","lower","model","undated","lookupModelsDevPricing","providerId","id","hit","withModelsDevPricing","m","pricing","EmailProviderError","message","options","code","emailErrorCode","status","callEmailApi","http","url","init","cause","toEmailAddress","recipient","toEmailAddresses","recipients","address","formatEmailAddress","name","email","prepareEmail","from","params","defaultFrom","sender","fromRequired","to","cc","bcc","GMAIL_API","GOOGLE_TOKEN_URL","GMAIL_SCOPES","PROVIDER","gmailConfigurationState","auth","gmailAccessToken","grant","bytesToBase64","binary","i","bytes","base64","base64Url","fromBase64Url","data","char","CRLF","headerText","headerAddress","partHeaders","type","partBody","body","gmailRawMessage","headers","mail","line","boundary","section","content","partOf","payload","mime","child","found","textOf","toEmailMessage","header","h","arrived","GmailEmailSender","state","token","sent","GMAIL_CONSENT_STEPS","EMAIL_SEND_PROVIDER_DEFINITIONS","EMAIL_READ_PROVIDER_DEFINITIONS","RESEND_EMAIL_URL","ResendEmailSender","list","addresses","BREVO_EMAIL_URL","brevoAddress","BrevoEmailSender","VARIANT_WORDS","DATE_WORD","TRAILING_GROUP","MODE_GROUP","canonicalModelName","rest","modes","group","words","mode","instability","byStability","left","right","buildModelIdentityIndex","providers","providerName","entries","identity","cost","resolveModel","best","modelOffers","pending","loaded","loadModelIdentityIndex","fetchFn"],"sources":["../src/providers/ProviderConfiguration.ts","../src/providers/ai/serverCredentials.ts","../src/providers/ai/modelsDevPricing.ts","../src/providers/email/EmailError.ts","../src/providers/email/EmailSend.ts","../src/providers/email/gmail/gmailApi.ts","../src/providers/email/gmail/GmailEmailSender.ts","../src/providers/email/definitions.ts","../src/providers/email/resend/ResendEmailSender.ts","../src/providers/email/brevo/BrevoEmailSender.ts","../src/providers/ai/modelIdentity.ts"],"sourcesContent":["export interface ProviderConfigurationState {\r\n configured: boolean;\r\n reason?: string;\r\n missingKeys?: string[];\r\n}\r\n\r\nexport interface ProviderConfigurable {\r\n isConfigured?(): boolean;\r\n getConfigurationState?(): ProviderConfigurationState;\r\n /**\r\n * Optional teardown hook. Called by setProvider() before an adapter is replaced\r\n * in the registry — e.g. unsubscribe realtime listeners, sign out, close connections.\r\n * Never called on unmount; only on explicit replacement.\r\n */\r\n dispose?(): Promise<void> | void;\r\n}\r\n\r\nexport const getMissingKeys = (\r\n config: Record<string, unknown> | undefined,\r\n keys: string[],\r\n prefix = ''\r\n): string[] => keys\r\n .filter((key) => {\r\n const value = config?.[key];\r\n return value === undefined || value === null || value === '';\r\n })\r\n .map((key) => `${prefix}${key}`);\r\n\r\nexport const createConfigurationState = (\r\n provider: string,\r\n missingKeys: string[]\r\n): ProviderConfigurationState => ({\r\n configured: missingKeys.length === 0,\r\n missingKeys,\r\n reason: missingKeys.length\r\n ? `${provider} is not configured. Missing: ${missingKeys.join(', ')}.`\r\n : undefined,\r\n});\r\n\r\nexport const getProviderConfigurationState = (\r\n provider: ProviderConfigurable | null | undefined,\r\n fallbackName = 'Provider'\r\n): ProviderConfigurationState => {\r\n if (!provider) {\r\n return {\r\n configured: false,\r\n reason: `${fallbackName} is not registered.`,\r\n };\r\n }\r\n\r\n if (provider.getConfigurationState) {\r\n return provider.getConfigurationState();\r\n }\r\n\r\n if (provider.isConfigured) {\r\n const configured = provider.isConfigured();\r\n return {\r\n configured,\r\n reason: configured ? undefined : `${fallbackName} is not configured.`,\r\n };\r\n }\r\n\r\n return { configured: true };\r\n};\r\n","// Server-held AI credentials (CR-102). An app that cannot hold provider keys in the browser builds\n// its adapters with a placeholder where the key goes; its proxy swaps the placeholder for the real\n// key, only towards a host of that provider. Pure module: no React, no config — the browser\n// registry, proxyFetch and the Node entry (`@llmnative/react/server`) all share it.\n\nexport const SERVER_CREDENTIAL_PREFIX = 'llmnative-server-credential';\n\nconst SCOPE_PATTERN = /^[A-Za-z0-9_-]+$/;\nconst PLACEHOLDER_PATTERN = /llmnative-server-credential:([A-Za-z0-9_-]+):([a-z-]+)/g;\n\n/** A provider connected on the server: its id, and what its URLs are built from. Never the key. */\nexport type ServerAIConnection = {\n id: string;\n /** Cloudflare: the account in every Workers AI URL. */\n accountId?: string;\n /** openai-compatible: the endpoint. */\n baseUrl?: string;\n};\n\n/** The placeholder an adapter writes where the key goes: `llmnative-server-credential:<scope>:<provider>`. */\nexport const serverCredential = (scope: string, provider: string): string => {\n if (!SCOPE_PATTERN.test(scope)) throw new Error(`Server credential scope \"${scope}\" may only contain letters, digits, \"-\" and \"_\".`);\n return `${SERVER_CREDENTIAL_PREFIX}:${scope}:${provider}`;\n};\n\n/** Every placeholder in `text`, as scope and provider. */\nexport const findServerCredentials = (text: string): Array<{ placeholder: string; scope: string; provider: string }> =>\n [...text.matchAll(PLACEHOLDER_PATTERN)].map(([placeholder, scope, provider]) => ({ placeholder, scope, provider }));\n\nexport const containsServerCredential = (text: string): boolean => text.includes(`${SERVER_CREDENTIAL_PREFIX}:`);\n\n/** The fixed API host of each built-in provider (openai-compatible and Cloudflare depend on the connection). */\nexport const AI_PROVIDER_HOSTS: Readonly<Record<string, string>> = {\n openai: 'api.openai.com',\n openrouter: 'openrouter.ai',\n opencode: 'opencode.ai',\n 'opencode-go': 'opencode.ai',\n deepseek: 'api.deepseek.com',\n gemini: 'generativelanguage.googleapis.com',\n anthropic: 'api.anthropic.com',\n mistral: 'api.mistral.ai',\n glm: 'open.bigmodel.cn',\n groq: 'api.groq.com',\n cloudflare: 'api.cloudflare.com',\n};\n\nconst trimSlashes = (path: string) => path.replace(/\\/+$/, '');\n\n/** Whether `target` is an API URL of the provider of `connection`: where its key may go. */\nexport const isAIProviderTarget = (connection: ServerAIConnection, target: URL): boolean => {\n if (connection.id === 'openai-compatible') {\n if (!connection.baseUrl) return false;\n const base = new URL(connection.baseUrl);\n const basePath = trimSlashes(base.pathname);\n return target.origin === base.origin && (target.pathname === basePath || target.pathname.startsWith(`${basePath}/`));\n }\n if (target.protocol !== 'https:' || target.host !== AI_PROVIDER_HOSTS[connection.id]) return false;\n // Cloudflare: one host for every account, so the key only goes to the account it belongs to.\n if (connection.id === 'cloudflare') {\n return Boolean(connection.accountId) && target.pathname.startsWith(`/client/v4/accounts/${encodeURIComponent(connection.accountId!.trim())}/`);\n }\n return true;\n};\n","import type { AIModelDescriptor, AIModelPricing } from './AIProvider';\r\n\r\n/** Public, CORS-open catalog (`access-control-allow-origin: *`) of models with their token\r\n * prices, in USD per 1M tokens — the same unit AIModelPricing uses. */\r\nexport const MODELS_DEV_URL = 'https://models.dev/api.json';\r\n\r\nconst CACHE_KEY = 'ai.pricing.modelsdev';\r\nconst CACHE_TTL_MS = 1000 * 60 * 60 * 24;\r\n\r\n/** Built-in provider id → models.dev provider keys, in lookup order. `openai-compatible` has\r\n * no entry: its endpoint is arbitrary, so no catalog can know its prices. */\r\nconst MODELS_DEV_PROVIDER_KEYS: Record<string, string[]> = {\r\n openai: ['openai'],\r\n openrouter: ['openrouter'],\r\n opencode: ['opencode'],\r\n 'opencode-go': ['opencode-go'],\r\n deepseek: ['deepseek'],\r\n gemini: ['google'],\r\n anthropic: ['anthropic'],\r\n mistral: ['mistral'],\r\n // The GLM adapter targets open.bigmodel.cn (zhipuai); `zai` is the international twin.\r\n glm: ['zhipuai', 'zai'],\r\n cloudflare: ['cloudflare-workers-ai'],\r\n groq: ['groq'],\r\n};\r\n\r\n/** Compact cache shape: providerKey → modelId → [input, output]. The raw catalog is ~5 MB,\r\n * this keeps only the providers above (~700 entries). */\r\ntype PricingIndex = Record<string, Record<string, [number, number]>>;\r\n\r\ntype ModelsDevCatalog = Record<string, { models?: Record<string, { cost?: { input?: unknown; output?: unknown } }> }>;\r\n\r\nlet pending: Promise<PricingIndex> | null = null;\r\n\r\nconst readCache = (): PricingIndex | null => {\r\n if (typeof localStorage === 'undefined') return null;\r\n try {\r\n const raw = localStorage.getItem(CACHE_KEY);\r\n if (!raw) return null;\r\n const cached = JSON.parse(raw) as { fetchedAt?: number; index?: PricingIndex };\r\n if (!cached.fetchedAt || !cached.index || Date.now() - cached.fetchedAt > CACHE_TTL_MS) return null;\r\n return cached.index;\r\n } catch {\r\n return null;\r\n }\r\n};\r\n\r\nconst writeCache = (index: PricingIndex) => {\r\n if (typeof localStorage === 'undefined') return;\r\n try {\r\n localStorage.setItem(CACHE_KEY, JSON.stringify({ fetchedAt: Date.now(), index }));\r\n } catch {\r\n // Ignore quota/storage failures: pricing still works in-memory for this session.\r\n }\r\n};\r\n\r\nexport const buildPricingIndex = (catalog: ModelsDevCatalog): PricingIndex => {\r\n const wanted = new Set(Object.values(MODELS_DEV_PROVIDER_KEYS).flat());\r\n const index: PricingIndex = {};\r\n for (const key of wanted) {\r\n const models = catalog[key]?.models ?? {};\r\n const prices: Record<string, [number, number]> = {};\r\n for (const [modelId, entry] of Object.entries(models)) {\r\n const input = entry?.cost?.input;\r\n const output = entry?.cost?.output;\r\n if (typeof input === 'number' && typeof output === 'number') prices[modelId] = [input, output];\r\n }\r\n index[key] = prices;\r\n }\r\n return index;\r\n};\r\n\r\n/** One fetch per session at most (in-flight promise shared by every provider), then the\r\n * localStorage copy for 24h. A failed fetch resolves to an empty index — every model then\r\n * shows \"price not found\" — and is not cached, so the next catalog load retries. */\r\nconst loadPricingIndex = (): Promise<PricingIndex> => {\r\n const cached = readCache();\r\n if (cached) return Promise.resolve(cached);\r\n if (!pending) {\r\n pending = fetch(MODELS_DEV_URL)\r\n .then((response) => (response.ok ? response.json() : Promise.reject(new Error(`HTTP ${response.status}`))))\r\n .then((catalog: ModelsDevCatalog) => {\r\n const index = buildPricingIndex(catalog);\r\n writeCache(index);\r\n return index;\r\n })\r\n .catch((error) => {\r\n console.warn('models.dev pricing unavailable', error);\r\n return {};\r\n })\r\n .finally(() => { pending = null; });\r\n }\r\n return pending;\r\n};\r\n\r\n/** Provider ids often carry a release date models.dev lists only under the undated alias\r\n * (e.g. `gpt-4o-mini-2024-07-18`); exact match wins, the stripped id is the fallback. */\r\nconst candidateIds = (model: string): string[] => {\r\n const lower = model.toLowerCase();\r\n const undated = lower.replace(/-(\\d{8}|\\d{4}-\\d{2}-\\d{2})$/, '');\r\n return [...new Set([model, lower, undated])];\r\n};\r\n\r\nexport const lookupModelsDevPricing = (index: PricingIndex, providerId: string, model: string): AIModelPricing | undefined => {\r\n for (const key of MODELS_DEV_PROVIDER_KEYS[providerId] ?? []) {\r\n const prices = index[key];\r\n if (!prices) continue;\r\n for (const id of candidateIds(model)) {\r\n const hit = prices[id];\r\n if (hit) return { input: hit[0], output: hit[1], currency: 'USD', source: 'models.dev' };\r\n }\r\n }\r\n return undefined;\r\n};\r\n\r\n/** Fills `pricing` from models.dev on the models the provider listing left without one.\r\n * Models are never dropped: a model with no match simply keeps `pricing` undefined. */\r\nexport const withModelsDevPricing = async (providerId: string, models: AIModelDescriptor[]): Promise<AIModelDescriptor[]> => {\r\n if (!MODELS_DEV_PROVIDER_KEYS[providerId] || models.every((m) => m.pricing)) return models;\r\n const index = await loadPricingIndex();\r\n return models.map((m) => {\r\n if (m.pricing) return m;\r\n const pricing = lookupModelsDevPricing(index, providerId, m.model);\r\n return pricing ? { ...m, pricing } : m;\r\n });\r\n};\r\n\r\n/** Test-only: drops the in-flight promise between cases. */\r\nexport const resetModelsDevPricingForTests = () => { pending = null; };\r\n","// The one error every email adapter throws (CR-130): a code the app can act on, the provider and the HTTP\n// status. The message never carries a key, a token or the body of the provider's answer.\n\nexport type EmailProviderErrorCode =\n /** The adapter misses a setting (key, sender, client, library). */\n | 'not-configured'\n /** The message itself is not valid: no recipient, no body. */\n | 'invalid-message'\n /** The provider refused the credentials (401/403, revoked token). */\n | 'unauthorized'\n /** The provider refused the message (400/422: sender not verified, address not valid). */\n | 'rejected'\n | 'rate-limited'\n /** The provider or the network did not answer. */\n | 'unavailable'\n | 'unknown';\n\nexport class EmailProviderError extends Error {\n readonly code: EmailProviderErrorCode;\n readonly provider: string;\n readonly status?: number;\n\n constructor(code: EmailProviderErrorCode, provider: string, message: string, options: { status?: number; cause?: unknown } = {}) {\n super(message, options.cause === undefined ? undefined : { cause: options.cause });\n this.name = 'EmailProviderError';\n this.code = code;\n this.provider = provider;\n this.status = options.status;\n }\n}\n\n/** The code of an HTTP status of a provider. */\nexport const emailErrorCode = (status: number): EmailProviderErrorCode => {\n if (status === 401 || status === 403) return 'unauthorized';\n if (status === 429) return 'rate-limited';\n if (status === 400 || status === 404 || status === 409 || status === 422) return 'rejected';\n if (status >= 500) return 'unavailable';\n return 'unknown';\n};\n\n/**\n * A JSON call to a provider: the answer, or an `EmailProviderError` with the status and the path only.\n * A network failure is `unavailable`.\n */\nexport async function callEmailApi<T>(\n provider: string,\n http: typeof fetch,\n url: string,\n init: RequestInit,\n): Promise<T> {\n let response: Response;\n try {\n response = await http(url, init);\n } catch (cause) {\n throw new EmailProviderError('unavailable', provider, `${provider} did not answer`, { cause });\n }\n if (!response.ok) {\n throw new EmailProviderError(emailErrorCode(response.status), provider, `${provider} answered ${response.status} on ${new URL(url).pathname}`, { status: response.status });\n }\n const text = await response.text();\n return (text ? JSON.parse(text) : {}) as T;\n}\n","import type { ProviderConfigurable } from '../ProviderConfiguration';\r\nimport { EmailProviderError } from './EmailError';\r\n\r\n// The category `emailSend` (CR-130): one contract to send a message, whatever provider sends it.\r\n\r\n/** An address, with the name shown to who reads it. */\r\nexport type EmailAddress = { email: string; name?: string };\r\n\r\n/** An address as text (`ada@example.com`) or with its name. */\r\nexport type EmailRecipient = string | EmailAddress;\r\n\r\nexport interface EmailSendParams {\r\n /** The sender; without it, the adapter's own `from`. */\r\n from?: EmailRecipient;\r\n to: EmailRecipient | EmailRecipient[];\r\n cc?: EmailRecipient | EmailRecipient[];\r\n bcc?: EmailRecipient | EmailRecipient[];\r\n replyTo?: EmailRecipient;\r\n subject: string;\r\n /** The body as HTML, as text, or both (the reader's client picks). At least one. */\r\n html?: string;\r\n text?: string;\r\n /** Gmail: the conversation the message joins. The other providers ignore it. */\r\n threadId?: string;\r\n}\r\n\r\nexport interface EmailSendResult {\r\n /** The id the provider gave the message. */\r\n id: string;\r\n threadId?: string;\r\n}\r\n\r\nexport interface EmailSendAdapter extends ProviderConfigurable {\r\n send(params: EmailSendParams): Promise<EmailSendResult>;\r\n}\r\n\r\n/** An address as `{ email, name? }`. */\r\nexport const toEmailAddress = (recipient: EmailRecipient): EmailAddress =>\r\n typeof recipient === 'string' ? { email: recipient.trim() } : { email: recipient.email.trim(), ...(recipient.name ? { name: recipient.name } : {}) };\r\n\r\n/** One or more addresses as a list, without the empty ones. */\r\nexport const toEmailAddresses = (recipients: EmailRecipient | EmailRecipient[] | undefined): EmailAddress[] =>\r\n (recipients === undefined ? [] : Array.isArray(recipients) ? recipients : [recipients])\r\n .map(toEmailAddress)\r\n .filter((address) => address.email !== '');\r\n\r\n/** An address as a header shows it: `Ada <ada@example.com>`, or the bare address. */\r\nexport const formatEmailAddress = ({ email, name }: EmailAddress): string =>\r\n name ? `${/[\",<>@;:]/.test(name) ? JSON.stringify(name) : name} <${email}>` : email;\r\n\r\n/**\r\n * The message an adapter sends: sender and recipients as addresses, at least one recipient and one body.\r\n * Throws `invalid-message` or (no sender, when the provider needs one) `not-configured`.\r\n */\r\nexport function prepareEmail(provider: string, params: EmailSendParams, defaultFrom: EmailRecipient | undefined, { fromRequired = true } = {}) {\r\n const from = params.from ?? defaultFrom;\r\n const sender = from ? toEmailAddress(from) : undefined;\r\n if (fromRequired && !sender?.email) throw new EmailProviderError('not-configured', provider, `${provider} has no sender`);\r\n const to = toEmailAddresses(params.to);\r\n const cc = toEmailAddresses(params.cc);\r\n const bcc = toEmailAddresses(params.bcc);\r\n if (to.length + cc.length + bcc.length === 0) throw new EmailProviderError('invalid-message', provider, 'The message has no recipient');\r\n if (!params.html && !params.text) throw new EmailProviderError('invalid-message', provider, 'The message has no body');\r\n return {\r\n from: sender?.email ? sender : undefined,\r\n to, cc, bcc,\r\n replyTo: params.replyTo ? toEmailAddress(params.replyTo) : undefined,\r\n subject: params.subject,\r\n html: params.html || undefined,\r\n text: params.text || undefined,\r\n threadId: params.threadId,\r\n };\r\n}\r\n\r\nexport type PreparedEmail = ReturnType<typeof prepareEmail>;\r\n","import { createConfigurationState, getMissingKeys, type ProviderConfigurationState } from '../../ProviderConfiguration';\r\nimport { callEmailApi, EmailProviderError } from '../EmailError';\r\nimport { formatEmailAddress, type PreparedEmail } from '../EmailSend';\r\nimport type { EmailMessage } from '../EmailRead';\r\n\r\n// The Gmail REST API as the Gmail adapters use it (CR-130): only `fetch`, `TextEncoder` and `btoa`/`atob`,\r\n// so the same code runs in a browser and on a server.\r\n\r\nexport const GMAIL_API = 'https://gmail.googleapis.com/gmail/v1/users/me';\r\nexport const GOOGLE_TOKEN_URL = 'https://oauth2.googleapis.com/token';\r\n\r\n/** The scopes of Gmail: read the mailbox, send from it. */\r\nexport const GMAIL_SCOPES = {\r\n read: 'https://www.googleapis.com/auth/gmail.readonly',\r\n send: 'https://www.googleapis.com/auth/gmail.send',\r\n} as const;\r\n\r\n/**\r\n * How a Gmail adapter gets its access token: a function (the browser's Google sign-in), or the OAuth client\r\n * of the app and the refresh token of the mailbox (a server).\r\n */\r\nexport type GmailAuth =\r\n | { accessToken: () => Promise<string> }\r\n | { clientId: string; clientSecret: string; refreshToken: string };\r\n\r\nexport const PROVIDER = 'Gmail';\r\n\r\n/** Whether the auth has what it needs. */\r\nexport function gmailConfigurationState(name: string, auth: GmailAuth | undefined): ProviderConfigurationState {\r\n if (auth && 'accessToken' in auth) return createConfigurationState(name, typeof auth.accessToken === 'function' ? [] : ['accessToken']);\r\n return createConfigurationState(name, getMissingKeys(auth as Record<string, unknown> | undefined, ['clientId', 'clientSecret', 'refreshToken']));\r\n}\r\n\r\n/** A fresh access token: from the function, or exchanging the refresh token. */\r\nexport async function gmailAccessToken(auth: GmailAuth, http: typeof fetch): Promise<string> {\r\n if ('accessToken' in auth) return auth.accessToken();\r\n const grant = await callEmailApi<{ access_token?: string }>(PROVIDER, http, GOOGLE_TOKEN_URL, {\r\n method: 'POST',\r\n headers: { 'content-type': 'application/x-www-form-urlencoded' },\r\n body: new URLSearchParams({\r\n client_id: auth.clientId,\r\n client_secret: auth.clientSecret,\r\n refresh_token: auth.refreshToken,\r\n grant_type: 'refresh_token',\r\n }),\r\n });\r\n if (!grant.access_token) throw new EmailProviderError('unauthorized', PROVIDER, 'Google returned no access token');\r\n return grant.access_token;\r\n}\r\n\r\n// ── Base64 ───────────────────────────────────────────────────────────────────\r\n\r\nconst bytesToBase64 = (bytes: Uint8Array): string => {\r\n let binary = '';\r\n for (let i = 0; i < bytes.length; i += 0x8000) binary += String.fromCharCode(...bytes.subarray(i, i + 0x8000));\r\n return btoa(binary);\r\n};\r\n\r\nconst base64 = (text: string): string => bytesToBase64(new TextEncoder().encode(text));\r\n\r\nexport const base64Url = (text: string): string => base64(text).replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '');\r\n\r\nexport const fromBase64Url = (data: string): string => {\r\n const binary = atob(data.replace(/-/g, '+').replace(/_/g, '/'));\r\n return new TextDecoder().decode(Uint8Array.from(binary, (char) => char.charCodeAt(0)));\r\n};\r\n\r\n// ── The message Gmail sends ───────────────────────────────────────────────────\r\n\r\nconst CRLF = '\\r\\n';\r\n\r\n/** A header value in UTF-8 when it is not plain ASCII (RFC 2047). */\r\nconst headerText = (text: string): string => (/^[\\x20-\\x7e]*$/.test(text) ? text : `=?UTF-8?B?${base64(text)}?=`);\r\n\r\nconst headerAddress = (address: { email: string; name?: string }): string =>\r\n address.name ? `${headerText(address.name)} <${address.email}>` : formatEmailAddress(address);\r\n\r\n/** The headers of a body part in UTF-8 and base64. */\r\nconst partHeaders = (type: string): string[] => [`Content-Type: ${type}; charset=UTF-8`, 'Content-Transfer-Encoding: base64'];\r\n\r\n/** A body in base64, 76 characters a line. */\r\nconst partBody = (body: string): string => (base64(body).match(/.{1,76}/g) ?? []).join(CRLF);\r\n\r\n/** The raw message (RFC 2822) in base64url, as `messages.send` takes it. */\r\nexport function gmailRawMessage(mail: PreparedEmail): string {\r\n const headers = [\r\n mail.from ? `From: ${headerAddress(mail.from)}` : null,\r\n mail.to.length ? `To: ${mail.to.map(headerAddress).join(', ')}` : null,\r\n mail.cc.length ? `Cc: ${mail.cc.map(headerAddress).join(', ')}` : null,\r\n mail.bcc.length ? `Bcc: ${mail.bcc.map(headerAddress).join(', ')}` : null,\r\n mail.replyTo ? `Reply-To: ${headerAddress(mail.replyTo)}` : null,\r\n `Subject: ${headerText(mail.subject)}`,\r\n 'MIME-Version: 1.0',\r\n ].filter((line): line is string => line !== null);\r\n let body: string;\r\n if (mail.html && mail.text) {\r\n const boundary = `llmnative-${Math.random().toString(36).slice(2)}`;\r\n headers.push(`Content-Type: multipart/alternative; boundary=\"${boundary}\"`);\r\n const section = (type: string, content: string) => [`--${boundary}`, ...partHeaders(type), '', partBody(content)].join(CRLF);\r\n body = [section('text/plain', mail.text), section('text/html', mail.html), `--${boundary}--`].join(CRLF);\r\n } else {\r\n headers.push(...partHeaders(mail.html ? 'text/html' : 'text/plain'));\r\n body = partBody(mail.html ?? mail.text ?? '');\r\n }\r\n return base64Url([...headers, '', body].join(CRLF));\r\n}\r\n\r\n// ── The messages Gmail reads ──────────────────────────────────────────────────\r\n\r\ntype GmailPart = { mimeType?: string; body?: { data?: string }; parts?: GmailPart[] };\r\n\r\n/** The first part of the tree with this MIME type and a body, decoded. */\r\nfunction partOf(payload: GmailPart | undefined, mime: string): string | undefined {\r\n if (!payload) return undefined;\r\n if (payload.mimeType === mime && payload.body?.data) return fromBase64Url(payload.body.data);\r\n for (const child of payload.parts ?? []) {\r\n const found = partOf(child, mime);\r\n if (found !== undefined) return found;\r\n }\r\n return undefined;\r\n}\r\n\r\n/** The text of a message: its text part wherever it is, else its HTML part without tags. */\r\nfunction textOf(payload: GmailPart | undefined): string {\r\n return partOf(payload, 'text/plain')\r\n ?? partOf(payload, 'text/html')?.replace(/<[^>]*>/g, ' ').replace(/\\s+/g, ' ').trim()\r\n ?? '';\r\n}\r\n\r\nexport type GmailApiMessage = {\r\n id: string;\r\n threadId?: string;\r\n labelIds?: string[];\r\n snippet?: string;\r\n internalDate?: string;\r\n payload?: GmailPart & { headers?: { name: string; value: string }[] };\r\n};\r\n\r\n/** A message of the API (`format=full`) as an `EmailMessage`, with the cursor after it. */\r\nexport function toEmailMessage(message: GmailApiMessage, cursor: string): EmailMessage {\r\n const header = (name: string) => String(message.payload?.headers?.find((h) => h.name.toLowerCase() === name)?.value ?? '');\r\n const arrived = message.internalDate ? new Date(Number(message.internalDate)) : new Date(header('date'));\r\n return {\r\n id: String(message.id),\r\n threadId: String(message.threadId ?? message.id),\r\n from: header('from'),\r\n to: header('to'),\r\n subject: header('subject'),\r\n date: (Number.isNaN(arrived.getTime()) ? new Date() : arrived).toISOString(),\r\n text: (textOf(message.payload) || String(message.snippet ?? '')).trim(),\r\n direction: (message.labelIds ?? []).includes('SENT') ? 'outbound' : 'inbound',\r\n cursor,\r\n };\r\n}\r\n","import type { ProviderConfigurationState } from '../../ProviderConfiguration';\nimport { callEmailApi, EmailProviderError } from '../EmailError';\nimport { prepareEmail, type EmailRecipient, type EmailSendAdapter, type EmailSendParams, type EmailSendResult } from '../EmailSend';\nimport { GMAIL_API, gmailAccessToken, gmailConfigurationState, gmailRawMessage, PROVIDER, type GmailAuth } from './gmailApi';\n\nexport type GmailEmailSenderConfig = {\n auth: GmailAuth;\n /** The sender shown; without it Gmail uses the address of the account (an alias must be set in Gmail). */\n from?: EmailRecipient;\n fetch?: typeof fetch;\n};\n\n/**\n * Sends from a Gmail mailbox (CR-130): with the browser's Google sign-in (`auth.accessToken`), or on a server\n * with the app's OAuth client and the mailbox's refresh token (scope `GMAIL_SCOPES.send`). `threadId` keeps a\n * reply in its conversation.\n */\nexport class GmailEmailSender implements EmailSendAdapter {\n constructor(protected readonly config: GmailEmailSenderConfig) {}\n\n getConfigurationState(): ProviderConfigurationState {\n return gmailConfigurationState('GmailEmailSender', this.config.auth);\n }\n\n isConfigured(): boolean {\n return this.getConfigurationState().configured;\n }\n\n async send(params: EmailSendParams): Promise<EmailSendResult> {\n const state = this.getConfigurationState();\n if (!state.configured) throw new EmailProviderError('not-configured', PROVIDER, state.reason ?? 'Gmail is not configured');\n const mail = prepareEmail(PROVIDER, params, this.config.from, { fromRequired: false });\n const http = this.config.fetch ?? globalThis.fetch;\n const token = await gmailAccessToken(this.config.auth, http);\n const sent = await callEmailApi<{ id?: string; threadId?: string }>(PROVIDER, http, `${GMAIL_API}/messages/send`, {\n method: 'POST',\n headers: { authorization: `Bearer ${token}`, 'content-type': 'application/json' },\n body: JSON.stringify({ raw: gmailRawMessage(mail), ...(mail.threadId ? { threadId: mail.threadId } : {}) }),\n });\n return { id: String(sent.id ?? ''), ...(sent.threadId ? { threadId: String(sent.threadId) } : {}) };\n }\n}\n","// Metadata for the email providers a \"connect\" UI can offer (CR-130): what each is called, the credentials it\n// needs and where to get them — separate from the runtime contracts (EmailSendAdapter, EmailReadAdapter).\n// The providers with an adapter in the framework: Resend, Brevo, Gmail (send); Gmail, IMAP (read).\nimport type { ProviderDescriptor } from '../ProviderDescriptor';\n\nexport type EmailSendProviderId = 'resend' | 'brevo' | 'gmail' | 'sendgrid' | 'mailgun' | 'postmark' | 'ses' | 'smtp';\nexport type EmailReadProviderId = 'gmail' | 'imap';\n\nconst GMAIL_CONSENT_STEPS = [\n 'Google Cloud console → APIs & Services → enable the Gmail API.',\n 'OAuth consent screen: add the Gmail scopes the app asks for.',\n 'Credentials → Create OAuth client ID → Web application, with the redirect URI of the app\\'s server.',\n 'In the app, connect the mailbox: Google asks the owner for consent.',\n];\n\nexport const EMAIL_SEND_PROVIDER_DEFINITIONS: ProviderDescriptor[] = [\n {\n id: 'resend',\n label: 'Resend',\n description: 'Developer-first transactional email API.',\n credentialFields: [\n { key: 'apiKey', label: 'API Key', type: 'password', placeholder: 're_...' },\n { key: 'from', label: 'Sender', type: 'text', placeholder: 'App <noreply@yourdomain.com>' },\n ],\n credentialsUrl: 'https://resend.com/api-keys',\n credentialsHint: 'Resend dashboard → API Keys → Create API Key. The sender\\'s domain must be verified under Domains.',\n },\n {\n id: 'brevo',\n label: 'Brevo',\n description: 'Transactional and marketing email, EU-based.',\n credentialFields: [\n { key: 'apiKey', label: 'API Key', type: 'password', placeholder: 'xkeysib-...' },\n { key: 'from', label: 'Sender', type: 'text', placeholder: 'App <noreply@yourdomain.com>' },\n ],\n credentialsUrl: 'https://app.brevo.com/settings/keys/api',\n credentialsHint: 'Brevo → SMTP & API → API Keys → Generate a new API key. The sender must be verified under Senders, Domains & Dedicated IPs.',\n },\n {\n id: 'gmail',\n label: 'Gmail',\n description: 'Send from a Gmail or Google Workspace mailbox, with the owner\\'s consent.',\n credentialFields: [],\n credentialsUrl: 'https://console.cloud.google.com/apis/credentials',\n credentialsSteps: GMAIL_CONSENT_STEPS,\n },\n {\n id: 'sendgrid',\n label: 'SendGrid',\n description: 'Scalable email delivery by Twilio.',\n credentialFields: [{ key: 'apiKey', label: 'API Key', type: 'password', placeholder: 'SG...' }],\n credentialsUrl: 'https://app.sendgrid.com/settings/api_keys',\n credentialsHint: 'SendGrid dashboard → Settings → API Keys → Create API Key.',\n },\n {\n id: 'mailgun',\n label: 'Mailgun',\n description: 'Email API with analytics and webhooks.',\n credentialFields: [\n { key: 'apiKey', label: 'API Key', type: 'password' },\n { key: 'domain', label: 'Mailgun domain', type: 'text', placeholder: 'mg.yourdomain.com' },\n ],\n credentialsUrl: 'https://app.mailgun.com/settings/api_security/api_keys',\n credentialsHint: 'Mailgun dashboard → Security → API Keys. The domain is the sending domain you verified under Sending → Domains.',\n },\n {\n id: 'postmark',\n label: 'Postmark',\n description: 'High-deliverability transactional email.',\n credentialFields: [{ key: 'serverToken', label: 'Server API Token', type: 'password' }],\n credentialsUrl: 'https://account.postmarkapp.com/servers',\n credentialsHint: 'Postmark → your Server → API Tokens tab (each server has its own token).',\n },\n {\n id: 'ses',\n label: 'AWS SES',\n description: 'Amazon Simple Email Service.',\n credentialFields: [\n { key: 'accessKeyId', label: 'Access Key ID', type: 'text' },\n { key: 'secretAccessKey', label: 'Secret Access Key', type: 'password' },\n { key: 'region', label: 'Region', type: 'text', placeholder: 'us-east-1' },\n ],\n credentialsUrl: 'https://console.aws.amazon.com/iam/home#/security_credentials',\n credentialsHint: 'AWS IAM → Security credentials → Access keys, for a user with SES send permissions.',\n },\n {\n id: 'smtp',\n label: 'SMTP',\n description: 'Any SMTP-compatible mail server.',\n credentialFields: [\n { key: 'host', label: 'Host', type: 'text', placeholder: 'smtp.example.com' },\n { key: 'port', label: 'Port', type: 'text', placeholder: '587' },\n { key: 'username', label: 'Username', type: 'text' },\n { key: 'password', label: 'Password', type: 'password' },\n ],\n credentialsHint: 'Ask your mail host or hosting provider for the SMTP host, port, and credentials.',\n },\n];\n\nexport const EMAIL_READ_PROVIDER_DEFINITIONS: ProviderDescriptor[] = [\n {\n id: 'gmail',\n label: 'Gmail',\n description: 'New mails arrive at once, through a Pub/Sub topic of Google Cloud.',\n credentialFields: [],\n credentialsUrl: 'https://console.cloud.google.com/cloudpubsub/topic/list',\n credentialsSteps: [\n ...GMAIL_CONSENT_STEPS,\n 'Pub/Sub: create a topic and give roles/pubsub.publisher on it to gmail-api-push@system.gserviceaccount.com.',\n ],\n },\n {\n id: 'imap',\n label: 'IMAP',\n description: 'Any other mailbox, read every few minutes.',\n credentialFields: [\n { key: 'host', label: 'IMAP server', type: 'text', placeholder: 'imap.example.com' },\n { key: 'port', label: 'Port', type: 'text', placeholder: '993' },\n { key: 'user', label: 'User', type: 'text', placeholder: 'name@example.com' },\n { key: 'password', label: 'Password', type: 'password' },\n ],\n credentialsHint: 'The IMAP settings of your mail provider. Use an app password where the provider has them.',\n },\n];\n","import { createConfigurationState, getMissingKeys, type ProviderConfigurationState } from '../../ProviderConfiguration';\nimport { callEmailApi, EmailProviderError } from '../EmailError';\nimport { formatEmailAddress, prepareEmail, type EmailRecipient, type EmailSendAdapter, type EmailSendParams, type EmailSendResult } from '../EmailSend';\n\nexport const RESEND_EMAIL_URL = 'https://api.resend.com/emails';\n\nexport type ResendEmailSenderConfig = {\n apiKey: string;\n /** The default sender, on a domain verified in Resend. */\n from?: EmailRecipient;\n /** The `fetch` to use (a proxy, a test). */\n fetch?: typeof fetch;\n};\n\n/**\n * Sends with Resend (CR-130). The key is a secret: use it on a server, not in a browser bundle.\n */\nexport class ResendEmailSender implements EmailSendAdapter {\n constructor(private readonly config: ResendEmailSenderConfig) {}\n\n getConfigurationState(): ProviderConfigurationState {\n return createConfigurationState('ResendEmailSender', getMissingKeys(this.config as Record<string, unknown>, ['apiKey']));\n }\n\n isConfigured(): boolean {\n return this.getConfigurationState().configured;\n }\n\n async send(params: EmailSendParams): Promise<EmailSendResult> {\n if (!this.isConfigured()) throw new EmailProviderError('not-configured', 'Resend', 'Resend has no API key');\n const mail = prepareEmail('Resend', params, this.config.from);\n const list = (addresses: typeof mail.to) => (addresses.length ? addresses.map(formatEmailAddress) : undefined);\n const sent = await callEmailApi<{ id?: string }>('Resend', this.config.fetch ?? globalThis.fetch, RESEND_EMAIL_URL, {\n method: 'POST',\n headers: { authorization: `Bearer ${this.config.apiKey}`, 'content-type': 'application/json' },\n body: JSON.stringify({\n from: formatEmailAddress(mail.from!),\n to: list(mail.to),\n cc: list(mail.cc),\n bcc: list(mail.bcc),\n reply_to: mail.replyTo ? formatEmailAddress(mail.replyTo) : undefined,\n subject: mail.subject,\n html: mail.html,\n text: mail.text,\n }),\n });\n return { id: String(sent.id ?? '') };\n }\n}\n","import { createConfigurationState, getMissingKeys, type ProviderConfigurationState } from '../../ProviderConfiguration';\nimport { callEmailApi, EmailProviderError } from '../EmailError';\nimport { prepareEmail, type EmailAddress, type EmailRecipient, type EmailSendAdapter, type EmailSendParams, type EmailSendResult } from '../EmailSend';\n\nexport const BREVO_EMAIL_URL = 'https://api.brevo.com/v3/smtp/email';\n\nexport type BrevoEmailSenderConfig = {\n apiKey: string;\n /** The default sender, a sender or domain verified in Brevo. */\n from?: EmailRecipient;\n /** The `fetch` to use (a proxy, a test). */\n fetch?: typeof fetch;\n};\n\n/** An address as Brevo takes it. */\nconst brevoAddress = ({ email, name }: EmailAddress) => (name ? { email, name } : { email });\n\n/**\n * Sends with Brevo's transactional API (CR-130). The key is a secret: use it on a server, not in a\n * browser bundle.\n */\nexport class BrevoEmailSender implements EmailSendAdapter {\n constructor(private readonly config: BrevoEmailSenderConfig) {}\n\n getConfigurationState(): ProviderConfigurationState {\n return createConfigurationState('BrevoEmailSender', getMissingKeys(this.config as Record<string, unknown>, ['apiKey']));\n }\n\n isConfigured(): boolean {\n return this.getConfigurationState().configured;\n }\n\n async send(params: EmailSendParams): Promise<EmailSendResult> {\n if (!this.isConfigured()) throw new EmailProviderError('not-configured', 'Brevo', 'Brevo has no API key');\n const mail = prepareEmail('Brevo', params, this.config.from);\n const list = (addresses: EmailAddress[]) => (addresses.length ? addresses.map(brevoAddress) : undefined);\n const sent = await callEmailApi<{ messageId?: string }>('Brevo', this.config.fetch ?? globalThis.fetch, BREVO_EMAIL_URL, {\n method: 'POST',\n headers: { 'api-key': this.config.apiKey, 'content-type': 'application/json', accept: 'application/json' },\n body: JSON.stringify({\n sender: brevoAddress(mail.from!),\n to: list(mail.to),\n cc: list(mail.cc),\n bcc: list(mail.bcc),\n replyTo: mail.replyTo ? brevoAddress(mail.replyTo) : undefined,\n subject: mail.subject,\n htmlContent: mail.html,\n textContent: mail.text,\n }),\n });\n return { id: String(sent.messageId ?? '') };\n }\n}\n","// Provider-agnostic identity of AI models (CR-103). models.dev names a model the same way on every\r\n// provider once release dates and variants are removed: \"DeepSeek V4.1 Flash\" is\r\n// `deepseek/deepseek-v4.1-flash` on OpenRouter, `deepseek-flash` on DeepSeek and\r\n// `@cf/deepseek-ai/deepseek-v4.1-flash-1017` on Cloudflare. Pure module: no React, no browser API.\r\n\r\nimport { MODELS_DEV_URL } from './modelsDevPricing';\r\n\r\n/** Words that name a variant of a model, not another model. */\r\nconst VARIANT_WORDS = new Set(['free', 'latest']);\r\n/** A trailing release date: MMDD, YYYYMMDD or YYYY-MM-DD. */\r\nconst DATE_WORD = /^(\\d{4}|\\d{8}|\\d{4}-\\d{2}-\\d{2})$/;\r\n/** A trailing group in parentheses: who hosts it, a region or a variant (\"(Alibaba Cloud)\", \"(EU)\", \"(Latest)\"). */\r\nconst TRAILING_GROUP = /\\s*\\(([^()]*)\\)\\s*$/;\r\n/** Groups that name another way of running the model, not another host: they stay in the name. */\r\nconst MODE_GROUP = /^(thinking|reasoning|non-reasoning|fast)$/i;\r\n\r\n/**\r\n * The key of a model name on any provider: lower case, without the host, region, release date, Free\r\n * or Latest that some providers add; a mode (\"(Thinking)\") stays.\r\n */\r\nexport const canonicalModelName = (name: string): string => {\r\n let rest = name.trim();\r\n const modes: string[] = [];\r\n for (let group = TRAILING_GROUP.exec(rest); group; group = TRAILING_GROUP.exec(rest)) {\r\n if (MODE_GROUP.test(group[1].trim())) modes.unshift(group[1].trim());\r\n rest = rest.slice(0, group.index);\r\n }\r\n const words = rest.toLowerCase().replace(/[()[\\]]/g, ' ').split(/\\s+/).filter(Boolean);\r\n while (words.length > 1 && (VARIANT_WORDS.has(words[words.length - 1]) || DATE_WORD.test(words[words.length - 1]))) words.pop();\r\n return [...words, ...modes.map((mode) => mode.toLowerCase())].join(' ');\r\n};\r\n\r\n/** One model of a provider, as models.dev lists it, with its price there when models.dev has one (CR-119). */\r\nexport type ModelIdentityEntry = { id: string; name: string; cost?: ModelCost };\r\n\r\n/** What a model costs on a provider: USD per 1M tokens. */\r\nexport type ModelCost = { input: number; output: number };\r\n\r\nexport type ModelIdentity = {\r\n /** canonicalModelName() of its name. */\r\n key: string;\r\n /** The name to show: the one of a stable id, without date or variant when there is one. */\r\n name: string;\r\n family?: string;\r\n /** models.dev provider key (the ids opencode uses) → the ids of the model there. */\r\n providers: Record<string, ModelIdentityEntry[]>;\r\n};\r\n\r\nexport type ModelIdentityIndex = {\r\n models: Record<string, ModelIdentity>;\r\n /** models.dev provider key → its name. */\r\n providers: Record<string, string>;\r\n};\r\n\r\ntype ModelsDevCatalog = Record<string, { name?: string; models?: Record<string, { name?: string; family?: string; cost?: { input?: unknown; output?: unknown } }> }>;\r\n\r\n/** How far an id is from the stable one: `~` aliases, free tiers, Latest and dated ids come last. */\r\nconst instability = ({ id, name }: ModelIdentityEntry): number => {\r\n const words = name.toLowerCase().split(/\\s+/);\r\n return (id.startsWith('~') ? 8 : 0)\r\n + (words.includes('free') ? 4 : 0)\r\n + (words.includes('latest') ? 2 : 0)\r\n + (DATE_WORD.test(words[words.length - 1] ?? '') ? 1 : 0)\r\n // A host or region in parentheses: the plain name of the same provider goes first.\r\n + (TRAILING_GROUP.test(name) ? 1 : 0);\r\n};\r\n\r\nconst byStability = (left: ModelIdentityEntry, right: ModelIdentityEntry) =>\r\n instability(left) - instability(right) || left.id.length - right.id.length || left.id.localeCompare(right.id);\r\n\r\n/** One entry per canonical model, with its ids on every provider of the models.dev catalog. */\r\nexport function buildModelIdentityIndex(catalog: ModelsDevCatalog): ModelIdentityIndex {\r\n const models: Record<string, ModelIdentity> = {};\r\n const providers: Record<string, string> = {};\r\n for (const [provider, { name: providerName, models: entries = {} }] of Object.entries(catalog)) {\r\n providers[provider] = providerName || provider;\r\n for (const [id, entry] of Object.entries(entries)) {\r\n const name = entry?.name?.trim() || id;\r\n const key = canonicalModelName(name);\r\n const identity = models[key] ??= { key, name, providers: {} };\r\n if (!identity.family && entry?.family) identity.family = entry.family;\r\n const input = entry?.cost?.input;\r\n const output = entry?.cost?.output;\r\n const cost = typeof input === 'number' && typeof output === 'number' ? { input, output } : undefined;\r\n (identity.providers[provider] ??= []).push({ id, name, ...(cost ? { cost } : {}) });\r\n // The name to show is the one of the most stable id seen so far.\r\n if (instability({ id, name }) < instability({ id: '', name: identity.name })) identity.name = name;\r\n }\r\n }\r\n for (const identity of Object.values(models)) {\r\n for (const entries of Object.values(identity.providers)) entries.sort(byStability);\r\n }\r\n return { models, providers };\r\n}\r\n\r\nexport type ResolvedModel = {\r\n /** models.dev provider key. */\r\n provider: string;\r\n /** The id of the model on that provider. */\r\n model: string;\r\n /** `provider/model`: the form opencode reads. */\r\n ref: string;\r\n};\r\n\r\n/**\r\n * The id of `model` (a name or a canonical key) on the first of `providers` that serves it, the most\r\n * stable id of that provider; nothing when none of them does.\r\n */\r\nexport function resolveModel(index: ModelIdentityIndex, model: string, providers: readonly string[]): ResolvedModel | undefined {\r\n const identity = index.models[canonicalModelName(model)];\r\n if (!identity) return undefined;\r\n for (const provider of providers) {\r\n const best = identity.providers[provider]?.[0];\r\n if (best) return { provider, model: best.id, ref: `${provider}/${best.id}` };\r\n }\r\n return undefined;\r\n}\r\n\r\n/** A provider that serves a model: its most stable id there, and what it costs there when known. */\r\nexport type ModelOffer = ResolvedModel & { cost?: ModelCost };\r\n\r\n/**\r\n * Every provider that serves `model` (a name or a canonical key), each with its most stable id and its\r\n * price: of `providers`, in their order, when given; else of the whole catalog, by provider key (CR-119).\r\n */\r\nexport function modelOffers(index: ModelIdentityIndex, model: string, providers?: readonly string[]): ModelOffer[] {\r\n const identity = index.models[canonicalModelName(model)];\r\n if (!identity) return [];\r\n const keys = providers ?? Object.keys(identity.providers).sort();\r\n return keys.flatMap((provider) => {\r\n const best = identity.providers[provider]?.[0];\r\n return best ? [{ provider, model: best.id, ref: `${provider}/${best.id}`, ...(best.cost ? { cost: best.cost } : {}) }] : [];\r\n });\r\n}\r\n\r\nlet pending: Promise<ModelIdentityIndex> | null = null;\r\nlet loaded: ModelIdentityIndex | null = null;\r\n\r\n/** The models.dev index, fetched once per session (the catalog is several MB: not cached on disk). */\r\nexport function loadModelIdentityIndex(fetchFn: typeof fetch = globalThis.fetch): Promise<ModelIdentityIndex> {\r\n if (loaded) return Promise.resolve(loaded);\r\n pending ??= fetchFn(MODELS_DEV_URL)\r\n .then((response) => (response.ok ? response.json() : Promise.reject(new Error(`models.dev: HTTP ${response.status}`))))\r\n .then((catalog: ModelsDevCatalog) => (loaded = buildModelIdentityIndex(catalog)))\r\n .finally(() => { pending = null; });\r\n return pending;\r\n}\r\n"],"mappings":"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"}