accelr8 3.0.3 → 3.0.5
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.
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
VERSION,
|
|
8
8
|
decode,
|
|
9
9
|
encode
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-CZENTCH6.js";
|
|
11
11
|
import {
|
|
12
12
|
DEFAULT_MODEL,
|
|
13
13
|
addAccount,
|
|
@@ -1186,23 +1186,41 @@ var DRAIN_KILL_MS = 2e3;
|
|
|
1186
1186
|
var MAX_PLAYERS = 8;
|
|
1187
1187
|
var STDERR_TAIL = 400;
|
|
1188
1188
|
var VERIFY_MIN_BYTES = SAMPLE_RATE * 2 / 10;
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1189
|
+
function playbackArgs(ffplayPath) {
|
|
1190
|
+
const ch = ffplayChannelFlag(ffplayPath);
|
|
1191
|
+
return [
|
|
1192
|
+
"-hide_banner",
|
|
1193
|
+
"-loglevel",
|
|
1194
|
+
"error",
|
|
1195
|
+
"-nodisp",
|
|
1196
|
+
"-sync",
|
|
1197
|
+
"audio",
|
|
1198
|
+
"-infbuf",
|
|
1199
|
+
"-f",
|
|
1200
|
+
"s16le",
|
|
1201
|
+
"-ar",
|
|
1202
|
+
String(SAMPLE_RATE),
|
|
1203
|
+
...ch,
|
|
1204
|
+
"-i",
|
|
1205
|
+
"-"
|
|
1206
|
+
];
|
|
1207
|
+
}
|
|
1208
|
+
var chFlagCache = null;
|
|
1209
|
+
function ffplayChannelFlag(ffplayPath) {
|
|
1210
|
+
if (chFlagCache) return chFlagCache;
|
|
1211
|
+
let modern = true;
|
|
1212
|
+
try {
|
|
1213
|
+
const out = spawnSync(ffplayPath, ["-hide_banner", "-version"], { encoding: "utf8", timeout: 3e3 });
|
|
1214
|
+
const m = ((out.stdout || "") + (out.stderr || "")).match(/version\s+n?(\d+)\.(\d+)/i);
|
|
1215
|
+
if (m) {
|
|
1216
|
+
const major = +m[1], minor = +m[2];
|
|
1217
|
+
modern = major > 5 || major === 5 && minor >= 1;
|
|
1218
|
+
}
|
|
1219
|
+
} catch {
|
|
1220
|
+
}
|
|
1221
|
+
chFlagCache = modern ? ["-ch_layout", "mono"] : ["-ac", "1"];
|
|
1222
|
+
return chFlagCache;
|
|
1223
|
+
}
|
|
1206
1224
|
function detectVoiceTools() {
|
|
1207
1225
|
const ffmpegPath = which("ffmpeg");
|
|
1208
1226
|
const ffplayPath = which("ffplay");
|
|
@@ -1422,7 +1440,7 @@ var VoicePlayback = class {
|
|
|
1422
1440
|
spawnPlayer(speakerId) {
|
|
1423
1441
|
let child;
|
|
1424
1442
|
try {
|
|
1425
|
-
child = spawn(this.tools.ffplayPath,
|
|
1443
|
+
child = spawn(this.tools.ffplayPath, playbackArgs(this.tools.ffplayPath), { stdio: ["pipe", "ignore", "ignore"] });
|
|
1426
1444
|
} catch {
|
|
1427
1445
|
return null;
|
|
1428
1446
|
}
|
|
@@ -1476,6 +1494,55 @@ var VoicePlayback = class {
|
|
|
1476
1494
|
if (this.players.get(speakerId) === p) this.players.delete(speakerId);
|
|
1477
1495
|
}
|
|
1478
1496
|
};
|
|
1497
|
+
function listMicDevices() {
|
|
1498
|
+
try {
|
|
1499
|
+
if (process.platform === "darwin") {
|
|
1500
|
+
const text = runForOutput(["-hide_banner", "-f", "avfoundation", "-list_devices", "true", "-i", ""]);
|
|
1501
|
+
const out = [];
|
|
1502
|
+
let inAudio = false;
|
|
1503
|
+
for (const line of text.split("\n")) {
|
|
1504
|
+
if (/audio devices/i.test(line)) {
|
|
1505
|
+
inAudio = true;
|
|
1506
|
+
continue;
|
|
1507
|
+
}
|
|
1508
|
+
if (/video devices/i.test(line)) {
|
|
1509
|
+
inAudio = false;
|
|
1510
|
+
continue;
|
|
1511
|
+
}
|
|
1512
|
+
if (!inAudio) continue;
|
|
1513
|
+
const m = line.match(/\[(\d+)\]\s+(.+?)\s*$/);
|
|
1514
|
+
if (m) out.push({ index: m[1], name: m[2] });
|
|
1515
|
+
}
|
|
1516
|
+
return out;
|
|
1517
|
+
}
|
|
1518
|
+
if (process.platform === "linux") {
|
|
1519
|
+
const text = runForOutput(["-hide_banner", "-sources", "pulse"]);
|
|
1520
|
+
const out = [];
|
|
1521
|
+
for (const line of text.split("\n")) {
|
|
1522
|
+
if (/auto-detected/i.test(line)) continue;
|
|
1523
|
+
const m = line.match(/^\s*\*?\s*(\S+)\s+\[(.+)\]\s*$/);
|
|
1524
|
+
if (m) out.push({ index: m[1], name: m[2] });
|
|
1525
|
+
}
|
|
1526
|
+
return out;
|
|
1527
|
+
}
|
|
1528
|
+
if (process.platform === "win32") {
|
|
1529
|
+
const text = runForOutput(["-hide_banner", "-list_devices", "true", "-f", "dshow", "-i", "dummy"]);
|
|
1530
|
+
const out = [];
|
|
1531
|
+
for (const line of text.split("\n")) {
|
|
1532
|
+
const m = line.match(/"(.+)"\s+\(audio\)/);
|
|
1533
|
+
if (m) out.push({ index: m[1], name: m[1] });
|
|
1534
|
+
}
|
|
1535
|
+
return out;
|
|
1536
|
+
}
|
|
1537
|
+
return [];
|
|
1538
|
+
} catch {
|
|
1539
|
+
return [];
|
|
1540
|
+
}
|
|
1541
|
+
}
|
|
1542
|
+
function runForOutput(args) {
|
|
1543
|
+
const res = spawnSync("ffmpeg", args, { encoding: "utf8", timeout: 5e3 });
|
|
1544
|
+
return (res.stderr ?? "") + (res.stdout ?? "");
|
|
1545
|
+
}
|
|
1479
1546
|
function errMsg(e) {
|
|
1480
1547
|
return e instanceof Error ? e.message : String(e);
|
|
1481
1548
|
}
|
|
@@ -1605,6 +1672,47 @@ var voiceOn = false;
|
|
|
1605
1672
|
var voiceTransmit = false;
|
|
1606
1673
|
var voiceCapture = null;
|
|
1607
1674
|
var voicePlayback = null;
|
|
1675
|
+
var voiceDevice = process.env.ACCELR8_MIC || void 0;
|
|
1676
|
+
var voiceRetries = 0;
|
|
1677
|
+
function autoPickMic() {
|
|
1678
|
+
if (process.platform === "darwin") {
|
|
1679
|
+
const mics = listMicDevices();
|
|
1680
|
+
if (!mics.length) return void 0;
|
|
1681
|
+
const builtin = mics.find((m) => /macbook|built.?in|internal/i.test(m.name));
|
|
1682
|
+
if (builtin) return builtin.index;
|
|
1683
|
+
const notPhone = mics.find((m) => !/iphone|ipad|continuity|desk view/i.test(m.name));
|
|
1684
|
+
return notPhone?.index;
|
|
1685
|
+
}
|
|
1686
|
+
if (process.platform === "win32") {
|
|
1687
|
+
const mics = listMicDevices();
|
|
1688
|
+
return mics[0]?.index;
|
|
1689
|
+
}
|
|
1690
|
+
return void 0;
|
|
1691
|
+
}
|
|
1692
|
+
function startCapture() {
|
|
1693
|
+
if (!voiceCapture) return;
|
|
1694
|
+
voiceCapture.start({
|
|
1695
|
+
device: voiceDevice,
|
|
1696
|
+
onChunk: (b64) => {
|
|
1697
|
+
voiceRetries = 0;
|
|
1698
|
+
if (voiceTransmit) sendMsg({ t: "voiceFrame", data: b64 });
|
|
1699
|
+
},
|
|
1700
|
+
onError: (m) => {
|
|
1701
|
+
if (!voiceTransmit) return;
|
|
1702
|
+
voiceRetries++;
|
|
1703
|
+
if (voiceRetries <= 6) {
|
|
1704
|
+
state.status = `mic dropped \xB7 reconnecting ${voiceRetries}\u2026`;
|
|
1705
|
+
setTimeout(() => {
|
|
1706
|
+
if (voiceTransmit && voiceCapture && !voiceCapture.active) startCapture();
|
|
1707
|
+
}, 200);
|
|
1708
|
+
} else {
|
|
1709
|
+
voiceTransmit = false;
|
|
1710
|
+
voiceRetries = 0;
|
|
1711
|
+
state.status = "mic keeps dropping \xB7 /voice mics to pick another \xB7 " + m.slice(0, 30);
|
|
1712
|
+
}
|
|
1713
|
+
}
|
|
1714
|
+
});
|
|
1715
|
+
}
|
|
1608
1716
|
function toggleVoice() {
|
|
1609
1717
|
if (voiceOn) return leaveVoice();
|
|
1610
1718
|
if (!state.project) {
|
|
@@ -1620,6 +1728,7 @@ function toggleVoice() {
|
|
|
1620
1728
|
voiceCapture = new VoiceCapture(voiceTools);
|
|
1621
1729
|
voiceOn = true;
|
|
1622
1730
|
voiceTransmit = false;
|
|
1731
|
+
if (voiceDevice === void 0 && voiceTools.capture) voiceDevice = autoPickMic();
|
|
1623
1732
|
sendMsg({ t: "voiceJoin" });
|
|
1624
1733
|
state.status = voiceTools.capture ? "voice on \xB7 /talk (or ctrl-g) to speak" : "voice on \xB7 listen only (no mic found)";
|
|
1625
1734
|
}
|
|
@@ -1642,21 +1751,15 @@ function toggleTalk() {
|
|
|
1642
1751
|
return;
|
|
1643
1752
|
}
|
|
1644
1753
|
if (voiceTransmit) {
|
|
1645
|
-
voiceCapture.stop();
|
|
1646
1754
|
voiceTransmit = false;
|
|
1755
|
+
voiceRetries = 0;
|
|
1756
|
+
voiceCapture.stop();
|
|
1647
1757
|
state.status = "muted \xB7 /talk (or ctrl-g) to speak";
|
|
1648
1758
|
} else {
|
|
1649
1759
|
voiceTransmit = true;
|
|
1760
|
+
voiceRetries = 0;
|
|
1650
1761
|
state.status = "\u25CF live \xB7 /mute (or ctrl-g) to stop";
|
|
1651
|
-
|
|
1652
|
-
onChunk: (b64) => {
|
|
1653
|
-
if (voiceTransmit) sendMsg({ t: "voiceFrame", data: b64 });
|
|
1654
|
-
},
|
|
1655
|
-
onError: (m) => {
|
|
1656
|
-
voiceTransmit = false;
|
|
1657
|
-
state.status = "mic error \xB7 " + m.slice(0, 44);
|
|
1658
|
-
}
|
|
1659
|
-
});
|
|
1762
|
+
startCapture();
|
|
1660
1763
|
}
|
|
1661
1764
|
}
|
|
1662
1765
|
function openHelp() {
|
|
@@ -3037,7 +3140,7 @@ function renderAccounts() {
|
|
|
3037
3140
|
puts(ix + 46, y, a.auth.kind === "claude_code_login" ? "claude login" : "api key", { color: C.dim });
|
|
3038
3141
|
puts(ix + 60, y, status, { color: dotColor });
|
|
3039
3142
|
const modeColor = a.mode === "auto" ? C.warn : a.mode === "plan" ? C.accent : C.good;
|
|
3040
|
-
button(w - 22, y, ` ${a.mode} `, { bgColor: C.tabOff, color: modeColor, bold: true }, () => cycleMode(a));
|
|
3143
|
+
button(w - 22, y, ` ${a.mode} `, { bgColor: C.tabOff, color: readableFg(modeColor, C.tabOff), bold: true }, () => cycleMode(a));
|
|
3041
3144
|
button(w - 11, y, " remove ", { bgColor: C.tabOff, color: C.text }, () => removeAgentAccount(a));
|
|
3042
3145
|
y += 1;
|
|
3043
3146
|
}
|
|
@@ -3536,6 +3639,19 @@ function slashCommand(text) {
|
|
|
3536
3639
|
toggleVoice();
|
|
3537
3640
|
} else if (cmd === "/talk" || cmd === "/mute") {
|
|
3538
3641
|
toggleTalk();
|
|
3642
|
+
} else if (cmd === "/voice mics" || cmd === "/mics") {
|
|
3643
|
+
const mics = listMicDevices();
|
|
3644
|
+
sidecarBlock = { title: "microphones", at: Date.now(), lines: mics.length ? mics.map((m) => `${m.index} ${m.name}${voiceDevice === m.index ? " \xB7 selected" : ""}`).concat("use: /voice mic <index>") : ["none found (need ffmpeg)"] };
|
|
3645
|
+
state.status = `${mics.length} mics \xB7 /voice mic <index> to pick`;
|
|
3646
|
+
} else if (cmd.startsWith("/voice mic ")) {
|
|
3647
|
+
const idx = cmd.slice("/voice mic ".length).trim();
|
|
3648
|
+
voiceDevice = idx || void 0;
|
|
3649
|
+
state.status = `mic set to [${idx}]` + (voiceOn ? " \xB7 reconnecting" : "");
|
|
3650
|
+
if (voiceTransmit && voiceCapture) {
|
|
3651
|
+
voiceCapture.stop();
|
|
3652
|
+
voiceRetries = 0;
|
|
3653
|
+
startCapture();
|
|
3654
|
+
}
|
|
3539
3655
|
} else {
|
|
3540
3656
|
state.status = `unknown command: ${cmd.split(" ")[0]}`;
|
|
3541
3657
|
}
|
package/dist/cli.js
CHANGED
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
DEFAULT_SERVER,
|
|
4
4
|
PORT,
|
|
5
5
|
VERSION
|
|
6
|
-
} from "./chunks/chunk-
|
|
6
|
+
} from "./chunks/chunk-CZENTCH6.js";
|
|
7
7
|
|
|
8
8
|
// src/cli.ts
|
|
9
9
|
var HELP = `
|
|
@@ -52,7 +52,7 @@ async function main() {
|
|
|
52
52
|
console.error(`invalid port: ${argVal(args, "--port")}`);
|
|
53
53
|
process.exit(1);
|
|
54
54
|
}
|
|
55
|
-
const { runServer } = await import("./chunks/server-
|
|
55
|
+
const { runServer } = await import("./chunks/server-OGAKQFOU.js");
|
|
56
56
|
runServer(port);
|
|
57
57
|
return;
|
|
58
58
|
}
|
|
@@ -76,7 +76,7 @@ async function main() {
|
|
|
76
76
|
const { cachedUpdate, refreshUpdateCache } = await import("./chunks/update-UEPNGB6Y.js");
|
|
77
77
|
refreshUpdateCache();
|
|
78
78
|
const update = cachedUpdate(VERSION);
|
|
79
|
-
const { runClient } = await import("./chunks/client-
|
|
79
|
+
const { runClient } = await import("./chunks/client-DGDZ22XC.js");
|
|
80
80
|
runClient({
|
|
81
81
|
name: argVal(args, "--name"),
|
|
82
82
|
server: argVal(args, "--server"),
|