Skip to content

Repository files navigation

even-codex-bridge

Text-first bridge to use Codex from Even G2 glasses.

Current scope

  • Even web app captures mic PCM frames and forwards to backend.
  • Backend streams PCM to ElevenLabs Scribe v2 Realtime STT.
  • Final STT text (or manual text submission) is sent to codex app-server.
  • Codex deltas/finals are rendered back on glasses.

Runtime mode

This repo is configured for YOLO mode:

  • approvalPolicy: "never"
  • sandboxPolicy: { type: "dangerFullAccess" }

Repo layout

  • server/index.js: websocket bridge entrypoint
  • server/codexRpcClient.js: JSON-RPC stdio client for codex app-server
  • server/codexSessionBridge.js: thread/turn orchestration + notification mapping
  • server/elevenLabsSttClient.js: realtime STT client for ElevenLabs
  • server/protocol.js: frontend/backend message validation
  • src/: Even web app frontend

Prerequisites

  • Node 20+
  • codex CLI installed and available in PATH (or set CODEX_BIN)
  • Even beta app + Even Hub dev mode
  • ElevenLabs API key with realtime speech-to-text access

Configure

  1. Copy .env.example to .env.
  2. Set at minimum:
  • CLIENT_SHARED_TOKEN
  • VITE_CLIENT_SHARED_TOKEN (must match exactly)
  • CODEX_CWD
  • VITE_WS_BASE_URL
  1. Optional model toggle config:
  • CODEX_MAIN_MODEL (default: gpt-5.3-codex; legacy fallback: CODEX_MODEL)
  • CODEX_FAST_MODEL (default: gpt-5.3-codex-spark)
  1. Optional session guidance config:
  • CODEX_SESSION_GUIDANCE sets thread-level developerInstructions for both thread/start and thread/resume.
  • If unset, a smart-glasses-safe default is applied.
  • Set CODEX_SESSION_GUIDANCE= (empty) to disable this guidance.
  1. Optional thread persistence (recommended):
  • ENABLE_DEFAULT_THREAD_RESUME=1
  • ENABLE_PERSIST_THREAD_STATE=1
  • THREAD_STATE_FILE=.runtime/thread-state.json
  1. STT configuration:
  • STT_PROVIDER=elevenlabs
  • STT_API_KEY=<your key>
  • STT_MODEL_ID=scribe_v2_realtime
  • STT_COMMIT_STRATEGY=vad
  • Optional VAD tuning (noise/false-positive control):
    • STT_VAD_THRESHOLD
    • STT_MIN_SPEECH_DURATION_MS
    • STT_MIN_SILENCE_DURATION_MS
    • STT_VAD_SILENCE_THRESHOLD_SECS

For text-only mode without mic STT, set STT_PROVIDER=none and use manual text submit.

Run

Terminal A:

npm run dev:server

npm run dev:server is supervised:

  • enforces Codex CLI version >= 0.101.0
  • prevents duplicate runs with .bridge-run.lock
  • auto-relaunches only when server exits with code 42 (used by /restart bridge|both)

Terminal B:

npm run dev:client

Or both:

npm run dev

Run server without supervision (rarely needed):

npm run dev:server:raw

Optional simulator autostart (dev-only)

For simulator/automation loops, you can boot directly into the running assistant:

  • Query flag: ?autostart=1
  • Env flag: VITE_AUTO_START=1
  • Optional query overrides (for ephemeral test sessions): ?ws=<wss-url>&token=<shared-token>&autostart=1

Example: https://codex-even-app.example.com/?autostart=1

Cloudflared tunnel (optional, for outside-network access)

Use this only when you want remote/public access to your local dev server. Local development on the same machine/LAN does not require Cloudflare.

Caution

Exposing local services to the internet increases risk. Use a strong CLIENT_SHARED_TOKEN, keep ALLOWED_ORIGINS tight, and prefer Cloudflare Access/WAF controls for public deployments. Never commit secrets (.env, tunnel tokens, credentials files, runtime logs).

One-time setup for a named tunnel + DNS routes:

cloudflared tunnel create codex-even
cloudflared tunnel route dns codex-even codex-even-app.example.com
cloudflared tunnel route dns codex-even codex-even-api.example.com

Run the dual-host tunnel (frontend + backend):

npm run tunnel

Run tunnel and provision DNS in the same step:

npm run tunnel -- --provision-dns

Run a temporary quick tunnel instead:

npm run tunnel:quick

Set matching env values:

  • VITE_WS_BASE_URL=wss://codex-even-api.example.com/ws
  • ALLOWED_ORIGINS=https://codex-even-app.example.com,http://localhost:5173,http://127.0.0.1:5173
  • Include your public app host in ALLOWED_ORIGINS (or set VITE_ALLOWED_HOSTS) so Vite dev/preview host checks allow that domain.

