Skip to content

feat: pricing & updates polish — validate rates, surface unpriced models, ccam update-check, fast/intro pricing flags#237

Merged
hoangsonww merged 5 commits into
masterfrom
feat/pricing-updates-cli-polish
Jul 17, 2026
Merged

feat: pricing & updates polish — validate rates, surface unpriced models, ccam update-check, fast/intro pricing flags#237
hoangsonww merged 5 commits into
masterfrom
feat/pricing-updates-cli-polish

Conversation

@hoangsonww

Copy link
Copy Markdown
Owner

Summary

Five self-contained enhancements centered on the pricing and updates surfaces, closing gaps where the API was ahead of the CLI (or where invalid input could corrupt cost math), plus the required doc sweep and a pre-commit-hook reliability fix.

1. fix(server): validate rate fields in PUT /api/pricing

Rate fields were passed to the upsert with only ?? 0, so a NaN (e.g. a typo'd CLI flag through Number()), a raw string, or a negative number was written into model_pricing as-is — silently corrupting all downstream cost math. Any present rate must now be a non-negative finite number (numeric strings coerced), else 400 INVALID_INPUT naming the offending field. Existing clients are unaffected.

2. feat(cli): surface unpriced models in ccam cost

/api/pricing/cost deliberately reports unpriced_models (usage priced at $0 because no rule matched) so the total stays honest — but the CLI dropped the field, showing a too-low total with no hint. ccam cost now prints a warning listing each unpriced model, its token volume, and the exact ccam pricing set fix.

3. feat(cli): ccam update-check

The updates route was the only major surface with no CLI command — ironic, since its whole design is "the user copies the printed command and runs it in a terminal". ccam update-check POSTs /api/updates/check (so open dashboard tabs refresh via the update_status broadcast) and prints the branch-/fork-aware status: behind-by count, situation note, and the copy-paste update command. Also fixes a stale CLAUDE.md reference to a scripts/self-update-restart.js that no longer exists.

4. feat(cli): fast-mode & intro pricing from ccam pricing set

The API and Settings UI have long supported fast-mode premiums and time-limited intro (promo) rates; the CLI could only send the four standard rates. Adds --cache-write-1h, --fast-input/--fast-output, and the --intro-*/--intro-until YYYY-MM-DD block (only sent when present, so a plain edit never clobbers a promo; bare --intro-until clears it). The pricing list — online and offline, now one shared renderer — gains Fast In/Out and Intro In/Out columns.

5. docs + hook de-flake

Full doc sweep per the update-project-docs mapping (docs/CLI.md, docs/API.md, README + VN/CN/KO, server/README, OpenAPI regen, wiki + zh/vi/ko i18n with the required cache bump). Also hardens .husky/pre-commit: each test suite retries once on failure, absorbing machine-load flakes (dozens of concurrent test servers + spawned CLI children) while a real regression still fails both runs and blocks the commit.

Testing

  • npm run test:server — all suites green (ran repeatedly, incl. 3 full pre-commit hook runs; new tests: PUT validation ×4, cost unpriced warning, update-check, fast/intro round-trip)
  • npm run test:client — 24 files / 261 tests green (via the pre-commit hook on every commit)
  • bash .claude/skills/file-headers/scripts/check-headers.sh — exit 0
  • doc-coverage matrix run for update-check / fast-input / intro-until — canonical + all translations hit

🤖 Generated with Claude Code

hoangsonww and others added 5 commits July 17, 2026 10:16
The route validated model_pattern/display_name presence and the intro_until
date shape, but every per-MTok rate field passed straight through to the
upsert with only `?? 0`. A typo'd value (Number("abc") -> NaN via the CLI's
Number() coercion, or a raw string/negative number from a direct API call)
was written into model_pricing as-is and silently corrupted all downstream
cost math until someone noticed the broken totals and fixed the row by hand.

Reject any present rate field that is not a non-negative finite number with
the route's structured 400 INVALID_INPUT error, and coerce accepted values
with Number() so a rate can never be bound into SQLite as text. Absent and
empty values keep defaulting to 0, so existing clients are unaffected.

Co-authored-by: seer-by-sentry[bot] <157164994+seer-by-sentry[bot]@users.noreply.github.com>
GET /api/pricing/cost deliberately reports unpriced_models — usage buckets
that matched no pricing rule and were priced at $0 — so the total is never
silently under-reported. The CLI's cost command dropped that field on the
floor: after a brand-new model id shipped, `ccam cost` showed a too-low
total with no hint that anything was missing.

