@profullstack/qryptchat 0.9.0 → 0.9.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.
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
CALL_TYPE,
|
|
3
3
|
parseCall
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-pg6thvma.js";
|
|
5
5
|
|
|
6
6
|
// src/cli/voice-agent.js
|
|
7
7
|
import { spawn } from "node:child_process";
|
|
@@ -172,13 +172,14 @@ function askBrain(cmd, input, { timeoutMs = 90000, env = process.env } = {}) {
|
|
|
172
172
|
clearTimeout(timer);
|
|
173
173
|
resolve({ say: parseBrain(out), error: code ? `brain exited ${code}: ${err.trim().slice(-200)}` : "" });
|
|
174
174
|
});
|
|
175
|
+
child.stdin.on("error", () => {});
|
|
175
176
|
child.stdin.end(JSON.stringify(input));
|
|
176
177
|
});
|
|
177
178
|
}
|
|
178
179
|
|
|
179
180
|
class CallSession {
|
|
180
|
-
constructor({ client, rtc, conversation, call, brain, voice, post, log, isHuman }) {
|
|
181
|
-
Object.assign(this, { client, rtc, conversation, call, brain, voice, post, log, isHuman });
|
|
181
|
+
constructor({ client, rtc, conversation, call, brain, voice, tts, post, log, isHuman }) {
|
|
182
|
+
Object.assign(this, { client, rtc, conversation, call, brain, voice, ttsUrl: tts, post, log, isHuman });
|
|
182
183
|
this.transcript = [];
|
|
183
184
|
this.queue = Promise.resolve();
|
|
184
185
|
this.speakToken = 0;
|
|
@@ -203,8 +204,7 @@ class CallSession {
|
|
|
203
204
|
await room.connect(url, token, { autoSubscribe: true, e2ee: e2eeOptions(this.call.key) });
|
|
204
205
|
this.source = new AudioSource(SPEAK_RATE, 1);
|
|
205
206
|
const track = LocalAudioTrack.createAudioTrack(`${this.client.me?.username || "agent"}-voice`, this.source);
|
|
206
|
-
const opts = new TrackPublishOptions;
|
|
207
|
-
opts.source = TrackSource.SOURCE_MICROPHONE;
|
|
207
|
+
const opts = new TrackPublishOptions({ source: TrackSource.SOURCE_MICROPHONE, dtx: false, red: true, audioEncoding: { maxBitrate: 64000n } });
|
|
208
208
|
await room.localParticipant.publishTrack(track, opts);
|
|
209
209
|
this.log(`joined the call in ${this.conversation.title}`);
|
|
210
210
|
this.watchdog = setInterval(() => this.checkAlone(), 5000);
|
|
@@ -327,6 +327,20 @@ class CallSession {
|
|
|
327
327
|
}
|
|
328
328
|
}
|
|
329
329
|
async tts(sentence) {
|
|
330
|
+
if (this.ttsUrl) {
|
|
331
|
+
try {
|
|
332
|
+
const res = await this.client.fetch(this.ttsUrl, {
|
|
333
|
+
method: "POST",
|
|
334
|
+
headers: { "Content-Type": "application/json" },
|
|
335
|
+
body: JSON.stringify({ text: sentence, voice: this.voice })
|
|
336
|
+
});
|
|
337
|
+
if (!res.ok)
|
|
338
|
+
throw new Error(`HTTP ${res.status}`);
|
|
339
|
+
return wavSamples(new Uint8Array(await res.arrayBuffer()));
|
|
340
|
+
} catch (e) {
|
|
341
|
+
this.log(`--tts failed (${e.message}); using qrypt.chat's voice`);
|
|
342
|
+
}
|
|
343
|
+
}
|
|
330
344
|
const res = await this.client.fetch(`${this.client.base}/api/voice/speak`, {
|
|
331
345
|
method: "POST",
|
|
332
346
|
headers: { Authorization: `Bearer ${await this.client.token()}`, "Content-Type": "application/json" },
|
|
@@ -373,7 +387,7 @@ async function latestCall(client, conversationId) {
|
|
|
373
387
|
}
|
|
374
388
|
return null;
|
|
375
389
|
}
|
|
376
|
-
async function runVoiceAgent(client, { brain, voice = "orion", post = false, greet = true, hearAgents = false, log = (s) => process.stderr.write(`qc agent call: ${s}
|
|
390
|
+
async function runVoiceAgent(client, { brain, voice = "orion", tts, post = false, greet = true, hearAgents = false, log = (s) => process.stderr.write(`qc agent call: ${s}
|
|
377
391
|
`), rtc, signal } = {}) {
|
|
378
392
|
if (!brain)
|
|
379
393
|
throw new Error('Usage: qc agent call --brain "<command>" [--voice orion] [--post] [--no-greet]');
|
|
@@ -389,7 +403,7 @@ async function runVoiceAgent(client, { brain, voice = "orion", post = false, gre
|
|
|
389
403
|
const join = async (conversation, call) => {
|
|
390
404
|
if (sessions.has(conversation.id) || Date.now() - Date.parse(call.at) > MAX_CALL_MS)
|
|
391
405
|
return;
|
|
392
|
-
const session = new CallSession({ client, rtc, conversation, call, brain, voice, post, log, isHuman });
|
|
406
|
+
const session = new CallSession({ client, rtc, conversation, call, brain, voice, tts, post, log, isHuman });
|
|
393
407
|
sessions.set(conversation.id, session);
|
|
394
408
|
session.onClose = () => sessions.delete(conversation.id);
|
|
395
409
|
try {
|
|
@@ -1251,7 +1251,7 @@ Usage:
|
|
|
1251
1251
|
qc open the chat client (signs you in first if needed)
|
|
1252
1252
|
qc fonts install|status|remove our OpenEmoji colour font as the terminal's emoji font
|
|
1253
1253
|
qc agent join <link> join as an AI agent from an invite (makes its own keys) [--name --username]
|
|
1254
|
-
qc agent call --brain "<cmd>" [--voice orion] [--post] [--no-greet]
|
|
1254
|
+
qc agent call --brain "<cmd>" [--voice orion] [--tts <url>] [--post] [--no-greet]
|
|
1255
1255
|
as an agent, join voice calls in your chats: hear people, ask <cmd> what
|
|
1256
1256
|
to say (JSON on stdin; it prints PASS or SAY: text), speak it
|
|
1257
1257
|
(needs: npm i -g @livekit/rtc-node)
|
|
@@ -1296,7 +1296,7 @@ function parseArgs2(argv) {
|
|
|
1296
1296
|
const [k, v] = a.slice(2).split("=", 2);
|
|
1297
1297
|
if (v !== undefined)
|
|
1298
1298
|
args.flags[k] = v;
|
|
1299
|
-
else if (["url", "n", "limit", "reply", "emoji", "pronouns", "website", "bio", "name", "username", "brain", "voice"].includes(k) && argv[i + 1] !== undefined && !argv[i + 1].startsWith("--"))
|
|
1299
|
+
else if (["url", "n", "limit", "reply", "emoji", "pronouns", "website", "bio", "name", "username", "brain", "voice", "tts"].includes(k) && argv[i + 1] !== undefined && !argv[i + 1].startsWith("--"))
|
|
1300
1300
|
args.flags[k] = argv[++i];
|
|
1301
1301
|
else
|
|
1302
1302
|
args.flags[k] = true;
|
|
@@ -1369,10 +1369,11 @@ async function main2(argv, { version = "0.0.0" } = {}) {
|
|
|
1369
1369
|
}
|
|
1370
1370
|
case "agent": {
|
|
1371
1371
|
if (rest[0] === "call") {
|
|
1372
|
-
const { runVoiceAgent } = await import("./chunk-
|
|
1372
|
+
const { runVoiceAgent } = await import("./chunk-997mmke4.js");
|
|
1373
1373
|
return runVoiceAgent(await client(flags), {
|
|
1374
1374
|
brain: typeof flags.brain === "string" ? flags.brain : undefined,
|
|
1375
1375
|
voice: typeof flags.voice === "string" ? flags.voice : undefined,
|
|
1376
|
+
tts: typeof flags.tts === "string" ? flags.tts : undefined,
|
|
1376
1377
|
post: Boolean(flags.post),
|
|
1377
1378
|
greet: !flags["no-greet"],
|
|
1378
1379
|
hearAgents: Boolean(flags["hear-agents"])
|
package/dist/commands.js
CHANGED
package/dist/mcp.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@profullstack/qryptchat",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.1",
|
|
4
4
|
"description": "qc: qrypt.chat in your terminal. A full-screen end-to-end encrypted chat client (ML-KEM-1024), plus a scriptable CLI and an MCP server.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|