By default the script runs tunnel codex-even. The script resolves the tunnel UUID and uses that for route/run calls (avoids name-resolution fallback to default tunnel config). Auth for npm run tunnel can come from any one of:

  • ~/.cloudflared/<tunnel-uuid>.json credentials file
  • TUNNEL_TOKEN environment variable
  • runtime token generation via cloudflared tunnel token <tunnel-name> (requires authenticated cloudflared CLI)

Protocol

Client -> server:

  • session.start: { appVersion, deviceInfo?, clientTs, resumeThreadId? }
  • audio.chunk: { seq, pcmB64, sampleRate:16000, format:"pcm16le", durationMs, byteLength }
  • text.submit: { text, clientTs? }
  • session.stop: { reason? }
  • ping: { clientTs }

Server -> client:

  • status: { phase, detail?, sessionId? }
  • transcript.delta: { text, turnId, role, replace?, ts }
  • transcript.final: { text, turnId, role, ts }
  • metrics: runtime counters
  • error: { code, message, recoverable }
  • pong: { serverTs, clientTs? }

Slash Commands

Accepted in text.submit as either typed slash or spoken "slash":

  • /help or slash help
  • /status or slash status
  • /debug or slash debug
  • /stop or slash stop
  • /reset or slash reset
  • /compact or slash compact
  • /effort or slash effort
  • /spark or slash spark
  • /fast or slash fast
  • /restart codex or slash restart codex
  • /restart bridge or slash restart bridge
  • /restart both or slash restart both
  • /thread or slash thread
  • /thread new or slash thread new

Behavior:

  • help: lists supported commands.
  • status: prints runtime status and key config toggles.
  • debug: prints current turn/transport diagnostics.
  • stop: requests interrupt of the active turn.
  • reset: starts a fresh thread (same effect as thread new).
  • compact: requests thread compaction.
  • During compaction lifecycle, bridge emits:
    • Compaction started.
    • Compaction complete.
  • effort: without args shows current model/effort; with arg sets effort for current model.
    • Allowed values: none, minimal, low, medium, high, xhigh
    • STT aliases like ex high / extra high normalize to xhigh
  • spark/fast: toggle between current model and configured fast model, restoring prior model+effort on toggle off.
  • restart codex: restarts only Codex app-server in-process, attempts thread resume, then falls back to a fresh thread.
  • restart bridge: exits server with code 42; supervised launcher restarts process.
  • restart both: same process restart path as restart bridge.
  • Spoken STT variants codec and codecs are accepted as codex for /restart.
  • thread: prints current thread id and active turn id.
  • thread new: starts a fresh thread (intentionally bypasses resume).

Stop/start behavior:

  • ENABLE_DEFAULT_THREAD_RESUME=1 enables resume-by-default on session.start.
  • ENABLE_DEFAULT_THREAD_RESUME=0 forces fresh thread/start on session.start (client/server resume IDs ignored).
  • The web app caches the latest sessionId and sends it as resumeThreadId on next session.start.
  • If ENABLE_PERSIST_THREAD_STATE=1, backend also persists the latest thread id to THREAD_STATE_FILE.
  • Backend attempts thread/resume first (client resumeThreadId, else persisted server thread id), then falls back to thread/start if resume is unavailable.
  • CODEX_SESSION_GUIDANCE is applied as thread developerInstructions on both thread/start and thread/resume.

UI behavior

  • text.submit (manual prompt box) remains a permanent dev/QA path.
  • Single click (CLICK_EVENT) toggles focus mode:
    • Focus mode shows assistant output + compact status (listening|muted + connection state).
    • Normal mode keeps richer transcript/status context.
  • Double click (DOUBLE_CLICK_EVENT) toggles mic state:
    • listening = mic on and STT ingest active.
    • muted = mic off (audioControl(false)), STT ingest paused.
  • Web app Mute Mic / Unmute Mic button mirrors the same mic toggle for manual-text-focused testing.
    • If audioControl(false) is rejected by host firmware, bridge applies a local mute fallback (stops forwarding audio frames to STT).
  • Ring paging is SDK-native.
    • App-side scroll math/windowing is disabled.
    • Scroll events are only logged for diagnostics; no app-level offset/direction transform is applied.
    • The SDK controls direction, paging, and scrollbar behavior.
  • Transcript is rendered newest-first (latest content at top) to align with SDK update behavior during streaming.
  • Transcript text is still capped (maxChars) before render to keep updates stable.
    • Older content outside the cap is truncated from the head.
  • Auto-mute safety policy:
    • Triggers on backend disconnect/reconnect only.
    • Auto-resume only occurs when mute reason is auto and backend connectivity recovers.
    • Manual mute is never auto-resumed.

Notes

  • Codex websocket transport is intentionally not used here; this bridge uses stdio app-server transport.
  • src/evenBridge.js includes SDK-safe startup fallback for createStartUpPageContainer one-time behavior.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages