@grinev/opencode-telegram-bot 0.1.0-rc.8 → 0.1.0

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.
package/.env.example CHANGED
@@ -1,7 +1,7 @@
1
1
  # Telegram Bot Token (from @BotFather)
2
2
  TELEGRAM_BOT_TOKEN=
3
3
 
4
- # Allowed Telegram User ID (fron @userinfobot)
4
+ # Allowed Telegram User ID (from @userinfobot)
5
5
  TELEGRAM_ALLOWED_USER_ID=
6
6
 
7
7
  # OpenCode API URL (optional, default: http://localhost:4096)
package/README.md CHANGED
@@ -1,72 +1,205 @@
1
- # OpenCode Telegram Client
1
+ # OpenCode Telegram Bot
2
2
 
3
- Мобильный клиент для OpenCode в Telegram, позволяющий управлять задачами разработки на локальном компьютере.
3
+ [![npm version](https://img.shields.io/npm/v/@grinev/opencode-telegram-bot)](https://www.npmjs.com/package/@grinev/opencode-telegram-bot)
4
+ [![CI](https://github.com/grinev/opencode-telegram-bot/actions/workflows/ci.yml/badge.svg)](https://github.com/grinev/opencode-telegram-bot/actions/workflows/ci.yml)
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
6
+ [![Node.js](https://img.shields.io/badge/node-%3E%3D20-brightgreen)](https://nodejs.org)
4
7
 
5
- ## Требования
8
+ Control your [OpenCode](https://opencode.ai) coding agent from your phone. Send tasks, switch models, monitor progress — all through Telegram.
6
9
 
7
- - Node.js 20+
8
- - Установленный OpenCode CLI (`opencode`)
10
+ No open ports, no exposed APIs. The bot runs on your machine alongside OpenCode and communicates exclusively through the Telegram Bot API.
9
11
 
10
- ## Установка
12
+ <p align="center">
13
+ <img src="assets/Screenshot-1.png" width="32%" alt="Sending a coding task and receiving file edit results" />
14
+ <img src="assets/Screenshot-2.png" width="32%" alt="Live session status with context usage and changed files" />
15
+ <img src="assets/Screenshot-3.png" width="32%" alt="Switching between AI models from favorites" />
16
+ </p>
11
17
 
12
- 1. Клонируйте репозиторий.
13
- 2. Установите зависимости:
14
- ```bash
15
- npm install
16
- ```
17
- 3. Скопируйте файл с переменными окружения:
18
- ```bash
19
- cp .env.example .env
20
- ```
21
- 4. Заполните `.env` файл (токен бота, ваш ID и путь к проекту).
22
- 5. Настройте избранные модели в OpenCode (Desktop/TUI). Бот автоматически читает их из локального state файла OpenCode (`model.json`).
23
- 6. Если избранные модели недоступны или файл не читается, бот использует fallback из `.env`:
24
- - `OPENCODE_MODEL_PROVIDER`
25
- - `OPENCODE_MODEL_ID`
18
+ ## Features
26
19
 
27
- ## Переменные окружения
20
+ - **Remote coding** — send prompts to OpenCode from anywhere, receive complete results with code sent as files
21
+ - **Session management** — create new sessions or continue existing ones, just like in the TUI
22
+ - **Live status** — pinned message with current project, model, context usage, and changed files list, updated in real time
23
+ - **Model switching** — pick any model from your OpenCode favorites directly in the chat
24
+ - **Agent modes** — switch between Plan and Build modes on the fly
25
+ - **Interactive Q&A** — answer agent questions and approve permissions via inline buttons
26
+ - **Context control** — compact context when it gets too large, right from the chat
27
+ - **Security** — strict user ID whitelist; no one else can access your bot, even if they find it
28
+ - **Localization** — English and Russian UI (`BOT_LOCALE=en|ru`)
28
29
 
29
- Основные переменные в `.env`:
30
+ ## Prerequisites
30
31
 
31
- - `TELEGRAM_BOT_TOKEN`токен бота (обязательно).
32
- - `TELEGRAM_ALLOWED_USER_ID`ваш Telegram user ID (обязательно).
33
- - `OPENCODE_MODEL_PROVIDER`провайдер модели по умолчанию (обязательно).
34
- - `OPENCODE_MODEL_ID` — ID модели по умолчанию (обязательно).
35
- - `SESSIONS_LIST_LIMIT` — сколько последних сессий показывать в `/sessions` (опционально, по умолчанию `10`).
32
+ - **Node.js 20+** [download](https://nodejs.org)
33
+ - **OpenCode**install from [opencode.ai](https://opencode.ai) or [GitHub](https://github.com/sst/opencode)
34
+ - **Telegram Bot** you'll create one during setup (takes 1 minute)
36
35
 
37
- ## Сборка и запуск
36
+ ## Quick Start
38
37
 
39
- ### Режим разработки
38
+ ### 1. Create a Telegram Bot
40
39
 
41
- Запуск без автоматической перезагрузки процесса:
40
+ 1. Open [@BotFather](https://t.me/BotFather) in Telegram and send `/newbot`
41
+ 2. Follow the prompts to choose a name and username
42
+ 3. Copy the **bot token** you receive (e.g. `123456:ABC-DEF1234...`)
42
43
 
43
- > В этом проекте auto-restart (watch/restart) намеренно не используется для runtime-бота,
44
- > чтобы не ломать активные SSE/polling соединения.
44
+ You'll also need your **Telegram User ID** — send any message to [@userinfobot](https://t.me/userinfobot) and it will reply with your numeric ID.
45
+
46
+ ### 2. Start OpenCode Server
47
+
48
+ In your project directory, start the OpenCode server:
49
+
50
+ ```bash
51
+ opencode serve
52
+ ```
53
+
54
+ > The bot connects to the OpenCode API at `http://localhost:4096` by default.
55
+
56
+ ### 3. Install & Run
57
+
58
+ The fastest way — run directly with `npx`:
59
+
60
+ ```bash
61
+ npx @grinev/opencode-telegram-bot
62
+ ```
63
+
64
+ On first launch, an interactive wizard will guide you through the configuration — it will ask for your bot token, user ID, and OpenCode API URL. After that, you're ready to go. Open your bot in Telegram and start sending tasks.
65
+
66
+ #### Alternative: Global Install
67
+
68
+ ```bash
69
+ npm install -g @grinev/opencode-telegram-bot
70
+ opencode-telegram start
71
+ ```
72
+
73
+ To reconfigure at any time:
74
+
75
+ ```bash
76
+ opencode-telegram config
77
+ ```
78
+
79
+ ## Supported Platforms
80
+
81
+ | Platform | Status |
82
+ | -------- | ------------------------------------------------------------------------------------------------------------------------------------ |
83
+ | macOS | Fully supported |
84
+ | Windows | Fully supported |
85
+ | Linux | Experimental — should work, but has not been extensively tested. You may need additional steps such as granting execute permissions. |
86
+
87
+ ## Bot Commands
88
+
89
+ | Command | Description |
90
+ | ----------------- | ------------------------------------------------------- |
91
+ | `/status` | Server health, current project, session, and model info |
92
+ | `/new` | Create a new session |
93
+ | `/stop` | Abort the current task |
94
+ | `/sessions` | Browse and switch between recent sessions |
95
+ | `/projects` | Switch between OpenCode projects |
96
+ | `/model` | Choose a model from your favorites |
97
+ | `/agent` | Switch agent mode (Plan / Build) |
98
+ | `/opencode_start` | Start the OpenCode server remotely |
99
+ | `/opencode_stop` | Stop the OpenCode server remotely |
100
+ | `/help` | Show available commands |
101
+
102
+ Any regular text message is sent as a prompt to the coding agent.
103
+
104
+ > `/opencode_start` and `/opencode_stop` are intended as emergency commands — for example, if you need to restart a stuck server while away from your computer. Under normal usage, start `opencode serve` yourself before launching the bot.
105
+
106
+ ## Configuration
107
+
108
+ ### Environment Variables
109
+
110
+ When installed via npm, the configuration wizard handles the initial setup. The `.env` file is stored in your platform's app data directory:
111
+
112
+ - **macOS:** `~/Library/Application Support/opencode-telegram-bot/.env`
113
+ - **Windows:** `%APPDATA%\opencode-telegram-bot\.env`
114
+ - **Linux:** `~/.config/opencode-telegram-bot/.env`
115
+
116
+ | Variable | Description | Required | Default |
117
+ | -------------------------- | -------------------------------------------- | :------: | ----------------------- |
118
+ | `TELEGRAM_BOT_TOKEN` | Bot token from @BotFather | Yes | — |
119
+ | `TELEGRAM_ALLOWED_USER_ID` | Your numeric Telegram user ID | Yes | — |
120
+ | `OPENCODE_API_URL` | OpenCode server URL | No | `http://localhost:4096` |
121
+ | `OPENCODE_SERVER_USERNAME` | Server auth username | No | `opencode` |
122
+ | `OPENCODE_SERVER_PASSWORD` | Server auth password | No | — |
123
+ | `OPENCODE_MODEL_PROVIDER` | Default model provider | Yes | `opencode` |
124
+ | `OPENCODE_MODEL_ID` | Default model ID | Yes | `big-pickle` |
125
+ | `BOT_LOCALE` | Bot UI language (`en` or `ru`) | No | `en` |
126
+ | `SESSIONS_LIST_LIMIT` | Max sessions shown in `/sessions` | No | `10` |
127
+ | `CODE_FILE_MAX_SIZE_KB` | Max file size (KB) to send as document | No | `100` |
128
+ | `LOG_LEVEL` | Log level (`debug`, `info`, `warn`, `error`) | No | `info` |
129
+
130
+ > **Keep your `.env` file private.** It contains your bot token. Never commit it to version control.
131
+
132
+ ### Model Configuration
133
+
134
+ The bot picks up your **favorite models** from OpenCode. To add a model to favorites:
135
+
136
+ 1. Open OpenCode TUI (`opencode`)
137
+ 2. Go to model selection
138
+ 3. Hover over the model you want and press **Ctrl+F** to add it to favorites
139
+
140
+ These favorites will appear in the `/model` command menu in Telegram.
141
+
142
+ A free model (`opencode/big-pickle`) is configured as the default fallback — if you haven't set up any favorites yet, the bot will use it automatically.
143
+
144
+ ## Security
145
+
146
+ The bot enforces a strict **user ID whitelist**. Only the Telegram user whose numeric ID matches `TELEGRAM_ALLOWED_USER_ID` can interact with the bot. Messages from any other user are silently ignored and logged as unauthorized access attempts.
147
+
148
+ Since the bot runs locally on your machine and connects to your local OpenCode server, there is no external attack surface beyond the Telegram Bot API itself.
149
+
150
+ ## Development
151
+
152
+ ### Running from Source
153
+
154
+ ```bash
155
+ git clone https://github.com/grinev/opencode-telegram-bot.git
156
+ cd opencode-telegram-bot
157
+ npm install
158
+ cp .env.example .env
159
+ # Edit .env with your bot token, user ID, and model settings
160
+ ```
161
+
162
+ Build and run:
45
163
 
46
164
  ```bash
47
165
  npm run dev
48
166
  ```
49
167
 
50
- ### Продакшн
168
+ ### Available Scripts
169
+
170
+ | Script | Description |
171
+ | ----------------------- | ----------------------------------- |
172
+ | `npm run dev` | Build and start (development) |
173
+ | `npm run build` | Compile TypeScript |
174
+ | `npm start` | Run compiled code |
175
+ | `npm run lint` | ESLint check (zero warnings policy) |
176
+ | `npm run format` | Format code with Prettier |
177
+ | `npm test` | Run tests (Vitest) |
178
+ | `npm run test:coverage` | Tests with coverage report |
179
+
180
+ > **Note:** No file watcher or auto-restart is used. The bot maintains persistent SSE and long-polling connections — automatic restarts would break them mid-task. After making changes, restart manually with `npm run dev`.
181
+
182
+ ## Troubleshooting
183
+
184
+ **Bot doesn't respond to messages**
185
+
186
+ - Make sure `TELEGRAM_ALLOWED_USER_ID` matches your actual Telegram user ID (check with [@userinfobot](https://t.me/userinfobot))
187
+ - Verify the bot token is correct
188
+
189
+ **"OpenCode server is not available"**
190
+
191
+ - Ensure `opencode serve` is running in your project directory
192
+ - Check that `OPENCODE_API_URL` points to the correct address (default: `http://localhost:4096`)
193
+
194
+ **No models in `/model` menu**
51
195
 
52
- 1. Соберите проект:
53
- ```bash
54
- npm run build
55
- ```
56
- 2. Запустите собранный код:
57
- ```bash
58
- npm start
59
- ```
196
+ - Add models to your OpenCode favorites: open OpenCode TUI, go to model selection, press **Ctrl+F** on desired models
60
197
 
61
- ## Команды
198
+ **Linux: permission denied errors**
62
199
 
63
- - `npm run lint` проверка кода линтером.
64
- - `npm run format` форматирование кода через Prettier.
65
- - `npm run test` — запуск тестов через Vitest.
66
- - `npm run test:coverage` — запуск тестов с coverage-отчётом.
200
+ - Make sure the CLI binary has execute permission: `chmod +x $(which opencode-telegram)`
201
+ - Check that the config directory is writable: `~/.config/opencode-telegram-bot/`
67
202
 
68
- ## Функционал
203
+ ## License
69
204
 
70
- Подробное описание функционала и плана разработки находится в [PRODUCT.md](./PRODUCT.md).
71
- План технических улучшений (архитектура + тесты) — в [TECHNICAL_IMPROVEMENTS.md](./TECHNICAL_IMPROVEMENTS.md).
72
- Инструкции для разработчиков и описание архитектуры — в [AGENTS.md](./AGENTS.md).
205
+ [MIT](LICENSE) © Ruslan Grinev
@@ -1,25 +1,57 @@
1
1
  import { config } from "../config.js";
2
+ /**
3
+ * Mapping of log levels to numeric values for comparison
4
+ * Used to determine if a message should be logged based on configured level
5
+ */
2
6
  const LOG_LEVELS = {
3
7
  debug: 0,
4
8
  info: 1,
5
9
  warn: 2,
6
10
  error: 3,
7
11
  };
12
+ /**
13
+ * Normalizes a string value to a valid LogLevel
14
+ * Falls back to 'info' if the value is invalid
15
+ *
16
+ * @param value - The log level string to normalize
17
+ * @returns A valid LogLevel
18
+ */
8
19
  function normalizeLogLevel(value) {
9
20
  if (value in LOG_LEVELS) {
10
21
  return value;
11
22
  }
12
23
  return "info";
13
24
  }
25
+ /**
26
+ * Formats the log message prefix with timestamp and level
27
+ *
28
+ * @param level - The log level for the message
29
+ * @returns Formatted prefix string
30
+ */
14
31
  function formatPrefix(level) {
15
32
  return `[${new Date().toISOString()}] [${level.toUpperCase()}]`;
16
33
  }
34
+ /**
35
+ * Formats individual arguments for logging
36
+ * Special handling for Error objects to extract stack trace
37
+ *
38
+ * @param arg - The argument to format
39
+ * @returns Formatted argument
40
+ */
17
41
  function formatArg(arg) {
18
42
  if (arg instanceof Error) {
19
43
  return arg.stack ?? `${arg.name}: ${arg.message}`;
20
44
  }
21
45
  return arg;
22
46
  }
47
+ /**
48
+ * Prepends formatted prefix to log arguments
49
+ * Handles different argument formats (string vs non-string first argument)
50
+ *
51
+ * @param level - The log level for prefix formatting
52
+ * @param args - The arguments to log
53
+ * @returns Array with prefix prepended
54
+ */
23
55
  function withPrefix(level, args) {
24
56
  const formattedArgs = args.map((arg) => formatArg(arg));
25
57
  const prefix = formatPrefix(level);
@@ -31,26 +63,62 @@ function withPrefix(level, args) {
31
63
  }
32
64
  return [prefix, ...formattedArgs];
33
65
  }
66
+ /**
67
+ * Determines if a message should be logged based on configured log level
68
+ * Messages with level >= configured level will be logged
69
+ *
70
+ * @param level - The level of the message to check
71
+ * @returns True if the message should be logged
72
+ */
34
73
  function shouldLog(level) {
35
74
  const configLevel = normalizeLogLevel(config.server.logLevel);
36
75
  return LOG_LEVELS[level] >= LOG_LEVELS[configLevel];
37
76
  }
77
+ /**
78
+ * Logger interface with methods for different log levels
79
+ * Each method checks if the message should be logged based on configured level
80
+ * and formats the output with timestamp and level prefix
81
+ */
38
82
  export const logger = {
83
+ /**
84
+ * Logs debug-level messages (most verbose)
85
+ * Used for detailed diagnostics and internal operations
86
+ *
87
+ * @param args - Arguments to log
88
+ */
39
89
  debug: (...args) => {
40
90
  if (shouldLog("debug")) {
41
91
  console.log(...withPrefix("debug", args));
42
92
  }
43
93
  },
94
+ /**
95
+ * Logs info-level messages
96
+ * Used for important events and general information
97
+ *
98
+ * @param args - Arguments to log
99
+ */
44
100
  info: (...args) => {
45
101
  if (shouldLog("info")) {
46
102
  console.log(...withPrefix("info", args));
47
103
  }
48
104
  },
105
+ /**
106
+ * Logs warning-level messages
107
+ * Used for recoverable errors and potential issues
108
+ *
109
+ * @param args - Arguments to log
110
+ */
49
111
  warn: (...args) => {
50
112
  if (shouldLog("warn")) {
51
113
  console.warn(...withPrefix("warn", args));
52
114
  }
53
115
  },
116
+ /**
117
+ * Logs error-level messages
118
+ * Used for critical failures and exceptions
119
+ *
120
+ * @param args - Arguments to log
121
+ */
54
122
  error: (...args) => {
55
123
  if (shouldLog("error")) {
56
124
  console.error(...withPrefix("error", args));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@grinev/opencode-telegram-bot",
3
- "version": "0.1.0-rc.8",
3
+ "version": "0.1.0",
4
4
  "description": "Telegram bot client for OpenCode to run and monitor coding tasks from chat.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -24,7 +24,6 @@
24
24
  "build": "tsc",
25
25
  "start": "node dist/index.js",
26
26
  "dev": "npm run build && npm start",
27
- "list-providers": "tsx scripts/list-providers.ts",
28
27
  "release:prepare": "node scripts/release-prepare.mjs",
29
28
  "release:rc": "node scripts/release-prepare.mjs rc",
30
29
  "lint": "eslint src --ext .ts --max-warnings=0",
@@ -34,7 +33,7 @@
34
33
  },
35
34
  "repository": {
36
35
  "type": "git",
37
- "url": "git+https://github.com/grinev/my-telegram-opencode-bot.git"
36
+ "url": "git+https://github.com/grinev/opencode-telegram-bot.git"
38
37
  },
39
38
  "keywords": [
40
39
  "opencode",
@@ -47,15 +46,14 @@
47
46
  "author": "grinev",
48
47
  "license": "MIT",
49
48
  "bugs": {
50
- "url": "https://github.com/grinev/my-telegram-opencode-bot/issues"
49
+ "url": "https://github.com/grinev/opencode-telegram-bot/issues"
51
50
  },
52
- "homepage": "https://github.com/grinev/my-telegram-opencode-bot#readme",
51
+ "homepage": "https://github.com/grinev/opencode-telegram-bot#readme",
53
52
  "dependencies": {
54
53
  "@grammyjs/menu": "^1.3.1",
55
54
  "@opencode-ai/sdk": "^1.1.21",
56
55
  "dotenv": "^17.2.3",
57
- "grammy": "^1.39.2",
58
- "pino": "^10.2.0"
56
+ "grammy": "^1.39.2"
59
57
  },
60
58
  "devDependencies": {
61
59
  "@types/node": "^25.0.8",
@@ -64,7 +62,6 @@
64
62
  "@vitest/coverage-v8": "^3.2.4",
65
63
  "eslint": "^8.57.1",
66
64
  "eslint-config-prettier": "^10.1.8",
67
- "pino-pretty": "^13.1.3",
68
65
  "prettier": "^3.8.0",
69
66
  "tsx": "^4.21.0",
70
67
  "typescript": "^5.9.3",