Skip to content

fix(ccusage): require Node 22 for Node runtime#1006

Merged
ryoppippi merged 4 commits into
mainfrom
codex/node-version-guard
May 16, 2026
Merged

fix(ccusage): require Node 22 for Node runtime#1006
ryoppippi merged 4 commits into
mainfrom
codex/node-version-guard

Conversation

@ryoppippi
Copy link
Copy Markdown
Owner

@ryoppippi ryoppippi commented May 16, 2026

Summary

  • detect an already-active Bun runtime from the Bun global object
  • when the wrapper is already running under Bun, import the Bun entrypoint directly and skip PATH lookup, Node version checks, and re-spawning
  • when launched by Node, keep the existing PATH lookup for Bun auto-run, then fall back to the Node bundle with a Node.js >=22.0.0 guard

Testing

  • pnpm --filter ccusage exec vitest run --changed HEAD
  • pnpm --filter ccusage build
  • pnpm run format
  • pnpm typecheck
  • pnpm run test

Summary by CodeRabbit

  • New Features

    • CLI now selects the best available runtime (prefers Bun when available) and supports direct execution under Bun.
  • Refactor

    • Improved runtime initialization and clearer error handling when Node is below required version and Bun is unavailable.
  • Tests

    • Added tests covering runtime selection and direct Bun execution behavior.

Review Change Stack

Guard the published CLI wrapper before it falls back to the Node entrypoint. When Bun is available, the wrapper still delegates to the Bun bundle without checking the Bun version.

When Bun auto-run is disabled or Bun is unavailable, Node.js 22 and older now exit with a clear runtime error instead of starting the Node bundle. Align the ccusage package engines field with that runtime requirement.
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 16, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: b84f4058-3a62-4318-b5de-7ae08cfc3d1a

📥 Commits

Reviewing files that changed from the base of the PR and between 4034577 and d97c612.

📒 Files selected for processing (1)
  • apps/ccusage/src/cli.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/ccusage/src/cli.ts

📝 Walkthrough

Walkthrough

The PR refactors the CLI to enforce Node.js 22+ with Bun fallback. A new resolveCliRuntime helper selects between Bun (main.bun.js) and Node (main.node.js) entrypoints based on Bun availability and Node version compatibility. When Bun is unavailable and Node is below 22, an error is returned instead of runtime selection. runCli is updated to call resolveCliRuntime, handle errors, and spawn the resolved runtime.

Changes

Node.js Version Gating and Runtime Selection

Layer / File(s) Summary
Runtime type definition and constants
apps/ccusage/src/cli.ts
CliRuntime union type and MIN_NODE_MAJOR_VERSION = 22 constant establish the contract for runtime selection outcomes.
Runtime resolution logic with version gating
apps/ccusage/src/cli.ts
resolveCliRuntime parses Node major version, checks Bun availability via env/config and PATH lookup, and returns either Bun or Node command/args, or an error message when Node is too old and Bun unavailable.
CLI main function integration
apps/ccusage/src/cli.ts
runCli imports the Bun entrypoint when running under Bun; otherwise it delegates to resolveCliRuntime, prints any error to stderr and exits 1, or spawns the selected runtime with inherited stdio.
Test coverage for runtime selection
apps/ccusage/src/cli.ts
Vitest tests validate Bun fallback when Node is below minimum, error handling when both are unavailable, and Bun-entrypoint import when executing under Bun.

Sequence Diagram(s)

sequenceDiagram
  participant runCli
  participant resolveCliRuntime
  participant BunExecutable
  participant NodeRuntime
  participant ChildProcess

  runCli->>resolveCliRuntime: determine runtime (Bun or Node) or error
  resolveCliRuntime-->>runCli: {command,args} or {errorMessage}
  alt errorMessage returned
    runCli->>runCli: write errorMessage to stderr and exit 1
  else Bun selected
    runCli->>BunExecutable: spawn `bun main.bun.js` (inherited stdio)
    BunExecutable-->>ChildProcess: process runs Bun entrypoint
  else Node selected
    runCli->>NodeRuntime: spawn `process.execPath main.node.js` (inherited stdio)
    NodeRuntime-->>ChildProcess: process runs Node entrypoint
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • ryoppippi/ccusage#979: Complements this PR by updating engines.node and TypeScript targets to enforce Node >=22.11.0 project-wide.

Poem

🐰 I hop through runtimes, quick and spry,
If Node is old, Bun hops by,
A tiny switch, a careful test,
The CLI finds which path is best,
Hooray for smooth command-line sky!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'fix(ccusage): require Node 22 for Node runtime' directly and accurately reflects the main change: enforcing Node.js version 22 as a minimum requirement for the Node runtime path, which is the primary concern when Bun is unavailable.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/node-version-guard

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

apps/ccusage/src/cli.ts

[baseline-browser-mapping] The data in this module is over two months old. To ensure accurate Baseline data, please update: npm i baseline-browser-mapping@latest -D
tsconfig.json is not found. we cannot use type-aware rules.

