You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fix(catalog): price free SKUs at $0 and fill remaining gaps from models.dev (#4)
* fix(catalog): price free SKUs at $0 and fill remaining gaps from models.dev
Drop hardcoded FALLBACK_COSTS so billed rates come from CLI, official docs, or models.dev instead of a plugin table. Command Code free SKUs stay $0 and do not inherit paid twins.
Co-authored-by: Cursor <cursoragent@cursor.com>
* ci: lint and format with oxlint and oxfmt like workit
Keep bun test. Add the same Oxc toolchain workit uses so CI can fail on lint and format instead of only tests and types.
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
bun run check # oxlint + oxfmt + bun test + tsc (same stack as workit)
78
78
```
79
79
80
80
```bash
81
81
bun run sync -- --remote # refresh models.json + manifest.json from command-code@latest
82
82
```
83
83
84
-
CI (`.github/workflows/catalog-sync.yml`) opens a PR every 6 hours when Command Code ships a new catalog. Merge after **check (test)**, **check (typecheck)**, and **check (pack)** are green. `.github/workflows/release.yml` then runs **semantic-release** (npm publish + GitHub Release + tag). Do not push to `main`.
84
+
CI (`.github/workflows/catalog-sync.yml`) opens a PR every 6 hours when Command Code ships a new catalog. Merge after **check (test)**, **check (typecheck)**, **check (lint)**, **check (format)**, and **check (pack)** are green. `.github/workflows/release.yml` then runs **semantic-release** (npm publish + GitHub Release + tag). Do not push to `main`.
85
85
86
86
The GitHub Actions secret name is `NPMJS` (same as workit). It is mapped to both `NPM_TOKEN` and `NODE_AUTH_TOKEN`. Use an npm **Automation** token (bypasses 2FA). A login token from `~/.npmrc` fails CI with `EOTP`. Catalog PRs get a real CI run when `RELEASE_SYNC_TOKEN` (or `CATALOG_PUSH_TOKEN`) is a PAT; `GITHUB_TOKEN` can open the PR but GitHub will not start workflows from that event.
Copy file name to clipboardExpand all lines: docs/2026-08-28-ci-catalog/spec.md
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -15,7 +15,7 @@ Watch `command-code` on npm every 6 hours, refresh the bundled catalog, publish
15
15
- Tag + GitHub Release **after** successful `npm publish` only. Never tag a version that is not on the registry.
16
16
- Catalog auto-commit uses `GITHUB_TOKEN` (or `CATALOG_PUSH_TOKEN` if protection blocks it). Those commits do **not** trigger other workflows, so catalog-sync must publish in the **same job**.
17
17
- Human merges to `main` run a separate release job that publishes only if `package.json` version is unpublished (code fixes).
18
-
- Cost-only CLI failure still ships (`degraded` if fallback/unmatched costs remain). Model extract failure → no publish, `catalog-break` issue.
18
+
- Cost-only CLI failure still ships (`degraded`only if unmatched placeholder costs remain). Model extract failure → no publish, `catalog-break` issue.
19
19
- Runtime catalog stays bundled `models.json`. No GitHub fetch at OpenCode startup.
20
20
- Hybrid OpenCode transport stays `@ai-sdk/openai-compatible` + Provider API; this package is the **plugin**, not the SDK `npm` field.
Copy file name to clipboardExpand all lines: docs/specs/2026-08-28-ci-catalog-automation.md
+14-13Lines changed: 14 additions & 13 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -61,6 +61,7 @@ Human- and CI-readable metadata about the bundled catalog. Shipped with the plug
61
61
"cli": 40,
62
62
"officialDocs": 25,
63
63
"thirdParty": 0,
64
+
"free": 0,
64
65
"fallback": 0,
65
66
"unmatched": 0
66
67
},
@@ -71,18 +72,18 @@ Human- and CI-readable metadata about the bundled catalog. Shipped with the plug
71
72
Write order: bump `package.json` version first, then write `manifest.json` with that `pluginVersion`, then one commit. Do not store `gitCommit` in the file (unknown until after commit; put SHA on the GitHub Release instead).
72
73
73
74
`status` rules:
74
-
-`healthy`: model catalog ok **and** every model has costs from `cli`, `officialDocs`, or `thirdParty`(`fallback` = 0 and`unmatched` = 0)
75
-
-`degraded`: model catalog ok, but at least one model used hardcoded fallback or has no cost
75
+
-`healthy`: model catalog ok **and** every model has a sourced price (`cli`, `officialDocs`, `thirdParty`/ models.dev, or `free`).`unmatched` = 0
76
+
-`degraded`: model catalog ok, but at least one model still has the unmatched placeholder `{ input: 0.5, output: 2 }`
76
77
-`broken`: model catalog extraction failed
77
78
78
-
`extraction.costCatalog` is the **best** source that contributed (`cli` | `docs` | `thirdParty` | `fallback` | `missing`).
79
+
`extraction.costCatalog` is the **best** source that contributed (`cli` | `docs` | `thirdParty` | `free` | `fallback` | `missing`).
79
80
80
81
Status values:
81
82
82
83
| status | Meaning | CI action |
83
84
|---|---|---|
84
-
|`healthy`| models ok; costs from CLI, official docs, and/or trusted API| release |
85
-
|`degraded`| models ok; some costs are hardcoded fallback or missing| release + note which models fell through |
85
+
|`healthy`| models ok; costs from CLI, official docs, models.dev, and/or free SKUs| release |
86
+
|`degraded`| models ok; some costs have no listed source| release + note which models fell through |
86
87
|`broken`| model catalog extraction failed | no release, open issue |
87
88
88
89
### `_version.txt` (existing)
@@ -100,10 +101,11 @@ Waterfall, per model, first hit wins. Later steps only fill models still missing
- Fallback page: [https://commandcode.ai/docs/resources/pricing-limits](https://commandcode.ai/docs/resources/pricing-limits) if `/models` fetch or parse fails.
102
103
- Store the **current billed** per-1M USD rates shown on the page (deal-adjusted when the page shows an effective price). Do not invent deal math.
103
-
3.**Trusted third-party API** — only if a provider is configured (`COST_ENRICHMENT_API_URL` secret/env). Default: **none**. Skip this step until one is added. Do not use OpenRouter (or similar) unless explicitly configured; their list prices are not Command Code’s billed rates.
104
-
4.**Hardcoded fallback** (`FALLBACK_COSTS` + existing default `{ input: 0.5, output: 2 }`) — last resort so the catalog still ships.
104
+
3.**Free SKUs** — catalog id/name matching `\bfree\b` or id ending `-free` → `{ input: 0, output: 0 }`. Runs **before** models.dev so Command Code free SKUs stay $0 even when models.dev lists a paid twin (e.g. Tencent Hy3).
105
+
4.**models.dev** — `GET https://models.dev/api.json` for remaining **paid** gaps. Exact id (case-insensitive), then last path segment as id, then exact display name. Reference prices, not Command Code billed rates. Do not apply to free SKUs.
106
+
5.**Unmatched placeholder**`{ input: 0.5, output: 2 }` — last resort so the catalog still ships. These are the only models that mark the catalog `degraded`.
105
107
106
-
Match docs/API rows to catalog models with **exact** id (case-insensitive) then **exact** display name (case-insensitive). No fuzzy matching. Unmatched models go to the next step.
108
+
Match docs/API rows to catalog models with **exact** id (case-insensitive) then **exact** display name (case-insensitive). models.dev also tries the last `/` segment of the catalog id as an exact id. No fuzzy matching. Unmatched models go to the next step.
107
109
108
110
Never fail the sync because costs are incomplete. Model catalog remains the hard requirement.
109
111
@@ -126,8 +128,8 @@ Steps:
126
128
3. Download tarball (reuse logic from `scripts/sync-models.ts --remote`).
127
129
4. Extract models (required) then run the **cost waterfall**:
128
130
- model catalog fail → status `broken`, stop (no commit, no publish).
129
-
- CLI costs fail or partial → continue; fill gaps from official docs, then optional trusted API, then hardcoded fallback.
130
-
- Record `costSources` on the manifest. `degraded` only if any model still used fallback/unmatched.
131
+
- CLI costs fail or partial → continue; fill gaps from official docs, then free SKUs ($0), then models.dev, then unmatched placeholder.
132
+
- Record `costSources` on the manifest. `degraded` only if any model is still unmatched.
131
133
5. Sanity floor: `modelCount >= max(20, floor(lastSuccessfulModelCount * 0.5))`. `lastSuccessfulModelCount` is `modelCount` from the last committed manifest with `status``healthy` or `degraded`. Fail as `broken` if below. If no prior manifest, use `20`.
132
134
6. Write `models.json`, `_version.txt`, bump patch in `package.json`, write `manifest.json`.
133
135
7. Run unit tests (including 1.38 costless fixture).
@@ -171,7 +173,7 @@ When a subsequent sync succeeds after manual fix:
171
173
| Event | Version bump | Publish |
172
174
|---|---|---|
173
175
| New command-code catalog (healthy) | patch | yes |
174
-
| New command-code catalog (degraded: some fallback costs) | patch | yes |
|`CATALOG_PUSH_TOKEN`| optional; only if branch protection blocks `GITHUB_TOKEN`|
258
-
|`COST_ENRICHMENT_API_URL`| optional; skip third-party cost step when unset |
259
260
260
261
If `main` is protected against `GITHUB_TOKEN`, set `CATALOG_PUSH_TOKEN`. One push path only.
261
262
262
263
## Acceptance Criteria
263
264
264
265
- User can run OpenCode with **no** global/local `command-code` install and get current models from the installed plugin (npm package, or a `file://` checkout that has been synced).
265
266
- Within 6 hours of a new `command-code` npm release, CI either commits a catalog update (and publishes a plugin patch if `NPM_TOKEN` is set) or opens/updates a `catalog-break` issue.
266
-
- Cost-only CLI regressions (like 1.38) still ship a catalog. Costs come from official docs when the CLI map fails; `degraded` only if docs/API also miss models.
267
+
- Cost-only CLI regressions (like 1.38) still ship a catalog. Costs come from official docs, then free SKUs ($0), then models.dev; `degraded` only if models remain unmatched.
267
268
- Successful sync never requires local `bun run sync` from the user.
268
269
- Failed model extraction never publishes a misleading npm release.
0 commit comments