zardbot-telegram 1.0.2 → 1.0.3

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,116 +1,116 @@
1
- # Telegram Bot Token (from @BotFather)
2
- TELEGRAM_BOT_TOKEN=
3
-
4
- # Allowed Telegram User ID (from @userinfobot)
5
- TELEGRAM_ALLOWED_USER_ID=
6
-
7
- # Telegram Proxy URL (optional)
8
- # Supports socks5://, socks4://, http://, https:// protocols
9
- # Examples:
10
- # TELEGRAM_PROXY_URL=socks5://proxy.example.com:1080
11
- # TELEGRAM_PROXY_URL=socks5://user:password@proxy.example.com:1080
12
- # TELEGRAM_PROXY_URL=http://proxy.example.com:8080
13
- # TELEGRAM_PROXY_URL=
14
-
15
- # OpenCode API URL (optional, default: http://localhost:4096)
16
- # OPENCODE_API_URL=http://localhost:4096
17
-
18
- # OpenCode Server Authentication (optional)
19
- # OPENCODE_SERVER_USERNAME=opencode
20
- # OPENCODE_SERVER_PASSWORD=
21
-
22
- # OpenCode Model Configuration (REQUIRED)
23
- # You must specify a default model provider and model ID
24
- # Examples:
25
- # Anthropic Claude 3.5 Sonnet: OPENCODE_MODEL_PROVIDER=anthropic, OPENCODE_MODEL_ID=claude-3-5-sonnet-20241022
26
- # OpenAI GPT-4 Turbo: OPENCODE_MODEL_PROVIDER=openai, OPENCODE_MODEL_ID=gpt-4-turbo
27
- # Groq Mixtral: OPENCODE_MODEL_PROVIDER=groq, OPENCODE_MODEL_ID=mixtral-8x7b-32768
28
- OPENCODE_MODEL_PROVIDER=opencode
29
- OPENCODE_MODEL_ID=big-pickle
30
-
31
- # Server Configuration (optional)
32
- # Logging level: debug, info, warn, error (default: info)
33
- # Use "debug" to see detailed diagnostic logs including all bot events
34
- # LOG_LEVEL=info
35
-
36
- # Bot Configuration (optional)
37
- # Maximum number of sessions shown in /sessions (default: 10)
38
- # SESSIONS_LIST_LIMIT=10
39
-
40
- # Maximum number of projects shown in /projects (default: 10)
41
- # PROJECTS_LIST_LIMIT=10
42
-
43
- # Maximum number of commands shown in /commands (default: 10)
44
- # COMMANDS_LIST_LIMIT=10
45
-
46
- # Maximum number of scheduled tasks allowed at once (default: 10)
47
- # TASK_LIMIT=10
48
-
49
- # Stream update throttle in milliseconds for assistant/tool message edits (default: 500)
50
- # Higher value = fewer Telegram edit requests, lower value = more real-time updates
51
- # RESPONSE_STREAM_THROTTLE_MS=500
52
-
53
- # Bot locale: supported locale code (default: en)
54
- # Supported locales: en, de, es, fr, ru, zh
55
- # BOT_LOCALE=en
56
-
57
- # Hide thinking indicator messages (default: false)
58
- # HIDE_THINKING_MESSAGES=false
59
-
60
- # Hide tool call service messages (default: false)
61
- # HIDE_TOOL_CALL_MESSAGES=false
62
-
63
- # Assistant message formatting mode (default: markdown)
64
- # markdown = convert assistant replies to Telegram MarkdownV2
65
- # raw = show assistant replies as plain text
66
- # MESSAGE_FORMAT_MODE=markdown
67
-
68
- # Code File Settings (optional)
69
- # Maximum file size in KB to send as document (default: 100)
70
- # CODE_FILE_MAX_SIZE_KB=100
71
-
72
- # Speech-to-Text / Voice Recognition (optional)
73
- # Enable voice message transcription by setting a Whisper-compatible API URL.
74
- # Works with OpenAI, Groq, whisper.cpp, or any Whisper-compatible endpoint.
75
- # If STT_API_URL is not set, voice messages will get a "not configured" reply.
76
- #
77
- # For local whisper.cpp server:
78
- # STT_API_URL=http://localhost:8080
79
- # STT_API_KEY= (leave empty for local servers)
80
- #
81
- # For OpenAI:
82
- # STT_API_URL=https://api.openai.com/v1
83
- # STT_API_KEY=sk-your-api-key
84
- # STT_MODEL=whisper-1
85
- #
86
- # For Groq:
87
- # STT_API_URL=https://api.groq.com/openai/v1
88
- # STT_API_KEY=gsk-your-api-key
89
- # STT_MODEL=whisper-large-v3-turbo
90
- # STT_API_URL=
91
- # STT_API_KEY=
92
- # STT_MODEL=whisper-large-v3-turbo
93
- # STT_LANGUAGE=
94
-
95
- # Text-to-Speech (optional)
96
- # Enable voice responses by setting up a TTS server.
97
- # Compatible with pocket-tts-server (recommended) or any OpenAI-compatible TTS API.
98
- #
99
- # Setup pocket-tts-server: https://github.com/ai-joe-git/pocket-tts-server
100
- # 82 celebrity voice clones included, local TTS, zero cloud dependency.
101
- #
102
- # For local pocket-tts-server:
103
- # TTS_API_URL=http://localhost:8000
104
- # TTS_DEFAULT_VOICE=david-attenborough-original
105
- #
106
- # For OpenAI:
107
- # TTS_API_URL=https://api.openai.com/v1
108
- # TTS_DEFAULT_VOICE=alloy
109
- # TTS_MODEL=tts-1
110
- # (requires TTS_API_KEY via OpenAI API key header)
111
- #
112
- # If TTS_API_URL is not set, the bot will only send text responses.
113
- # TTS_API_URL=
114
- # TTS_DEFAULT_VOICE=david-attenborough-original
115
- # TTS_MODEL=tts-1
116
- # TTS_SPEED=1.0
1
+ # Telegram Bot Token (from @BotFather)
2
+ TELEGRAM_BOT_TOKEN=
3
+
4
+ # Allowed Telegram User ID (from @userinfobot)
5
+ TELEGRAM_ALLOWED_USER_ID=
6
+
7
+ # Telegram Proxy URL (optional)
8
+ # Supports socks5://, socks4://, http://, https:// protocols
9
+ # Examples:
10
+ # TELEGRAM_PROXY_URL=socks5://proxy.example.com:1080
11
+ # TELEGRAM_PROXY_URL=socks5://user:password@proxy.example.com:1080
12
+ # TELEGRAM_PROXY_URL=http://proxy.example.com:8080
13
+ # TELEGRAM_PROXY_URL=
14
+
15
+ # OpenCode API URL (optional, default: http://localhost:4096)
16
+ # OPENCODE_API_URL=http://localhost:4096
17
+
18
+ # OpenCode Server Authentication (optional)
19
+ # OPENCODE_SERVER_USERNAME=opencode
20
+ # OPENCODE_SERVER_PASSWORD=
21
+
22
+ # OpenCode Model Configuration (REQUIRED)
23
+ # You must specify a default model provider and model ID
24
+ # Examples:
25
+ # Anthropic Claude 3.5 Sonnet: OPENCODE_MODEL_PROVIDER=anthropic, OPENCODE_MODEL_ID=claude-3-5-sonnet-20241022
26
+ # OpenAI GPT-4 Turbo: OPENCODE_MODEL_PROVIDER=openai, OPENCODE_MODEL_ID=gpt-4-turbo
27
+ # Groq Mixtral: OPENCODE_MODEL_PROVIDER=groq, OPENCODE_MODEL_ID=mixtral-8x7b-32768
28
+ OPENCODE_MODEL_PROVIDER=opencode
29
+ OPENCODE_MODEL_ID=big-pickle
30
+
31
+ # Server Configuration (optional)
32
+ # Logging level: debug, info, warn, error (default: info)
33
+ # Use "debug" to see detailed diagnostic logs including all bot events
34
+ # LOG_LEVEL=info
35
+
36
+ # Bot Configuration (optional)
37
+ # Maximum number of sessions shown in /sessions (default: 10)
38
+ # SESSIONS_LIST_LIMIT=10
39
+
40
+ # Maximum number of projects shown in /projects (default: 10)
41
+ # PROJECTS_LIST_LIMIT=10
42
+
43
+ # Maximum number of commands shown in /commands (default: 10)
44
+ # COMMANDS_LIST_LIMIT=10
45
+
46
+ # Maximum number of scheduled tasks allowed at once (default: 10)
47
+ # TASK_LIMIT=10
48
+
49
+ # Stream update throttle in milliseconds for assistant/tool message edits (default: 500)
50
+ # Higher value = fewer Telegram edit requests, lower value = more real-time updates
51
+ # RESPONSE_STREAM_THROTTLE_MS=500
52
+
53
+ # Bot locale: supported locale code (default: en)
54
+ # Supported locales: en, de, es, fr, ru, zh
55
+ # BOT_LOCALE=en
56
+
57
+ # Hide thinking indicator messages (default: false)
58
+ # HIDE_THINKING_MESSAGES=false
59
+
60
+ # Hide tool call service messages (default: false)
61
+ # HIDE_TOOL_CALL_MESSAGES=false
62
+
63
+ # Assistant message formatting mode (default: markdown)
64
+ # markdown = convert assistant replies to Telegram MarkdownV2
65
+ # raw = show assistant replies as plain text
66
+ # MESSAGE_FORMAT_MODE=markdown
67
+
68
+ # Code File Settings (optional)
69
+ # Maximum file size in KB to send as document (default: 100)
70
+ # CODE_FILE_MAX_SIZE_KB=100
71
+
72
+ # Speech-to-Text / Voice Recognition (optional)
73
+ # Enable voice message transcription by setting a Whisper-compatible API URL.
74
+ # Works with OpenAI, Groq, whisper.cpp, or any Whisper-compatible endpoint.
75
+ # If STT_API_URL is not set, voice messages will get a "not configured" reply.
76
+ #
77
+ # For local whisper.cpp server:
78
+ # STT_API_URL=http://localhost:8080
79
+ # STT_API_KEY= (leave empty for local servers)
80
+ #
81
+ # For OpenAI:
82
+ # STT_API_URL=https://api.openai.com/v1
83
+ # STT_API_KEY=sk-your-api-key
84
+ # STT_MODEL=whisper-1
85
+ #
86
+ # For Groq:
87
+ # STT_API_URL=https://api.groq.com/openai/v1
88
+ # STT_API_KEY=gsk-your-api-key
89
+ # STT_MODEL=whisper-large-v3-turbo
90
+ # STT_API_URL=
91
+ # STT_API_KEY=
92
+ # STT_MODEL=whisper-large-v3-turbo
93
+ # STT_LANGUAGE=
94
+
95
+ # Text-to-Speech (optional)
96
+ # Enable voice responses by setting up a TTS server.
97
+ # Compatible with pocket-tts-server (recommended) or any OpenAI-compatible TTS API.
98
+ #
99
+ # Setup pocket-tts-server: https://github.com/ai-joe-git/pocket-tts-server
100
+ # 82 celebrity voice clones included, local TTS, zero cloud dependency.
101
+ #
102
+ # For local pocket-tts-server:
103
+ # TTS_API_URL=http://localhost:8000
104
+ # TTS_DEFAULT_VOICE=david-attenborough-original
105
+ #
106
+ # For OpenAI:
107
+ # TTS_API_URL=https://api.openai.com/v1
108
+ # TTS_DEFAULT_VOICE=alloy
109
+ # TTS_MODEL=tts-1
110
+ # (requires TTS_API_KEY via OpenAI API key header)
111
+ #
112
+ # If TTS_API_URL is not set, the bot will only send text responses.
113
+ # TTS_API_URL=
114
+ # TTS_DEFAULT_VOICE=david-attenborough-original
115
+ # TTS_MODEL=tts-1
116
+ # TTS_SPEED=1.0
package/LICENSE CHANGED
@@ -1,21 +1,21 @@
1
- MIT License
2
-
3
- Copyright (c) 2026 Ruslan Grinev
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.
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Ruslan Grinev
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.
package/README.md CHANGED
@@ -1,250 +1,250 @@
1
- # ZardBot Telegram
2
-
3
- [![npm version](https://img.shields.io/npm/v/zardbot-telegram)](https://www.npmjs.com/package/zardbot-telegram)
4
- [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
5
- [![Node.js](https://img.shields.io/badge/node-%3E%3D20-brightgreen)](https://nodejs.org)
6
-
7
- ZardBot Telegram is a Telegram client for [OpenCode](https://opencode.ai) with **Text-to-Speech (TTS)** and **Speech-to-Text (STT)** support.
8
-
9
- Run AI coding tasks, monitor progress, switch models, and manage sessions from your phone -- with voice input and voice responses!
10
-
11
- **Voice Features:**
12
- - 🎤 **Speech-to-Text (STT)** -- Send voice messages, transcribe via whisper.cpp
13
- - 🔊 **Text-to-Speech (TTS)** -- Agent responses read aloud with celebrity voices
14
-
15
- **Credits:** Forked from [opencode-telegram-bot](https://github.com/grinev/opencode-telegram-bot) by Ruslan Grinev, enhanced with TTS/STT support.
16
-
17
- ---
18
-
19
- ## Features
20
-
21
- All features from the original OpenCode Telegram Bot, plus:
22
-
23
- - **Voice input (STT)** -- Send voice/audio messages, transcribe them via Whisper-compatible API
24
- - **Voice output (TTS)** -- Agent responses spoken aloud with 82+ celebrity voices
25
- - **Voice selection** -- `/voice` command to choose from available voices
26
- - **Local TTS** -- Works with [pocket-tts-server](https://github.com/ai-joe-git/pocket-tts-server) for zero-cloud voice
27
-
28
- ### Core Features (from original)
29
-
30
- - **Remote coding** -- send prompts to OpenCode from anywhere, receive results with code files
31
- - **Session management** -- create new sessions or continue existing ones
32
- - **Live status** -- pinned message with project, model, context usage, subagent activity
33
- - **Model switching** -- pick models from favorites and recent history
34
- - **Agent modes** -- switch between Plan and Build modes
35
- - **Subagent activity** -- watch live subagent progress in chat
36
- - **Custom Commands** -- run OpenCode custom commands from inline menu
37
- - **Interactive Q&A** -- answer agent questions and approve permissions via buttons
38
- - **File attachments** -- send images, PDFs, and text-based files to OpenCode
39
- - **Scheduled tasks** -- schedule prompts to run later or on recurring intervals
40
- - **Context control** -- compact context when it gets too large
41
- - **Security** -- strict user ID whitelist
42
- - **Localization** -- UI in English, Deutsch, Espanol, Francais, Russkii, Zhongwen
43
-
44
- ---
45
-
46
- ## Prerequisites
47
-
48
- - **Node.js 20+** -- [download](https://nodejs.org)
49
- - **OpenCode** -- install from [opencode.ai](https://opencode.ai) or [GitHub](https://github.com/sst/opencode)
50
- - **Telegram Bot** -- create one via [@BotFather](https://t.me/BotFather)
51
-
52
- ### Optional (for voice features)
53
-
54
- - **pocket-tts-server** -- Local TTS with 82+ celebrity voices: [GitHub](https://github.com/ai-joe-git/pocket-tts-server)
55
- - **whisper.cpp** -- Local STT: [GitHub](https://github.com/ggerganov/whisper.cpp)
56
-
57
- ---
58
-
59
- ## Quick Start
60
-
61
- ### 1. Create a Telegram Bot
62
-
63
- 1. Open [@BotFather](https://t.me/BotFather) and send `/newbot`
64
- 2. Follow prompts to choose name and username
65
- 3. Copy the **bot token**
66
-
67
- Get your **Telegram User ID** from [@userinfobot](https://t.me/userinfobot).
68
-
69
- ### 2. Start OpenCode Server
70
-
71
- ```bash
72
- opencode serve
73
- ```
74
-
75
- The bot connects to `http://localhost:4096` by default.
76
-
77
- ### 3. Install & Run
78
-
79
- ```bash
80
- npx zardbot-telegram
81
- ```
82
-
83
- An interactive wizard will guide you through configuration.
84
-
85
- #### Alternative: Global Install
86
-
87
- ```bash
88
- npm install -g zardbot-telegram
89
- zardbot-telegram start
90
- ```
91
-
92
- To reconfigure at any time:
93
-
94
- ```bash
95
- zardbot-telegram config
96
- ```
97
-
98
- The setup wizard will guide you through configuration.
99
-
100
- ---
101
-
102
- ## Bot Commands
103
-
104
- | Command | Description |
105
- |---------|-------------|
106
- | `/status` | Server health, project, session, model info |
107
- | `/new` | Create a new session |
108
- | `/abort` | Abort current task |
109
- | `/sessions` | Browse and switch sessions |
110
- | `/projects` | Switch between projects |
111
- | `/rename` | Rename current session |
112
- | `/voice` | Select TTS voice (when TTS configured) |
113
- | `/commands` | Run custom commands |
114
- | `/task` | Create scheduled task |
115
- | `/tasklist` | Manage scheduled tasks |
116
- | `/opencode_start` | Start OpenCode server remotely |
117
- | `/opencode_stop` | Stop OpenCode server remotely |
118
- | `/help` | Show available commands |
119
-
120
- ---
121
-
122
- ## Configuration
123
-
124
- Config stored in `.env`:
125
-
126
- - **macOS:** `~/Library/Application Support/zardbot-telegram/.env`
127
- - **Windows:** `%APPDATA%\zardbot-telegram\.env`
128
- - **Linux:** `~/.config/zardbot-telegram/.env`
129
-
130
- ### Environment Variables
131
-
132
- | Variable | Description | Required | Default |
133
- |----------|-------------|:--------:|---------|
134
- | `TELEGRAM_BOT_TOKEN` | Bot token from @BotFather | Yes | -- |
135
- | `TELEGRAM_ALLOWED_USER_ID` | Your Telegram user ID | Yes | -- |
136
- | `TELEGRAM_PROXY_URL` | Proxy for Telegram API | No | -- |
137
- | `OPENCODE_API_URL` | OpenCode server URL | No | `http://localhost:4096` |
138
- | `OPENCODE_SERVER_USERNAME` | Server auth username | No | `opencode` |
139
- | `OPENCODE_SERVER_PASSWORD` | Server auth password | No | -- |
140
- | `OPENCODE_MODEL_PROVIDER` | Default model provider | Yes | `opencode` |
141
- | `OPENCODE_MODEL_ID` | Default model ID | Yes | `big-pickle` |
142
- | `BOT_LOCALE` | UI language (`en`, `de`, `es`, `fr`, `ru`, `zh`) | No | `en` |
143
- | `LOG_LEVEL` | Log level (`debug`, `info`, `warn`, `error`) | No | `info` |
144
-
145
- ### Speech-to-Text (STT)
146
-
147
- | Variable | Description | Required | Default |
148
- |----------|-------------|:--------:|---------|
149
- | `STT_API_URL` | Whisper-compatible API URL | No | -- |
150
- | `STT_API_KEY` | API key for STT provider | No | -- |
151
- | `STT_MODEL` | STT model name | No | `whisper-large-v3-turbo` |
152
- | `STT_LANGUAGE` | Language hint | No | -- |
153
-
154
- #### STT Providers
155
-
156
- **Local whisper.cpp:**
157
- ```env
158
- STT_API_URL=http://localhost:8080
159
- STT_API_KEY=
160
- ```
161
-
162
- **OpenAI:**
163
- ```env
164
- STT_API_URL=https://api.openai.com/v1
165
- STT_API_KEY=sk-your-api-key
166
- STT_MODEL=whisper-1
167
- ```
168
-
169
- **Groq:**
170
- ```env
171
- STT_API_URL=https://api.groq.com/openai/v1
172
- STT_API_KEY=gsk-your-api-key
173
- STT_MODEL=whisper-large-v3-turbo
174
- ```
175
-
176
- ### Text-to-Speech (TTS)
177
-
178
- | Variable | Description | Required | Default |
179
- |----------|-------------|:--------:|---------|
180
- | `TTS_API_URL` | TTS server URL | No | -- |
181
- | `TTS_DEFAULT_VOICE` | Default voice ID | No | `david-attenborough-original` |
182
- | `TTS_MODEL` | TTS model name | No | `tts-1` |
183
- | `TTS_SPEED` | Speech speed multiplier | No | `1.0` |
184
-
185
- #### TTS Setup (pocket-tts-server)
186
-
187
- ```env
188
- TTS_API_URL=http://localhost:8000
189
- TTS_DEFAULT_VOICE=david-attenborough-original
190
- ```
191
-
192
- Setup [pocket-tts-server](https://github.com/ai-joe-git/pocket-tts-server):
193
- - 82+ celebrity voice clones
194
- - Local processing (no cloud)
195
- - No API costs
196
-
197
- **Available Voices:**
198
- - `david-attenborough-original`
199
- - `morgan-freeman-original`
200
- - `jarvis-iron-man`
201
- - `margot-robie`
202
- - `chris-evans`
203
- - And many more...
204
-
205
- Use `/voice` in the bot to select from available voices.
206
-
207
- ---
208
-
209
- ## Development
210
-
211
- ### Running from Source
212
-
213
- ```bash
214
- git clone https://github.com/ai-joe-git/zardbot-telegram.git
215
- cd zardbot-telegram
216
- npm install
217
- cp .env.example .env
218
- # Edit .env with your configuration
219
- npm run dev
220
- ```
221
-
222
- ### Scripts
223
-
224
- | Script | Description |
225
- |--------|-------------|
226
- | `npm run dev` | Build and start |
227
- | `npm run build` | Compile TypeScript |
228
- | `npm start` | Run compiled code |
229
- | `npm run lint` | ESLint check |
230
- | `npm run format` | Format with Prettier |
231
- | `npm test` | Run tests |
232
-
233
- ---
234
-
235
- ## Security
236
-
237
- Strict **user ID whitelist**. Only `TELEGRAM_ALLOWED_USER_ID` can interact with the bot.
238
-
239
- ---
240
-
241
- ## License
242
-
243
- [MIT](LICENSE)
244
-
245
- ---
246
-
247
- ## Acknowledgments
248
-
249
- - **Ruslan Grinev** -- Original [opencode-telegram-bot](https://github.com/grinev/opencode-telegram-bot)
250
- - **OpenCode Team** -- [opencode.ai](https://opencode.ai)
1
+ # ZardBot Telegram
2
+
3
+ [![npm version](https://img.shields.io/npm/v/zardbot-telegram)](https://www.npmjs.com/package/zardbot-telegram)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
5
+ [![Node.js](https://img.shields.io/badge/node-%3E%3D20-brightgreen)](https://nodejs.org)
6
+
7
+ ZardBot Telegram is a Telegram client for [OpenCode](https://opencode.ai) with **Text-to-Speech (TTS)** and **Speech-to-Text (STT)** support.
8
+
9
+ Run AI coding tasks, monitor progress, switch models, and manage sessions from your phone -- with voice input and voice responses!
10
+
11
+ **Voice Features:**
12
+ - 🎤 **Speech-to-Text (STT)** -- Send voice messages, transcribe via whisper.cpp
13
+ - 🔊 **Text-to-Speech (TTS)** -- Agent responses read aloud with celebrity voices
14
+
15
+ **Credits:** Forked from [opencode-telegram-bot](https://github.com/grinev/opencode-telegram-bot) by Ruslan Grinev, enhanced with TTS/STT support.
16
+
17
+ ---
18
+
19
+ ## Features
20
+
21
+ All features from the original OpenCode Telegram Bot, plus:
22
+
23
+ - **Voice input (STT)** -- Send voice/audio messages, transcribe them via Whisper-compatible API
24
+ - **Voice output (TTS)** -- Agent responses spoken aloud with 82+ celebrity voices
25
+ - **Voice selection** -- `/voice` command to choose from available voices
26
+ - **Local TTS** -- Works with [pocket-tts-server](https://github.com/ai-joe-git/pocket-tts-server) for zero-cloud voice
27
+
28
+ ### Core Features (from original)
29
+
30
+ - **Remote coding** -- send prompts to OpenCode from anywhere, receive results with code files
31
+ - **Session management** -- create new sessions or continue existing ones
32
+ - **Live status** -- pinned message with project, model, context usage, subagent activity
33
+ - **Model switching** -- pick models from favorites and recent history
34
+ - **Agent modes** -- switch between Plan and Build modes
35
+ - **Subagent activity** -- watch live subagent progress in chat
36
+ - **Custom Commands** -- run OpenCode custom commands from inline menu
37
+ - **Interactive Q&A** -- answer agent questions and approve permissions via buttons
38
+ - **File attachments** -- send images, PDFs, and text-based files to OpenCode
39
+ - **Scheduled tasks** -- schedule prompts to run later or on recurring intervals
40
+ - **Context control** -- compact context when it gets too large
41
+ - **Security** -- strict user ID whitelist
42
+ - **Localization** -- UI in English, Deutsch, Espanol, Francais, Russkii, Zhongwen
43
+
44
+ ---
45
+
46
+ ## Prerequisites
47
+
48
+ - **Node.js 20+** -- [download](https://nodejs.org)
49
+ - **OpenCode** -- install from [opencode.ai](https://opencode.ai) or [GitHub](https://github.com/sst/opencode)
50
+ - **Telegram Bot** -- create one via [@BotFather](https://t.me/BotFather)
51
+
52
+ ### Optional (for voice features)
53
+
54
+ - **pocket-tts-server** -- Local TTS with 82+ celebrity voices: [GitHub](https://github.com/ai-joe-git/pocket-tts-server)
55
+ - **whisper.cpp** -- Local STT: [GitHub](https://github.com/ggerganov/whisper.cpp)
56
+
57
+ ---
58
+
59
+ ## Quick Start
60
+
61
+ ### 1. Create a Telegram Bot
62
+
63
+ 1. Open [@BotFather](https://t.me/BotFather) and send `/newbot`
64
+ 2. Follow prompts to choose name and username
65
+ 3. Copy the **bot token**
66
+
67
+ Get your **Telegram User ID** from [@userinfobot](https://t.me/userinfobot).
68
+
69
+ ### 2. Start OpenCode Server
70
+
71
+ ```bash
72
+ opencode serve
73
+ ```
74
+
75
+ The bot connects to `http://localhost:4096` by default.
76
+
77
+ ### 3. Install & Run
78
+
79
+ ```bash
80
+ npx zardbot-telegram
81
+ ```
82
+
83
+ An interactive wizard will guide you through configuration.
84
+
85
+ #### Alternative: Global Install
86
+
87
+ ```bash
88
+ npm install -g zardbot-telegram
89
+ zardbot-telegram start
90
+ ```
91
+
92
+ To reconfigure at any time:
93
+
94
+ ```bash
95
+ zardbot-telegram config
96
+ ```
97
+
98
+ The setup wizard will guide you through configuration.
99
+
100
+ ---
101
+
102
+ ## Bot Commands
103
+
104
+ | Command | Description |
105
+ |---------|-------------|
106
+ | `/status` | Server health, project, session, model info |
107
+ | `/new` | Create a new session |
108
+ | `/abort` | Abort current task |
109
+ | `/sessions` | Browse and switch sessions |
110
+ | `/projects` | Switch between projects |
111
+ | `/rename` | Rename current session |
112
+ | `/voice` | Select TTS voice (when TTS configured) |
113
+ | `/commands` | Run custom commands |
114
+ | `/task` | Create scheduled task |
115
+ | `/tasklist` | Manage scheduled tasks |
116
+ | `/opencode_start` | Start OpenCode server remotely |
117
+ | `/opencode_stop` | Stop OpenCode server remotely |
118
+ | `/help` | Show available commands |
119
+
120
+ ---
121
+
122
+ ## Configuration
123
+
124
+ Config stored in `.env`:
125
+
126
+ - **macOS:** `~/Library/Application Support/zardbot-telegram/.env`
127
+ - **Windows:** `%APPDATA%\zardbot-telegram\.env`
128
+ - **Linux:** `~/.config/zardbot-telegram/.env`
129
+
130
+ ### Environment Variables
131
+
132
+ | Variable | Description | Required | Default |
133
+ |----------|-------------|:--------:|---------|
134
+ | `TELEGRAM_BOT_TOKEN` | Bot token from @BotFather | Yes | -- |
135
+ | `TELEGRAM_ALLOWED_USER_ID` | Your Telegram user ID | Yes | -- |
136
+ | `TELEGRAM_PROXY_URL` | Proxy for Telegram API | No | -- |
137
+ | `OPENCODE_API_URL` | OpenCode server URL | No | `http://localhost:4096` |
138
+ | `OPENCODE_SERVER_USERNAME` | Server auth username | No | `opencode` |
139
+ | `OPENCODE_SERVER_PASSWORD` | Server auth password | No | -- |
140
+ | `OPENCODE_MODEL_PROVIDER` | Default model provider | Yes | `opencode` |
141
+ | `OPENCODE_MODEL_ID` | Default model ID | Yes | `big-pickle` |
142
+ | `BOT_LOCALE` | UI language (`en`, `de`, `es`, `fr`, `ru`, `zh`) | No | `en` |
143
+ | `LOG_LEVEL` | Log level (`debug`, `info`, `warn`, `error`) | No | `info` |
144
+
145
+ ### Speech-to-Text (STT)
146
+
147
+ | Variable | Description | Required | Default |
148
+ |----------|-------------|:--------:|---------|
149
+ | `STT_API_URL` | Whisper-compatible API URL | No | -- |
150
+ | `STT_API_KEY` | API key for STT provider | No | -- |
151
+ | `STT_MODEL` | STT model name | No | `whisper-large-v3-turbo` |
152
+ | `STT_LANGUAGE` | Language hint | No | -- |
153
+
154
+ #### STT Providers
155
+
156
+ **Local whisper.cpp:**
157
+ ```env
158
+ STT_API_URL=http://localhost:8080
159
+ STT_API_KEY=
160
+ ```
161
+
162
+ **OpenAI:**
163
+ ```env
164
+ STT_API_URL=https://api.openai.com/v1
165
+ STT_API_KEY=sk-your-api-key
166
+ STT_MODEL=whisper-1
167
+ ```
168
+
169
+ **Groq:**
170
+ ```env
171
+ STT_API_URL=https://api.groq.com/openai/v1
172
+ STT_API_KEY=gsk-your-api-key
173
+ STT_MODEL=whisper-large-v3-turbo
174
+ ```
175
+
176
+ ### Text-to-Speech (TTS)
177
+
178
+ | Variable | Description | Required | Default |
179
+ |----------|-------------|:--------:|---------|
180
+ | `TTS_API_URL` | TTS server URL | No | -- |
181
+ | `TTS_DEFAULT_VOICE` | Default voice ID | No | `david-attenborough-original` |
182
+ | `TTS_MODEL` | TTS model name | No | `tts-1` |
183
+ | `TTS_SPEED` | Speech speed multiplier | No | `1.0` |
184
+
185
+ #### TTS Setup (pocket-tts-server)
186
+
187
+ ```env
188
+ TTS_API_URL=http://localhost:8000
189
+ TTS_DEFAULT_VOICE=david-attenborough-original
190
+ ```
191
+
192
+ Setup [pocket-tts-server](https://github.com/ai-joe-git/pocket-tts-server):
193
+ - 82+ celebrity voice clones
194
+ - Local processing (no cloud)
195
+ - No API costs
196
+
197
+ **Available Voices:**
198
+ - `david-attenborough-original`
199
+ - `morgan-freeman-original`
200
+ - `jarvis-iron-man`
201
+ - `margot-robie`
202
+ - `chris-evans`
203
+ - And many more...
204
+
205
+ Use `/voice` in the bot to select from available voices.
206
+
207
+ ---
208
+
209
+ ## Development
210
+
211
+ ### Running from Source
212
+
213
+ ```bash
214
+ git clone https://github.com/ai-joe-git/zardbot-telegram.git
215
+ cd zardbot-telegram
216
+ npm install
217
+ cp .env.example .env
218
+ # Edit .env with your configuration
219
+ npm run dev
220
+ ```
221
+
222
+ ### Scripts
223
+
224
+ | Script | Description |
225
+ |--------|-------------|
226
+ | `npm run dev` | Build and start |
227
+ | `npm run build` | Compile TypeScript |
228
+ | `npm start` | Run compiled code |
229
+ | `npm run lint` | ESLint check |
230
+ | `npm run format` | Format with Prettier |
231
+ | `npm test` | Run tests |
232
+
233
+ ---
234
+
235
+ ## Security
236
+
237
+ Strict **user ID whitelist**. Only `TELEGRAM_ALLOWED_USER_ID` can interact with the bot.
238
+
239
+ ---
240
+
241
+ ## License
242
+
243
+ [MIT](LICENSE)
244
+
245
+ ---
246
+
247
+ ## Acknowledgments
248
+
249
+ - **Ruslan Grinev** -- Original [opencode-telegram-bot](https://github.com/grinev/opencode-telegram-bot)
250
+ - **OpenCode Team** -- [opencode.ai](https://opencode.ai)
@@ -108,9 +108,10 @@ export async function handleVoiceCallback(ctx) {
108
108
  if (callbackData.action === "select" && callbackData.voice) {
109
109
  setCurrentTtsVoice(callbackData.voice);
110
110
  setTtsEnabled(true);
111
+ await ctx.answerCallbackQuery({ text: t("tts.voice_changed", { voice: callbackData.voice }) });
111
112
  const msg = ctx.callbackQuery?.message;
112
113
  if (msg && "message_id" in msg) {
113
- await ctx.api.editMessageText(ctx.chat.id, msg.message_id, t("tts.voice_changed", { voice: callbackData.voice }), { reply_markup: undefined });
114
+ await ctx.api.editMessageText(ctx.chat.id, msg.message_id, t("tts.menu_current", { voice: callbackData.voice }), { reply_markup: undefined });
114
115
  }
115
116
  return true;
116
117
  }
@@ -33,14 +33,14 @@ export async function sendTtsVoiceMessage(api, chatId, text) {
33
33
  if (!truncatedText.trim()) {
34
34
  return false;
35
35
  }
36
- logger.debug(`[TTS] Synthesizing speech for ${truncatedText.length} chars with voice ${voice}`);
36
+ logger.info(`[TTS] Synthesizing speech: ${truncatedText.length} chars, voice=${voice}`);
37
37
  try {
38
38
  const result = await synthesizeSpeech(truncatedText, voice);
39
39
  // Send as voice message using InputFile with Buffer
40
40
  await api.sendVoice(chatId, new InputFile(result.audio, "voice.ogg"), {
41
41
  disable_notification: true,
42
42
  });
43
- logger.debug(`[TTS] Voice message sent: ${result.audio.length} bytes`);
43
+ logger.info(`[TTS] Voice message sent: ${result.audio.length} bytes`);
44
44
  return true;
45
45
  }
46
46
  catch (err) {
@@ -17,17 +17,33 @@ function getModelKey(providerID, modelID) {
17
17
  }
18
18
  function getOpenCodeConfigFilePaths() {
19
19
  const paths = [];
20
- // Standard XDG config locations
21
- const xdgConfigHome = process.env.XDG_CONFIG_HOME;
22
20
  const homeDir = process.env.HOME || process.env.USERPROFILE || os.homedir();
23
- if (xdgConfigHome) {
24
- paths.push(path.join(xdgConfigHome, "opencode", "opencode.jsonc"));
21
+ if (process.platform === "win32") {
22
+ const appData = process.env.APPDATA;
23
+ const localAppData = process.env.LOCALAPPDATA;
24
+ if (appData) {
25
+ paths.push(path.join(appData, "opencode", "opencode.jsonc"));
26
+ }
27
+ if (localAppData) {
28
+ paths.push(path.join(localAppData, "opencode", "opencode.jsonc"));
29
+ }
30
+ if (homeDir) {
31
+ paths.push(path.join(homeDir, ".opencode", "opencode.jsonc"));
32
+ }
25
33
  }
26
- if (homeDir) {
27
- paths.push(path.join(homeDir, ".config", "opencode", "opencode.jsonc"));
34
+ else {
35
+ // Standard XDG config locations
36
+ const xdgConfigHome = process.env.XDG_CONFIG_HOME;
37
+ if (xdgConfigHome) {
38
+ paths.push(path.join(xdgConfigHome, "opencode", "opencode.jsonc"));
39
+ }
40
+ if (homeDir) {
41
+ paths.push(path.join(homeDir, ".config", "opencode", "opencode.jsonc"));
42
+ paths.push(path.join(homeDir, ".opencode", "opencode.jsonc"));
43
+ }
44
+ // Add common alternative locations
45
+ paths.push("/root/.config/opencode/opencode.jsonc");
28
46
  }
29
- // Add common alternative locations
30
- paths.push("/root/.config/opencode/opencode.jsonc");
31
47
  return paths;
32
48
  }
33
49
  async function readJsoncConfigFiles() {
@@ -249,11 +265,26 @@ function normalizeRecentModels(state) {
249
265
  }));
250
266
  }
251
267
  function getOpenCodeModelStatePath() {
268
+ const homeDir = process.env.HOME || process.env.USERPROFILE || "";
269
+ if (process.platform === "win32") {
270
+ const appData = process.env.APPDATA;
271
+ const localAppData = process.env.LOCALAPPDATA;
272
+ // Check multiple possible locations in priority order
273
+ if (appData)
274
+ return path.join(appData, "opencode", "model.json");
275
+ if (localAppData)
276
+ return path.join(localAppData, "opencode", "opencode", "model.json");
277
+ if (localAppData)
278
+ return path.join(localAppData, "opencode", "model.json");
279
+ if (homeDir)
280
+ return path.join(homeDir, ".opencode", "model.json");
281
+ if (homeDir)
282
+ return path.join(homeDir, ".local", "state", "opencode", "model.json");
283
+ }
252
284
  const xdgStateHome = process.env.XDG_STATE_HOME;
253
285
  if (xdgStateHome && xdgStateHome.trim().length > 0) {
254
286
  return path.join(xdgStateHome, "opencode", "model.json");
255
287
  }
256
- const homeDir = process.env.HOME || process.env.USERPROFILE || "";
257
288
  return path.join(homeDir, ".local", "state", "opencode", "model.json");
258
289
  }
259
290
  /**
@@ -288,7 +288,7 @@ class PinnedMessageManager {
288
288
  logger.debug(`[PinnedManager] session.diff() result: error=${!!error}, data.length=${data?.length ?? 0}`);
289
289
  if (!error && data && data.length > 0) {
290
290
  this.state.changedFiles = data.map((d) => ({
291
- file: d.file,
291
+ file: d.file ?? "",
292
292
  additions: d.additions,
293
293
  deletions: d.deletions,
294
294
  }));
@@ -260,12 +260,12 @@ async function querySessionDirectoriesFromSqlite(dbPath) {
260
260
  });
261
261
  try {
262
262
  const rows = db
263
- .prepare(`
264
- SELECT directory, MAX(time_updated) AS updated
265
- FROM session
266
- GROUP BY directory
267
- ORDER BY updated DESC
268
- LIMIT ?
263
+ .prepare(`
264
+ SELECT directory, MAX(time_updated) AS updated
265
+ FROM session
266
+ GROUP BY directory
267
+ ORDER BY updated DESC
268
+ LIMIT ?
269
269
  `)
270
270
  .all(SQLITE_FALLBACK_QUERY_LIMIT);
271
271
  return rows
@@ -96,7 +96,7 @@ export async function synthesizeSpeech(text, voice) {
96
96
  response_format: "wav",
97
97
  speed: config.tts.speed || 1.0,
98
98
  };
99
- logger.debug(`[TTS] Sending speech synthesis request: url=${url}, voice=${selectedVoice}, text=${text.length} chars`);
99
+ logger.info(`[TTS] Synthesizing speech: voice=${selectedVoice}, text=${text.length} chars`);
100
100
  const controller = new AbortController();
101
101
  const timeout = setTimeout(() => controller.abort(), TTS_REQUEST_TIMEOUT_MS);
102
102
  try {
@@ -115,7 +115,7 @@ export async function synthesizeSpeech(text, voice) {
115
115
  const contentType = response.headers.get("content-type") || "audio/wav";
116
116
  const arrayBuffer = await response.arrayBuffer();
117
117
  const audioBuffer = Buffer.from(arrayBuffer);
118
- logger.debug(`[TTS] Synthesized audio: ${audioBuffer.length} bytes, type=${contentType}`);
118
+ logger.info(`[TTS] Synthesized audio: ${audioBuffer.length} bytes, type=${contentType}`);
119
119
  return { audio: audioBuffer, contentType };
120
120
  }
121
121
  catch (err) {
package/package.json CHANGED
@@ -1,79 +1,79 @@
1
- {
2
- "name": "zardbot-telegram",
3
- "version": "1.0.2",
4
- "description": "ZardBot Telegram Bot - A Telegram client for OpenCode with TTS and STT support.",
5
- "type": "module",
6
- "main": "./dist/index.js",
7
- "bin": {
8
- "zardbot-telegram": "dist/cli.js"
9
- },
10
- "exports": {
11
- ".": "./dist/index.js",
12
- "./cli": "./dist/cli.js"
13
- },
14
- "files": [
15
- "dist",
16
- "README.md",
17
- "LICENSE",
18
- ".env.example"
19
- ],
20
- "engines": {
21
- "node": ">=20"
22
- },
23
- "scripts": {
24
- "build": "tsc",
25
- "start": "node dist/index.js",
26
- "dev": "npm run build && npm start",
27
- "release:prepare": "node scripts/release-prepare.mjs",
28
- "release:rc": "node scripts/release-prepare.mjs rc",
29
- "release:notes:preview": "node scripts/release-notes-preview.mjs",
30
- "lint": "eslint src --ext .ts --max-warnings=0",
31
- "format": "prettier --write \"src/**/*.ts\"",
32
- "test": "vitest run",
33
- "test:coverage": "vitest run --coverage"
34
- },
35
- "repository": {
36
- "type": "git",
37
- "url": "git+https://github.com/zardusai-cyber/zardbot-telegram.git"
38
- },
39
- "keywords": [
40
- "zardbot",
41
- "opencode",
42
- "telegram",
43
- "telegram-bot",
44
- "grammy",
45
- "tts",
46
- "stt",
47
- "developer-tools",
48
- "cli"
49
- ],
50
- "author": "ZardBot Team",
51
- "license": "MIT",
52
- "bugs": {
53
- "url": "https://github.com/ai-joe-git/zardbot-telegram/issues"
54
- },
55
- "homepage": "https://github.com/ai-joe-git/zardbot-telegram#readme",
56
- "dependencies": {
57
- "@grammyjs/menu": "^1.3.1",
58
- "@opencode-ai/sdk": "^1.1.21",
59
- "better-sqlite3": "^12.6.2",
60
- "dotenv": "^17.2.3",
61
- "grammy": "^1.39.2",
62
- "https-proxy-agent": "^7.0.6",
63
- "socks-proxy-agent": "^8.0.5",
64
- "telegram-markdown-v2": "^0.0.4"
65
- },
66
- "devDependencies": {
67
- "@types/better-sqlite3": "^7.6.13",
68
- "@types/node": "^25.0.8",
69
- "@typescript-eslint/eslint-plugin": "^8.53.0",
70
- "@typescript-eslint/parser": "^8.53.0",
71
- "@vitest/coverage-v8": "^3.2.4",
72
- "eslint": "^8.57.1",
73
- "eslint-config-prettier": "^10.1.8",
74
- "prettier": "^3.8.0",
75
- "tsx": "^4.21.0",
76
- "typescript": "^5.9.3",
77
- "vitest": "^3.2.4"
78
- }
79
- }
1
+ {
2
+ "name": "zardbot-telegram",
3
+ "version": "1.0.3",
4
+ "description": "ZardBot Telegram Bot - A Telegram client for OpenCode with TTS and STT support.",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "bin": {
8
+ "zardbot-telegram": "dist/cli.js"
9
+ },
10
+ "exports": {
11
+ ".": "./dist/index.js",
12
+ "./cli": "./dist/cli.js"
13
+ },
14
+ "files": [
15
+ "dist",
16
+ "README.md",
17
+ "LICENSE",
18
+ ".env.example"
19
+ ],
20
+ "engines": {
21
+ "node": ">=20"
22
+ },
23
+ "scripts": {
24
+ "build": "tsc",
25
+ "start": "node dist/index.js",
26
+ "dev": "npm run build && npm start",
27
+ "release:prepare": "node scripts/release-prepare.mjs",
28
+ "release:rc": "node scripts/release-prepare.mjs rc",
29
+ "release:notes:preview": "node scripts/release-notes-preview.mjs",
30
+ "lint": "eslint src --ext .ts --max-warnings=0",
31
+ "format": "prettier --write \"src/**/*.ts\"",
32
+ "test": "vitest run",
33
+ "test:coverage": "vitest run --coverage"
34
+ },
35
+ "repository": {
36
+ "type": "git",
37
+ "url": "git+https://github.com/zardusai-cyber/zardbot-telegram.git"
38
+ },
39
+ "keywords": [
40
+ "zardbot",
41
+ "opencode",
42
+ "telegram",
43
+ "telegram-bot",
44
+ "grammy",
45
+ "tts",
46
+ "stt",
47
+ "developer-tools",
48
+ "cli"
49
+ ],
50
+ "author": "ZardBot Team",
51
+ "license": "MIT",
52
+ "bugs": {
53
+ "url": "https://github.com/ai-joe-git/zardbot-telegram/issues"
54
+ },
55
+ "homepage": "https://github.com/ai-joe-git/zardbot-telegram#readme",
56
+ "dependencies": {
57
+ "@grammyjs/menu": "^1.3.1",
58
+ "@opencode-ai/sdk": "^1.1.21",
59
+ "better-sqlite3": "^12.6.2",
60
+ "dotenv": "^17.2.3",
61
+ "grammy": "^1.39.2",
62
+ "https-proxy-agent": "^7.0.6",
63
+ "socks-proxy-agent": "^8.0.5",
64
+ "telegram-markdown-v2": "^0.0.4"
65
+ },
66
+ "devDependencies": {
67
+ "@types/better-sqlite3": "^7.6.13",
68
+ "@types/node": "^25.0.8",
69
+ "@typescript-eslint/eslint-plugin": "^8.53.0",
70
+ "@typescript-eslint/parser": "^8.53.0",
71
+ "@vitest/coverage-v8": "^3.2.4",
72
+ "eslint": "^8.57.1",
73
+ "eslint-config-prettier": "^10.1.8",
74
+ "prettier": "^3.8.0",
75
+ "tsx": "^4.21.0",
76
+ "typescript": "^5.9.3",
77
+ "vitest": "^3.2.4"
78
+ }
79
+ }