- DistributionCross-platform npm CLI
- Early adoption1,500 downloads in 20 days
- InterfacesResponsive TUI, text, and JSON
- IntegrationsOpenCode, pi, and Copilot CLI
- Verification100+ tests and packed-artifact checks
Why I Built It
Stack: TypeScript · Bun · Node.js · React · Ink · GitHub Actions · npm
Checking my Codex allowance interrupted the same workflow every time. Usage windows and reset times were on the analytics page, while reset-credit coupons were visible in ChatGPT. Those coupons restore part of the available Codex usage before its normal reset.
I wanted one quick answer in the terminal, so I built a CLI for myself. Ten days after the first line of code, I published @simonesiega/codex-limits, my first public npm package. It passed 1,500 downloads in its first 20 days.
Today the package includes a responsive terminal dashboard, plain-text and JSON commands, diagnostics, confirmed coupon redemption, and integrations for OpenCode, pi, and GitHub Copilot CLI.
Publishing changed the standard for the project. It now had to survive different operating systems, Node.js versions, terminal dimensions, Codex installations, incomplete local state, and network failures—not only my machine.
One Interpretation of the Data
Codex Limits can receive live account information and can recover selected missing fields from local Codex session data. Local inspection is read-only and constrained by file-size, traversal, entry, and response limits.
The precedence rule is deliberately narrow:
valid live value -> use it
missing live field -> fill it from valid local state
neither is reliable -> return a warning, never an invented value
Both sources pass through one normalization layer. The Ink dashboard, text commands, documented JSON, diagnostics, and agent adapters all consume the resulting model instead of maintaining separate parsing or fallback logic.
This matters most when the upstream response is partial. A UI-specific fallback could disagree with automation even though both are describing the same account. Keeping data meaning in one place makes those disagreements harder to introduce.
Commands Have Different Powers
The CLI uses a declarative registry rather than a large conditional entry point. Each command declares its metadata, argument rules, handler, required services, and one of three safety categories:
- read-only for the dashboard, status, coupons, and doctor;
- local-write for installing or removing recognized agent configuration;
- remote-mutation for consuming a reset coupon.
The router provides only the capabilities assigned to that category. A status command cannot consume a coupon because its handler never receives the service that performs the request.
codex-limits reset is the only remote account mutation. It refreshes the available coupons, displays the selection, requires an interactive terminal and an explicit y or yes, and uses an idempotency key for the request. An unclear response is reported as unconfirmed rather than success.
Sensitive values follow the same least-access approach. Tokens, account IDs, headers, cookies, private paths, raw files, and internal coupon identifiers stay out of normal output, diagnostics, public JSON, and rendering adapters. Preventing those values from reaching presentation code is safer than trying to redact every final string.
Coding-Agent Integrations
OpenCode was the first agent host I supported. It forced a useful constraint: the integration should display limits locally without turning private usage data into model context.
The adapter receives a sanitized read-only result, renders its own host UI, and has no reset capability. It does not send a user prompt or limits data to the LLM. Once that contract worked, pi and GitHub Copilot CLI could follow it without duplicating Codex discovery or authentication logic.
Each integration registers a descriptor containing its metadata, environment guidance, installation, removal, and inspection behavior. Shared lifecycle commands operate on those descriptors instead of hard-coding every host. Adding another agent still requires host-specific rendering and configuration work, but not another implementation of the security-sensitive core.
Testing What Users Install
A repository build is not the final npm product. Users receive a tarball with specific files, exports, declarations, executable metadata, and runtime assumptions.
The package validator runs npm pack, inspects the resulting contents and public entry points, rejects development-only files, extracts the tarball, and smoke-tests the packed CLI and agent bundles. CI runs packed-runtime checks across Linux, Windows, and macOS. Separate compatibility jobs install real OpenCode, pi, and GitHub Copilot CLI hosts and exercise the generated integrations inside them.
Releases use npm Trusted Publishing through GitHub Actions, with provenance instead of a long-lived npm token. The workflow publishes only after the version and validation gates pass.
This was my first direct experience of the package itself being a test target. Source tests can pass while a missing declaration, wrong export, or excluded bundle still breaks the software people install.
What Shipped
The current package provides:
- responsive TUI, plain-text, and machine-readable usage views;
- reset times and reset-credit coupon status;
codex-limits doctorfor safe environment and connectivity diagnostics;- coupon redemption by displayed index or earliest expiration, guarded by confirmation;
- agent installation and removal commands;
- OpenCode, pi, and GitHub Copilot CLI adapters;
- support for Node.js 20 or newer on Windows, macOS, and Linux;
- automated package validation and provenance-backed npm releases.
The repository contains more than 180 automated tests across core behavior, commands, output contracts, safety rules, terminal rendering, and integration logic. The supported public interfaces are the CLI and documented JSON; internal core modules are not presented as a general-purpose JavaScript API.
What Changed for Me
Before this project, I treated a CLI mostly as an executable wrapper around internal code. A public command is closer to an API: names, flags, exit codes, JSON fields, installation paths, exports, and failure behavior can all become dependencies for somebody else.
I also became more careful about where safety guarantees live. A comment saying “do not expose this value” is weaker than an interface that never provides the value. The same applies to mutation: a read path is easier to trust when it cannot reach the write operation at all.
Codex Limits was the first project where maintenance felt different from adding features for myself. Compatibility, documentation, packaging, and predictable failures became product work.
Where It Goes Next
Future agent support should continue through the descriptor and read-only adapter model. I also want upstream response changes to stop at normalization: a new usage window or modified payload should require one focused update rather than coordinated edits to the TUI, CLI output, JSON, and every host integration.
Real-host compatibility tests will need to evolve as the supported agents do. The target is deliberately uneventful maintenance—one change in the correct layer while existing interfaces continue to behave as documented.
