refactor: remove env var fallbacks

This commit is contained in:
banteg
2025-12-28 20:53:13 +04:00
parent 6338c9f635
commit 89ddf71e9c
6 changed files with 56 additions and 83 deletions
+12 -6
View File
@@ -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
+17 -22
View File
@@ -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(
+8 -22
View File
@@ -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)
+13 -22
View File
@@ -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
+2 -3
View File
@@ -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)
@@ -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)