Skip to content

Generate models.json from the API endpoint instead of scraping the CLI bundle - #6

Draft
KRoperUK wants to merge 5 commits into
brent-weatherall:mainfrom
KRoperUK:feat/sync-models-from-api
Draft

Generate models.json from the API endpoint instead of scraping the CLI bundle#6
KRoperUK wants to merge 5 commits into
brent-weatherall:mainfrom
KRoperUK:feat/sync-models-from-api

Conversation

@KRoperUK

Copy link
Copy Markdown

Rebased successor to #4 (closed). It was rewritten to stack cleanly on #5#1; GitHub wouldn't reopen #4 after the history rewrite, hence a fresh PR.

Stacked on #5 (and #1). Review only the top commit (Source model list from /provider/v1/models…). Once #1 and #5 merge, this reduces to that one commit.

This targets the offline generator — complementary to the runtime auto-sync in #1/#5. The build-time scripts/sync-models.ts is what produces the bundled models.json, and today it parses the minified command-code CLI bundle with fragile string anchors (e.g. 'SONNET_4_6:{id:"claude-sonnet-4-6"'). That's the root cause of the staleness #1 works around at runtime.

Change

  • Source the model list (id, name, context window) from the public GET /provider/v1/models endpoint — the same authoritative source feat: auto-sync models from CommandCode API at startup #1 uses at runtime.
  • Keep downloading the CLI bundle, but only to enrich entries with pricing (the endpoint doesn't expose cost). Fields the endpoint lacks (reasoning, tool_call) come from a small MODEL_META map; tier is derived from the id namespace.
  • Models without bundle pricing are kept with zeroed cost + a warning instead of being dropped (the old script return null-skipped them).
  • Removed the brittle catalog scraping (extractModelCatalog, extractSpecConstants, HARDCODED_EXTRAS, the provider-based TIER_MAP).
  • Added tests/unit/models-json.test.ts to guard the generated file's shape.

Relationship to #1 / #5

This makes models.json a generated artifact rather than a hand-edited file. Net effect on the bundled list is small (both land on the same 30 models), but for the handful of brand-new, not-yet-priced models (mimo ×2, nemotron, Kimi-K2.7-Code, MiniMax-M3, Qwen3.7-Plus, Step-3.7-Flash) it sets cost to 0 (unknown) rather than a manual estimate. If you'd rather keep curated estimates, those belong in the script's FALLBACK_COSTS map so they survive regeneration.

Verification

  • bun run sync → 30 models incl. xiaomi/mimo-v2.5-pro (1M ctx); no anchor/parse exceptions.
  • bun run generate-readme updates the table.
  • bun test tests/unit/90 pass; tsc --noEmit clean.

🤖 Generated with Claude Code

augustoolucas and others added 5 commits June 4, 2026 12:28
- Fetch model list from CommandCode API on plugin config hook
- Merge with local models.json (API provides context_length, local provides curated cost/tier/reasoning)
- Persist merged result back to models.json for offline fallback
- Treat empty API response as failure to avoid wiping local models
- Add idempotency to saveModels (only write when content changed)
- Add new models and fix context sizes in models.json
- Add unit tests for mergeModels (pure) and integration tests using existing mockFetch helpers
- Switch from disk write to in-memory merge; sync updates no longer
  persist between startups
- Add config-file opt-out at
  ~/.config/opencode/commandcode-go-opencode-provider.json
  with `disableModelSync: true` to skip the API fetch entirely
- loadModels: throw actionable error on missing/corrupt bundled
  models.json ("please reinstall commandcode-go-opencode-provider",
  original error chained via Error.cause)
- Tests: add withFakeConfig / withMissingModels / withCorruptModels
  helpers and structural assertions for bundled, opt-out, and
  load-error paths
- README: document the opt-out workflow and bundled-list refresh
  via `bun run sync`
…adata

Builds on the auto-sync work. Three improvements to how the runtime sync
discovers models:

- Drop the COMMANDCODE_API_KEY requirement for the model fetch. The listing
  endpoint (/provider/v1/models) is public, so requiring the env var meant
  users who authenticate via /connect (the common case — it doesn't export the
  env var) got no auto-sync at all. The request now runs unauthenticated when no
  key is present, and still sends the key as a Bearer token when it is.
- Use the display name the API returns for new API-only models, instead of
  deriving it from the id (e.g. "MiMo V2.5 Pro" rather than "mimo-v2.5-pro").
- Derive tier from the id namespace instead of hardcoding "open-source", and
  stop fabricating a 0.5/2 price for unpriced models (zeroed instead, so cost
  accounting isn't fed an invented rate).

Tests: rework the "missing API key" case to assert the fetch still happens with
no Authorization header and merges API-only models; add a test for the
API-provided name. 87 pass, typecheck clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…I bundle

The model list was extracted by parsing the minified command-code CLI bundle
with fragile string anchors (e.g. 'SONNET_4_6:{id:"claude-sonnet-4-6"'), which
drifted out of date: models.json had 21 models while Command Code now serves 30
(missing mimo, claude-opus-4-8, claude-fable-5, nemotron, and others).

Use the public OpenAI-compatible endpoint https://api.commandcode.ai/provider/v1/models
as the source of truth for which models exist, their names, and context windows.
The CLI bundle is still downloaded, but now only to enrich entries with pricing
(the endpoint does not expose cost). Fields the endpoint lacks (reasoning,
tool_call) come from a small MODEL_META overrides map with sensible defaults.

Models without bundle pricing are kept with zeroed cost and a warning rather than
being silently dropped, so newly released models appear immediately.

- Remove brittle catalog scraping (extractModelCatalog, extractSpecConstants,
  HARDCODED_EXTRAS, provider-based TIER_MAP); derive tier from id namespace.
- Regenerate models.json (21 -> 30) and README table.
- Add tests/unit/models-json.test.ts to guard the models.json shape.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants