Telegram Monitor
Read-only opencode plugin that reports session lifecycle, token usage and waiting permissions/questions to a Telegram bot.
0
317
55 in 7 days
35.2
Multi-signal model
9 days ago
2026-09-25
Install and configure
opencode.jsonWrites to this project's opencode.json — applies to this repository only.
opencode.json
{
"$schema": "https://opencode.ai/config.json",
"plugin": ["opencode-telegram-monitor@1.0.0"]
}Writes to ~/.config/opencode/opencode.json — applies to every project.
~/.config/opencode/opencode.json
{
"$schema": "https://opencode.ai/config.json",
"plugin": ["opencode-telegram-monitor@1.0.0"]
}If you want to modify the plugin locally, install it into the project and reference the local path.
shell
pnpm add -D opencode-telegram-monitorOpenCode loads npm dependencies through its embedded runtime on startup and caches them locally — no manual global install needed.
A opencode plugin that keeps you in the loop on your opencode sessions from Telegram.
It watches opencode sessions in real time and reports their lifecycle — started, busy, idle, retried, completed, failed or cancelled — plus token usage and cost, to a Telegram bot chat of your choice. The bot never acts on its own: permission prompts render three inline buttons (Allow once / Allow always / Deny) and question prompts render a stepped answer wizard, and both write back to opencode only when you explicitly tap or submit something in Telegram. Everything else stays in opencode.
Features
- Session lifecycle notifications — track sessions through
idle/busy/retrystates andcompleted/failed/cancelledoutcomes, delivered straight to Telegram. - Token usage & cost — aggregated input / output / reasoning / cache tokens with estimated cost per session.
- Project registry & inline menu — a registry of monitored projects (
~/.otg/projects.json) with an inline-keyboard menu (/menu) to manage them from the chat. - Explicit Telegram replies for permissions and questions — permission prompts render Allow once / Allow always / Deny buttons; question prompts render a stepped wizard (options, custom input, submit / cancel). A reply is written back to opencode only when you explicitly tap a button; the plugin never answers on its own.
- Cross-process poller lock — when several opencode windows are open on the same machine, a file-based lock (
PollerLock) guarantees only one instance polls Telegram at a time. - Proxy support — optional HTTP/HTTPS proxy (with auth and CONNECT tunneling) for reaching the Telegram Bot API.
- Resilient messaging — long polling (
getUpdates, 25 s interval), retries with backoff, message length clamping, and bot-token redaction in all logs.
Requirements
- opencode v2 — the plugin targets
2.0.15. This release is v2-only: opencode v1 users should stay on the published0.6.xline. - A Telegram bot token (create one with @BotFather) and your Telegram
chatId - Only for a from-source build: Node.js ≥ 18 (declared in
engines) and Bun onPATH(scripts/build.mjsinvokesbun build)
Installation
From npm (recommended)
The plugin is published as opencode-telegram-monitor and is loaded from the opencode config as a directory package (a package name — not a file path):
Add the plugin to
~/.config/opencode/opencode.json. Pin a concrete version (not@latest) — a pinned version makes opencode install that exact package into its cache as a directory package, so the plugin's own self-update is the only thing that ever replaces the files:{ "$schema": "https://opencode.ai/config.json", "plugin": ["opencode-telegram-monitor@1.0.0"] }opencode v2 accepts both the legacy
pluginarray and the v2pluginskey (string or{package, options}); entries that are bare package names are resolved from npm.Restart opencode. The package is installed automatically into the opencode cache (
~/.cache/opencode/) on first start, and opencode loads the bundledmonitor.tsfrom the package directory.If you previously installed a local copy, remove it to avoid double-loading the plugin (npm and local copies load side by side, which would run two plugin instances):
rm -f ~/.config/opencode/plugin/telegram-session-monitor.ts rm -f ~/.config/opencode/plugins/telegram-session-monitor.tsThe version in the config never needs manual bumping — see Automatic updates below.
From source (local file)
opencode v2 auto-discovers plugins in <configDir>/plugin/ and <configDir>/plugins/: every .ts/.js file and every subdirectory there is loaded as a plugin, no config entry needed. Both directories are scanned — pick one and keep the plugin there only (the example below uses plugin/); don't keep copies in both, to avoid duplicate-loading confusion. Copy the single-file bundle in:
Build the plugin into the single-file
monitor.tsbundle, then copy that artifact into your opencode plugin directory:node scripts/build.mjs # requires Bun on PATH mkdir -p ~/.config/opencode/plugin cp monitor.ts ~/.config/opencode/plugin/telegram-session-monitor.tsRestart opencode. The plugin loads automatically from the plugin directory.
Keep helper files out of plugin/ and plugins/ — every file there is treated as a plugin, and anything without a { id, setup } default export fails to load. A config entry must point at a directory (a package directory with its own package.json): opencode v2 rejects absolute .ts file paths with configured plugin path must be a directory.
Automatic updates
When installed from npm, the plugin checks for a newer release on the npm registry once per opencode start (5 seconds after startup, non-blocking). If a newer version is found, it:
- Downloads the new tarball into a staging directory (
~/.otg/update-staging/). - Verifies the version in the staged package's
package.jsonmatches the expected version. - Atomically swaps the cached plugin directory (old directory is renamed as a backup, then replaced), re-verifies, and only then removes the backup.
- Sends a Telegram notification; restart opencode to load the new version.
Any failure along the way — including being offline — leaves the previously installed version completely untouched, so opencode always loads a working plugin. A local-file installation (see above) is never auto-updated.
Publishing a new release
Releases are tag-driven: pushing a v* tag triggers the publish workflow, which verifies the version and publishes to npm with Trusted Publishing (OIDC) — no token, no git write-back.
The workflow refuses to publish if the tag version does not match the version pinned in package.json — the single source of truth, injected into the bundle at build time — so keep the two release-facing pins in sync before tagging:
| Place | Field |
|---|---|
package.json |
"version": "x.y.z" (single source of truth) |
README.md |
npm install pin opencode-telegram-monitor@x.y.z |
The built monitor.ts never stores an editable version: scripts/build.mjs reads package.json and injects the version into the bundle (bun build --define __PLUGIN_VERSION__), so the artifact always reports the pinned version.
For a bugfix (patch) release, bump only the lowest number — never the middle one (1.0.0 → 1.0.1, not 1.1.0):
# 1. set the new version (package.json + README.md; the bundle picks it up at build time)
node scripts/set-version.mjs v1.0.1
# 2. verify the tag you are about to create matches the pinned version (exits non-zero on mismatch)
node scripts/check-version.mjs v1.0.1
# 3. commit, tag, push — the workflow verifies again and publishes
git add package.json README.md
git commit -m "feat(monitor): ..."
git tag v1.0.1
git push origin main
git push origin v1.0.1
Local tag guard (pre-push hook)
A pre-push hook ships in .githooks/pre-push: pushing any refs/tags/* runs
node scripts/check-version.mjs <tag> locally and refuses the push on a
mismatch (branches pass through). This catches a bad tag before it reaches CI.
Enable it once per clone:
git config core.hooksPath .githooks
Release types: bump the third number for bugfixes, the second for new features, the first for breaking changes.
Configuration
The plugin reads its configuration from ~/.otg/telegram.json:
{
"botToken": "123456789:ABCdef...",
"chatId": "987654321",
"proxy": "http://user:pass@proxy.example.com:8080"
}
| Field | Required | Description |
|---|---|---|
botToken |
yes | Your Telegram bot token (validated on load). |
chatId |
yes | The chat the bot is allowed to talk to / listen from. |
proxy |
no | Optional http:// or https:// proxy URL (may include auth). |
If the config is missing or invalid, the plugin logs an error and disables itself instead of crashing opencode.
Telegram commands
Available
| Command | Description |
|---|---|
/menu |
Manage monitored projects (inline keyboard). |
/help |
Show this help. |
Planned (not available yet)
| Command | Description |
|---|---|
/start |
Check the plugin connection and bot health. |
/sessions |
List active sessions. |
/use <short-id> |
Select a session to inspect. |
/status |
Show the selected session's status. |
/usage |
Show the selected session's token usage and cost. |
How it works
- The plugin subscribes to opencode's event stream through
client.event.subscribe()and maintains an in-memory projection of every session: state, outcome, tools, waiting prompts and token totals. All v2 types are defined locally — the plugin has zero runtime@opencode-ai/*dependencies and ships as one self-contained bundle. - A background poller talks to the Telegram Bot API (
getUpdateslong polling) so you can send commands from the chat; replies are sent back through the same channel with retries. - When several opencode processes share one machine,
PollerLock(~/.otg/) elects a single poller to avoid duplicategetUpdatesconsumers. - A self-healing registrar re-asserts the current project into the registry every 5 minutes, so a project removed via
/menucomes back as disabled while its window stays open.
State & data files (~/.otg/)
| File | Purpose |
|---|---|
telegram.json |
Plugin configuration (bot token, chat id). |
projects.json |
Registry of monitored projects. |
tgdiag.log |
Diagnostics log (token-redacted). |
*.lock |
Cross-process poller lock files. |
Security notes
- The bot is read-only by default — the only way it acts on your behalf is when you explicitly tap an approval button on a permission prompt (2026-09-02+) or submit / cancel an answer in a question wizard. It never answers or takes actions on its own.
- Messages are limited to the originating
chatId; updates from any other chat are ignored. - The bot token is redacted (
[REDACTED]) in all log output and diagnostics. - The plugin runs locally and talks to the public Telegram Bot API only.
License
Similar plugins
Wololo Notifications
@jfrz38/opencode-wololo-notifications
Age of Empires II sound notifications for OpenCode with configurable events and custom audio mappings.
Notch
@navopw/opencode-notch
Dynamic Island-style macOS notch notifications for OpenCode
Smart Notify
opencode-smart-notify
Desktop notifications for OpenCode that stay quiet when auto-approve handles the request