@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-e39k2cyf.js";
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-s094t64g.js");
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
@@ -5,7 +5,7 @@ import {
5
5
  parseArgs2,
6
6
  findChat2,
7
7
  main2
8
- } from "./chunk-e39k2cyf.js";
8
+ } from "./chunk-pg6thvma.js";
9
9
  import"./chunk-8gpysrd6.js";
10
10
  export {
11
11
  HELP2 as HELP,
package/dist/mcp.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import"./chunk-8gpysrd6.js";
2
2
  import {
3
3
  findChat2
4
- } from "./chunk-e39k2cyf.js";
4
+ } from "./chunk-pg6thvma.js";
5
5
 
6
6
  // src/cli/mcp.js
7
7
  import { createInterface } from "node:readline";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@profullstack/qryptchat",
3
- "version": "0.9.0",
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": {