pi-airpx

Log in to the airpx LLM proxy (https://airpx.cc) with an sk-proxy API key and auto-import its model catalog (prices, context windows, reasoning, vision) into the pi coding agent.

Packages

Package details

extension

Install pi-airpx from npm and Pi will load the resources declared by the package manifest.

$ pi install npm:pi-airpx
Package
pi-airpx
Version
0.1.3
Published
Jul 22, 2026
Downloads
55/mo · 55/wk
Author
axel.baum.listo
License
MIT
Types
extension
Size
32.9 KB
Dependencies
0 dependencies · 0 peers
Pi manifest JSON
{
  "extensions": [
    "./extensions/index.ts"
  ]
}

Security note

Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.

README

pi-airpx

npm

Log in to the airpx LLM proxy with an sk-proxy-… key and auto-import its model catalog (prices, context windows, reasoning/vision flags) into the pi coding agent — instead of hand-maintaining hundreds of entries in models.json.

The airpx proxy is an OpenAI-/Anthropic-compatible gateway that routes one sk-proxy-… key to many upstream models (Claude, GPT, Gemini, Kimi, DeepSeek, GLM, Qwen, Grok, …) with pooled keys, fallback routing, and unified pricing. This plugin makes those models appear natively in pi's /model picker.

By default the plugin loads the proxy's main list: the anonymous GET https://airpx.cc/v1/models response (proxy_public_models, the ~33 super-current canonical models), with pricing and any global discount already applied. It skips alias rows and provider-group pseudo rows, and it does not load free/service models beyond what the main list already exposes.

The list is fetched anonymously (no Authorization header) on purpose: a key's role is derived from its owner's user role, so a normal user key may return the full ~348-model catalog. The curated main list is only obtainable anonymously.

Install / run

# ad-hoc
pi -e ./extensions/index.ts
# then inside pi:
/login airpx        # paste your sk-proxy-... key

Or via env var (no /login needed):

AIRPX_API_KEY=sk-proxy-... pi -e ./extensions/index.ts

As an installed package — add to ~/.pi/agent/settings.jsonpackages:

{ "packages": ["npm:pi-airpx"] }

pi installs it from npm automatically. Then run /login airpx once and paste your sk-proxy-… key (get one at https://airpx.cc).

Get a key

Sign in at https://airpx.cc, create an sk-proxy-… API key, and use it with /login airpx or AIRPX_API_KEY. The same key works with any OpenAI-compatible client pointed at https://airpx.cc/v1.

Config

Env Default Meaning
AIRPX_API_KEY sk-proxy-... key (alternative to /login airpx)
AIRPX_BASE_URL https://airpx.cc/v1 proxy base URL (use http://127.0.0.1:18100/v1 for local dev)
AIRPX_ALL_MODELS set to 1 to fetch the FULL keyed catalog (still skips alias/pseudo rows)

How it works

  • async factory resolves a key (auth.json airpx entry → AIRPX_API_KEY), fetches /v1/models (anonymously by default), maps → pi.registerProvider("airpx", { models }).
  • /login airpx prompts for the key, validates it with an explicit keyed probe (a bad/mistyped key is rejected, not silently saved), then fetches the default catalog and re-registers the provider live (no /reload).
  • The on-disk cache (~/.pi/agent/airpx-catalog.json) is a last-good snapshot: a successful fetch replaces it with exactly the fresh response (so removed/re-priced models don't linger, and AIRPX_ALL_MODELS never permanently pollutes the default list); the cache is used only as a fallback when a fetch fails, so pi still boots offline.
  • First-run hint: with no key configured the models load but pi keeps them unavailable in /model until auth is set (documented pi behavior), so the plugin prints a one-line hint pointing you to /login airpx.

Field mapping (proxy → pi)

proxy pi
id id
display_name (else id) name
reasoning reasoning
supports_vision input: ["text","image"] else ["text"]
context_window (0 → 128000) contextWindow
max_output_tokens (0 → 16384) maxTokens
effective_pricing|pricing .input cost.input (0 if absent)
…​.output cost.output (0 if absent)
…​.cache_read cost.cacheRead (0 if absent)
…​.cache_write cost.cacheWrite (0 if absent)

A small hand-maintained _OVERRIDES table adds per-model tweaks (currently gpt-5.3-codexapi: "openai-responses" and kimi-for-codingname: "Kimi K2.6 Coding").

Notes:

  • Prices are runtime-only — they come from the live /v1/models response and are never committed. Test fixtures deliberately use undiscounted pricing (not live effective_pricing) so they don't rot.
  • Only canonical ids are loaded — alias rows (those carrying alias_of) are skipped, as are provider-group pseudo rows (e.g. anthropic, detected by a known provider name or by having neither pricing nor supported_parameters, not by context_window === 0).

Tests

npm test
# = node --experimental-strip-types --test

Pure, offline unit tests of the mapper (src/map.ts) against a frozen fixture (test/fixtures/v1_models.sample.json) — no network.

Architecture

For the design rationale (module boundary, SOLID/DRY/KISS walkthrough, data flow, failure model, extension points) see docs/ARCHITECTURE.md.

TODO / open

  • Map thinkingLevelMap from supported_parameters (xhigh/max) per model.
  • Replace the _OVERRIDES table once the proxy exposes api_format per model.
  • Decide openai-completions vs anthropic-messages per model family (currently all via openai-completions; the proxy normalizes).