Skip to content

benedict2310/telecodex

Repository files navigation

TeleCodex

TeleCodex is a Telegram bridge for the OpenAI Codex CLI SDK. It keeps a Codex thread alive from your phone, streams agent responses and tool output in real time, and lets you hand the thread back to the CLI whenever you want.

Features

  • Per-context sessions — each Telegram chat or forum topic gets its own independent Codex session with separate thread, model, and busy state
  • Streaming responses — agent text edits in-place as Codex generates it
  • Full tool visibility — shell commands, file changes, web searches, MCP calls, and error items shown with configurable verbosity
  • Live plan display — Codex's todo list rendered as a separate message and updated as steps complete
  • Voice transcription — send a voice message or audio file; TeleCodex transcribes it (local parakeet-coreml or OpenAI Whisper) and forwards the text to Codex
  • Image input — send a photo (with optional caption) to pass screenshots or images directly to Codex
  • File ingest & artifacts — send a document to stage it for Codex; generated files are delivered back as Telegram documents
  • Session browser/sessions lists recent threads from ~/.codex, grouped by workspace; tap to switch
  • Telegram login/login authenticates against the Codex CLI via device auth flow, no terminal needed
  • Launch profiles/launch_profiles selects the sandbox + approval mode for new or reattached threads in the current Telegram context (/launch remains an alias)
  • Model picker/model shows available models and lets you switch for new threads
  • Reasoning effort/effort lets you dial from minimal to xhigh for new threads
  • Optional message reactions — 👀 while processing, 👍 on success when enabled; silently degrades in chats without reaction support
  • Friendly errors — common SDK and network errors are translated to actionable messages with command hints
  • Token usage — session token totals shown on /session, with optional per-turn footer in replies
  • Handback flow/handback prints a ready-to-run codex resume <id> command (copied to clipboard on macOS)
  • User allowlist — only configured Telegram user IDs can interact with the bot
  • Docker-friendly — workspace auto-detected (/workspace in containers, cwd otherwise)

Prerequisites

  • Node.js 22+
  • A Telegram bot token from @BotFather
  • The Codex CLI installed and authenticated on the host:
    • API key auth: set CODEX_API_KEY
    • ChatGPT login: codex login on the machine, or use /login from Telegram
  • (Optional) ffmpeg — required for local voice transcription via parakeet-coreml
  • (Optional) OPENAI_API_KEY — enables OpenAI Whisper as a voice transcription fallback

Setup

  1. Install dependencies:

    npm install
  2. Copy the example environment file:

    cp .env.example .env
  3. Fill in .env:

    Variable Required Description
    TELEGRAM_BOT_TOKEN Bot token from @BotFather
    TELEGRAM_ALLOWED_USER_IDS Comma-separated Telegram user IDs
    CODEX_API_KEY API key for Codex (alternative to ChatGPT login)
    CODEX_MODEL Default model, e.g. gpt-5.4, o3
    CODEX_SANDBOX_MODE read-only, workspace-write (default), danger-full-access
    CODEX_APPROVAL_POLICY never (default), on-request, on-failure, untrusted
    CODEX_LAUNCH_PROFILES_JSON Optional JSON array of named launch profiles for /launch_profiles
    CODEX_DEFAULT_LAUNCH_PROFILE Default launch profile id (defaults to default)
    ENABLE_UNSAFE_LAUNCH_PROFILES Set to true to allow extra danger-full-access launch profiles
    TOOL_VERBOSITY all, summary (default), errors-only, none
    SHOW_TURN_TOKEN_USAGE Show the per-turn in/cached/out footer in final replies (false by default)
    MAX_FILE_SIZE Max upload size in bytes (default 20971520 = 20 MB)
    ENABLE_TELEGRAM_LOGIN Allow /login and /logout from Telegram (true by default)
    ENABLE_TELEGRAM_REACTIONS Enable Telegram emoji reactions like 👀 / 👍 (false by default)
    OPENAI_API_KEY Enables OpenAI Whisper voice transcription fallback
  4. Start the bot:

    npm run dev

Telegram Commands