Oops! Something went wrong! :(

ESLint: 9.35.0

Error [ERR_MODULE_NOT_FOUND]: Cannot find package 'eslint-plugin-format' imported from /node_modules/.pnpm/@antfu+eslint-config@4.19.0_@vue+compiler-sfc@3.5.30_eslint@9.35.0_typescript@5.9.2_vit_670a2c5c75d4275eabd7bc195a173ee6/node_modules/@antfu/eslint-config/dist/index.js
at Object.getPackageJSONURL (node:internal/modules/package_json_reader:301:9)
at packageResolve (node:internal/modules/esm/resolve:764:81)
at moduleResolve (node:internal/modules/esm/resolve:855:18)
at defaultResolve (node:internal/modules/esm/resolve:988:11)
at #cachedDefaultResolve (node:internal/modules/esm/loader:697:20)
at #resolveAndMaybeBlockOnLoaderThread (node:internal/modules/esm/loader:714:38)
at ModuleLoader.resolveSync (node:internal/modules/esm/loader:746:52)
at #resolve (node:internal/modules/esm/loader:679:17)
at ModuleLoader.getOrCreateModuleJob (node:internal/modules/esm/loader:599:35)
at node:internal/modules/esm/loader:628:32


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Lower the Node runtime guard from Node.js 23 to Node.js 22 so it matches the package engine and intended support floor.

Bun delegation remains unchanged. The guard only rejects Node.js 21 and older when the wrapper falls back to the Node bundle.
@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented May 16, 2026

Open in StackBlitz

@ccusage/amp

npx https://pkg.pr.new/ryoppippi/ccusage/@ccusage/amp@1006

ccusage

npx https://pkg.pr.new/ryoppippi/ccusage@1006

@ccusage/codex

npx https://pkg.pr.new/ryoppippi/ccusage/@ccusage/codex@1006

@ccusage/opencode

npx https://pkg.pr.new/ryoppippi/ccusage/@ccusage/opencode@1006

@ccusage/pi

npx https://pkg.pr.new/ryoppippi/ccusage/@ccusage/pi@1006

commit: d97c612

@ryoppippi ryoppippi changed the title fix(ccusage): require Node 23 for Node runtime fix(ccusage): require Node 22 for Node runtime May 16, 2026
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 16, 2026

ccusage performance comparison

This compares the PR build against the base branch build on the same CI runner.

Committed fixture performance

Committed small fixture for stable PR-to-PR feedback and output-shape regressions.

Fixture: apps/ccusage/test/fixtures/claude
Runtime: package ccusage bin from apps/ccusage/package.json through bun -b, --offline --json, measured by hyperfine with 2 warmups and 7 runs.

Command Base median PR median PR vs base
daily --offline --json 119.1ms 94.3ms 1.26x
session --offline --json 117.8ms 92.3ms 1.28x
blocks --offline --json 118.5ms 94.4ms 1.26x

Large real-world-shaped fixture performance

Generated fixture around 1 GiB shaped from aggregate local Claude-log statistics: thousands of JSONL files, many small sessions, and a long tail of larger sessions. No real prompts, paths, or outputs are stored in the fixture.

Fixture: /home/runner/work/_temp/ccusage-large-fixture
Runtime: package ccusage bin from apps/ccusage/package.json through bun -b, --offline --json, measured by hyperfine with 0 warmups and 1 runs.

Command Base median PR median PR vs base
daily --offline --json 1.404s 1.393s 1.01x

Package size

Package artifact Base PR Delta Ratio
packed ccusage-*.tgz 63.80 KiB 64.06 KiB +0.26 KiB 1.00x

Lower medians and smaller packed package sizes are better. CI runner noise still applies; use same-run ratios as directional PR feedback, not release guarantees.

@cloudflare-workers-and-pages
Copy link
Copy Markdown

cloudflare-workers-and-pages Bot commented May 16, 2026

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
⛔ Deployment terminated
View logs
ccusage-guide d97c612 May 16 2026, 07:01 PM

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
apps/ccusage/src/cli.ts (1)

158-189: ⚡ Quick win

Add test coverage for Node.js 23+ success case.

The current tests validate that Node 22 is rejected and that Bun is used when available. Consider adding a test case for the primary scenario this PR enables: Node.js 23+ should be accepted when Bun is unavailable.

🧪 Suggested test case
 	it('rejects Node.js 22 when Bun is unavailable', () => {
 		expect(
 			resolveCliRuntime({
 				argv: ['daily'],
 				distDir: '/app/dist',
 				findBunPath: () => undefined,
 				isRunningInBun: false,
 				nodeVersion: 'v22.13.1',
 				processExecPath: '/usr/bin/node',
 			}),
 		).toEqual({
 			errorMessage: 'ccusage requires Bun or Node.js >=23.0.0. Current Node.js: v22.13.1\n',
 		});
 	});
+
+	it('accepts Node.js 23 when Bun is unavailable', () => {
+		expect(
+			resolveCliRuntime({
+				argv: ['daily'],
+				distDir: '/app/dist',
+				findBunPath: () => undefined,
+				isRunningInBun: false,
+				nodeVersion: 'v23.0.0',
+				processExecPath: '/usr/bin/node',
+			}),
+		).toEqual({
+			args: ['/app/dist/main.node.js', 'daily'],
+			command: '/usr/bin/node',
+		});
+	});
 });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/ccusage/src/cli.ts` around lines 158 - 189, Add a test that asserts
resolveCliRuntime accepts Node.js >= v23 when Bun is unavailable: call
resolveCliRuntime with isRunningInBun: false, findBunPath: () => undefined,
nodeVersion: 'v23.0.0' (or higher), argv and distDir as in existing tests and
expect a successful result (command set to processExecPath and args pointing to
distDir/main.js with the provided argv) to verify the main Node.js path is
chosen; locate the tests around resolveCliRuntime in the existing describe block
to add this case.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@apps/ccusage/src/cli.ts`:
- Around line 158-189: Add a test that asserts resolveCliRuntime accepts Node.js
>= v23 when Bun is unavailable: call resolveCliRuntime with isRunningInBun:
false, findBunPath: () => undefined, nodeVersion: 'v23.0.0' (or higher), argv
and distDir as in existing tests and expect a successful result (command set to
processExecPath and args pointing to distDir/main.js with the provided argv) to
verify the main Node.js path is chosen; locate the tests around
resolveCliRuntime in the existing describe block to add this case.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c1301b7c-3e6b-46cd-91bd-f43da14251d6

