A Telegram bot daemon that runs on your Windows PC and lets you drive the local opencode agent from your phone: send prompts, switch models/agents/projects, watch responses stream into a single Telegram message, approve tool permissions, and inspect diffs — all with zero open ports (Telegram long polling only).
Telegram (your phone)
│ long polling
▼
pocket-opencode daemon (Bun + grammY)
│ HTTP + SSE (localhost only)
▼
opencode serve --port 4096
▼
your projects
- Bun ≥ 1.1
opencodeCLI on your PATH (opencode --versionworks)- A Telegram bot token (create a bot via @BotFather)
- Your Telegram user ID (get it from @userinfobot)
bun installCopy-Item config.example.json config.json(PowerShell) and fill in:- your bot token from @BotFather
- your numeric Telegram user ID in
"allow" - your project name/path in
"projects"
bun run start
On first start the daemon prints a 6-digit pairing code in the terminal and the bot replies "Pairing required" to any message. Send /pair <code> in Telegram once — the bot is then bound to that chat. Only user IDs in "allow" are ever processed; everyone else is silently ignored.
config.example.json is the template — copy it to config.json and replace the placeholders:
{
"telegram_token": "123456:REPLACE_WITH_YOUR_TELEGRAM_BOT_TOKEN",
"opencode_port": 4096,
"db_path": "pocket.db",
"allow": [111111111],
"projects": [
{ "name": "myproject", "path": "C:/code/myproject" }
]
}telegram_token— the token @BotFather gave you when you created the bot.allow— array of Telegram user IDs allowed to talk to the bot. Your numeric ID comes from @userinfobot. This is the hard allowlist: nobody else is processed.projects— real folders on this PC; add more entries to switch between them with/cd.opencode_port/db_path— optional; defaults shown.
config.json is gitignored — your token never gets committed.
| Command | Action |
|---|---|
| (plain text) | send a prompt to opencode in the active project |
/status |
active project, session, model/agent/thinking overrides, opencode health |
/new |
start a fresh opencode session |
/model |
pick a model (inline keyboard) |
/agent |
pick an agent (inline keyboard) |
/think <low|medium|high|max> |
store a thinking-level preference |
/cd |
switch project (inline keyboard; sessions kept per project) |
/stop |
abort the current run |
/undo |
revert opencode's last change |
/diff |
show changed files (+additions/-deletions) |
opencode emits message.part.updated SSE events; the daemon coalesces deltas (~1.2 s cadence) and edits one Telegram message in place — no message spam. session.idle finalizes the message; permission.updated surfaces an Allow/Deny inline keyboard wired to opencode's permission API.
/think stores your preference and shows it in /status. The opencode SDK (1.18.x) does not expose a thinking/reasoning parameter on prompts, so it is applied best-effort wherever opencode supports it.
- Hard allowlist: user IDs not in
"allow"are dropped before any processing. - First-run pairing code gates chat control (printed once to the terminal).
- opencode is talked to over
127.0.0.1only; nothing listens on external interfaces. config.json(contains your bot token) is gitignored;*.dbstate files too.
bun run start
Ctrl+C stops the daemon gracefully (bot stops, SQLite closes).