Skip to content

feat(codex): native overlay integration, CDP-free (deprecate CDP adapter) - #6

Merged
SergioChan merged 7 commits into
SergioChan:mainfrom
fzn0728:feat/codex-native-overlay
Aug 16, 2026
Merged

feat(codex): native overlay integration, CDP-free (deprecate CDP adapter)#6
SergioChan merged 7 commits into
SergioChan:mainfrom
fzn0728:feat/codex-native-overlay

Conversation

@fzn0728

@fzn0728 fzn0728 commented Aug 16, 2026

Copy link
Copy Markdown
Collaborator

Summary

Two related changes, unified on the native overlay model:

  1. Codex now runs through the native overlay instead of CDP. The overlay meters a frontmost Codex window by reading the active thread from Codex's own local state database (~/.codex/state_5.sqlite, threads.recency_at_ms filtered to thread_source='user') and the existing rollout files — no loopback debugging port, no launch flags, and no quit/relaunch of Codex. This removes the crash-on-restart failure mode and the requirement that Codex be started with CDP.
  2. The always-on desktop widget (a machine-wide global face + menu-bar controls), which the overlay reuses.

The CDP adapter is deprecated, not deleted — it still works, but prints a notice pointing at the app and is slated for removal once the native path has shipped.

Major changes vs current main (v0.3.1)

  • New Codex data path (CDP-free): CodexSnapshotRuntime resolves the active thread from state_5.sqlite and reuses RolloutStore + MetricsEngine for tokens. Fails closed — a selected thread with no telemetry stays unbound rather than adopting another thread's numbers.
  • One overlay, two hosts: the overlay bridge gains a codex-snapshot command; the native MeterController gains a bound Codex face (third mode) so a frontmost Codex window gets a real binding instead of the global face. state_5's schema is an internal of Codex Desktop — the reader degrades to unbound if it ever changes.
  • Host-agnostic install: the overlay no longer requires Claude.app (app.asar dropped from the startup gate; install.sh verifies Claude only when present). Codex-only machines can install it.
  • Renamed to "Token Widget": CFBundleName and the source-install bundle drop "for Claude". Bundle id, executable name, and LaunchAgent label are unchanged so existing installs and self-update keep working.
  • Always-on widget: machine-wide global face and menu-bar item (show/hide, always-on-desktop, dashboard, quit-that-actually-quits via LaunchAgent bootout).
  • CDP Codex adapter deprecated: installer notice + README/CHANGELOG; code left in place.

Why AX wasn't used (Codex differs from Claude)

Claude binds via an AXURL on its web area. Codex's Accessibility tree exposes no thread identity (bare app://-/index.html, no per-thread route, no selection state), so the Claude approach doesn't port. The local state DB is the CDP-free equivalent. The official codex app-server RPC also works and cleanly provides account-level totals; it's noted for follow-up but not required here.

Tests

  • npm test — 190/190, including a Codex-only install (no Claude.app), the codex-snapshot bridge over stdio, and the fail-closed binding cases.
  • npm run check clean; swiftc -typecheck clean; Info.plist lints.

Not covered

  • No live signed-build/install run (that would take over a running Codex session), so no Codex version cited for live validation. Verified via unit/integration tests and typecheck.
  • The CDP path's removal, the app-server RPC for account-level global stats, and a fuller rename of internal identifiers are deliberately left for follow-ups.

Built on top of the always-on-widget work, so this branch includes both.

fzn0728 and others added 7 commits August 15, 2026 21:50
The overlay no longer lives only inside a bound Claude Desktop session.
When no exact session binding exists, the widget can now show a "global"
face with machine-wide totals across Claude Code, Codex, and Cline.

- overlay-bridge: new global-snapshot command returning identity plus
  usage-history totals (today, lifetime, streak), memoized for 60s
- runtime UI: data-mode=global face — TODAY label, identity header,
  lifetime/streak from meterStats, idle gauge; settings identity
  rendering shared between session and global faces
- native overlay: menu-bar StatusBarController with show/always-visible
  toggles persisted to visibility.json, LaunchAgent bootout on quit so
  KeepAlive stops resurrecting the app, and Codex window following via
  codexBundleID

Tests: 145/145 pass (node --test).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
First slice of the Codex native-overlay migration (option 1). Resolves the
active Codex thread without CDP by reading the desktop app-server's own
state_5.sqlite (threads.recency_at_ms, filtered to thread_source='user'),
then reuses the existing RolloutStore + MetricsEngine for token telemetry.

- thread-state.mjs: read-only state DB reader, fails closed to null on a
  missing file, locked handle, or schema drift so a caller never binds to
  the wrong thread
- snapshot-runtime.mjs: CodexSnapshotRuntime mirroring ClaudeSnapshotRuntime's
  interface; binding.exact reflects a proven bind, and a selected thread with
  no telemetry stays unbound rather than adopting another thread's numbers

Verified: Token Widget's bundled node (v22.22) reads state_5 via node:sqlite
with no flag. Tests 188/188, npm run check clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Extend the overlay bridge with a codex-snapshot command so one overlay app
and one bridge serve both hosts. The Codex runtime is built lazily, so a
Claude-only session never opens the state database or scans ~/.codex.

Filter node:sqlite's experimental warning out of the bridge's stderr so it
does not repeat into the LaunchAgent log on every start.

Tests 189/189, npm run check clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A frontmost Codex window now gets a bound face driven by the codex-snapshot
bridge command instead of the machine-wide global face. The bridge resolves
the active thread from the state database, so the native side supplies no
session id and reads no Accessibility tree — it only pins the panel and polls.

Adds a third overlay mode alongside the Claude session and global faces, with
the mode transitions resetting each other so no stale numbers survive a
host switch. swiftc -typecheck clean; JS suite 189/189.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Make the shared overlay host-agnostic so Codex-only machines can install it:

- drop the Claude model catalog (app.asar) from the overlay's required-files
  startup gate; ClaudeContextWindowResolver already degrades to nil when it is
  absent, and Codex context windows come from the rollout snapshot
- install.sh verifies Claude.app only when present (preserving the genuine-app
  check) and installs Codex-only support when it is absent, instead of exiting

Adds a regression test installing the overlay with no Claude.app. Suite
190/190, npm run check clean, swiftc -typecheck clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Now that the overlay serves both Claude and Codex, drop "for Claude" from the
user-visible name. Sets CFBundleName to "Token Widget" (the display name shown
in the Accessibility list and menu bar, previously "Token Widget for Claude"),
renames the source-install bundle to "Token Widget.app" to match the DMG, and
updates the permission-grant instructions in docs and scripts to suit.

Bundle identifier, executable name, and LaunchAgent label are left unchanged so
existing installs and the self-update flow keep working. "Token Widget for
Claude Code" (the integration name) is preserved. Suite 190/190, check + swiftc
clean, plist lints.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The CDP source installer now prints a deprecation notice pointing users at the
Token Widget app (silenceable via TOKEN_METER_CODEX_CDP_ACK=1) and still runs.
README, the Codex integration README, and CHANGELOG present the native overlay
as the recommended way to meter Codex and mark the adapter as legacy.

The CDP code is left in place and functional — deprecate first, remove later,
once the native path has shipped and proven itself against Codex's state schema.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@vercel

vercel Bot commented Aug 16, 2026

Copy link
Copy Markdown

@fzn0728 is attempting to deploy a commit to the t54 labs Team on Vercel.

A member of the Team first needs to authorize it.

@SergioChan
SergioChan merged commit 060d6aa into SergioChan:main Aug 16, 2026
0 of 2 checks passed
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