opencode-voice-plugin 0.0.0-stage → 0.1.1
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/LICENSE +21 -0
- package/README.md +398 -2
- package/README.pt-BR.md +389 -0
- package/package.json +72 -4
- package/src/capture.ts +253 -0
- package/src/ffmpeg.ts +157 -0
- package/src/index.ts +11 -0
- package/src/settings.ts +162 -0
- package/src/transcribe.ts +111 -0
- package/src/tui.tsx +391 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 kabrau
|
|
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,3 +1,399 @@
|
|
|
1
|
-
#
|
|
1
|
+
# opencode-voice-plugin
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**English** · [Português (BR)](README.pt-BR.md)
|
|
4
|
+
|
|
5
|
+
Voice dictation for **OpenCode V2**: a microphone icon (`○`) in the prompt footer. Click it (or press
|
|
6
|
+
`ctrl+y`), speak, and the transcribed text becomes a message in the session.
|
|
7
|
+
|
|
8
|
+
Transcription uses **OpenRouter** (`POST /api/v1/audio/transcriptions`), and the API key is reused
|
|
9
|
+
from OpenCode itself when you already have OpenRouter connected — no environment variable, no
|
|
10
|
+
duplicating secrets. It works on **Windows, Linux and macOS** from the same code, and installs
|
|
11
|
+
**machine-wide** (not per project).
|
|
12
|
+
|
|
13
|
+
[Repository](https://github.com/kabrau/opencode-voice-plugin) ·
|
|
14
|
+
[Issues](https://github.com/kabrau/opencode-voice-plugin/issues) ·
|
|
15
|
+
[Design](DESIGN.md) · MIT
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## How it works
|
|
20
|
+
|
|
21
|
+
1. `○` in the prompt footer (visible inside a session).
|
|
22
|
+
2. Click it, press `ctrl+y`, or type `/voice` → recording starts (`●` + seconds).
|
|
23
|
+
3. Click again (or wait for `maxSeconds`) → it stops and transcribes (`…`).
|
|
24
|
+
4. The **Dictation** dialog opens with the text — edit if you want and confirm → the message is sent.
|
|
25
|
+
|
|
26
|
+
It also works on the **home screen**: since there is no session yet, `ctrl+y` **creates one right
|
|
27
|
+
away** (before recording) and sends the text there — the same as typing and pressing Enter. Turn it
|
|
28
|
+
off with `"allowFromHome": false` if you prefer the microphone to work only inside a session.
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## Requirements
|
|
33
|
+
|
|
34
|
+
- OpenCode **V2** (`opencode --version`).
|
|
35
|
+
- **ffmpeg** on the system:
|
|
36
|
+
- Windows: `choco install ffmpeg`
|
|
37
|
+
- Debian/Ubuntu: `sudo apt install ffmpeg pulseaudio-utils`
|
|
38
|
+
- macOS: `brew install ffmpeg`
|
|
39
|
+
- An **OpenRouter** API key (see [Cost](#cost) and [API key](#api-key)).
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## Install (via npm — recommended)
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
opencode plugin add opencode-voice-plugin
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
That's it — no cloning, no copying files. To update or remove:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
opencode plugin update opencode-voice-plugin
|
|
53
|
+
opencode plugin remove opencode-voice-plugin
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
> To **configure options** (icons, keybind, device, etc.), see
|
|
57
|
+
> [Where to declare options](#where-to-declare-options) — that is a detail `plugin add` alone
|
|
58
|
+
> does not cover.
|
|
59
|
+
|
|
60
|
+
### Without npm (straight from GitHub)
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
opencode plugin add github:kabrau/opencode-voice-plugin
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## Manual install (machine-wide)
|
|
69
|
+
|
|
70
|
+
If you prefer a local copy, OpenCode discovers plugins in the global config directory:
|
|
71
|
+
|
|
72
|
+
```
|
|
73
|
+
<global-config>/plugins/<name>/index.ts # server entrypoint
|
|
74
|
+
<global-config>/plugins/<name>/tui.tsx # TUI entrypoint (the plugin itself)
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
On Windows the global config lives in `C:\Users\<you>\.config\opencode`. On Linux/macOS it is
|
|
78
|
+
`~/.config/opencode` (or `$XDG_CONFIG_HOME/opencode`).
|
|
79
|
+
|
|
80
|
+
### Windows (PowerShell)
|
|
81
|
+
|
|
82
|
+
```powershell
|
|
83
|
+
git clone https://github.com/kabrau/opencode-voice-plugin.git
|
|
84
|
+
$dest = "$env:USERPROFILE\.config\opencode\plugins\voice"
|
|
85
|
+
New-Item -ItemType Directory -Force -Path $dest | Out-Null
|
|
86
|
+
Copy-Item .\opencode-voice-plugin\src\* $dest
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### Linux / macOS
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
git clone https://github.com/kabrau/opencode-voice-plugin.git
|
|
93
|
+
dest="${XDG_CONFIG_HOME:-$HOME/.config}/opencode/plugins/voice"
|
|
94
|
+
mkdir -p "$dest"
|
|
95
|
+
cp opencode-voice-plugin/src/* "$dest/"
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Then **restart `opencode`**.
|
|
99
|
+
|
|
100
|
+
### Alternative install (pointing at the clone)
|
|
101
|
+
|
|
102
|
+
Point the global `cli.json` at the cloned repository. This keeps the plugin updated via `git pull`
|
|
103
|
+
and is the way to pass options, since the discovery folder has nowhere to declare them:
|
|
104
|
+
|
|
105
|
+
```json
|
|
106
|
+
{
|
|
107
|
+
"plugins": [
|
|
108
|
+
{
|
|
109
|
+
"package": "file:///path/to/opencode-voice-plugin",
|
|
110
|
+
"options": {
|
|
111
|
+
"language": "en",
|
|
112
|
+
"keybind": "ctrl+y"
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
]
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
> The path can be an absolute path or a `file://` URL. **Add** it to the existing `plugins` array in
|
|
120
|
+
> `~/.config/opencode/cli.json`, without deleting the rest of the file.
|
|
121
|
+
|
|
122
|
+
---
|
|
123
|
+
|
|
124
|
+
## API key
|
|
125
|
+
|
|
126
|
+
The plugin resolves the key in this order:
|
|
127
|
+
|
|
128
|
+
1. **Environment variable** — `OPENROUTER_API_KEY` (or whatever `apiKeyEnv` says), if present.
|
|
129
|
+
2. **OpenCode's own credential store** — if you already connected OpenRouter, the plugin uses that
|
|
130
|
+
key automatically. **Nothing to configure.**
|
|
131
|
+
|
|
132
|
+
In other words: if OpenRouter already shows up as connected, you **do not** need to create any
|
|
133
|
+
variable. Check with:
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
opencode auth list # should list "OpenRouter ... stored"
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
If you prefer the env var (or do not have OpenRouter connected):
|
|
140
|
+
|
|
141
|
+
```powershell
|
|
142
|
+
setx OPENROUTER_API_KEY "sk-or-..." # Windows; reopen the terminal afterwards
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
export OPENROUTER_API_KEY="sk-or-..." # Linux/macOS (~/.bashrc / ~/.zshrc)
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Create a key at <https://openrouter.ai/settings/keys>. Either way the key is **never** logged,
|
|
150
|
+
copied or stored by the plugin — and reading the store is read-only, from the file OpenCode itself
|
|
151
|
+
maintains (`<data>/opencode/auth.json`).
|
|
152
|
+
|
|
153
|
+
For an env-var-only policy, use `"apiKeyFromAuth": false`; to read from another integration (for
|
|
154
|
+
example the Console one), change `authIntegration`.
|
|
155
|
+
|
|
156
|
+
---
|
|
157
|
+
|
|
158
|
+
## Configuration
|
|
159
|
+
|
|
160
|
+
### Where to declare options
|
|
161
|
+
|
|
162
|
+
Options are read from **`cli.json`** (global config), **not** from `opencode.jsonc`. This holds even
|
|
163
|
+
if you installed by copying into `plugins/voice/`: add the entry below to the `plugins` array in
|
|
164
|
+
`~/.config/opencode/cli.json`, keeping the rest of the file intact.
|
|
165
|
+
|
|
166
|
+
```json
|
|
167
|
+
{
|
|
168
|
+
"plugins": [
|
|
169
|
+
{
|
|
170
|
+
"package": "C:\\Users\\<you>\\.config\\opencode\\plugins\\voice",
|
|
171
|
+
"options": {
|
|
172
|
+
"autoSubmit": true
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
]
|
|
176
|
+
}
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
> Verified in both directions: with this entry `context.options` receives the object; declaring the
|
|
180
|
+
> same options in the `plugins` array of **`opencode.jsonc`** makes the TUI component receive `{}`.
|
|
181
|
+
> There is no duplication from the plugin also being discovered in the global folder.
|
|
182
|
+
|
|
183
|
+
Every option is optional.
|
|
184
|
+
|
|
185
|
+
| Option | Default | Description |
|
|
186
|
+
|---|---|---|
|
|
187
|
+
| `apiKeyEnv` | `"OPENROUTER_API_KEY"` | Name of the environment variable holding the key. Takes priority over the store. |
|
|
188
|
+
| `apiKeyFromAuth` | `true` | When the env var is absent, use the key already stored by OpenCode. |
|
|
189
|
+
| `authIntegration` | `"openrouter"` | OpenCode integration to read the key from. |
|
|
190
|
+
| `model` | `"openai/whisper-large-v3"` | OpenRouter STT model. |
|
|
191
|
+
| `language` | `"auto"` | ISO-639-1 code, or `"auto"` to auto-detect. |
|
|
192
|
+
| `baseUrl` | `"https://openrouter.ai/api/v1"` | Base URL of a provider compatible with `/audio/transcriptions`. |
|
|
193
|
+
| `device` | `null` | Microphone. `null` = auto-detect. Per-OS formats below. |
|
|
194
|
+
| `ffmpegPath` | `null` | Path to ffmpeg. `null` = look in PATH and known locations. |
|
|
195
|
+
| `keybind` | `"ctrl+y"` | Dictation shortcut. Use `"<leader>v"` if you prefer (`<leader>` = `ctrl+x`). |
|
|
196
|
+
| `autoSubmit` | `false` | `true` sends right away, skipping the review dialog. |
|
|
197
|
+
| `allowFromHome` | `true` | Allow dictating on the home screen, creating the session on the spot. |
|
|
198
|
+
| `maxSeconds` | `55` | Automatic recording cutoff. Safety ceiling is `120`. |
|
|
199
|
+
| `keepAudio` | `false` | `true` keeps the temporary WAV (debugging). |
|
|
200
|
+
| `icons` | `{ idle: "○", recording: "●", transcribing: "…" }` | Icon glyphs. Override if your font supports emoji. |
|
|
201
|
+
|
|
202
|
+
> **Why the default glyphs are `○ ● …` and not 🎤:** emoji outside the BMP (such as `🎤`) are missing
|
|
203
|
+
> from many console fonts and render as `?`; worse, their width is ambiguous, which **misaligns the
|
|
204
|
+
> clickable area** (clicks are routed by coordinate hit-testing). The default glyphs are BMP,
|
|
205
|
+
> single-width, and present in Consolas/Cascadia/DejaVu. If your terminal renders emoji correctly,
|
|
206
|
+
> use for example:
|
|
207
|
+
>
|
|
208
|
+
> ```json
|
|
209
|
+
> { "options": { "icons": { "idle": "🎤", "recording": "🔴", "transcribing": "⏳" } } }
|
|
210
|
+
> ```
|
|
211
|
+
|
|
212
|
+
`device` formats:
|
|
213
|
+
|
|
214
|
+
| OS | Format | Example |
|
|
215
|
+
|---|---|---|
|
|
216
|
+
| Windows | exact DirectShow name | `"Microphone (C922 Pro Stream Webcam)"` |
|
|
217
|
+
| Linux | Pulse/PipeWire source name | `"default"`, `"@DEFAULT_SOURCE@"`, `"alsa_input.pci-0000_00_1f.3.analog-stereo"` |
|
|
218
|
+
| Linux (ALSA) | `hw:CARD,DEV` | `"hw:1,0"` |
|
|
219
|
+
| macOS | numeric index | `"0"` |
|
|
220
|
+
|
|
221
|
+
---
|
|
222
|
+
|
|
223
|
+
## Usage
|
|
224
|
+
|
|
225
|
+
| Action | How |
|
|
226
|
+
|---|---|
|
|
227
|
+
| Start / stop | click the icon, or `ctrl+y`, or `/voice` |
|
|
228
|
+
| Choose microphone | command palette → **"Voice: choose microphone"** (the choice is remembered) |
|
|
229
|
+
| Send without reviewing | `"autoSubmit": true` in the options |
|
|
230
|
+
|
|
231
|
+
Icon states: `○` idle · `● Ns` recording · `…` transcribing. Errors always become a toast and never
|
|
232
|
+
freeze the TUI.
|
|
233
|
+
|
|
234
|
+
---
|
|
235
|
+
|
|
236
|
+
## Cost
|
|
237
|
+
|
|
238
|
+
`openai/whisper-large-v3` is billed **per token** (the audio is tokenized), and the price varies by
|
|
239
|
+
provider. Values read from the OpenRouter API in Oct/2026 (per 1M prompt tokens):
|
|
240
|
+
|
|
241
|
+
| Provider | Price (prompt) |
|
|
242
|
+
|---|---|
|
|
243
|
+
| DeepInfra | US$ 7.50 / 1M tokens |
|
|
244
|
+
| Together | US$ 25.00 / 1M tokens |
|
|
245
|
+
| Groq | US$ 30.83 / 1M tokens |
|
|
246
|
+
|
|
247
|
+
OpenRouter routes to an available provider automatically — the real cost of each request comes back
|
|
248
|
+
in the `usage.cost` field of the response. As a rough order of magnitude, OpenRouter's own
|
|
249
|
+
documentation shows a 9.2 s transcription costing **US$ 0.000508**.
|
|
250
|
+
|
|
251
|
+
**Check the current numbers** on the model page: <https://openrouter.ai/openai/whisper-large-v3>
|
|
252
|
+
(and the spend dashboard at <https://openrouter.ai/activity>). Do not trust the figures above long
|
|
253
|
+
term.
|
|
254
|
+
|
|
255
|
+
To switch models, just change `model` in the options (browse models with
|
|
256
|
+
`output_modalities=transcription` at <https://openrouter.ai/models?output_modalities=transcription>).
|
|
257
|
+
|
|
258
|
+
---
|
|
259
|
+
|
|
260
|
+
## Troubleshooting
|
|
261
|
+
|
|
262
|
+
**The plugin does not show up / did not load**
|
|
263
|
+
|
|
264
|
+
Check the log (it exists on Windows too):
|
|
265
|
+
|
|
266
|
+
```
|
|
267
|
+
C:\Users\<you>\.local\share\opencode\log\opencode.log # Windows
|
|
268
|
+
~/.local/share/opencode/log/opencode.log # Linux/macOS
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
Look for `role=cli`. Confirm the files are in `<global-config>/plugins/voice/` and restart
|
|
272
|
+
`opencode`.
|
|
273
|
+
|
|
274
|
+
**"ffmpeg not found" toast**
|
|
275
|
+
|
|
276
|
+
Install ffmpeg (commands above) or point `ffmpegPath` at it explicitly. On Windows the plugin
|
|
277
|
+
automatically descends from the Chocolatey *shim*
|
|
278
|
+
(`C:\ProgramData\chocolatey\bin\ffmpeg.exe`) to the real binary at
|
|
279
|
+
`C:\ProgramData\chocolatey\lib\ffmpeg\tools\ffmpeg\bin\ffmpeg.exe`, because the shim may not forward
|
|
280
|
+
the stop command to the recording process.
|
|
281
|
+
|
|
282
|
+
**"Missing API key" toast**
|
|
283
|
+
|
|
284
|
+
The variable is not visible to the process. On Windows `setx` only applies to **new** terminals —
|
|
285
|
+
close and reopen. Check with `echo $env:OPENROUTER_API_KEY` (PowerShell) or `echo $OPENROUTER_API_KEY`.
|
|
286
|
+
|
|
287
|
+
**Records silence / nothing is transcribed**
|
|
288
|
+
|
|
289
|
+
The default device is probably a virtual cable. Run **"Voice: choose microphone"** and pick the real
|
|
290
|
+
microphone. The heuristic already avoids names like `CABLE Output`, `Stereo Mix`, `loopback`,
|
|
291
|
+
`Virtual` and `.monitor`, but not every virtual device gives itself away by name.
|
|
292
|
+
|
|
293
|
+
**Linux: no microphone / PulseAudio or ALSA error**
|
|
294
|
+
|
|
295
|
+
Install `pulseaudio-utils` (it provides `pactl`, used to list microphones). The plugin uses
|
|
296
|
+
`-f pulse` when it finds `pactl`, otherwise it falls back to `-f alsa` with `default`. To list
|
|
297
|
+
sources manually:
|
|
298
|
+
|
|
299
|
+
```bash
|
|
300
|
+
pactl list short sources # ignore lines ending in .monitor
|
|
301
|
+
arecord -l # ALSA alternative
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
**WSL2**
|
|
305
|
+
|
|
306
|
+
Audio arrives through WSLg as a Pulse source (usually `RDPSource`). If `pactl info` fails, run
|
|
307
|
+
`wsl --shutdown` on Windows, reopen, and check the microphone permission under
|
|
308
|
+
*Settings → Privacy → Microphone*. As a last resort, set `device` explicitly.
|
|
309
|
+
|
|
310
|
+
**The icon shows as `?`** (or another odd character)
|
|
311
|
+
|
|
312
|
+
Your console font lacks the configured glyph. The default (`○ ● …`) works almost everywhere. If you
|
|
313
|
+
switched to emoji through `icons`, go back to the default or use a font with emoji support.
|
|
314
|
+
|
|
315
|
+
**I cannot click the icon**
|
|
316
|
+
|
|
317
|
+
1. Make sure the terminal sends mouse events: on **mintty/Git Bash**, check *Options → Mouse*
|
|
318
|
+
(it should be enabled). OpenTUI turns on mouse tracking at startup.
|
|
319
|
+
2. Prefer the default glyphs (`○ ● …`). Emoji have ambiguous width and shift the clickable area.
|
|
320
|
+
3. Click **right on** the glyph (there is one space of padding on each side).
|
|
321
|
+
4. Meanwhile the shortcut does exactly the same: `ctrl+y` (or `<leader>v`: `ctrl+x`, then `v`), or
|
|
322
|
+
`ctrl+p` → **"Voice: dictate"**, or `/voice`.
|
|
323
|
+
|
|
324
|
+
**Transcription fails or is slow**
|
|
325
|
+
|
|
326
|
+
The provider times out at around **60 s per request**. That is why `maxSeconds` defaults to 55. If
|
|
327
|
+
you raise it, the dialog warns that recordings over 1 minute may fail.
|
|
328
|
+
|
|
329
|
+
**The beginning of speech is lost (Windows)**
|
|
330
|
+
|
|
331
|
+
DirectShow takes about 1 s to open the device. Wait for `●` to appear, then start speaking.
|
|
332
|
+
|
|
333
|
+
---
|
|
334
|
+
|
|
335
|
+
## Development
|
|
336
|
+
|
|
337
|
+
```bash
|
|
338
|
+
npm install
|
|
339
|
+
npm run typecheck
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
No runtime dependencies — only Node built-ins (`child_process`, `fs`, `os`, `path`, `buffer`), which
|
|
343
|
+
work under the Bun runtime embedded in OpenCode.
|
|
344
|
+
|
|
345
|
+
All platform-specific logic is isolated in `src/capture.ts`. The other files contain no
|
|
346
|
+
`if (platform)`.
|
|
347
|
+
|
|
348
|
+
> Note: `@opencode/plugin` declares `engines: node >= 26.4.0`. On Node 24 `npm install` prints an
|
|
349
|
+
> `EBADENGINE` warning, harmless for typechecking.
|
|
350
|
+
|
|
351
|
+
### Layout
|
|
352
|
+
|
|
353
|
+
```
|
|
354
|
+
src/index.ts server entrypoint (empty; required by the discovery layout)
|
|
355
|
+
src/tui.tsx the plugin: icon slot, shortcuts, state machine, delivery
|
|
356
|
+
src/capture.ts the ONLY module with platform-specific logic
|
|
357
|
+
src/ffmpeg.ts binary resolution, process execution, clean stop
|
|
358
|
+
src/transcribe.ts OpenRouter /audio/transcriptions
|
|
359
|
+
src/settings.ts options + env + defaults
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
Design decisions and the API research (what was verified and what is **not** possible) are in
|
|
363
|
+
[DESIGN.md](DESIGN.md) (currently in Portuguese).
|
|
364
|
+
|
|
365
|
+
---
|
|
366
|
+
|
|
367
|
+
## Known limitations
|
|
368
|
+
|
|
369
|
+
- **One-shot dictation:** one recording = one message. There is no continuous/streaming dictation and
|
|
370
|
+
no text-to-speech.
|
|
371
|
+
- **No direct writing into the prompt input:** V2 exposes no way — to any plugin — to insert text into
|
|
372
|
+
the composer. This was verified three ways: (1) the `UI` API has nothing for prompt/composer
|
|
373
|
+
(checked in 2.0.29 and 2.0.32); (2) the server OpenAPI has no `/tui/*` route; (3) the internal
|
|
374
|
+
`tui.prompt.append` event is consumed by the TUI (`insertText` on the composer) but **nothing
|
|
375
|
+
publishes it** — there is no HTTP endpoint and no RPC surface. V1 had `client.tui.appendPrompt`,
|
|
376
|
+
removed in V2. That is why the text goes through the review dialog (pre-filled and editable), and
|
|
377
|
+
the request to bring it back is at
|
|
378
|
+
[anomalyco/opencode#51209](https://github.com/anomalyco/opencode/issues/51209)
|
|
379
|
+
(see also [#54681](https://github.com/anomalyco/opencode/issues/54681)). Once the API exists,
|
|
380
|
+
swapping the dialog for "insert into the prompt" is a small, local change.
|
|
381
|
+
- **Cloud transcription:** the audio is sent to OpenRouter and to the selected provider. Do not use it
|
|
382
|
+
with sensitive information if that is a concern.
|
|
383
|
+
- **Linux and macOS** were validated through the commands and the logic, but the end-to-end test was
|
|
384
|
+
done on Windows only. A real Linux test is still pending.
|
|
385
|
+
|
|
386
|
+
---
|
|
387
|
+
|
|
388
|
+
## Security
|
|
389
|
+
|
|
390
|
+
- The key comes from the environment variable or, if absent, from OpenCode's own store (read-only).
|
|
391
|
+
It is never logged, copied or persisted by the plugin.
|
|
392
|
+
- The WAV is written to `os.tmpdir()` and deleted after transcription (unless `keepAudio: true`).
|
|
393
|
+
- A recording in progress is stopped when the plugin is unloaded.
|
|
394
|
+
|
|
395
|
+
---
|
|
396
|
+
|
|
397
|
+
## License
|
|
398
|
+
|
|
399
|
+
MIT — see [LICENSE](LICENSE).
|