Command Description
/start Welcome & status (concise for returning users)
/help Grouped command reference
/new Start a fresh thread (workspace picker if multiple workspaces)
/session Current thread ID, workspace, model, effort, and token totals
/sessions Browse recent threads grouped by workspace; tap to switch
/switch <id> Switch directly to a thread by ID
/retry Resend the last prompt
/abort Cancel the current turn
/launch_profiles Select launch profile for new or reattached threads (/launch alias kept)
/model View and change the model
/effort Set reasoning effort: minimal · low · medium · high · xhigh
/auth Check authentication status
/login Start Codex device-auth flow from Telegram
/logout Sign out of Codex
/voice Check voice transcription backend status
/handback Print codex resume <id> for CLI handoff
/attach <id> Bind an existing Codex thread to this forum topic

Voice, image & file input

  • Voice / audio — send any voice message or audio file; TeleCodex transcribes it and sends the result to Codex
  • Photos — send a photo with an optional caption; the image is forwarded to Codex as visual input
  • Documents — send a file (with optional caption); TeleCodex stages it in the workspace, runs Codex, and delivers any generated files back as Telegram documents

Tool verbosity

Mode What you see
all Every tool start, streaming output, and result
summary (default) A short grouped footer such as Tools used: 3x bash, 2x subagents, web_fetch
errors-only Only failed tool calls
none Silent

Per-turn token usage is hidden by default. Set SHOW_TURN_TOKEN_USAGE=true if you want the in / cached / out footer appended to final replies.

Launch profiles

  • TeleCodex always provides a built-in default profile synthesized from CODEX_SANDBOX_MODE and CODEX_APPROVAL_POLICY
  • Built-in Telegram-visible presets are:
    • Default
    • Read Only
    • Review
    • Full Access when ENABLE_UNSAFE_LAUNCH_PROFILES=true
  • Workspace Write is not listed separately because it is already the default behavior in the shipped config
  • Optional extra profiles can be configured with CODEX_LAUNCH_PROFILES_JSON, for example:
    [
      { "id": "readonly", "label": "Read Only", "sandboxMode": "read-only", "approvalPolicy": "never" },
      { "id": "review", "label": "Review", "sandboxMode": "workspace-write", "approvalPolicy": "on-request" }
    ]
  • /launch_profiles changes only future thread creation or reattachment in the current chat/topic context; it does not mutate an already active thread in place
  • Extra danger-full-access profiles are blocked unless ENABLE_UNSAFE_LAUNCH_PROFILES=true
  • Selecting a danger-full-access profile from Telegram requires an explicit confirmation step

Multi-Session Architecture

Each Telegram chat or forum topic is identified by a context key — the chat ID alone for private chats, or chatId:threadId for forum topics. This means every topic in a supergroup gets its own independent Codex session.

The SessionRegistry maps context keys to CodexSessionService instances:

┌───────────────────┐      ┌───────────────────────────────┐
│ Private Chat A     │─────▶│ CodexSessionService (thread X) │
│ key: "111"         │      └───────────────────────────────┘
├───────────────────┤      ┌───────────────────────────────┐
│ Group B / Topic 1  │─────▶│ CodexSessionService (thread Y) │
│ key: "222:1"       │      └───────────────────────────────┘
├───────────────────┤      ┌───────────────────────────────┐
│ Group B / Topic 2  │─────▶│ CodexSessionService (thread Z) │
│ key: "222:2"       │      └───────────────────────────────┘
└───────────────────┘
  • First message in a context → creates a new CodexSessionService → starts a new Codex thread
  • Subsequent messages → same context key → same session → conversation continues
  • /new → replaces the thread within the same context (optionally picking a workspace first)
  • /sessions → lists all Codex threads from ~/.codex, lets you switch within the current context
  • /attach <id> → resumes a specific Codex CLI thread (useful for picking up work started in the terminal)

Session metadata (thread ID, workspace, launch profile, model, effort) is persisted to .telecodex/contexts.json and restored on restart so threads survive bot reboots.

Each context has independent busy-state tracking, so a running prompt in one topic doesn't block another.

Handoff: Telegram → CLI

  1. Run /handback in Telegram
  2. TeleCodex replies with:
    cd '/path/to/project' && codex resume 'thread-abc123'
  3. Paste and run in your terminal