📥 Commits

Reviewing files that changed from the base of the PR and between f91ad16 and 2946f1d.

📒 Files selected for processing (2)
  • apps/ccusage/package.json
  • apps/ccusage/src/cli.ts

Use the Bun global object to recognise when the wrapper is already running under Bun. That covers bun --bun/-b execution without relying on process.versions.bun.

When Bun is already active, runtime resolution returns the Bun entrypoint directly and does not scan PATH or evaluate the Node version guard.
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/ccusage/src/cli.ts`:
- Around line 109-113: The conditional that decides the Bun runtime path
currently checks bunAutoRunValue against CCUSAGE_BUN_AUTO_RUN_DISABLED_VALUE
before isBunRuntime, causing an active Bun process to be gated by the auto-run
env; update the ternary logic so isBunRuntime is evaluated first (return
processExecPath when isBunRuntime is true), otherwise apply the bunAutoRunValue
check and call findBunPath() only when auto-run is enabled; make the same
reorder for the identical block that appears around lines 121-124, referencing
the same symbols isBunRuntime, CCUSAGE_BUN_AUTO_RUN_DISABLED_VALUE,
processExecPath, and findBunPath to locate and fix both occurrences.
- Around line 11-18: Replace the custom union type CliRuntime with the byethrow
Result type: import the Result type from byethrow and define CliRuntime as
Result<{ args: string[]; command: string }, string> (or Result<SuccessShape,
ErrorMessageType>), then update all places that construct or pattern-match on
CliRuntime (including the other occurrences referenced) to use byethrow
conventions (e.g., Result.ok / Result.err or the repo’s standard helpers)
instead of the { errorMessage } | { command,args } union.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: d059e6cd-15ff-461d-b36c-bc4374d8103e

📥 Commits

Reviewing files that changed from the base of the PR and between 2946f1d and 4034577.

📒 Files selected for processing (1)
  • apps/ccusage/src/cli.ts

Comment thread apps/ccusage/src/cli.ts
Comment on lines +11 to +18
type CliRuntime =
| {
args: string[];
command: string;
}
| {
errorMessage: string;
};
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Replace custom Result union with byethrow Result type

This file introduces manual { errorMessage } | { command,args } Result modeling. The repo rule for apps/ccusage/src/**/*.ts requires byethrow for Result-based error handling; please align this branch to byethrow to keep error flow consistent.

As per coding guidelines, “Use byethrow for Result-based error handling”.

Also applies to: 121-124

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/ccusage/src/cli.ts` around lines 11 - 18, Replace the custom union type
CliRuntime with the byethrow Result type: import the Result type from byethrow
and define CliRuntime as Result<{ args: string[]; command: string }, string> (or
Result<SuccessShape, ErrorMessageType>), then update all places that construct
or pattern-match on CliRuntime (including the other occurrences referenced) to
use byethrow conventions (e.g., Result.ok / Result.err or the repo’s standard
helpers) instead of the { errorMessage } | { command,args } union.

Comment thread apps/ccusage/src/cli.ts Outdated
Short-circuit the CLI wrapper when it is already running under Bun. Instead of spawning another Bun process, the wrapper dynamically imports the Bun entrypoint and returns before PATH lookup or Node runtime checks.

Node-launched wrapper behaviour is unchanged: it still scans PATH for Bun when auto-run is enabled, then falls back to the Node entrypoint with a Node.js 22 minimum.
@ryoppippi ryoppippi merged commit 0078fcb into main May 16, 2026
23 of 24 checks passed
@ryoppippi ryoppippi deleted the codex/node-version-guard branch May 16, 2026 18:43
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.

1 participant