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/README.pt-BR.md
ADDED
|
@@ -0,0 +1,389 @@
|
|
|
1
|
+
# opencode-voice-plugin
|
|
2
|
+
|
|
3
|
+
[English](README.md) · **Português (BR)**
|
|
4
|
+
|
|
5
|
+
Ditado por voz para o **OpenCode V2**: um ícone de microfone (`○`) no rodapé do prompt. Clique nele
|
|
6
|
+
(ou use `ctrl+y`), fale, e o texto transcrito vira uma mensagem da sessão.
|
|
7
|
+
|
|
8
|
+
A transcrição usa o **OpenRouter** (`POST /api/v1/audio/transcriptions`) e a chave é reaproveitada do
|
|
9
|
+
próprio OpenCode quando você já tem o OpenRouter conectado — sem variável de ambiente e sem duplicar
|
|
10
|
+
segredo. Funciona em **Windows, Linux e macOS** a partir do mesmo código, e é instalado **na máquina**
|
|
11
|
+
(não por projeto).
|
|
12
|
+
|
|
13
|
+
[Repositório](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
|
+
## Como funciona
|
|
20
|
+
|
|
21
|
+
1. `○` no rodapé do prompt (aparece dentro de uma sessão).
|
|
22
|
+
2. Clique, ou `ctrl+y`, ou digite `/voice` → começa a gravar (`●` + segundos).
|
|
23
|
+
3. Clique de novo (ou espere `maxSeconds`) → para, transcreve (`…`).
|
|
24
|
+
4. Abre o dialog **Ditado** com o texto — edite se quiser e confirme → a mensagem é enviada na sessão.
|
|
25
|
+
|
|
26
|
+
Na **tela inicial** também funciona: como ali ainda não existe sessão, o `ctrl+y` **cria a sessão na
|
|
27
|
+
hora** (antes de gravar) e o texto é enviado nela — o mesmo que digitar e dar Enter. Desligue com
|
|
28
|
+
`"allowFromHome": false` se preferir que o microfone só aja dentro de uma sessão.
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## Requisitos
|
|
33
|
+
|
|
34
|
+
- OpenCode **V2** (`opencode --version`).
|
|
35
|
+
- **ffmpeg** no sistema:
|
|
36
|
+
- Windows: `choco install ffmpeg`
|
|
37
|
+
- Debian/Ubuntu: `sudo apt install ffmpeg pulseaudio-utils`
|
|
38
|
+
- macOS: `brew install ffmpeg`
|
|
39
|
+
- Uma chave de API do **OpenRouter** (veja [Custo](#custo) e [Chave da API](#chave-da-api)).
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## Instalação (via npm — recomendado)
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
opencode plugin add opencode-voice-plugin
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Pronto — sem clonar nem copiar arquivo. Atualizar e remover:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
opencode plugin update opencode-voice-plugin
|
|
53
|
+
opencode plugin remove opencode-voice-plugin
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
> Para **configurar options** (ícones, atalho, dispositivo, etc.), veja
|
|
57
|
+
> [Onde declarar as options](#onde-declarar-as-options) — é um detalhe que o `plugin add`
|
|
58
|
+
> sozinho não cobre.
|
|
59
|
+
|
|
60
|
+
### Sem npm (direto do GitHub)
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
opencode plugin add github:kabrau/opencode-voice-plugin
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## Instalação manual (na máquina)
|
|
69
|
+
|
|
70
|
+
Se preferir cópia local, o OpenCode descobre plugins no diretório global de configuração:
|
|
71
|
+
|
|
72
|
+
```
|
|
73
|
+
<config-global>/plugins/<nome>/index.ts # entrypoint de servidor
|
|
74
|
+
<config-global>/plugins/<nome>/tui.tsx # entrypoint da TUI (o plugin)
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
No Windows o config global fica em `C:\Users\<você>\.config\opencode`. No Linux/macOS, em
|
|
78
|
+
`~/.config/opencode` (ou `$XDG_CONFIG_HOME/opencode`).
|
|
79
|
+
|
|
80
|
+
### Windows (PowerShell)
|
|
81
|
+
|
|
82
|
+
```powershell
|
|
83
|
+
git clone https://github.com/kabrau/opencode-voice-plugin.git
|
|
84
|
+
$destino = "$env:USERPROFILE\.config\opencode\plugins\voice"
|
|
85
|
+
New-Item -ItemType Directory -Force -Path $destino | Out-Null
|
|
86
|
+
Copy-Item .\opencode-voice-plugin\src\* $destino
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### Linux / macOS
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
git clone https://github.com/kabrau/opencode-voice-plugin.git
|
|
93
|
+
destino="${XDG_CONFIG_HOME:-$HOME/.config}/opencode/plugins/voice"
|
|
94
|
+
mkdir -p "$destino"
|
|
95
|
+
cp opencode-voice-plugin/src/* "$destino/"
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Depois, **reinicie o `opencode`**.
|
|
99
|
+
|
|
100
|
+
### Instalação alternativa (apontando para o clone)
|
|
101
|
+
|
|
102
|
+
Aponte o `cli.json` global para o repositório clonado. Isso mantém o plugin atualizado via `git pull`
|
|
103
|
+
e é o jeito de passar options, já que a pasta de descoberta não tem onde declará-las:
|
|
104
|
+
|
|
105
|
+
```json
|
|
106
|
+
{
|
|
107
|
+
"plugins": [
|
|
108
|
+
{
|
|
109
|
+
"package": "file:///caminho/para/opencode-voice-plugin",
|
|
110
|
+
"options": {
|
|
111
|
+
"language": "pt",
|
|
112
|
+
"keybind": "ctrl+y"
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
]
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
> O caminho pode ser um caminho absoluto ou uma URL `file://`. **Adicione** ao array `plugins`
|
|
120
|
+
> existente em `~/.config/opencode/cli.json`, sem apagar o resto do arquivo.
|
|
121
|
+
|
|
122
|
+
---
|
|
123
|
+
|
|
124
|
+
## Chave da API
|
|
125
|
+
|
|
126
|
+
O plugin resolve a chave nesta ordem:
|
|
127
|
+
|
|
128
|
+
1. **Variável de ambiente** — `OPENROUTER_API_KEY` (ou o nome em `apiKeyEnv`), se existir.
|
|
129
|
+
2. **Store do próprio OpenCode** — se você já conectou o OpenRouter, o plugin usa essa chave
|
|
130
|
+
automaticamente. **Nada a configurar.**
|
|
131
|
+
|
|
132
|
+
Ou seja: se o OpenRouter já aparece conectado, você **não precisa** criar variável nenhuma.
|
|
133
|
+
Confira com:
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
opencode auth list # deve listar "OpenRouter ... stored"
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Se preferir a env var (ou não tiver o OpenRouter conectado):
|
|
140
|
+
|
|
141
|
+
```powershell
|
|
142
|
+
setx OPENROUTER_API_KEY "sk-or-..." # Windows; reabra o terminal depois
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
export OPENROUTER_API_KEY="sk-or-..." # Linux/macOS (~/.bashrc / ~/.zshrc)
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Gere uma chave em <https://openrouter.ai/settings/keys>. Nos dois casos a chave **nunca** é
|
|
150
|
+
logada, copiada nem gravada pelo plugin — e a leitura do store é somente-leitura, do arquivo
|
|
151
|
+
que o próprio OpenCode mantém (`<data>/opencode/auth.json`).
|
|
152
|
+
|
|
153
|
+
Para uma postura "só env var", use `"apiKeyFromAuth": false` nas options; para ler de outra
|
|
154
|
+
integração (ex.: a do Console), ajuste `authIntegration`.
|
|
155
|
+
|
|
156
|
+
---
|
|
157
|
+
|
|
158
|
+
## Configuração
|
|
159
|
+
|
|
160
|
+
### Onde declarar as options
|
|
161
|
+
|
|
162
|
+
As options são lidas do **`cli.json`** (config global), **não** do `opencode.jsonc`. Isso vale
|
|
163
|
+
mesmo que você tenha instalado por cópia em `plugins/voice/`: adicione a entrada abaixo ao array
|
|
164
|
+
`plugins` do `~/.config/opencode/cli.json`, preservando o resto do arquivo.
|
|
165
|
+
|
|
166
|
+
```json
|
|
167
|
+
{
|
|
168
|
+
"plugins": [
|
|
169
|
+
{
|
|
170
|
+
"package": "C:\\Users\\<você>\\.config\\opencode\\plugins\\voice",
|
|
171
|
+
"options": {
|
|
172
|
+
"autoSubmit": true
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
]
|
|
176
|
+
}
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
> Verificado nas duas direções: com essa entrada, `context.options` recebe o objeto; declarando as
|
|
180
|
+
> mesmas options no array `plugins` do **`opencode.jsonc`**, o componente de TUI recebe `{}`.
|
|
181
|
+
> Não há duplicação do plugin por estar também descoberto na pasta global.
|
|
182
|
+
|
|
183
|
+
Todas as options são opcionais.
|
|
184
|
+
|
|
185
|
+
| Option | Default | Descrição |
|
|
186
|
+
|---|---|---|
|
|
187
|
+
| `apiKeyEnv` | `"OPENROUTER_API_KEY"` | Nome da variável de ambiente com a chave. Tem prioridade sobre o store. |
|
|
188
|
+
| `apiKeyFromAuth` | `true` | Se a env var não existir, usa a chave já guardada pelo OpenCode. |
|
|
189
|
+
| `authIntegration` | `"openrouter"` | Integração do OpenCode de onde ler a chave. |
|
|
190
|
+
| `model` | `"openai/whisper-large-v3"` | Modelo de STT do OpenRouter. |
|
|
191
|
+
| `language` | `"auto"` | Código ISO-639-1, ou `"auto"` para detecção automática. |
|
|
192
|
+
| `baseUrl` | `"https://openrouter.ai/api/v1"` | Base do provedor compatível com `/audio/transcriptions`. |
|
|
193
|
+
| `device` | `null` | Microfone. `null` = autodetectar. Formatos por SO abaixo. |
|
|
194
|
+
| `ffmpegPath` | `null` | Caminho do ffmpeg. `null` = procurar no PATH e em locais conhecidos. |
|
|
195
|
+
| `keybind` | `"ctrl+y"` | Atalho do ditado. Use `"<leader>v"` se preferir (`<leader>` = `ctrl+x`). |
|
|
196
|
+
| `autoSubmit` | `false` | `true` envia direto, sem o dialog de revisão. |
|
|
197
|
+
| `allowFromHome` | `true` | Permite ditar na tela inicial, criando a sessão na hora. |
|
|
198
|
+
| `maxSeconds` | `55` | Corte automático da gravação. Teto de segurança em `120`. |
|
|
199
|
+
| `keepAudio` | `false` | `true` mantém o WAV temporário (debug). |
|
|
200
|
+
| `icons` | `{ idle: "○", recording: "●", transcribing: "…" }` | Glifos do ícone. Sobrescreva se sua fonte suportar emoji. |
|
|
201
|
+
|
|
202
|
+
> **Por que os glifos padrão são `○ ● …` e não 🎤:** emojis fora do BMP (como `🎤`) não
|
|
203
|
+
> existem na fonte de muitos consoles e aparecem como `?`; além disso a largura deles é
|
|
204
|
+
> ambígua, o que **desalinha a área clicável** (o clique roteia por hit-test de coordenadas).
|
|
205
|
+
> Os glifos padrão são do BMP, largura 1 e existem em Consolas/Cascadia/DejaVu. Se o seu
|
|
206
|
+
> terminal renderiza emoji corretamente, use por exemplo:
|
|
207
|
+
>
|
|
208
|
+
> ```json
|
|
209
|
+
> { "options": { "icons": { "idle": "🎤", "recording": "🔴", "transcribing": "⏳" } } }
|
|
210
|
+
> ```
|
|
211
|
+
|
|
212
|
+
Formatos de `device`:
|
|
213
|
+
|
|
214
|
+
| SO | Formato | Exemplo |
|
|
215
|
+
|---|---|---|
|
|
216
|
+
| Windows | nome exato do DirectShow | `"Microphone (C922 Pro Stream Webcam)"` |
|
|
217
|
+
| Linux | nome do source Pulse/PipeWire | `"default"`, `"@DEFAULT_SOURCE@"`, `"alsa_input.pci-0000_00_1f.3.analog-stereo"` |
|
|
218
|
+
| Linux (ALSA) | `hw:CARD,DEV` | `"hw:1,0"` |
|
|
219
|
+
| macOS | índice numérico | `"0"` |
|
|
220
|
+
|
|
221
|
+
---
|
|
222
|
+
|
|
223
|
+
## Uso
|
|
224
|
+
|
|
225
|
+
| Ação | Como |
|
|
226
|
+
|---|---|
|
|
227
|
+
| Iniciar / parar | clique no ícone, ou `ctrl+y`, ou `/voice` |
|
|
228
|
+
| Escolher microfone | command palette → **"Voice: choose microphone"** (a escolha fica salva) |
|
|
229
|
+
| Enviar sem revisar | `"autoSubmit": true` nas options |
|
|
230
|
+
|
|
231
|
+
Estados do ícone: `○` ocioso · `● Ns` gravando · `…` transcrevendo. Erros sempre viram um toast,
|
|
232
|
+
nunca travam a TUI.
|
|
233
|
+
|
|
234
|
+
---
|
|
235
|
+
|
|
236
|
+
## Custo
|
|
237
|
+
|
|
238
|
+
O `openai/whisper-large-v3` é cobrado **por token** (o áudio é tokenizado), e o preço varia por
|
|
239
|
+
provedor. Valores consultados na API do OpenRouter em out/2026 (por 1M de tokens de prompt):
|
|
240
|
+
|
|
241
|
+
| Provedor | Preço (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
|
+
O OpenRouter roteia automaticamente para um provedor disponível — o custo real de cada requisição
|
|
248
|
+
vem no campo `usage.cost` da resposta. Como ordem de grandeza, o exemplo da própria documentação do
|
|
249
|
+
OpenRouter mostra uma transcrição de 9,2 s custando **US$ 0,000508**.
|
|
250
|
+
|
|
251
|
+
**Confira os valores atuais** na página do modelo: <https://openrouter.ai/openai/whisper-large-v3>
|
|
252
|
+
(e o painel de gastos em <https://openrouter.ai/activity>). Não confie nos números acima a longo prazo.
|
|
253
|
+
|
|
254
|
+
Para trocar de modelo, basta mudar `model` nas options (veja os modelos com
|
|
255
|
+
`output_modalities=transcription` em <https://openrouter.ai/models?output_modalities=transcription>).
|
|
256
|
+
|
|
257
|
+
---
|
|
258
|
+
|
|
259
|
+
## Troubleshooting
|
|
260
|
+
|
|
261
|
+
**O plugin não aparece / não carregou**
|
|
262
|
+
Verifique o log (existe mesmo no Windows):
|
|
263
|
+
|
|
264
|
+
```
|
|
265
|
+
C:\Users\<você>\.local\share\opencode\log\opencode.log # Windows
|
|
266
|
+
~/.local/share/opencode/log/opencode.log # Linux/macOS
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
Procure por `role=cli`. Confirme que os arquivos estão em `<config-global>/plugins/voice/` e
|
|
270
|
+
reinicie o `opencode`.
|
|
271
|
+
|
|
272
|
+
**"ffmpeg não encontrado" (toast)**
|
|
273
|
+
Instale o ffmpeg (comandos acima) ou aponte `ffmpegPath` explicitamente. No Windows, o plugin já
|
|
274
|
+
desce automaticamente do *shim* do Chocolatey
|
|
275
|
+
(`C:\ProgramData\chocolatey\bin\ffmpeg.exe`) para o binário real em
|
|
276
|
+
`C:\ProgramData\chocolatey\lib\ffmpeg\tools\ffmpeg\bin\ffmpeg.exe`, porque o shim pode não repassar
|
|
277
|
+
o comando de parada ao processo de gravação.
|
|
278
|
+
|
|
279
|
+
**"Chave da API ausente" (toast)**
|
|
280
|
+
A variável não está visível para o processo. No Windows, `setx` só vale para **novos** terminais —
|
|
281
|
+
feche e reabra. Confira com `echo $env:OPENROUTER_API_KEY` (PowerShell) ou `echo $OPENROUTER_API_KEY`.
|
|
282
|
+
|
|
283
|
+
**Grava silêncio / nada é transcrito**
|
|
284
|
+
Provavelmente o dispositivo padrão é um cabo virtual. Rode **"Voice: choose microphone"** e selecione
|
|
285
|
+
o microfone de verdade. A heurística já evita nomes como `CABLE Output`, `Stereo Mix`, `loopback`,
|
|
286
|
+
`Virtual` e `.monitor`, mas nem todo dispositivo virtual se denuncia pelo nome.
|
|
287
|
+
|
|
288
|
+
**Linux: sem microfone / erro do PulseAudio ou ALSA**
|
|
289
|
+
Instale `pulseaudio-utils` (fornece o `pactl`, usado para listar os microfones). O plugin usa
|
|
290
|
+
`-f pulse` quando encontra o `pactl`, senão cai para `-f alsa` com `default`. Para listar os sources
|
|
291
|
+
manualmente:
|
|
292
|
+
|
|
293
|
+
```bash
|
|
294
|
+
pactl list short sources # ignore linhas terminando em .monitor
|
|
295
|
+
arecord -l # alternativa via ALSA
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
**WSL2**
|
|
299
|
+
O áudio chega pelo WSLg como um source do Pulse (normalmente `RDPSource`). Se `pactl info` falhar,
|
|
300
|
+
rode `wsl --shutdown` no Windows, reabra e verifique a permissão de microfone em
|
|
301
|
+
*Configurações → Privacidade → Microfone*. Em último caso, use `device` explícito.
|
|
302
|
+
|
|
303
|
+
**O ícone aparece como `?`** (ou outro caractere estranho)
|
|
304
|
+
Sua fonte de console não tem o glifo configurado. O padrão (`○ ● …`) funciona na maioria.
|
|
305
|
+
Se você trocou por emoji via `icons`, volte para o padrão ou use uma fonte com emoji.
|
|
306
|
+
|
|
307
|
+
**Não consigo clicar no ícone**
|
|
308
|
+
1. Confirme que o terminal envia eventos de mouse: no **mintty/Git Bash**, veja
|
|
309
|
+
*Options → Mouse* (deve estar habilitado). O OpenTUI liga o mouse tracking ao iniciar.
|
|
310
|
+
2. Prefira os glifos padrão (`○ ● …`). Emoji têm largura ambígua e deslocam a área clicável.
|
|
311
|
+
3. Clique **em cima** do glifo (há um espaço de folga de cada lado).
|
|
312
|
+
4. Enquanto isso, o atalho faz exatamente o mesmo: `ctrl+y` (ou `<leader>v`: `ctrl+x`, depois `v`), ou
|
|
313
|
+
`ctrl+p` → **"Voice: dictate"**, ou `/voice`.
|
|
314
|
+
|
|
315
|
+
**A transcrição falha ou demora**
|
|
316
|
+
O provedor estoura em cerca de **60 s por requisição**. Por isso `maxSeconds` é 55 por padrão. Se você
|
|
317
|
+
aumentar, o dialog avisa que gravações acima de 1 minuto podem falhar.
|
|
318
|
+
|
|
319
|
+
**O começo da fala se perde (Windows)**
|
|
320
|
+
O DirectShow leva cerca de 1 s para abrir o dispositivo. Espere o `●` aparecer e só então comece a
|
|
321
|
+
falar.
|
|
322
|
+
|
|
323
|
+
---
|
|
324
|
+
|
|
325
|
+
## Desenvolvimento
|
|
326
|
+
|
|
327
|
+
```bash
|
|
328
|
+
npm install
|
|
329
|
+
npm run typecheck
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
Sem dependências de runtime — apenas módulos nativos do Node (`child_process`, `fs`, `os`, `path`,
|
|
333
|
+
`buffer`), que funcionam sob o Bun embutido no OpenCode.
|
|
334
|
+
|
|
335
|
+
Toda a lógica por plataforma está isolada em `src/capture.ts`. Os demais arquivos não contêm
|
|
336
|
+
`if (platform)`.
|
|
337
|
+
|
|
338
|
+
> Observação: `@opencode/plugin` declara `engines: node >= 26.4.0`. No Node 24 o `npm install` emite
|
|
339
|
+
> um aviso `EBADENGINE`, inofensivo para o typecheck.
|
|
340
|
+
|
|
341
|
+
### Estrutura
|
|
342
|
+
|
|
343
|
+
```
|
|
344
|
+
src/index.ts entrypoint de servidor (vazio, exigido pelo layout de descoberta)
|
|
345
|
+
src/tui.tsx plugin: slot do ícone, atalhos, máquina de estados, envio
|
|
346
|
+
src/capture.ts ÚNICO módulo com lógica por plataforma
|
|
347
|
+
src/ffmpeg.ts resolução do binário, execução, parada limpa
|
|
348
|
+
src/transcribe.ts OpenRouter /audio/transcriptions
|
|
349
|
+
src/settings.ts options + env + defaults
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
As decisões de design e a pesquisa de API (o que foi verificado e o que **não** é possível)
|
|
353
|
+
estão em [DESIGN.md](DESIGN.md).
|
|
354
|
+
|
|
355
|
+
---
|
|
356
|
+
|
|
357
|
+
## Limitações conhecidas
|
|
358
|
+
|
|
359
|
+
- **Ditado avulso:** uma gravação = uma mensagem. Não há ditado contínuo/streaming nem leitura das
|
|
360
|
+
respostas em voz alta (TTS).
|
|
361
|
+
- **Sem escrita direta no input do prompt:** a V2 não expõe — a nenhum plugin — um meio de inserir
|
|
362
|
+
texto no campo do prompt. Isso foi verificado em três frentes: (1) a API `UI` não tem nada de
|
|
363
|
+
prompt/composer (nem na 2.0.29 nem na 2.0.32); (2) o OpenAPI do servidor não tem rota `/tui/*`;
|
|
364
|
+
(3) o evento interno `tui.prompt.append` é consumido pela TUI (`insertText` no composer), mas
|
|
365
|
+
**nada o publica** — não há endpoint HTTP nem superfície RPC. A V1 tinha
|
|
366
|
+
`client.tui.appendPrompt`, removido na V2. Por isso o texto passa pelo dialog de revisão
|
|
367
|
+
(já preenchido e editável), e o pedido de reabrir isso está em
|
|
368
|
+
[anomalyco/opencode#51209](https://github.com/anomalyco/opencode/issues/51209)
|
|
369
|
+
(veja também [#54681](https://github.com/anomalyco/opencode/issues/54681)). Quando a API existir,
|
|
370
|
+
trocar o dialog por "inserir no prompt" é uma mudança pequena e localizada.
|
|
371
|
+
- **Transcrição na nuvem:** o áudio é enviado ao OpenRouter e ao provedor escolhido. Não use com
|
|
372
|
+
informação sensível se isso for um problema.
|
|
373
|
+
- **Linux e macOS** foram validados pelos comandos e pela lógica, mas o teste ponta a ponta foi feito
|
|
374
|
+
apenas no Windows. Teste real em Linux: pendente.
|
|
375
|
+
|
|
376
|
+
---
|
|
377
|
+
|
|
378
|
+
## Segurança
|
|
379
|
+
|
|
380
|
+
- A chave vem da variável de ambiente ou, se ela não existir, do store do próprio OpenCode
|
|
381
|
+
(leitura somente-leitura). Nunca é logada, copiada nem persistida pelo plugin.
|
|
382
|
+
- O WAV é gravado em `os.tmpdir()` e apagado depois de transcrever (salvo `keepAudio: true`).
|
|
383
|
+
- Uma gravação em andamento é encerrada quando o plugin é descarregado.
|
|
384
|
+
|
|
385
|
+
---
|
|
386
|
+
|
|
387
|
+
## Licença
|
|
388
|
+
|
|
389
|
+
MIT — veja [LICENSE](LICENSE).
|
package/package.json
CHANGED
|
@@ -1,6 +1,74 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-voice-plugin",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "Voice dictation for OpenCode V2: click a mic in the prompt footer, speak, and send the transcribed text.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"import": "./src/index.ts",
|
|
10
|
+
"types": "./src/index.ts"
|
|
11
|
+
},
|
|
12
|
+
"./tui": {
|
|
13
|
+
"import": "./src/tui.tsx",
|
|
14
|
+
"types": "./src/tui.tsx"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"src",
|
|
19
|
+
"README.md",
|
|
20
|
+
"README.pt-BR.md",
|
|
21
|
+
"LICENSE"
|
|
22
|
+
],
|
|
23
|
+
"repository": {
|
|
24
|
+
"type": "git",
|
|
25
|
+
"url": "git+https://github.com/kabrau/opencode-voice-plugin.git"
|
|
26
|
+
},
|
|
27
|
+
"homepage": "https://github.com/kabrau/opencode-voice-plugin#readme",
|
|
28
|
+
"bugs": {
|
|
29
|
+
"url": "https://github.com/kabrau/opencode-voice-plugin/issues"
|
|
30
|
+
},
|
|
31
|
+
"author": "kabrau",
|
|
32
|
+
"keywords": [
|
|
33
|
+
"opencode",
|
|
34
|
+
"plugin",
|
|
35
|
+
"voice",
|
|
36
|
+
"dictation",
|
|
37
|
+
"speech-to-text",
|
|
38
|
+
"stt",
|
|
39
|
+
"whisper",
|
|
40
|
+
"openrouter",
|
|
41
|
+
"tui"
|
|
42
|
+
],
|
|
43
|
+
"scripts": {
|
|
44
|
+
"typecheck": "tsc --noEmit",
|
|
45
|
+
"prepublishOnly": "npm run typecheck"
|
|
46
|
+
},
|
|
47
|
+
"dependencies": {
|
|
48
|
+
"@opencode/plugin": "2.0.29"
|
|
49
|
+
},
|
|
50
|
+
"peerDependencies": {
|
|
51
|
+
"@opentui/core": ">=0.5.17",
|
|
52
|
+
"@opentui/solid": ">=0.5.17",
|
|
53
|
+
"solid-js": ">=1.9.0"
|
|
54
|
+
},
|
|
55
|
+
"peerDependenciesMeta": {
|
|
56
|
+
"@opentui/core": {
|
|
57
|
+
"optional": true
|
|
58
|
+
},
|
|
59
|
+
"@opentui/solid": {
|
|
60
|
+
"optional": true
|
|
61
|
+
},
|
|
62
|
+
"solid-js": {
|
|
63
|
+
"optional": true
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
"devDependencies": {
|
|
67
|
+
"@opencode/theme": "2.0.29",
|
|
68
|
+
"@opentui/core": "0.6.6",
|
|
69
|
+
"@opentui/solid": "0.6.6",
|
|
70
|
+
"@types/node": "22.13.9",
|
|
71
|
+
"solid-js": "1.9.12",
|
|
72
|
+
"typescript": "5.9.3"
|
|
73
|
+
}
|
|
74
|
+
}
|