|
| 1 | +--- |
| 2 | +title: "Who Needs OpenClaw When You Have GitHub Copilot CLI Extensions?" |
| 3 | +description: >- |
| 4 | + I built a Telegram bridge to GitHub Copilot CLI in one file. ~420 lines. No |
| 5 | + gateway, no daemon, no framework. Here's how the extension SDK makes full |
| 6 | + agent frameworks feel like overkill — and what happens when you add NVIDIA |
| 7 | + OpenShell to the mix. |
| 8 | +pubDate: 2026-04-03 |
| 9 | +tags: |
| 10 | + - AI |
| 11 | + - GitHub Copilot |
| 12 | + - Telegram |
| 13 | + - Developer Tools |
| 14 | + - NVIDIA OpenShell |
| 15 | +draft: false |
| 16 | +--- |
| 17 | + |
| 18 | +I was lying in bed last night thinking about [OpenClaw](https://github.com/openclaw/openclaw) — the open-source personal AI assistant framework that connects to 20+ messaging channels (WhatsApp, Telegram, Slack, Discord, Signal, iMessage, Teams, IRC, Matrix...). It's an impressive project. Gateway daemon, WebSocket control plane, custom agent runtime, multi-agent routing, onboarding wizard, companion apps, voice wake words, live canvas. Thousands of lines of infrastructure code. |
| 19 | + |
| 20 | +Then I thought: **what if I could do the core thing — chat with an AI coding agent from my phone — in a single file?** |
| 21 | + |
| 22 | +Turns out, you can. |
| 23 | + |
| 24 | +## The Extension |
| 25 | + |
| 26 | +GitHub Copilot CLI has an extension system. You drop an `.mjs` file in `.github/extensions/`, and it gets forked as a child process with access to the full session via JSON-RPC. The SDK gives you everything: |
| 27 | + |
| 28 | +- **`session.send()`** — inject prompts programmatically |
| 29 | +- **`session.on("assistant.message")`** — capture responses in real-time |
| 30 | +- **Custom tools** — register new tools the agent can call |
| 31 | +- **Lifecycle hooks** — react to session start, end, errors |
| 32 | +- **Full Node.js runtime** — `fetch`, `fs`, timers, whatever you need |
| 33 | + |
| 34 | +With these primitives, bridging Telegram to a Copilot CLI session is trivial. |
| 35 | + |
| 36 | +## How It Works |
| 37 | + |
| 38 | +``` |
| 39 | +📱 Telegram → Long Polling → Extension → session.send() → Copilot CLI |
| 40 | + ↓ |
| 41 | +📱 Telegram ← sendMessage ← Extension ← assistant.message event |
| 42 | +``` |
| 43 | + |
| 44 | +The extension uses Telegram's [long polling](https://core.telegram.org/bots/api#getupdates) — an HTTP request to `getUpdates` with a `timeout` parameter. Telegram holds the connection open and returns **instantly** when a new message arrives. No webhooks, no public URL, no infrastructure. Just an open HTTP request. |
| 45 | + |
| 46 | +When a message comes in, `session.send()` injects it as a user prompt. When the agent responds, the `assistant.message` event fires and we forward it back via `sendMessage`. The entire bridge is bidirectional and near-real-time. |
| 47 | + |
| 48 | +## The Build Session |
| 49 | + |
| 50 | +I built this live in a single Copilot CLI session. The whole thing — research, implementation, debugging, iteration — happened in about an hour. Here's the interesting part: the bugs were more educational than the code. |
| 51 | + |
| 52 | +### Bug 1: The Conflict Problem |
| 53 | + |
| 54 | +Telegram only allows **one** `getUpdates` consumer per bot token. When Copilot CLI reloads extensions (which happens on `/clear` or code changes), it kills the old process and starts a new one. But the old process's HTTP request was still hanging — 25 seconds of long poll timeout. The new instance starts polling before the old connection dies. |
| 55 | + |
| 56 | +``` |
| 57 | +⚠️ Telegram API error: Conflict: terminated by other getUpdates request; |
| 58 | +make sure that only one bot instance is running |
| 59 | +``` |
| 60 | + |
| 61 | +The fix was multi-layered: |
| 62 | +- Reduced poll timeout from 25s to 10s (old connections release faster) |
| 63 | +- Added conflict detection with silent 3s backoff retry |
| 64 | +- Added a 2s startup delay for new instances |
| 65 | + |
| 66 | +### Bug 2: Duplicate Polling |
| 67 | + |
| 68 | +`onSessionStart` fires on every session transition — not just the first one. Each fire spawned a new polling loop. Multiple consumers fighting over the same bot token. |
| 69 | + |
| 70 | +The fix was dead simple: **move polling out of `onSessionStart` entirely**. Start it immediately when the script loads, right after `joinSession()`. One script execution = one poll loop. |
| 71 | + |
| 72 | +### Bug 3: Windows Signal Handling |
| 73 | + |
| 74 | +We initially added `SIGTERM`/`SIGINT` handlers to abort the poll request on process exit. Except on Windows, Node.js doesn't fire these handlers. The CLI communicates with extensions over stdio (JSON-RPC), so the real signal that the parent disconnected is `stdin.close`. We added that listener, but ultimately found it simpler to just remove all the signal handlers and let the conflict backoff handle it gracefully. |
| 75 | + |
| 76 | +## The Result |
| 77 | + |
| 78 | +One file. ~420 lines. Zero dependencies beyond what Copilot CLI already provides. And it **works**: |
| 79 | + |
| 80 | +``` |
| 81 | +[Telegram from Hector]: Can you tell me what stuff I have been working on? |
| 82 | +``` |
| 83 | + |
| 84 | +Copilot checks my session history, queries my GitHub PRs, and sends back a detailed summary — all from Telegram. Full access to the codebase, terminal, git, GitHub APIs, MCP servers, everything. This isn't a chatbot wrapper. It's the real Copilot CLI, remote-controlled from my phone. |
| 85 | + |
| 86 | +## OpenClaw vs. One File |
| 87 | + |
| 88 | +Here's the comparison that makes the point: |
| 89 | + |
| 90 | +| | OpenClaw | This Extension | |
| 91 | +|---|---------|----------------| |
| 92 | +| **Setup** | npm install, onboarding wizard, gateway daemon, systemd service | Drop one `.mjs` file, add bot token to `.env` | |
| 93 | +| **Infrastructure** | Gateway server, WebSocket control plane, session model, media pipeline | Nothing. The CLI *is* the infrastructure | |
| 94 | +| **Agent runtime** | Custom Pi agent runtime with RPC, tool streaming | GitHub Copilot — already the best coding agent available | |
| 95 | +| **Lines of code** | Thousands | ~420 | |
| 96 | + |
| 97 | +The trade-off is real: OpenClaw is a **product** — polished, multi-channel, multi-user, always-on. This extension is a **hack** — single-channel, single-user, runs while your terminal is open. But for the use case of "I want to talk to my coding agent from my phone," the hack wins on simplicity by a mile. |
| 98 | + |
| 99 | +And adding another channel? Just write another extension file. Same pattern, different API. |
| 100 | + |
| 101 | +## The Future: Safe OpenClaw |
| 102 | + |
| 103 | +Here's where it gets interesting. This project is step one. Step two is [one PR away](https://github.com/NVIDIA/OpenShell-Community/pull/60). |
| 104 | + |
| 105 | +[NVIDIA OpenShell](https://github.com/NVIDIA/OpenShell) is the runtime environment for autonomous agents — sandboxed execution with a policy engine, L7 proxy with credential injection, and network-level security. Think Docker for AI agents, but with enterprise-grade isolation. |
| 106 | + |
| 107 | +PR #60 adds GitHub Copilot API endpoints to OpenShell's base sandbox policy. Once merged, Copilot CLI can run **inside an OpenShell sandbox** with: |
| 108 | + |
| 109 | +- L7 proxy credential injection (no API keys stored in the sandbox) |
| 110 | +- Network policy enforcement (only Copilot API endpoints are reachable) |
| 111 | +- Full sandboxed filesystem (agent can't escape) |
| 112 | +- Already verified end-to-end: `/models`, `/chat/completions`, `/mcp/readonly` all working |
| 113 | + |
| 114 | +Combine the three layers: |
| 115 | + |
| 116 | +| Layer | Technology | Role | |
| 117 | +|-------|-----------|------| |
| 118 | +| **Agent** | GitHub Copilot CLI | The actual AI coding agent | |
| 119 | +| **Sandbox** | NVIDIA OpenShell | Secure, isolated execution | |
| 120 | +| **Interface** | This Telegram extension | Remote access from your phone | |
| 121 | +| **Auth** | OpenShell L7 proxy | Credential injection, zero stored keys | |
| 122 | + |
| 123 | +That's OpenClaw's entire value proposition — a personal AI assistant accessible from messaging apps — but built on the best coding agent available, running inside NVIDIA's security infrastructure, controlled from Telegram with a single-file extension. |
| 124 | + |
| 125 | +**Safe OpenClaw.** No gateway daemon. No custom agent runtime. No framework. Just proven infrastructure composed together. |
| 126 | + |
| 127 | +## Try It Yourself |
| 128 | + |
| 129 | +The full source is on GitHub: [htekdev/gh-cli-telegram-extension](https://github.com/htekdev/gh-cli-telegram-extension) |
| 130 | + |
| 131 | +Setup takes 2 minutes: |
| 132 | + |
| 133 | +1. Create a bot via [@BotFather](https://t.me/BotFather) on Telegram |
| 134 | +2. Drop the extension in `.github/extensions/telegram-bridge/` |
| 135 | +3. Add your bot token to `.env` |
| 136 | +4. Start a Copilot CLI session |
| 137 | + |
| 138 | +Send `/start` to your bot. You're in. |
| 139 | + |
| 140 | +--- |
| 141 | + |
| 142 | +*Built live in a single Copilot CLI session. The extension was written, debugged, and iterated entirely through the CLI — including from Telegram itself once the bridge was working. Proof that the tool can build its own interfaces.* |
0 commit comments