On macOS the command is also copied to the clipboard automatically.

Architecture

Telegram ←→ Grammy bot (auto-retry, HTML formatting, inline keyboards)
                |
                v
        SessionRegistry  ──→  per-context CodexSessionService instances
                |
                ├── @openai/codex-sdk  ──→  spawns Codex CLI subprocess
                │     └── ThreadEvents (agent text, commands, file changes,
                │                       MCP calls, web searches, todo lists,
                │                       reasoning, errors, token usage)
                ├── CodexStateReader  ──→  ~/.codex/state_*.sqlite  (threads)
                │                    ──→  ~/.codex/models_cache.json (models)
                ├── CodexAuth        ──→  codex login/logout subprocess
                ├── Attachments      ──→  .telecodex/inbox/<turnId>/ (staged files)
                ├── Artifacts        ──→  .telecodex/outbox/<turnId>/ (generated files)
                └── VoiceTranscriber  ──→  parakeet-coreml (local)
                                     ──→  OpenAI Whisper (cloud fallback)

Project Layout

TeleCodex/
├── src/
│   ├── index.ts           — startup, signal handling, polling loop
│   ├── bot.ts             — Telegram bot, all commands and handlers
│   ├── bot-ui.ts          — pure render helpers (/help, /start, session labels)
│   ├── codex-launch.ts    — launch profile parsing, validation, and formatting
│   ├── codex-session.ts   — CodexSessionService wrapping the SDK
│   ├── codex-state.ts     — SQLite reader for thread/model discovery
│   ├── codex-auth.ts      — Codex CLI auth (login status, device auth, logout)
│   ├── session-registry.ts — per-context session map with persistence
│   ├── context-key.ts     — Telegram chat/topic → context key derivation
│   ├── attachments.ts     — file staging (sanitization, size limits)
│   ├── artifacts.ts       — generated file collection and Telegram delivery
│   ├── error-messages.ts  — SDK/network error → user-friendly translation
│   ├── voice.ts           — voice transcription (parakeet / Whisper)
│   ├── config.ts          — environment loading and validation
│   └── format.ts          — Markdown → Telegram HTML conversion
├── test/                  — 15 test files, 180+ tests (vitest)
├── .env.example
├── Dockerfile
├── docker-compose.yml
├── tsconfig.json
└── vitest.config.ts

Docker

docker compose up --build

The compose file:

  • loads environment from .env
  • mounts ~/.codex for auth state and persisted threads
  • mounts ./workspace as /workspace
  • runs as a non-root user

Development

npm run dev      # run with tsx (no build step)
npm run build    # compile TypeScript
npm test         # run vitest

Release Automation

TeleCodex does not yet use the TelePi npm release pipeline, but the exact Trusted Publishing process has been documented so it can be adopted here.

See:

  • docs/npm-trusted-publishing.md

That playbook covers:

  • making the package publishable on npm
  • adding a tag-driven GitHub Actions workflow
  • configuring npm Trusted Publishing
  • the maintainer release flow (npm version ... + git push --follow-tags)

Security Notes

  • Only users in TELEGRAM_ALLOWED_USER_IDS can interact with the bot
  • Default sandbox mode is workspace-write — Codex can read and write within the working directory
  • Use danger-full-access only if you fully trust the user and the host environment
  • The built-in Full Access profile and any extra danger-full-access launch profiles are opt-in via ENABLE_UNSAFE_LAUNCH_PROFILES=true
  • Default approval policy is never — suited for headless/automated use
  • /launch_profiles only selects from validated configured profiles; Telegram users cannot submit arbitrary sandbox or approval values
  • CODEX_API_KEY (agent auth) and OPENAI_API_KEY (voice transcription) are separate credentials
  • /login and /logout can be disabled by setting ENABLE_TELEGRAM_LOGIN=false
  • Files uploaded via Telegram are sanitized (name, size, type) before staging in the workspace
  • All Markdown output is sanitized before being sent as Telegram HTML

About

Telegram bridge for the OpenAI Codex CLI SDK

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Languages