@hxnnxs/opencode-voice 0.1.5 → 0.1.6
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/CHANGELOG.md +6 -0
- package/bin/opencode-voice.js +7 -1
- package/lib/download.js +1 -1
- package/lib/engine.js +32 -23
- package/lib/engines.js +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project are documented here.
|
|
4
4
|
|
|
5
|
+
## 0.1.6 - 2026-06-17
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- Fixed Windows recorder startup to use resolved recorder command paths (including bundled ffmpeg) directly when spawning, preventing startup failures when ffmpeg is present only by absolute path.
|
|
10
|
+
|
|
5
11
|
## 0.1.5 - 2026-06-17
|
|
6
12
|
|
|
7
13
|
### Fixed
|
package/bin/opencode-voice.js
CHANGED
|
@@ -162,7 +162,13 @@ function printEngineRetry({ error, nextAttempt, attempts }) {
|
|
|
162
162
|
|
|
163
163
|
async function installCommand() {
|
|
164
164
|
const pluginArgs = args.filter((arg) => arg !== "--no-engine");
|
|
165
|
-
const
|
|
165
|
+
const spawnOptions = { stdio: "inherit" };
|
|
166
|
+
if (process.platform === "win32") spawnOptions.shell = true;
|
|
167
|
+
const result = spawnSync("opencode", ["plugin", packageName(), ...pluginArgs], spawnOptions);
|
|
168
|
+
if (result.error) {
|
|
169
|
+
console.error(`Failed to run opencode: ${result.error.message}`);
|
|
170
|
+
process.exit(1);
|
|
171
|
+
}
|
|
166
172
|
if ((result.status ?? 1) !== 0) process.exit(result.status ?? 1);
|
|
167
173
|
|
|
168
174
|
if (!hasFlag("--no-engine")) {
|
package/lib/download.js
CHANGED
|
@@ -70,7 +70,7 @@ function contentRangeStart(value) {
|
|
|
70
70
|
return match ? Number(match[1]) : null;
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
-
async function replaceFile(source, destination) {
|
|
73
|
+
export async function replaceFile(source, destination) {
|
|
74
74
|
await fs.promises.unlink(destination).catch(() => {});
|
|
75
75
|
|
|
76
76
|
for (let attempt = 1; attempt <= 5; attempt++) {
|
package/lib/engine.js
CHANGED
|
@@ -182,8 +182,11 @@ function parseWindowsMicrophones(stderr) {
|
|
|
182
182
|
}
|
|
183
183
|
|
|
184
184
|
export function listMicrophones() {
|
|
185
|
-
|
|
186
|
-
|
|
185
|
+
const arecordCommand = resolveCommand("arecord");
|
|
186
|
+
const ffmpegCommand = resolveCommand("ffmpeg");
|
|
187
|
+
|
|
188
|
+
if (process.platform === "linux" && arecordCommand) {
|
|
189
|
+
const result = spawnSync(arecordCommand, ["-L"], { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] });
|
|
187
190
|
const devices = result.stdout
|
|
188
191
|
.split(/\r?\n/)
|
|
189
192
|
.map((line) => line.trim())
|
|
@@ -191,8 +194,8 @@ export function listMicrophones() {
|
|
|
191
194
|
return ["default", ...devices.filter((item) => item !== "default")];
|
|
192
195
|
}
|
|
193
196
|
|
|
194
|
-
if (process.platform === "darwin" &&
|
|
195
|
-
const result = spawnSync(
|
|
197
|
+
if (process.platform === "darwin" && ffmpegCommand) {
|
|
198
|
+
const result = spawnSync(ffmpegCommand, ["-hide_banner", "-f", "avfoundation", "-list_devices", "true", "-i", ""], {
|
|
196
199
|
encoding: "utf8",
|
|
197
200
|
stdio: ["ignore", "ignore", "pipe"],
|
|
198
201
|
});
|
|
@@ -203,8 +206,8 @@ export function listMicrophones() {
|
|
|
203
206
|
.map((id) => `:${id}`);
|
|
204
207
|
}
|
|
205
208
|
|
|
206
|
-
if (process.platform === "win32" &&
|
|
207
|
-
const result = spawnSync(
|
|
209
|
+
if (process.platform === "win32" && ffmpegCommand) {
|
|
210
|
+
const result = spawnSync(ffmpegCommand, ["-hide_banner", "-f", "dshow", "-list_devices", "true", "-i", "dummy"], {
|
|
208
211
|
encoding: "utf8",
|
|
209
212
|
stdio: ["ignore", "ignore", "pipe"],
|
|
210
213
|
});
|
|
@@ -218,54 +221,60 @@ export function listMicrophones() {
|
|
|
218
221
|
function buildRecorders(file, settings = {}) {
|
|
219
222
|
const mic = settings.mic || "";
|
|
220
223
|
const recorders = [];
|
|
224
|
+
const arecordCommand = resolveCommand("arecord", settings);
|
|
225
|
+
const ffmpegCommand = resolveCommand("ffmpeg", settings);
|
|
226
|
+
const soxCommand = resolveCommand("sox", settings);
|
|
221
227
|
|
|
222
|
-
if (process.platform === "linux" &&
|
|
228
|
+
if (process.platform === "linux" && arecordCommand) {
|
|
223
229
|
recorders.push({
|
|
224
230
|
label: mic ? `arecord (${mic})` : "arecord (default)",
|
|
225
|
-
command:
|
|
231
|
+
command: arecordCommand,
|
|
226
232
|
args: ["-q", "-f", "S16_LE", "-r", "16000", "-c", "1", "-t", "wav", ...(mic ? ["-D", mic] : []), file],
|
|
227
233
|
});
|
|
228
234
|
}
|
|
229
235
|
|
|
230
|
-
if (process.platform === "linux" &&
|
|
236
|
+
if (process.platform === "linux" && ffmpegCommand) {
|
|
231
237
|
if (!mic) {
|
|
232
238
|
recorders.push({
|
|
233
239
|
label: "ffmpeg pulse (default)",
|
|
234
|
-
command:
|
|
240
|
+
command: ffmpegCommand,
|
|
235
241
|
args: ["-hide_banner", "-loglevel", "error", "-y", "-f", "pulse", "-i", "default", "-ac", "1", "-ar", "16000", file],
|
|
236
242
|
});
|
|
237
243
|
}
|
|
238
244
|
|
|
239
245
|
recorders.push({
|
|
240
246
|
label: `ffmpeg alsa (${mic || "default"})`,
|
|
241
|
-
command:
|
|
247
|
+
command: ffmpegCommand,
|
|
242
248
|
args: ["-hide_banner", "-loglevel", "error", "-y", "-f", "alsa", "-i", mic || "default", "-ac", "1", "-ar", "16000", file],
|
|
243
249
|
});
|
|
244
250
|
}
|
|
245
251
|
|
|
246
|
-
if (process.platform === "darwin" &&
|
|
252
|
+
if (process.platform === "darwin" && ffmpegCommand) {
|
|
247
253
|
recorders.push({
|
|
248
254
|
label: `ffmpeg avfoundation (${mic || ":0"})`,
|
|
249
|
-
command:
|
|
255
|
+
command: ffmpegCommand,
|
|
250
256
|
args: ["-hide_banner", "-loglevel", "error", "-y", "-f", "avfoundation", "-i", mic || ":0", "-ac", "1", "-ar", "16000", file],
|
|
251
257
|
});
|
|
252
258
|
}
|
|
253
259
|
|
|
254
|
-
if (process.platform === "win32" &&
|
|
255
|
-
const
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
260
|
+
if (process.platform === "win32" && ffmpegCommand) {
|
|
261
|
+
const ffmpegCmd = ffmpegCommand;
|
|
262
|
+
if (ffmpegCmd) {
|
|
263
|
+
const inputs = [...new Set([normalizeWindowsAudioInput(mic), "audio=default"])];
|
|
264
|
+
for (const input of inputs) {
|
|
265
|
+
recorders.push({
|
|
266
|
+
label: `ffmpeg dshow (${input.replace(/^audio=/, "")})`,
|
|
267
|
+
command: ffmpegCmd,
|
|
268
|
+
args: ["-hide_banner", "-loglevel", "error", "-y", "-f", "dshow", "-i", input, "-ac", "1", "-ar", "16000", file],
|
|
269
|
+
});
|
|
270
|
+
}
|
|
262
271
|
}
|
|
263
272
|
}
|
|
264
273
|
|
|
265
|
-
if (
|
|
274
|
+
if (soxCommand) {
|
|
266
275
|
recorders.push({
|
|
267
276
|
label: "sox default",
|
|
268
|
-
command:
|
|
277
|
+
command: soxCommand,
|
|
269
278
|
args: ["-d", "-r", "16000", "-c", "1", "-b", "16", file],
|
|
270
279
|
});
|
|
271
280
|
}
|
package/lib/engines.js
CHANGED
|
@@ -5,7 +5,7 @@ import { createGunzip } from "node:zlib";
|
|
|
5
5
|
import { spawn } from "node:child_process";
|
|
6
6
|
import { Readable, Transform } from "node:stream";
|
|
7
7
|
import { pipeline } from "node:stream/promises";
|
|
8
|
-
import { sha256, ensureDir } from "./download.js";
|
|
8
|
+
import { sha256, ensureDir, replaceFile } from "./download.js";
|
|
9
9
|
import { getCacheDir } from "./models.js";
|
|
10
10
|
import { getBundledEngineDir, resolveCommand } from "./engine.js";
|
|
11
11
|
|