#12·skills

refactor: use @openrouter/sdk typed client in scripts instead of hand-rolled fetch to eliminate API shape drift

Author: perry-the-pr-reviewer[bot]Created Apr 13, 2026Updated Apr 13, 2026

Problem

All scripts (list-models.ts, get-endpoints.ts, compare-models.ts, etc.) share a hand-rolled HTTP layer in lib.ts:

export async function fetchApi(path: string, apiKey?: string): Promise<any> {
  const url = `https://openrouter.ai/api/v1${path}`;
  const res = await fetch(url, { headers });
  // manual switch(res.status) error handling...
  return res.json(); // return type: any
}

The return type is any throughout. This means:

  • New API fields (canonical_slug, knowledge_cutoff, default_parameters) go undiscovered until someone manually audits the response and files a docs PR like #10
  • Error handling is duplicated across scripts
  • There's no compile-time signal when the API shape changes

PR #10 is a direct symptom: it exists because the skill docs drifted from the live API, and the fix was to manually embed JSON shapes in SKILL.md — creating a second source of truth that will drift again.

What to do instead

Replace fetchApi() in each lib.ts with the @openrouter/sdk typed client (@openrouter/sdk covers model listing, chat completions, credits, and OAuth). This gives:

  • Typed responses — new/removed fields surface as compile errors, not silent drift
  • Standardized error handling — no more per-script switch (res.status) blocks
  • Free updates — SDK version bumps propagate API changes to all scripts automatically

The CLI interface, custom formatting logic (formatModel(), pricing per-million conversion, status code → string), and custom algorithms stay in the scripts. Only the HTTP transport layer changes.

Tradeoff to consider

Adding @openrouter/sdk as a dependency increases the install footprint. The current scripts have only tsx as a devDep. If portability across minimal agent environments is a hard constraint, an alternative is generating TypeScript types from openrouter.ai/openapi.yaml at build time (no runtime dependency, types only).

Reviewed by Perry