From 89ddf71e9ccb41b57336025e2b6644bd4c2e41c5 Mon Sep 17 00:00:00 2001 From: banteg <4562643+banteg@users.noreply.github.com> Date: Sun, 28 Dec 2025 20:53:13 +0400 Subject: [PATCH] refactor: remove env var fallbacks --- codex/codex_telegram_bridge/bridge_common.py | 18 ++++++--- codex/codex_telegram_bridge/exec_bridge.py | 39 ++++++++----------- codex/codex_telegram_bridge/mcp_bridge.py | 30 ++++---------- codex/codex_telegram_bridge/readme.md | 35 +++++++---------- codex/codex_telegram_bridge/tmux_notify.py | 5 +-- codex/codex_telegram_bridge/tmux_reply_bot.py | 12 ++---- 6 files changed, 56 insertions(+), 83 deletions(-) diff --git a/codex/codex_telegram_bridge/bridge_common.py b/codex/codex_telegram_bridge/bridge_common.py index edbd13c..bde94f2 100644 --- a/codex/codex_telegram_bridge/bridge_common.py +++ b/codex/codex_telegram_bridge/bridge_common.py @@ -87,9 +87,6 @@ def chunk_text(text: str, limit: int = DEFAULT_CHUNK_LEN) -> List[str]: class TelegramClient: """ Minimal Telegram Bot API client using standard library (no requests dependency). - - Env: - TELEGRAM_BOT_TOKEN """ def __init__(self, token: str, timeout_s: int = 120) -> None: @@ -255,11 +252,11 @@ class RouteStore: self._conn.close() -def parse_allowed_chat_ids(env_value: str) -> Optional[set[int]]: +def parse_allowed_chat_ids(value: str) -> Optional[set[int]]: """ - Parse ALLOWED_CHAT_IDS="123,456" + Parse a comma-separated chat id string like "123,456". """ - v = (env_value or "").strip() + v = (value or "").strip() if not v: return None out: set[int] = set() @@ -291,3 +288,12 @@ def parse_chat_id_list(value: Any) -> Optional[set[int]]: out.add(int(item)) return out or None return None + + +def resolve_chat_ids(config: Dict[str, Any]) -> Optional[set[int]]: + chat_ids = parse_chat_id_list(config_get(config, "chat_id")) + if chat_ids is None: + chat_ids = parse_chat_id_list(config_get(config, "allowed_chat_ids")) + if chat_ids is None: + chat_ids = parse_chat_id_list(config_get(config, "startup_chat_ids")) + return chat_ids diff --git a/codex/codex_telegram_bridge/exec_bridge.py b/codex/codex_telegram_bridge/exec_bridge.py index 0138305..4314f48 100644 --- a/codex/codex_telegram_bridge/exec_bridge.py +++ b/codex/codex_telegram_bridge/exec_bridge.py @@ -14,8 +14,7 @@ from bridge_common import ( RouteStore, config_get, load_telegram_config, - parse_allowed_chat_ids, - parse_chat_id_list, + resolve_chat_ids, ) # -------------------- Codex runner -------------------- @@ -146,27 +145,18 @@ class CodexExecRunner: def main() -> None: config = load_telegram_config() - token = os.environ.get("TELEGRAM_BOT_TOKEN") or config_get(config, "bot_token") or "" - db_path = os.environ.get("BRIDGE_DB") or config_get(config, "bridge_db") or "./bridge_routes.sqlite3" - allowed = parse_allowed_chat_ids(os.environ.get("ALLOWED_CHAT_IDS", "")) - if allowed is None: - allowed = parse_chat_id_list(config_get(config, "allowed_chat_ids")) - startup_ids = parse_allowed_chat_ids(os.environ.get("STARTUP_CHAT_IDS", "")) - if startup_ids is None: - startup_ids = parse_chat_id_list(config_get(config, "startup_chat_ids")) - if startup_ids is None: - startup_ids = allowed - startup_msg = os.environ.get("STARTUP_MESSAGE") or config_get( - config, "startup_message" - ) or "✅ exec_bridge started (codex exec)." + token = config_get(config, "bot_token") or "" + db_path = config_get(config, "bridge_db") or "./bridge_routes.sqlite3" + chat_ids = resolve_chat_ids(config) + allowed = chat_ids + startup_ids = chat_ids + startup_msg = config_get(config, "startup_message") or "✅ exec_bridge started (codex exec)." startup_pwd = os.getcwd() startup_msg = f"{startup_msg}\nPWD: {startup_pwd}" - codex_cmd = os.environ.get("CODEX_CMD") or config_get(config, "codex_cmd") or "codex" - workspace = os.environ.get("CODEX_WORKSPACE") or config_get(config, "codex_workspace") - raw_exec_args = os.environ.get("CODEX_EXEC_ARGS") - if raw_exec_args is None: - raw_exec_args = config_get(config, "codex_exec_args") or "" + codex_cmd = config_get(config, "codex_cmd") or "codex" + workspace = config_get(config, "codex_workspace") + raw_exec_args = config_get(config, "codex_exec_args") or "" if isinstance(raw_exec_args, list): extra_args = [str(v) for v in raw_exec_args] else: @@ -194,7 +184,12 @@ def main() -> None: store = RouteStore(db_path) runner = CodexExecRunner(codex_cmd=codex_cmd, workspace=workspace, extra_args=extra_args) - pool = ThreadPoolExecutor(max_workers=int(os.environ.get("MAX_WORKERS", "4"))) + max_workers = config_get(config, "max_workers") + if isinstance(max_workers, str): + max_workers = int(max_workers) if max_workers.strip() else None + elif not isinstance(max_workers, int): + max_workers = None + pool = ThreadPoolExecutor(max_workers=max_workers or 4) offset: Optional[int] = None log(f"[startup] pwd={startup_pwd}") @@ -207,7 +202,7 @@ def main() -> None: except Exception as e: log(f"[startup] failed to send startup message to chat_id={chat_id}: {e}") else: - log("[startup] no STARTUP_CHAT_IDS or ALLOWED_CHAT_IDS set; skipping startup message") + log("[startup] no chat_id configured; skipping startup message") def handle(chat_id: int, user_msg_id: int, text: str, resume_session: Optional[str]) -> None: log( diff --git a/codex/codex_telegram_bridge/mcp_bridge.py b/codex/codex_telegram_bridge/mcp_bridge.py index 5e6a3be..f730a65 100644 --- a/codex/codex_telegram_bridge/mcp_bridge.py +++ b/codex/codex_telegram_bridge/mcp_bridge.py @@ -1,7 +1,6 @@ from __future__ import annotations import json -import os import shlex import subprocess import threading @@ -14,8 +13,7 @@ from bridge_common import ( RouteStore, config_get, load_telegram_config, - parse_allowed_chat_ids, - parse_chat_id_list, + resolve_chat_ids, ) MCP_PROTOCOL_VERSION = "2025-06-18" @@ -252,34 +250,22 @@ class MCPStdioClient: def main() -> None: config = load_telegram_config() - token = os.environ.get("TELEGRAM_BOT_TOKEN") or config_get(config, "bot_token") or "" - db_path = os.environ.get("BRIDGE_DB") or config_get(config, "bridge_db") or "./bridge_routes.sqlite3" - allowed = parse_allowed_chat_ids(os.environ.get("ALLOWED_CHAT_IDS", "")) - if allowed is None: - allowed = parse_chat_id_list(config_get(config, "allowed_chat_ids")) + token = config_get(config, "bot_token") or "" + db_path = config_get(config, "bridge_db") or "./bridge_routes.sqlite3" + allowed = resolve_chat_ids(config) # How to start Codex MCP server: # default: "codex mcp-server" (can also be "npx -y codex mcp-server") - raw_mcp_cmd = os.environ.get("CODEX_MCP_CMD") - if raw_mcp_cmd is None: - raw_mcp_cmd = config_get(config, "codex_mcp_cmd") or "codex mcp-server" + raw_mcp_cmd = config_get(config, "codex_mcp_cmd") or "codex mcp-server" if isinstance(raw_mcp_cmd, list): mcp_cmd = [str(v) for v in raw_mcp_cmd] else: mcp_cmd = shlex.split(str(raw_mcp_cmd)) # Optional defaults for tool args (you can override as you like) - default_cwd = os.environ.get("CODEX_WORKSPACE") or config_get(config, "codex_workspace") - default_sandbox = ( - os.environ.get("CODEX_SANDBOX") - or config_get(config, "codex_sandbox") - or "workspace-write" - ) - default_approval = ( - os.environ.get("CODEX_APPROVAL_POLICY") - or config_get(config, "codex_approval_policy") - or "never" - ) + default_cwd = config_get(config, "codex_workspace") + default_sandbox = config_get(config, "codex_sandbox") or "workspace-write" + default_approval = config_get(config, "codex_approval_policy") or "never" bot = TelegramClient(token) store = RouteStore(db_path) diff --git a/codex/codex_telegram_bridge/readme.md b/codex/codex_telegram_bridge/readme.md index d958456..998fa3d 100644 --- a/codex/codex_telegram_bridge/readme.md +++ b/codex/codex_telegram_bridge/readme.md @@ -12,31 +12,29 @@ All options store a mapping from `(chat_id, bot_message_id)` to a route so repli 1. Ensure `uv` is installed. 2. Use the scripts in this folder as-is (no extra dependencies). -3. Set `TELEGRAM_BOT_TOKEN` and (optionally) `ALLOWED_CHAT_IDS`, or put them in `~/.codex/telegram.toml`. +3. Put your Telegram credentials in `~/.codex/telegram.toml`. Example `~/.codex/telegram.toml`: ```toml bot_token = "123:abc" -allowed_chat_ids = [123456789] -startup_chat_ids = [123456789] -startup_message = "✅ exec_bridge started (codex exec)." +chat_id = 123456789 ``` -Environment variables always override the TOML file. +For Python < 3.11, install `tomli` to read TOML. `chat_id` is used both for allowed messages +and startup notifications. + +Optional keys (by mode): + +- common: `bridge_db`, `allowed_chat_ids`, `startup_chat_ids` +- exec/resume: `startup_message`, `codex_cmd`, `codex_workspace`, `codex_exec_args`, `max_workers` +- MCP server: `codex_mcp_cmd`, `codex_workspace`, `codex_sandbox`, `codex_approval_policy` ## Option 1: exec/resume Run: ```bash -export TELEGRAM_BOT_TOKEN="123:abc" -export BRIDGE_DB="./bridge_routes.sqlite3" -export CODEX_CMD="codex" -export CODEX_WORKSPACE="/path/to/repo" -export CODEX_EXEC_ARGS="--full-auto" -export STARTUP_CHAT_IDS="123456789" # optional; defaults to ALLOWED_CHAT_IDS if set -export STARTUP_MESSAGE="✅ exec_bridge started (codex exec)." # optional; PWD is appended uv run exec_bridge.py ``` @@ -45,12 +43,6 @@ uv run exec_bridge.py Run: ```bash -export TELEGRAM_BOT_TOKEN="123:abc" -export BRIDGE_DB="./bridge_routes.sqlite3" -export CODEX_MCP_CMD="codex mcp-server" -export CODEX_WORKSPACE="/path/to/repo" -export CODEX_SANDBOX="workspace-write" -export CODEX_APPROVAL_POLICY="never" uv run mcp_bridge.py ``` @@ -59,18 +51,17 @@ uv run mcp_bridge.py Reply injector: ```bash -export TELEGRAM_BOT_TOKEN="123:abc" -export BRIDGE_DB="./bridge_routes.sqlite3" -export ALLOWED_CHAT_IDS="123456789" uv run tmux_reply_bot.py ``` Notifier (call from your existing hook): ```bash -uv run tmux_notify.py --chat-id "$CHAT_ID" --tmux-target "codex1:0.0" --text "$TURN_TEXT" +uv run tmux_notify.py --tmux-target "codex1:0.0" --text "$TURN_TEXT" ``` +Add `--chat-id` if `chat_id` is not set in `~/.codex/telegram.toml`. + ## Files - `bridge_common.py`: shared Telegram client, chunking, and routing store diff --git a/codex/codex_telegram_bridge/tmux_notify.py b/codex/codex_telegram_bridge/tmux_notify.py index 38bb6be..e0b5a62 100644 --- a/codex/codex_telegram_bridge/tmux_notify.py +++ b/codex/codex_telegram_bridge/tmux_notify.py @@ -1,7 +1,6 @@ from __future__ import annotations import argparse -import os import sys from typing import Optional @@ -22,13 +21,13 @@ def main() -> None: ap.add_argument( "--db", type=str, - default=os.environ.get("BRIDGE_DB") or config_get(config, "bridge_db") or "./bridge_routes.sqlite3", + default=config_get(config, "bridge_db") or "./bridge_routes.sqlite3", ) ap.add_argument("--reply-to", type=int, default=None, help="Optional Telegram message_id to reply to") ap.add_argument("--text", type=str, default=None, help="Message text. If omitted, read stdin.") args = ap.parse_args() - token = os.environ.get("TELEGRAM_BOT_TOKEN") or config_get(config, "bot_token") or "" + token = config_get(config, "bot_token") or "" bot = TelegramClient(token) store = RouteStore(args.db) diff --git a/codex/codex_telegram_bridge/tmux_reply_bot.py b/codex/codex_telegram_bridge/tmux_reply_bot.py index 3469447..5dfabfe 100644 --- a/codex/codex_telegram_bridge/tmux_reply_bot.py +++ b/codex/codex_telegram_bridge/tmux_reply_bot.py @@ -1,6 +1,5 @@ from __future__ import annotations -import os import subprocess import time from typing import Optional @@ -10,8 +9,7 @@ from bridge_common import ( RouteStore, config_get, load_telegram_config, - parse_allowed_chat_ids, - parse_chat_id_list, + resolve_chat_ids, ) @@ -29,11 +27,9 @@ def tmux_send_text(target: str, text: str, press_enter: bool = True) -> None: def main() -> None: config = load_telegram_config() - token = os.environ.get("TELEGRAM_BOT_TOKEN") or config_get(config, "bot_token") or "" - db_path = os.environ.get("BRIDGE_DB") or config_get(config, "bridge_db") or "./bridge_routes.sqlite3" - allowed = parse_allowed_chat_ids(os.environ.get("ALLOWED_CHAT_IDS", "")) - if allowed is None: - allowed = parse_chat_id_list(config_get(config, "allowed_chat_ids")) + token = config_get(config, "bot_token") or "" + db_path = config_get(config, "bridge_db") or "./bridge_routes.sqlite3" + allowed = resolve_chat_ids(config) bot = TelegramClient(token) store = RouteStore(db_path)