import { AuthStorage, type ExtensionAPI } from "@earendil-works/pi-coding-agent"; import { fetchInceptronModels } from "./discovery.js"; import { INCEPTRON_FALLBACK_MODELS } from "./models.js"; const DEFAULT_BASE_URL = "https://api.inceptron.io/v1"; const PROVIDER_ID = "inceptron"; /** * Resolve the discovery key from the same sources pi uses at request time, * in pi's precedence order: a stored credential (what `/login` writes to * auth.json, supporting `$env` / `!command` forms) wins over the plain * `INCEPTRON_API_KEY` env var. * * We can't simply call `AuthStorage.getApiKey(PROVIDER_ID)` and stop there: a * standalone AuthStorage only knows pi's built-in provider→env map (which has * no entry for our custom `inceptron` id) and lacks the model-registry fallback * resolver, so it would never see `INCEPTRON_API_KEY`. Hence the explicit env * fallback. Never throws — discovery is best-effort and must not block startup. */ async function resolveDiscoveryKey(): Promise { try { const stored = await AuthStorage.create().getApiKey(PROVIDER_ID); if (stored) return stored; } catch { // Bad auth.json / failing `!command` — fall through to the env var. } return process.env.INCEPTRON_API_KEY?.trim() || undefined; } /** * pi extension: registers Inceptron (https://www.inceptron.io) as an LLM * provider. Inceptron exposes an OpenAI-compatible chat API, so we use pi's * built-in `openai-completions` transport and only supply registration, * credentials, and model metadata. * * The factory is async: when a key is resolvable (see {@link resolveDiscoveryKey}) * we try to discover the live model list from `GET /v1/models`, falling back to * the curated catalog. pi awaits the async factory before provider registration, * so discovered models are available immediately without `/reload`. */ export default async function inceptron(pi: ExtensionAPI): Promise { const baseUrl = process.env.INCEPTRON_BASE_URL?.trim() || DEFAULT_BASE_URL; // Resolve a key only to decide whether to attempt discovery. The provider is // always registered with the `$INCEPTRON_API_KEY` reference so pi's own // credential hierarchy (CLI flag → auth.json → env → models.json) still // applies at request time even if no key is resolvable right now. Resolving // from auth.json here (not just the env var) means a key saved via `/login` // also feeds discovery — including for `pi --list-models`, which runs this // factory but never starts an interactive session. const apiKey = await resolveDiscoveryKey(); const discovered = apiKey ? await fetchInceptronModels(baseUrl, apiKey) : null; const models = discovered ?? INCEPTRON_FALLBACK_MODELS; pi.registerProvider(PROVIDER_ID, { name: "Inceptron", baseUrl, apiKey: "$INCEPTRON_API_KEY", api: "openai-completions", authHeader: true, models, }); }