Print a warning block under the per-model chart listing each unpriced model
with its token volume, plus the exact `ccam pricing set` invocation that
fixes it. Nothing changes when every model is priced.

Co-authored-by: OpenAI Codex <codex@openai.com>
The updates route (GET /api/updates/status, POST /api/updates/check) was the
only major dashboard surface with no ccam command — ironic, because its whole
design is "the dashboard never restarts itself; the user copies the printed
command and runs it in a terminal". The terminal is exactly where that
answer belongs.

`ccam update-check` POSTs /api/updates/check (so an open dashboard tab picks
up the same fresh result via the update_status broadcast) and prints the
branch-aware status: behind-by count, the situation note for fork/feature-
branch checkouts, and the copy-paste update command. Non-git installs and
unreachable remotes report informationally with exit 0; offline (server
down) refuses with the standard server-only reason.

Also corrects the stale CLAUDE.md repo-map line that pointed at a
scripts/self-update-restart.js which no longer exists — update detection
lives in server/lib/update-check.js.

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
PUT /api/pricing has accepted fast_input/fast_output_per_mtok and the full
intro_* promo block for a while, and the Settings UI edits all of them — but
the CLI could only send the four standard rates, so fast-mode premiums and
time-limited launch pricing were unmanageable (and invisible) from the
terminal.

pricing set gains --cache-write-1h, --fast-input/--fast-output, and
--intro-input/--intro-output/--intro-cache-read/--intro-cache-write/
--intro-cache-write-1h/--intro-until YYYY-MM-DD. The intro block is only
sent when an --intro-* flag is actually present, honoring the API contract
that a plain rate edit never clobbers an existing promo; a bare
--intro-until clears it, mirroring the Settings UI.

The pricing list (online and offline fallback, now one shared renderer)
gains Fast In/Out and Intro In/Out columns so those rates are visible, and
the new flags are registered with the REPL tab-completer.

Co-authored-by: GPT-5 Codex <noreply@openai.com>
… the pre-commit hook

Documentation sweep for this PR's feature set, applied across the full doc
surface per the update-project-docs mapping:

- docs/CLI.md: ccam update-check row (Administration), pricing set
  fast/intro flag rows, unpriced-model warning on the cost row, and the
  offline-mode server-required list.
- README.md + README-VN/CN/KO: the ccam CLI block gains update-check, the
  pricing set flag lines, and the cost warning note (comments translated in
  each language's existing register; commands/flags kept in English).
- docs/API.md, server/README.md, server/openapi-extra (+ regenerated
  openapi.yaml): PUT /api/pricing numeric-rate validation documented
  (400 INVALID_INPUT naming the offending field; numeric strings coerced).
- wiki/index.html + i18n-content.js (zh/vi/ko) with the required cache bump
  (sw.js wiki-v41, i18n-content.js?v=32): CLI table + Pricing/Insights/
  Administration descriptions, and an Update Notifier paragraph pointing at
  ccam update-check.

Also hardens .husky/pre-commit against machine-load flakiness: the suite
spins up dozens of concurrent test servers plus spawned CLI children with
hard kill timeouts, and a loaded machine occasionally produced a one-off
failure that passed on the very next run. Each suite now retries once on
failure — a real regression is deterministic and still fails both runs,
blocking the commit, so the gate stays exactly as strict while commits stop
being a dice roll on machine load.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 17, 2026 17:34
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Caution

The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased.

@cursor

cursor Bot commented Jul 17, 2026

Copy link
Copy Markdown

Bugbot is not enabled for your account, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@github-actions github-actions Bot added bug Something isn't working documentation Improvements or additions to documentation enhancement New feature or request good first issue Good for newcomers help wanted Extra attention is needed question Further information is requested labels Jul 17, 2026
@hoangsonww
hoangsonww merged commit c6f1093 into master Jul 17, 2026
20 checks passed
@github-actions github-actions Bot locked and limited conversation to collaborators Jul 17, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

bug Something isn't working documentation Improvements or additions to documentation enhancement New feature or request good first issue Good for newcomers help wanted Extra attention is needed question Further information is requested

Projects

Development

Successfully merging this pull request may close these issues.

2 participants