codex-telegram-bridge 0.5.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- codex_telegram_bridge-0.5.3/LICENSE +21 -0
- codex_telegram_bridge-0.5.3/PKG-INFO +620 -0
- codex_telegram_bridge-0.5.3/README.md +593 -0
- codex_telegram_bridge-0.5.3/agent_runtime/__init__.py +17 -0
- codex_telegram_bridge-0.5.3/agent_runtime/adapters/__init__.py +6 -0
- codex_telegram_bridge-0.5.3/agent_runtime/adapters/base.py +32 -0
- codex_telegram_bridge-0.5.3/agent_runtime/adapters/codex_repl.py +109 -0
- codex_telegram_bridge-0.5.3/agent_runtime/approvals.py +87 -0
- codex_telegram_bridge-0.5.3/agent_runtime/capabilities.py +59 -0
- codex_telegram_bridge-0.5.3/agent_runtime/locks.py +111 -0
- codex_telegram_bridge-0.5.3/agent_runtime/transport.py +77 -0
- codex_telegram_bridge-0.5.3/agent_runtime/types.py +33 -0
- codex_telegram_bridge-0.5.3/bridge_setup.py +1134 -0
- codex_telegram_bridge-0.5.3/bridge_watchdog.py +192 -0
- codex_telegram_bridge-0.5.3/codex_audio_transcribe.py +66 -0
- codex_telegram_bridge-0.5.3/codex_repl_bridge.py +5414 -0
- codex_telegram_bridge-0.5.3/codex_telegram_bridge.egg-info/PKG-INFO +620 -0
- codex_telegram_bridge-0.5.3/codex_telegram_bridge.egg-info/SOURCES.txt +29 -0
- codex_telegram_bridge-0.5.3/codex_telegram_bridge.egg-info/dependency_links.txt +1 -0
- codex_telegram_bridge-0.5.3/codex_telegram_bridge.egg-info/entry_points.txt +4 -0
- codex_telegram_bridge-0.5.3/codex_telegram_bridge.egg-info/requires.txt +4 -0
- codex_telegram_bridge-0.5.3/codex_telegram_bridge.egg-info/top_level.txt +6 -0
- codex_telegram_bridge-0.5.3/pyproject.toml +53 -0
- codex_telegram_bridge-0.5.3/setup.cfg +4 -0
- codex_telegram_bridge-0.5.3/telegram_agent_bridge.py +589 -0
- codex_telegram_bridge-0.5.3/tests/test_agent_runtime.py +174 -0
- codex_telegram_bridge-0.5.3/tests/test_bridge.py +278 -0
- codex_telegram_bridge-0.5.3/tests/test_public_export.py +51 -0
- codex_telegram_bridge-0.5.3/tests/test_repl_bridge.py +1907 -0
- codex_telegram_bridge-0.5.3/tests/test_setup_wizard.py +239 -0
- codex_telegram_bridge-0.5.3/tests/test_watchdog.py +126 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Codex Telegram Bridge contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,620 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: codex-telegram-bridge
|
|
3
|
+
Version: 0.5.3
|
|
4
|
+
Summary: Control a live Codex CLI session from Telegram.
|
|
5
|
+
Author: Kang Daejong
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/ssamssae/codex-telegram-bridge
|
|
8
|
+
Project-URL: Repository, https://github.com/ssamssae/codex-telegram-bridge
|
|
9
|
+
Project-URL: Releases, https://github.com/ssamssae/codex-telegram-bridge/releases
|
|
10
|
+
Project-URL: Issues, https://github.com/ssamssae/codex-telegram-bridge/issues
|
|
11
|
+
Keywords: codex,telegram,cli,automation,agent
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Environment :: Console
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
18
|
+
Classifier: Topic :: Communications :: Chat
|
|
19
|
+
Classifier: Topic :: Software Development
|
|
20
|
+
Requires-Python: >=3.10
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
License-File: LICENSE
|
|
23
|
+
Provides-Extra: asr
|
|
24
|
+
Requires-Dist: faster-whisper; extra == "asr"
|
|
25
|
+
Requires-Dist: imageio-ffmpeg; extra == "asr"
|
|
26
|
+
Dynamic: license-file
|
|
27
|
+
|
|
28
|
+
# Codex Telegram Bridge
|
|
29
|
+
|
|
30
|
+
[](https://github.com/ssamssae/codex-telegram-bridge/releases)
|
|
31
|
+
[](LICENSE)
|
|
32
|
+
|
|
33
|
+
Control your live Codex CLI session from Telegram. Only Codex is supported.
|
|
34
|
+
Other AI CLIs are intentionally out of scope; a Claude bridge should be a
|
|
35
|
+
separate Claude-specific program, not a shared mode in this repository.
|
|
36
|
+
|
|
37
|
+
Codex Telegram Bridge is a phone remote for your already-running Codex TUI. Send
|
|
38
|
+
prompts, screenshots, videos, voice notes, and files from Telegram; the bridge
|
|
39
|
+
pastes them into your visible tmux Codex session, watches Codex's structured
|
|
40
|
+
JSONL session log, then mirrors final answers and generated media back to
|
|
41
|
+
Telegram.
|
|
42
|
+
|
|
43
|
+
Default `repl` mode is REPL sync, not a separate hidden chat. Your terminal
|
|
44
|
+
stays the source of truth, the transcript remains readable, and Telegram becomes
|
|
45
|
+
the remote control when you are away from the keyboard.
|
|
46
|
+
|
|
47
|
+
Install with `pipx`, then run the setup wizard:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
pipx install "git+https://github.com/ssamssae/codex-telegram-bridge.git@v0.5.1"
|
|
51
|
+
codex-telegram-bridge setup
|
|
52
|
+
codex-telegram-bridge doctor
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Or install from a clone:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
git clone https://github.com/ssamssae/codex-telegram-bridge.git
|
|
59
|
+
cd codex-telegram-bridge
|
|
60
|
+
python3 bridge_setup.py setup
|
|
61
|
+
python3 bridge_setup.py doctor
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Token safety rule: BotFather shows the bot token in Telegram, but paste it only
|
|
65
|
+
into the local terminal setup wizard. In Telegram, send only `/start`, `/ping`,
|
|
66
|
+
or normal prompts to your bot.
|
|
67
|
+
|
|
68
|
+
## Public Export Model
|
|
69
|
+
|
|
70
|
+
This public repository is maintained from a private operator source through a
|
|
71
|
+
sanitized export step. The export keeps the reusable bridge behavior, setup
|
|
72
|
+
wizard, and BYO signal contract, while stripping private chat ids, token paths,
|
|
73
|
+
hostnames, node labels, and local automation paths before release.
|
|
74
|
+
|
|
75
|
+
Do not copy another operator's private wrapper scripts into your setup. Treat
|
|
76
|
+
`CRB_SIGNAL_PATH` / `TAB_LOCAL_INPUT` as the public integration boundary: your
|
|
77
|
+
cron job, local queue, or orchestrator writes one prompt line to the FIFO, and
|
|
78
|
+
the bridge owns only local delivery into the visible Codex session plus Telegram
|
|
79
|
+
mirroring.
|
|
80
|
+
|
|
81
|
+
Release: <https://github.com/ssamssae/codex-telegram-bridge/releases/latest>
|
|
82
|
+
|
|
83
|
+
Promo video from the v0.3 demo release:
|
|
84
|
+
<https://github.com/ssamssae/codex-telegram-bridge/releases/download/v0.3.10/codex-telegram-bridge-promo-v0.3.10.mp4>
|
|
85
|
+
|
|
86
|
+
The repo also includes a simpler one-shot `codex exec` mode. Both modes are
|
|
87
|
+
Codex-only by design so maintenance stays focused and predictable.
|
|
88
|
+
|
|
89
|
+
This is not MCP. It is a small standalone relay daemon. Default `repl` mode:
|
|
90
|
+
|
|
91
|
+
```text
|
|
92
|
+
Telegram message/media
|
|
93
|
+
-> getUpdates polling
|
|
94
|
+
-> single-user chat_id allowlist
|
|
95
|
+
-> paste a prompt into tmux -L codex / Codex TUI
|
|
96
|
+
-> watch Codex JSONL session logs
|
|
97
|
+
-> mirror final answers to Telegram
|
|
98
|
+
|
|
99
|
+
Codex CLI input
|
|
100
|
+
-> JSONL user event
|
|
101
|
+
-> Telegram "typing..." while Codex is working
|
|
102
|
+
-> recover Telegram "typing..." if the bridge restarts while the Codex pane is still busy
|
|
103
|
+
-> send a one-shot fallback progress reply if final_answer is delayed
|
|
104
|
+
-> periodic detailed progress updates for long Telegram-origin turns
|
|
105
|
+
-> final answer mirrored to Telegram
|
|
106
|
+
|
|
107
|
+
Codex approval prompt
|
|
108
|
+
-> detect "Would you like to run..." in the tmux pane
|
|
109
|
+
-> send Telegram buttons for the visible approval choices
|
|
110
|
+
-> mark the selected button and remove stale choices
|
|
111
|
+
-> inject the selected key back into the Codex TUI
|
|
112
|
+
|
|
113
|
+
Codex selection prompt
|
|
114
|
+
-> detect numbered/lettered menus and y/n confirmations in the tmux pane
|
|
115
|
+
-> send Telegram buttons for the visible options
|
|
116
|
+
-> mark the selected button and remove stale choices
|
|
117
|
+
-> inject shortcut keys or arrow+Enter navigation back into the Codex TUI
|
|
118
|
+
|
|
119
|
+
Codex slash command
|
|
120
|
+
-> detect single-line commands such as /model from Telegram
|
|
121
|
+
-> submit them with Enter instead of the normal queued prompt key
|
|
122
|
+
-> mirror "Unrecognized command" errors back to Telegram
|
|
123
|
+
-> clear the Codex composer before Telegram input so stale typo commands cannot be appended
|
|
124
|
+
-> keep Telegram typing and progress updates active for long-running commands such as /goal
|
|
125
|
+
-> auto-request a missing second /goal + 상세스펙/상세설명 copy-paste payload once, and split combined payloads into two Telegram messages
|
|
126
|
+
-> scope copy-paste deduplication to the current Telegram prompt so an explicit resend can send the same /goal body again
|
|
127
|
+
-> classify Korean two-message copy-paste requests with bare "골", 두번/두 번, repeated 보내 verbs, 상세스팩 typo, and single-message override words
|
|
128
|
+
|
|
129
|
+
Answer media attachments
|
|
130
|
+
-> detect local image/video/audio paths in final answers
|
|
131
|
+
-> hide the local path in Telegram
|
|
132
|
+
-> send the actual media with sendPhoto/sendVideo/sendVoice/sendAudio
|
|
133
|
+
|
|
134
|
+
Service restart
|
|
135
|
+
-> run a local watchdog every 60 seconds when installed as a service
|
|
136
|
+
-> restart or kickstart the bridge if the user service is inactive
|
|
137
|
+
-> load a persistent JSONL cursor and final-answer dedup ring
|
|
138
|
+
-> resume from the cursor when it still matches the current session file
|
|
139
|
+
-> otherwise tail-scan recent JSONL after the latest user event
|
|
140
|
+
-> backfill the latest eligible final answer once, then resume live watching
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## Why REPL Sync
|
|
144
|
+
|
|
145
|
+
- You can keep using the local Codex TUI normally while Telegram mirrors the
|
|
146
|
+
final answers.
|
|
147
|
+
- Telegram-origin prompts are pasted into the same visible transcript instead of
|
|
148
|
+
disappearing into a separate hidden thread.
|
|
149
|
+
- Terminal-origin prompts can still show Telegram `typing...` and final-answer
|
|
150
|
+
mirrors, so the phone stays informed even when the work started locally.
|
|
151
|
+
- Long-running Telegram-origin turns send progress reports with the task label,
|
|
152
|
+
optional task id, elapsed time, latest public progress note, next step, and
|
|
153
|
+
blocker status.
|
|
154
|
+
- Telegram-origin turns also send one early fallback progress reply after 90
|
|
155
|
+
seconds by default, so a turn that is producing commentary/tool activity but
|
|
156
|
+
has not emitted `final_answer` yet does not look silent from Telegram.
|
|
157
|
+
- If the daemon restarts or joins mid-turn, it checks the visible Codex pane
|
|
158
|
+
every 10 seconds by default and restarts Telegram `typing...` while Codex is
|
|
159
|
+
still working. Set `CRB_TYPING_LIVENESS_SECONDS=0` to disable this recovery
|
|
160
|
+
loop.
|
|
161
|
+
- Approval and selection prompts remain real Codex TUI prompts; the bridge sends
|
|
162
|
+
Telegram buttons for the visible options and injects the selected key back
|
|
163
|
+
into tmux.
|
|
164
|
+
- The daemon uses polling and a single `chat_id` allowlist, so no public webhook
|
|
165
|
+
or inbound port is required.
|
|
166
|
+
|
|
167
|
+
## Quickstart
|
|
168
|
+
|
|
169
|
+
The setup wizard is designed for first-time users. It shows six steps:
|
|
170
|
+
|
|
171
|
+
```text
|
|
172
|
+
[1/6] Paste the BotFather token
|
|
173
|
+
[2/6] Connect your Telegram chat
|
|
174
|
+
[3/6] Check the local Codex mode
|
|
175
|
+
[4/6] Write the private config
|
|
176
|
+
[5/6] Install the background service
|
|
177
|
+
[6/6] Send a setup-complete test message
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
Important: paste the BotFather token only into the terminal when the setup
|
|
181
|
+
wizard asks for it. In Telegram, send only `/start`, `/ping`, or normal prompts.
|
|
182
|
+
|
|
183
|
+
1. Install and log in to Codex on the same machine. Start Codex in a named tmux
|
|
184
|
+
session for full REPL mode:
|
|
185
|
+
|
|
186
|
+
```bash
|
|
187
|
+
tmux -L codex new -s codex
|
|
188
|
+
codex
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
If you only want text-only one-shot mode without a visible Codex TUI, you can use
|
|
192
|
+
`codex-telegram-bridge setup --mode exec` instead.
|
|
193
|
+
|
|
194
|
+
2. Create a Telegram bot with [@BotFather](https://t.me/BotFather) and copy the bot token.
|
|
195
|
+
Do not send this token in any Telegram chat. Paste it only into the local
|
|
196
|
+
terminal setup wizard in the next step.
|
|
197
|
+
|
|
198
|
+
3. Run the setup wizard. If you used `pipx`, run:
|
|
199
|
+
|
|
200
|
+
```bash
|
|
201
|
+
codex-telegram-bridge setup
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
If you installed from a clone, run:
|
|
205
|
+
|
|
206
|
+
```bash
|
|
207
|
+
git clone https://github.com/ssamssae/codex-telegram-bridge.git
|
|
208
|
+
cd codex-telegram-bridge
|
|
209
|
+
python3 bridge_setup.py setup
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
The wizard will:
|
|
213
|
+
|
|
214
|
+
- validate the BotFather token with Telegram `getMe`
|
|
215
|
+
- ask you to send `/start` to the bot in Telegram
|
|
216
|
+
- detect your numeric `chat_id` automatically
|
|
217
|
+
- show what it is doing at each step
|
|
218
|
+
- write a private `~/.config/telegram-agent-bridge.env` with mode `0600`
|
|
219
|
+
- install `~/.local/bin/telegram-agent-bridge-run`
|
|
220
|
+
- install and start a user service with systemd on Linux/WSL or launchd on macOS
|
|
221
|
+
- install a watchdog timer/LaunchAgent that recovers an inactive bridge service
|
|
222
|
+
- send a setup-complete test message
|
|
223
|
+
|
|
224
|
+
Default setup mode is `repl`, which supports the visible Codex CLI transcript,
|
|
225
|
+
Telegram text, image prompts, video thumbnails/metadata, audio-file delivery,
|
|
226
|
+
generic file delivery, optional audio transcription, answer mirroring, Telegram
|
|
227
|
+
`typing...`, and Codex approval prompts. It also stores a JSONL cursor so a
|
|
228
|
+
daemon restart can resume watching the current Codex session and backfill the
|
|
229
|
+
latest eligible final answer that was produced while the bridge was down. If a
|
|
230
|
+
final answer contains a local image, video, or audio path or markdown link to an
|
|
231
|
+
allowed media file, the bridge hides that local path from the Telegram text and
|
|
232
|
+
sends the actual media attachment.
|
|
233
|
+
|
|
234
|
+
4. Check the installation:
|
|
235
|
+
|
|
236
|
+
```bash
|
|
237
|
+
codex-telegram-bridge doctor
|
|
238
|
+
# or, from a clone:
|
|
239
|
+
python3 bridge_setup.py doctor
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
5. Send `/ping` to the bot. Then send a normal prompt.
|
|
243
|
+
|
|
244
|
+
Token safety rule: BotFather shows the token in Telegram, but you should copy it
|
|
245
|
+
from BotFather and paste it into the local setup wizard in your terminal. In
|
|
246
|
+
Telegram, send only `/start` or normal prompts to your bot.
|
|
247
|
+
|
|
248
|
+
For local terminal input without scraping the Codex TUI, either type into the
|
|
249
|
+
foreground bridge process or write one prompt per line to the FIFO:
|
|
250
|
+
|
|
251
|
+
```bash
|
|
252
|
+
printf '%s\n' 'continue from the terminal' > ~/.local/state/telegram-agent-bridge/input.fifo
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
Prompts and final answers are mirrored to both Telegram and terminal output.
|
|
256
|
+
|
|
257
|
+
## BYO Signal Contract
|
|
258
|
+
|
|
259
|
+
Scripts that push work into a live agent usually contain local SSH aliases,
|
|
260
|
+
node names, chat ids, token paths, and tmux assumptions. This project only needs
|
|
261
|
+
a small local input contract: write one UTF-8 line to the configured signal
|
|
262
|
+
FIFO.
|
|
263
|
+
|
|
264
|
+
Enable a signal FIFO for `repl` mode:
|
|
265
|
+
|
|
266
|
+
```bash
|
|
267
|
+
export CRB_SIGNAL_PATH="$HOME/.local/state/telegram-agent-bridge/input.fifo"
|
|
268
|
+
codex-telegram-bridge
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
The bridge creates the FIFO if it does not already exist. Any local process that
|
|
272
|
+
can write to this path can inject a prompt into Codex, so keep it under your
|
|
273
|
+
private state directory and do not expose it through a network share.
|
|
274
|
+
|
|
275
|
+
Signal payloads can be plain text:
|
|
276
|
+
|
|
277
|
+
```bash
|
|
278
|
+
printf '%s\n' 'review the latest failing test' > "$CRB_SIGNAL_PATH"
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
Or one JSON object per line with `prompt`, `text`, or `message`:
|
|
282
|
+
|
|
283
|
+
```bash
|
|
284
|
+
printf '%s\n' '{"prompt":"run the smoke checks and summarize failures"}' > "$CRB_SIGNAL_PATH"
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
A minimal generic trigger wrapper is included:
|
|
288
|
+
|
|
289
|
+
```bash
|
|
290
|
+
examples/triggers/byo-signal-submit.sh "summarize the current git diff"
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
Use this as the boundary for your own cron job, webhook receiver, task queue, or
|
|
294
|
+
multi-node orchestrator. Keep site-specific dispatch logic outside this repo;
|
|
295
|
+
the public bridge only owns the local signal contract and Codex/Telegram flow.
|
|
296
|
+
|
|
297
|
+
## Setup Commands
|
|
298
|
+
|
|
299
|
+
Interactive install:
|
|
300
|
+
|
|
301
|
+
```bash
|
|
302
|
+
codex-telegram-bridge setup
|
|
303
|
+
# or:
|
|
304
|
+
python3 bridge_setup.py setup
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
Text-only one-shot install:
|
|
308
|
+
|
|
309
|
+
```bash
|
|
310
|
+
codex-telegram-bridge setup --mode exec
|
|
311
|
+
# or:
|
|
312
|
+
python3 bridge_setup.py setup --mode exec
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
Install optional local audio transcription dependencies for voice/audio files:
|
|
316
|
+
|
|
317
|
+
```bash
|
|
318
|
+
codex-telegram-bridge setup --install-asr
|
|
319
|
+
# or:
|
|
320
|
+
python3 bridge_setup.py setup --install-asr
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
Non-interactive install, useful for scripts:
|
|
324
|
+
|
|
325
|
+
```bash
|
|
326
|
+
codex-telegram-bridge setup \
|
|
327
|
+
--token '123456:BOT_TOKEN' \
|
|
328
|
+
--chat-id '123456789' \
|
|
329
|
+
--non-interactive \
|
|
330
|
+
-y
|
|
331
|
+
|
|
332
|
+
# or, from a clone:
|
|
333
|
+
python3 bridge_setup.py setup \
|
|
334
|
+
--token '123456:BOT_TOKEN' \
|
|
335
|
+
--chat-id '123456789' \
|
|
336
|
+
--non-interactive \
|
|
337
|
+
-y
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
Health check:
|
|
341
|
+
|
|
342
|
+
```bash
|
|
343
|
+
codex-telegram-bridge doctor
|
|
344
|
+
# or:
|
|
345
|
+
python3 bridge_setup.py doctor
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
Uninstall the service and runner while keeping your private config:
|
|
349
|
+
|
|
350
|
+
```bash
|
|
351
|
+
codex-telegram-bridge uninstall
|
|
352
|
+
# or:
|
|
353
|
+
python3 bridge_setup.py uninstall
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
Remove the private config too:
|
|
357
|
+
|
|
358
|
+
```bash
|
|
359
|
+
codex-telegram-bridge uninstall --purge
|
|
360
|
+
# or:
|
|
361
|
+
python3 bridge_setup.py uninstall --purge
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
## Manual Setup
|
|
365
|
+
|
|
366
|
+
If you do not want the setup wizard to install a service, create a private env
|
|
367
|
+
file manually:
|
|
368
|
+
|
|
369
|
+
```bash
|
|
370
|
+
cp config.example.env ~/.config/telegram-agent-bridge.env
|
|
371
|
+
chmod 600 ~/.config/telegram-agent-bridge.env
|
|
372
|
+
$EDITOR ~/.config/telegram-agent-bridge.env
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
Run the daemon in the foreground:
|
|
376
|
+
|
|
377
|
+
```bash
|
|
378
|
+
set -a
|
|
379
|
+
. ~/.config/telegram-agent-bridge.env
|
|
380
|
+
set +a
|
|
381
|
+
python3 telegram_agent_bridge.py
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
## Configuration
|
|
385
|
+
|
|
386
|
+
Required settings are intentionally small and explicit.
|
|
387
|
+
|
|
388
|
+
| Variable | Required | Default | Description |
|
|
389
|
+
| --- | --- | --- | --- |
|
|
390
|
+
| `TAB_BRIDGE_MODE` | no | `repl` | `repl` for visible Codex CLI sync, or `exec` for text-only one-shot mode. |
|
|
391
|
+
| `TAB_BOT_TOKEN` | yes | none | Telegram bot token from BotFather. Keep it secret. |
|
|
392
|
+
| `TAB_CHAT_ID` | yes | none | The only Telegram chat id allowed to control Codex. Other chats are ignored. |
|
|
393
|
+
| `TAB_AGENT` | no | `codex` | Compatibility setting. Only `codex` is supported; other values are rejected. |
|
|
394
|
+
| `TAB_AGENT_CMD` | no | `codex` | Codex command or wrapper command. Split like shell arguments. |
|
|
395
|
+
| `TAB_STATE_DIR` | no | `~/.local/state/telegram-agent-bridge` | Offset and thread-id state directory. |
|
|
396
|
+
| `TAB_PREFIX` | no | empty | Prefix shown on the first Telegram reply chunk, for example an emoji or node label. |
|
|
397
|
+
| `TAB_PREFIX_LINE` | no | `0` | When `1`, puts `TAB_PREFIX` on its own first line. |
|
|
398
|
+
| `TAB_WORKDIR` | no | `~` | Working directory for the Codex process. Codex also receives `-C TAB_WORKDIR`. |
|
|
399
|
+
| `TAB_WORKDIR_LOCK` | no | `1` | Acquire a local workdir lock around one-shot Codex turns. |
|
|
400
|
+
| `TAB_TIMEOUT` | no | `600` | Per-turn timeout in seconds. |
|
|
401
|
+
| `TAB_TG_CHUNK` | no | `4096` | Telegram message chunk size. |
|
|
402
|
+
| `TAB_TYPING_INTERVAL` | no | `4` | Seconds between repeated Telegram `typing` actions while Codex is running. |
|
|
403
|
+
| `CRB_SIGNAL_PATH` | no | `TAB_LOCAL_INPUT` when set | FIFO path for external/local signal prompts in `repl` mode. Set to `0`/`off` to disable. |
|
|
404
|
+
| `TAB_LOCAL_INPUT` | no | `~/.local/state/telegram-agent-bridge/input.fifo` on POSIX | Compatibility FIFO path for local terminal prompts and `CRB_SIGNAL_PATH` fallback. Set to `0`/`off` to disable. |
|
|
405
|
+
| `TAB_STDIN_INPUT` | no | auto | Read local prompts from stdin. Defaults to on only when stdin is a TTY. |
|
|
406
|
+
| `TAB_CODEX_DANGEROUS_BYPASS` | no | `0` | When `1`, adds `--dangerously-bypass-approvals-and-sandbox` to Codex. |
|
|
407
|
+
| `TAB_CODEX_EXTRA_ARGS` | no | empty | Extra arguments inserted after `codex exec --json -o <tmp>`. |
|
|
408
|
+
| `CRB_TMUX_SOCKET` | repl only | `codex` | tmux socket for the visible Codex TUI. |
|
|
409
|
+
| `CRB_TMUX_SESSION` | repl only | `codex` | tmux session or target for the visible Codex TUI. |
|
|
410
|
+
| `CRB_TMUX_SUBMIT_KEY` | repl only | `Tab` | key sent after pasting Telegram prompts into Codex. |
|
|
411
|
+
| `CRB_TYPING_MAX_SECONDS` | no | `7200` | Maximum lifetime for repeated Telegram `typing` actions during one visible Codex turn. |
|
|
412
|
+
| `CRB_TELEGRAM_FALLBACK_SECONDS` | no | `90` | One-shot fallback progress reply delay for Telegram-origin REPL prompts when `final_answer` is delayed. Set `0` to disable. |
|
|
413
|
+
| `CRB_FLOW_MIRROR` | no | `1` | Mirror public Codex progress/commentary steps to Telegram with the `⚙️ 작업 흐름` header. |
|
|
414
|
+
| `CRB_REASONING_MIRROR` | no | `1` | Mirror Codex's public reasoning summary to Telegram with the `🧠 코덱스 사고` header, sent right after the final answer. Only the runtime-public summary is sent (never raw chain-of-thought); copy-payload replies do not emit a reasoning mirror. Set `0` to disable. |
|
|
415
|
+
| `CRB_LONG_RUNNING_PROGRESS_SECONDS` | no | `0` | Legacy periodic progress interval for long-running Telegram-origin REPL prompts. The flow mirror replaces it by default; set a positive second value to re-enable. |
|
|
416
|
+
| `CRB_AUDIO_TRANSCRIBE_CMD` | no | empty | Optional command template for audio transcription. Use `{path}` for the media file. |
|
|
417
|
+
| `CRB_APPROVAL_TTL_SECONDS` | no | `300` | Seconds before a Telegram approval button is treated as stale. |
|
|
418
|
+
| `CRB_STATE_PATH` | no | `TAB_STATE_DIR/codex-repl-bridge-<node>.state.json` | Persistent JSONL cursor and final-answer dedup state for `repl` mode. |
|
|
419
|
+
| `CRB_BACKFILL` | no | `1` | When enabled, startup without a valid cursor tail-scans the current session for a fresh missed final answer. |
|
|
420
|
+
| `CRB_BACKFILL_MAX` | no | `1` | Maximum missed final answers to backfill on startup. Clamped to `1`-`3`. |
|
|
421
|
+
| `CRB_BACKFILL_WINDOW_SEC` | no | `600` | Maximum age for startup backfill candidates. |
|
|
422
|
+
| `CRB_TAIL_SCAN_BYTES` | no | `65536` | Bytes to scan from the end of the Codex JSONL session when cursorless backfill is needed. |
|
|
423
|
+
| `CRB_STATE_RING_CAP` | no | `64` | Number of mirrored final-answer keys retained for deduplication. |
|
|
424
|
+
| `CRB_KILL` | no | `0` | Emergency switch that blocks Telegram answer sends while keeping the process alive. |
|
|
425
|
+
| `CRB_ATTACHMENT_ROOTS` | no | state dir, workdir, `/tmp` | `:`-separated roots where answer-referenced local media files may be uploaded from. |
|
|
426
|
+
| `CRB_MAX_ATTACHMENT_BYTES` | no | `52428800` | Maximum size for local answer attachments. |
|
|
427
|
+
|
|
428
|
+
## REPL Mode Media Support
|
|
429
|
+
|
|
430
|
+
`TAB_BRIDGE_MODE=repl` supports:
|
|
431
|
+
|
|
432
|
+
- text messages
|
|
433
|
+
- Telegram photos and image documents
|
|
434
|
+
- Telegram videos, video notes, animations, and video documents
|
|
435
|
+
- Telegram voice/audio files
|
|
436
|
+
- generic Telegram document files
|
|
437
|
+
- Telegram `typing...` while Codex is generating
|
|
438
|
+
- terminal-origin Codex prompts mirrored back to Telegram
|
|
439
|
+
- Codex command approval prompts mirrored to Telegram with buttons
|
|
440
|
+
- Codex numbered/lettered selection prompts and y/n confirmations mirrored to
|
|
441
|
+
Telegram with buttons
|
|
442
|
+
- local image/video/audio paths in final answers hidden from Telegram text and
|
|
443
|
+
sent as Telegram attachments
|
|
444
|
+
|
|
445
|
+
Images are saved under `TAB_STATE_DIR` and passed to Codex as local paths in the
|
|
446
|
+
prompt. Video messages include the local video path, Telegram thumbnail when
|
|
447
|
+
available, and metadata. If `ffmpeg` is available, the bridge can extract video
|
|
448
|
+
frames. Audio messages include the local audio path and, when
|
|
449
|
+
`CRB_AUDIO_TRANSCRIBE_CMD` is configured, a transcript. Generic document files
|
|
450
|
+
are saved under the same local media directory and passed to Codex with
|
|
451
|
+
`local_path`, caption, MIME type, file name, and file size metadata.
|
|
452
|
+
|
|
453
|
+
The setup wizard can install a local `faster-whisper` transcription environment:
|
|
454
|
+
|
|
455
|
+
```bash
|
|
456
|
+
python3 bridge_setup.py setup --install-asr
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
This is optional because it downloads Python packages and Whisper model files.
|
|
460
|
+
|
|
461
|
+
## REPL Mode Restart Backfill
|
|
462
|
+
|
|
463
|
+
In `repl` mode the bridge stores a JSONL cursor and a small final-answer dedup
|
|
464
|
+
ring in `CRB_STATE_PATH`. On restart, if that cursor still matches the current
|
|
465
|
+
Codex session file, the bridge resumes reading from the saved offset.
|
|
466
|
+
|
|
467
|
+
If there is no valid cursor and `CRB_START_AT_END=1`, the bridge scans the tail
|
|
468
|
+
of the current Codex JSONL session, finds the latest user event, and backfills
|
|
469
|
+
up to `CRB_BACKFILL_MAX` fresh final answers after that user event. Candidates
|
|
470
|
+
must be inside `CRB_BACKFILL_WINDOW_SEC` and absent from the dedup ring. This
|
|
471
|
+
reduces the common failure mode where Codex finished while the Telegram bridge
|
|
472
|
+
service was restarting.
|
|
473
|
+
|
|
474
|
+
## Service Watchdog
|
|
475
|
+
|
|
476
|
+
When the setup wizard installs a background service, it also installs a small
|
|
477
|
+
watchdog.
|
|
478
|
+
|
|
479
|
+
On Linux and WSL, the wizard writes:
|
|
480
|
+
|
|
481
|
+
- `~/.config/systemd/user/telegram-agent-bridge-watchdog.service`
|
|
482
|
+
- `~/.config/systemd/user/telegram-agent-bridge-watchdog.timer`
|
|
483
|
+
|
|
484
|
+
The timer runs every 60 seconds. If `telegram-agent-bridge.service` is inactive,
|
|
485
|
+
the watchdog starts it and writes a status file at
|
|
486
|
+
`~/.local/state/telegram-agent-bridge/watchdog.status`.
|
|
487
|
+
|
|
488
|
+
On macOS, the wizard writes:
|
|
489
|
+
|
|
490
|
+
- `~/Library/LaunchAgents/com.user.telegram-agent-bridge-watchdog.plist`
|
|
491
|
+
|
|
492
|
+
The LaunchAgent runs every 60 seconds. If
|
|
493
|
+
`com.user.telegram-agent-bridge` is not running, the watchdog kickstarts it and
|
|
494
|
+
writes the same status file.
|
|
495
|
+
|
|
496
|
+
This covers the failure mode where the bridge was explicitly stopped or left
|
|
497
|
+
inactive. The normal `Restart=always` and launchd `KeepAlive` settings still
|
|
498
|
+
handle ordinary crashes.
|
|
499
|
+
|
|
500
|
+
## REPL Mode Approval Prompts
|
|
501
|
+
|
|
502
|
+
When Codex is not running in a bypass/YOLO approval mode, it may pause inside the
|
|
503
|
+
terminal with a prompt such as:
|
|
504
|
+
|
|
505
|
+
```text
|
|
506
|
+
Would you like to run the following command?
|
|
507
|
+
1. Yes, proceed (y)
|
|
508
|
+
2. Yes, and don't ask again... (p)
|
|
509
|
+
3. No, and tell Codex what to do differently (esc)
|
|
510
|
+
```
|
|
511
|
+
|
|
512
|
+
In `repl` mode the bridge watches the tmux pane for that prompt and sends a
|
|
513
|
+
Telegram message with buttons:
|
|
514
|
+
|
|
515
|
+
- `1. Yes`
|
|
516
|
+
- `2. Yes, don't ask again`
|
|
517
|
+
- `3. No`
|
|
518
|
+
|
|
519
|
+
You can also reply with the visible number or shortcut, for example `1`, `2`,
|
|
520
|
+
`y`, `p`, `esc`, or `/approve 1`. The bridge injects the matching key back into
|
|
521
|
+
the Codex TUI, edits the original Telegram approval message to show the selected
|
|
522
|
+
button, and removes stale choices.
|
|
523
|
+
|
|
524
|
+
## REPL Mode Selection Prompts
|
|
525
|
+
|
|
526
|
+
The bridge also watches for non-approval Codex TUI choices, for example model
|
|
527
|
+
pickers, mode pickers, numbered/lettered menus, and inline confirmations such as
|
|
528
|
+
`[y/N]` or `(yes/no)`. When a selection prompt is visible, Telegram receives
|
|
529
|
+
buttons for the visible options.
|
|
530
|
+
|
|
531
|
+
If a visible option has a shortcut such as `(y)`, `(n)`, `(esc)`, or `(enter)`,
|
|
532
|
+
the bridge sends that shortcut back to Codex. If there is no explicit shortcut,
|
|
533
|
+
it uses the currently highlighted `›` row and sends Up/Down plus Enter. Text
|
|
534
|
+
replies such as `1`, `2`, `a`, `y`, `yes`, `n`, `no`, and `/choose 2` are also
|
|
535
|
+
accepted while the prompt is active.
|
|
536
|
+
|
|
537
|
+
## Answer Media Attachments
|
|
538
|
+
|
|
539
|
+
If Codex answers with a local image, video, or audio path, the bridge removes
|
|
540
|
+
that local path from the Telegram text and uploads the actual media. This works
|
|
541
|
+
for raw paths and markdown links such as:
|
|
542
|
+
|
|
543
|
+
```text
|
|
544
|
+
Here is the screenshot: [screenshot.png](/path/to/project/screenshot.png)
|
|
545
|
+
Here is the clip: /path/to/project/demo.mp4
|
|
546
|
+
Here is the audio: [voice.oga](/path/to/project/voice.oga)
|
|
547
|
+
```
|
|
548
|
+
|
|
549
|
+
The bridge uses Telegram's media-specific upload methods when possible:
|
|
550
|
+
`sendPhoto`, `sendVideo`, `sendVoice`, and `sendAudio`; unsupported media falls
|
|
551
|
+
back to `sendDocument`.
|
|
552
|
+
|
|
553
|
+
For safety, only media files under `CRB_ATTACHMENT_ROOTS` are uploaded and
|
|
554
|
+
hidden from the Telegram text. By default those roots are the bridge state
|
|
555
|
+
directory, `TAB_WORKDIR`, and `/tmp`. Large files are skipped according to
|
|
556
|
+
`CRB_MAX_ATTACHMENT_BYTES`.
|
|
557
|
+
|
|
558
|
+
## Codex Execution
|
|
559
|
+
|
|
560
|
+
It runs:
|
|
561
|
+
|
|
562
|
+
```text
|
|
563
|
+
codex exec --json -o <tmp-answer-file> -C <TAB_WORKDIR> <prompt>
|
|
564
|
+
codex exec --json -o <tmp-answer-file> -C <TAB_WORKDIR> resume <thread_id> <prompt>
|
|
565
|
+
```
|
|
566
|
+
|
|
567
|
+
The bridge stores the first `thread.started.thread_id` and resumes it on later messages. If a stored thread id is stale, the bridge clears it and retries once as a fresh Codex thread.
|
|
568
|
+
|
|
569
|
+
## Service Examples
|
|
570
|
+
|
|
571
|
+
Examples live in:
|
|
572
|
+
|
|
573
|
+
- `examples/systemd/telegram-agent-bridge.service`
|
|
574
|
+
- `examples/systemd/telegram-agent-bridge-watchdog.service`
|
|
575
|
+
- `examples/systemd/telegram-agent-bridge-watchdog.timer`
|
|
576
|
+
- `examples/launchd/com.user.telegram-agent-bridge.plist`
|
|
577
|
+
- `examples/launchd/com.user.telegram-agent-bridge-watchdog.plist`
|
|
578
|
+
|
|
579
|
+
Review the `PATH` in each file. Services often start with a smaller environment than your shell, so include the directory where `codex` is installed.
|
|
580
|
+
|
|
581
|
+
## Roadmap
|
|
582
|
+
|
|
583
|
+
These are product directions, not promises in the current release:
|
|
584
|
+
|
|
585
|
+
- queue controls for safe queueing, interruption, and side tasks
|
|
586
|
+
- inline Telegram settings for mode and delivery preferences
|
|
587
|
+
- richer Markdown fallback when Telegram rejects formatted messages
|
|
588
|
+
- optional multi-user and topic allowlists for small private groups
|
|
589
|
+
- richer service supervision dashboards and remote health summaries
|
|
590
|
+
- idle cleanup and maintenance commands for long-running bridge installs
|
|
591
|
+
|
|
592
|
+
## Security Notes
|
|
593
|
+
|
|
594
|
+
- Treat `TAB_BOT_TOKEN` like a password. Do not commit it, paste it into logs, or share it.
|
|
595
|
+
- Do not paste the BotFather token into your Telegram bot chat. Paste it only
|
|
596
|
+
into the local terminal setup wizard. The only Telegram message needed during
|
|
597
|
+
setup is `/start`.
|
|
598
|
+
- Keep `TAB_CHAT_ID` set to the intended chat id. This single-user allowlist is the main safety boundary.
|
|
599
|
+
- Run this only on a trusted personal machine or trusted server. Telegram messages become Codex prompts.
|
|
600
|
+
- Protect `CRB_SIGNAL_PATH`/`TAB_LOCAL_INPUT`. Anyone who can write to the FIFO can send prompts to Codex.
|
|
601
|
+
- Be careful with `TAB_CODEX_DANGEROUS_BYPASS=1`. It adds `--dangerously-bypass-approvals-and-sandbox`, allowing Codex to act without normal approval and sandbox protections.
|
|
602
|
+
- Prefer a limited working directory in `TAB_WORKDIR` when possible.
|
|
603
|
+
- This daemon uses Telegram polling, not a public inbound webhook. You do not need to expose a local port.
|
|
604
|
+
|
|
605
|
+
## Advanced Settings
|
|
606
|
+
|
|
607
|
+
Beyond the keys documented above, the bridges read further tuning knobs
|
|
608
|
+
(direct `CRB_BOT_TOKEN`/`CRB_CHAT_ID` overrides, `CRB_TOKEN_FILE`, media
|
|
609
|
+
helper binaries, generated-image autosend, watchdog probe tuning, and the
|
|
610
|
+
exec-backend tmux fallbacks). Every key ships with a safe default; the full
|
|
611
|
+
annotated list lives at the bottom of `config.example.env`.
|
|
612
|
+
|
|
613
|
+
## Development
|
|
614
|
+
|
|
615
|
+
The core runtime uses only the Python standard library. The optional `asr`
|
|
616
|
+
extra installs local audio transcription dependencies.
|
|
617
|
+
|
|
618
|
+
```bash
|
|
619
|
+
python3 -m unittest discover -s tests
|
|
620
|
+
```
|