{"id":48083399,"url":"https://github.com/krablante/codex-telegram-gateway","last_synced_at":"2026-04-25T02:15:40.349Z","repository":{"id":348327595,"uuid":"1197422265","full_name":"Krablante/codex-telegram-gateway","owner":"Krablante","description":"A Telegram forum-topic gateway for the local Codex CLI runtime.","archived":false,"fork":false,"pushed_at":"2026-03-31T17:08:01.000Z","size":566,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-03-31T19:14:16.495Z","etag":null,"topics":["automation","codex","forum-topics","homelab","nodejs","openai","telegram","telegram-bot"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Krablante.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2026-03-31T15:10:49.000Z","updated_at":"2026-03-31T17:08:03.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/Krablante/codex-telegram-gateway","commit_stats":null,"previous_names":["krablante/codex-telegram-gateway"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/Krablante/codex-telegram-gateway","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Krablante%2Fcodex-telegram-gateway","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Krablante%2Fcodex-telegram-gateway/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Krablante%2Fcodex-telegram-gateway/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Krablante%2Fcodex-telegram-gateway/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Krablante","download_url":"https://codeload.github.com/Krablante/codex-telegram-gateway/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Krablante%2Fcodex-telegram-gateway/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31403952,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-04T10:20:44.708Z","status":"ssl_error","status_checked_at":"2026-04-04T10:20:06.846Z","response_time":60,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["automation","codex","forum-topics","homelab","nodejs","openai","telegram","telegram-bot"],"created_at":"2026-04-04T15:00:19.922Z","updated_at":"2026-04-25T02:15:40.342Z","avatar_url":"https://github.com/Krablante.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003cimg src=\"./assets/readme/codex-telegram-gateway-banner.svg\" alt=\"codex-telegram-gateway banner\"\u003e\n\u003c/p\u003e\n\n\u003ch1 align=\"center\"\u003ecodex-telegram-gateway\u003c/h1\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003cstrong\u003eRun Codex from Telegram without dragging a heavyweight agent stack into every prompt.\u003c/strong\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  One topic = one session. \u003ccode\u003eSpike\u003c/code\u003e talks directly to \u003ccode\u003ecodex exec --json\u003c/code\u003e. \u003ccode\u003eZoo\u003c/code\u003e adds an optional project-status board.\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://github.com/Krablante/codex-telegram-gateway/releases\"\u003e\n    \u003cimg src=\"https://img.shields.io/github/v/release/Krablante/codex-telegram-gateway?style=for-the-badge\" alt=\"GitHub release\"\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://github.com/Krablante/codex-telegram-gateway/actions/workflows/ci.yml\"\u003e\n    \u003cimg src=\"https://img.shields.io/github/actions/workflow/status/Krablante/codex-telegram-gateway/ci.yml?branch=main\u0026style=for-the-badge\" alt=\"CI status\"\u003e\n  \u003c/a\u003e\n  \u003ca href=\"./LICENSE\"\u003e\n    \u003cimg src=\"https://img.shields.io/badge/License-MIT-blue.svg?style=for-the-badge\" alt=\"MIT License\"\u003e\n  \u003c/a\u003e\n  \u003cimg src=\"https://img.shields.io/badge/Node-20%2B-339933?style=for-the-badge\u0026logo=node.js\u0026logoColor=white\" alt=\"Node 20+\"\u003e\n  \u003cimg src=\"https://img.shields.io/badge/Telegram-Forum%20Topics-26A5E4?style=for-the-badge\u0026logo=telegram\u0026logoColor=white\" alt=\"Telegram forum topics\"\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"./docs/setup.md\"\u003eSetup\u003c/a\u003e\n  ·\n  \u003ca href=\"./docs/index.md\"\u003eDocs\u003c/a\u003e\n  ·\n  \u003ca href=\"./docs/telegram-surface.md\"\u003eTelegram Surface\u003c/a\u003e\n  ·\n  \u003ca href=\"./docs/runbook.md\"\u003eRunbook\u003c/a\u003e\n  ·\n  \u003ca href=\"./CHANGELOG.md\"\u003eChangelog\u003c/a\u003e\n\u003c/p\u003e\n\n`codex-telegram-gateway` is a lean Telegram front end for the local Codex CLI. It keeps the normal path simple: Telegram forum topics, one bot, your local machine, and the repos/auth/tools that Codex already uses.\n\nThe current runtime is intentionally single-bot. `Spike` is the worker. The old second-bot autonomy stack has been removed from the public surface.\n\n## Why People Use It\n\n- one task, one topic, one durable session\n- direct Telegram -\u003e `Spike` -\u003e `codex` flow for normal work\n- less prompt overhead than heavier always-on agent stacks\n- real local files and commands, not a fake hosted wrapper\n- recovery that survives long-running work\n\n## Mental Model\n\n| Piece | Role |\n| --- | --- |\n| `General` topic | global controls, `/guide`, `/help`, `/global`, creating new work topics |\n| work topic | the actual task lane |\n| `Spike` | the live worker that reads code, edits files, runs commands, and sends progress/final replies |\n| `Zoo` topic | optional menu-only project status lane |\n| local state root | durable memory: sessions, briefs, logs, queued prompts, artifacts |\n\nArchitecture at a glance:\n\n```text\nTelegram forum\n├─ General\n│  ├─ /help\n│  ├─ /guide\n│  └─ /global\n├─ Work topics\n│  ├─ plain prompts -\u003e Spike\n│  ├─ /q, /wait, /suffix, /compact, /purge\n└─ Zoo\n   └─ optional menu-only project cards\n\nTelegram surface -\u003e codex-telegram-gateway -\u003e codex exec --json -\u003e local repos/files/state\n```\n\n## Highlights\n\n- one Telegram topic maps to one durable local session\n- live follow-ups can steer into an active run\n- commentary-style progress delivery instead of raw tool noise\n- attachment-aware prompts, including file-first flows\n- `/new Topic Name` topic creation when the bot has Telegram rights\n- `/help` visual card and `/guide` beginner PDF\n- topic-local and global menus through `/menu` and `/global`\n- queued prompts with `/q`\n- compacted recovery memory rebuilt from the clean exchange log\n- operator-only emergency private chat lane\n- optional `Zoo` topic for project snapshots\n\n## Code Direction\n\nThe repo has now moved to an explicit modular handler system and should stay that way.\n\n- keep central shells such as `command-router.js` thin\n- add new Telegram behavior in domain handlers under `src/telegram/command-handlers/`\n- split tests by the same ownership instead of regrowing giant central suites\n- prefer small shared helper modules only when multiple handlers truly share one contract\n\n## Canonical paths\n\n- repo root: wherever you cloned the repo, for example `/path/to/codex-telegram-gateway`\n- state root: `${XDG_STATE_HOME:-~/.local/state}/codex-telegram-gateway`\n- runtime env: `${XDG_CONFIG_HOME:-~/.config}/codex-telegram-gateway/runtime.env`\n\n## Read This Next\n\n- [docs/index.md](./docs/index.md) — doc map\n- [docs/architecture.md](./docs/architecture.md) — runtime shape and flow\n- [docs/telegram-surface.md](./docs/telegram-surface.md) — commands, waits, suffixes, rendering, file delivery\n- [docs/deployment.md](./docs/deployment.md) — env, services, host bootstrap\n- [docs/testing.md](./docs/testing.md) — doctor, smoke, soak, live-user testing\n- [docs/runbook.md](./docs/runbook.md) and [docs/runbook-rus.md](./docs/runbook-rus.md) — operator troubleshooting and recovery\n- [docs/state-contract.md](./docs/state-contract.md) — mutable state surfaces\n\n## Quick Start\n\nLinux/operator path:\n\n```bash\ncd /path/to/codex-telegram-gateway\nnpm ci\ninstall -d -m700 \"${XDG_CONFIG_HOME:-$HOME/.config}/codex-telegram-gateway\"\ninstall -m600 .env.example \"${XDG_CONFIG_HOME:-$HOME/.config}/codex-telegram-gateway/runtime.env\"\n$EDITOR \"${XDG_CONFIG_HOME:-$HOME/.config}/codex-telegram-gateway/runtime.env\"\nENV_FILE=\"${XDG_CONFIG_HOME:-$HOME/.config}/codex-telegram-gateway/runtime.env\" make doctor\nENV_FILE=\"${XDG_CONFIG_HOME:-$HOME/.config}/codex-telegram-gateway/runtime.env\" make run\n```\n\nFirst-time minimum in the runtime env:\n\n- `TELEGRAM_BOT_TOKEN`\n- `TELEGRAM_ALLOWED_USER_ID` or `TELEGRAM_ALLOWED_USER_IDS`\n- `TELEGRAM_FORUM_CHAT_ID`\n- `WORKSPACE_ROOT`\n- optional `DEFAULT_SESSION_BINDING_PATH`\n- optional `CODEX_CONFIG_PATH`\n- optional `CODEX_LIMITS_COMMAND` or `CODEX_LIMITS_SESSIONS_ROOT` when limits should come from another Codex host\n- optional `CURRENT_HOST_ID` and `HOST_REGISTRY_PATH` for multi-host setups\n\n`DEFAULT_SESSION_BINDING_PATH` only changes where plain `/new Topic Name` starts when no explicit `cwd=...` is provided. If it is unset, ordinary `/new` falls back to `WORKSPACE_ROOT`.\nIf the explicit binding path contains spaces, quote it, for example `/new cwd=\"C:/Users/Example/Source Repos\" Audit topic`.\nLimits snapshot lookup precedence is `CODEX_LIMITS_SESSIONS_ROOT` -\u003e `CODEX_SESSIONS_ROOT` -\u003e `~/.codex/sessions`.\n\n`/limits` is available as a direct command, is folded into `/status`, and is shown on the root `/global` and `/menu` panels. Capped accounts show the current `5h` and `7d` windows; unlimited accounts are rendered explicitly as unlimited.\n`CODEX_LIMITS_COMMAND` now runs without an implicit shell. Prefer a JSON argv array such as `[\"python3\",\"/opt/read-limits.py\"]`; simple argv-only strings like `python3 /opt/read-limits.py` still work for compatibility. If you need pipes, redirection, or inline env assignments, use a wrapper script or make the shell explicit in argv.\nWhen `CODEX_LIMITS_COMMAND` is used, set the optional JSON `source` field to the short label you want surfaced in Telegram; otherwise the bot falls back to the generic `command` label instead of echoing the raw shell command.\n\nThe bot should be an admin in the forum chat. Topic creation and cleanup flows work best when it can post, edit, delete, pin, and manage topics.\n\n`/menu` also accepts the Telegram-style `/\u003ccommand\u003e@YourBot` form, shows an in-menu `Status` screen, and recreates the pinned topic panel cleanly when you reopen it so old menu messages do not pile up. Telegram may still keep its own pin service notice, but the gateway no longer guesses and deletes neighboring message ids.\nIf a topic already has a live Spike run, plain follow-up text is steered into that same run as many times as needed. If live steer hits a short transient failure, the gateway retries briefly before falling back to the next prompt queue; use `/q` only when you explicitly mean \"run this next after the current one\". If upstream aborts a turn, the gateway now retries that same top-level run on the same Codex thread before it falls back to a fresh-thread rebuild; accepted live-steer images are replayed into the recovery attempt, ordinary upstream-interrupted turns still use a bounded two-retry budget, and a final answer that already landed before the abort is kept as `completed` instead of being thrown away. If local continuity metadata is stale or incomplete, Spike now repairs that continuity from real Codex history surfaces such as `thread/list`, `provider_session_id`, rollout metadata, and `session_key` before it gives up and drops to a brief rebuild.\n\nNative Windows:\n\n```powershell\ncd O:\\workspace\\codex-telegram-gateway\ncopy .env.example .env\nscripts\\windows\\install.cmd\nscripts\\windows\\install-codex.cmd\nscripts\\windows\\doctor.cmd\nscripts\\windows\\run.cmd\n```\n\nUse `WORKSPACE_ROOT` and `DEFAULT_SESSION_BINDING_PATH` with Windows paths such as `O:/workspace`.\n\nOn native Windows, when `ENV_FILE` is unset the repo first uses `%LOCALAPPDATA%\\codex-telegram-gateway\\runtime.env` if it already exists, then falls back to repo-local `.env`, and otherwise uses that default state path. Runtime state lives under `%LOCALAPPDATA%\\codex-telegram-gateway` by default. If you want the repo-local `.env` to win while an older state env already exists, run commands as `set ENV_FILE=.env \u0026\u0026 ...` (or PowerShell `$env:ENV_FILE='.env'`). The `scripts\\windows\\*.cmd` wrappers avoid the common PowerShell `npm.ps1` execution-policy trap, change into the repo root before launching Node, and keep installs on the reproducible `npm ci --ignore-scripts` path.\nThe repo now ships a real `.env.example`, so the `copy .env.example .env` bootstrap path is not a doc-only placeholder anymore. `CODEX_BIN_PATH` is intentionally left empty there so native Windows can fall through to `codex.cmd`; if you override it manually, prefer `codex.cmd` or an absolute `...\\codex.cmd` path.\n\nInstall the Codex CLI once before the first run:\n\n```powershell\nscripts\\windows\\install-codex.cmd\n```\n\n`make`, `systemd`, and `service-install` remain Linux-only.\n\n## Baseline Commands\n\nLinux/operator path:\n\n```bash\nmake doctor\nmake check-syntax\nmake lint\nmake typecheck\nmake test\nmake test-exec\nmake hygiene\nmake test-live\nmake run\nmake user-e2e\nmake user-spike-audit\nmake service-install\nmake service-status\nmake service-rollout\nmake service-restart\nmake service-restart-live\nmake service-hard-restart\nmake admin ARGS='status'\n```\n\nWindows-native path:\n\n```powershell\nscripts\\windows\\doctor.cmd\nscripts\\windows\\test.cmd\nscripts\\windows\\test-live.cmd\nscripts\\windows\\run.cmd\nscripts\\windows\\admin.cmd status\n```\n\nLive user-account bootstrap:\n\n```bash\nmake user-login\nmake user-status\nmake user-e2e\nmake user-spike-audit\n```\n\nWindows-native equivalent:\n\n```powershell\nscripts\\windows\\user-login.cmd\nscripts\\windows\\user-status.cmd\nscripts\\windows\\user-e2e.cmd\nscripts\\windows\\user-spike-audit.cmd\n```\n\n## Notes\n\n- `Spike` is the only live worker\n- default worker turns use `codex exec --json`; `CODEX_GATEWAY_BACKEND=app-server` is legacy fallback/debug only and requires `CODEX_ENABLE_LEGACY_APP_SERVER=1`\n- native Windows now supports direct `.env`-based startup without host-specific Linux paths or WSL-only assumptions; use `WORKSPACE_ROOT` when you want to pin the workspace explicitly\n- Windows wrappers intentionally use `npm ci --ignore-scripts`; this repo does not need package install scripts, and skipping them avoids flaky transitive `postinstall` failures on some Windows setups\n- `make user-login` and `scripts\\windows\\user-login.cmd` now use a small built-in Node terminal prompt layer; the old `input`/`inquirer`/`lodash` stack is no longer in the production dependency graph\n- `service-install` is intentionally Linux-only here because it targets `systemd --user`; it resolves `CODEX_BIN_PATH` without invoking a shell, pins `CODEX_CONFIG_PATH` into the user unit, preserves the installing shell `PATH` inside the user unit so repo-local helpers and user shims stay reachable, and Spike requires `systemd \u003e= 250` for `ExitType=cgroup`\n- on Linux, `make service-rollout` and `make service-restart` are the soft rollout path for Spike: the command waits until the replacement generation has actually taken leader traffic, while already active run topics keep finishing on the retiring generation; use `make service-hard-restart` only when you really want a blind restart\n- `make service-restart` and `make service-restart-live` use the same safe session-aware rollout flow\n- while `/compact` is rebuilding the brief, direct prompt starts for that topic are blocked instead of racing a second Spike run against the fresh-start handoff\n- `make test-live` and `make user-spike-audit` are the quickest deep validations for real Codex continuity and heavy user-account scenarios; native Windows now ships matching wrapper scripts for both\n- `make admin ARGS='status'` now also shows heartbeat freshness, pid liveness, the configured and resolved `CODEX_BIN_PATH`, `CODEX_CONFIG_PATH`, and parsed MCP server names, so operators can confirm the live Codex profile before assuming tool loss\n- `/status` now separates configured limits from the live effective rollout window when they differ, so operators can see both the intended config and the current in-flight session reality\n- fallback `codex app-server` launches are still available for debugging the legacy transport, but they are no longer the default runtime path\n- native resume/interrupt recovery now follows real Codex session history first, including `thread/list`, `provider_session_id`, rollout metadata, and `session_key`, instead of treating every local continuity gap like a forced fresh start\n- Windows process-tree shutdown now uses `taskkill /t` fallback instead of assuming POSIX-only negative-pid signaling, so interrupted Codex runs are less likely to leave orphaned child processes behind\n- local loopback IPC now retries blocked or reserved loopback ports on native Windows instead of failing the forwarding server on the first bind error\n- Windows runtime helpers now also normalize case-insensitive `PATH` / `PATHEXT`, reject unsafe `%` shell-routed `.cmd` arguments, sanitize reserved attachment names, and retry transient atomic-replace filesystem failures instead of surfacing brittle host-specific edge cases\n- if native Windows leaves the websocket alive but the rollout already wrote `task_complete`, Spike can still finish that run from the rollout signal instead of staying stuck in `running`\n- stalled disconnect recovery and early-start failures now reap orphaned live-run state instead of leaving a fake forever-running topic behind\n- upstream interrupted Codex turns now surface as interrupted instead of being misreported as ordinary failures\n- container-backed MCP tools such as `pitlane` and `large_file` often see the workspace through a `/workspace/...` mirror; host workspace paths need to be translated before calling those tools\n- Telegram replies are rendered through a Telegram-safe HTML normalizer; headings, standard and expandable quotes, code, links, and readable nested lists are preserved\n- final Spike replies now retry transient Telegram/network send failures beyond plain `retry after`, and if the final send still never comes back the gateway keeps the answer visible in the existing progress bubble instead of dropping it completely\n- temporary Telegram `retry_after` throttles during ordinary reply sends are now retried inline, so the same update is not replayed just because Telegram briefly rate-limited one response\n- local file refs stay human-readable in chat instead of leaking long host paths\n- non-git workspace bindings stay valid for normal runs, and `/diff` now answers inline that the binding is not a git repo instead of poisoning the poll cycle\n- `General` now has `/clear`, which is bot-triggered and bot-tracked: it preserves the active global menu and removes the tracked General clutter without needing a user-session backend\n- `General -\u003e Bot Settings` also carries a separate global model/reasoning pair for the temporary `/compact` summarizer, so brief rebuilds do not have to reuse the live worker profile; manual `/compact` first rebuilds `active-brief.md` with current workspace state plus still-active user rules and delivery instructions, and only then clears continuity for an intentional fresh start\n- the root `/global` menu in `General` now stacks `Bot Settings` / `Language` first, then `Guide` / `Help`, with `Wait` / `Suffix` and `Zoo` / `Clear` below\n- Zoo is menu-only in normal operation, keeps buttons in English, localizes the card text to the topic language, assigns each new pet a stable random identity from the unused creature and temperament pools when possible, shows a creature-role header above the card, keeps the pet card gently animating while that pet screen stays open, paginates the root list, shifts refresh text from a generic first-frame status into temperament-driven ASCII pose swaps, shows previous-vs-current trend arrows in the stat block, keeps lower detail text inside an expandable quote, and normalizes duplicate repo names to path-derived labels with `[priv]` or `[pub]` suffixes when private/public twins exist\n- if `zoo/topic.json` is lost, incomplete, or quarantined, the next live Zoo menu callback now rebuilds the stored chat/topic/menu binding instead of degrading into silent `zoo:` button no-ops\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkrablante%2Fcodex-telegram-gateway","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkrablante%2Fcodex-telegram-gateway","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkrablante%2Fcodex-telegram-gateway/lists"}