opencharm 0.8.0 → 0.8.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/dist/main.mjs CHANGED
@@ -997,7 +997,7 @@ function sendAdmin(socketPath, request) {
997
997
  }
998
998
  //#endregion
999
999
  //#region package.json
1000
- var version = "0.8.0";
1000
+ var version = "0.8.1";
1001
1001
  function channelToCube(value) {
1002
1002
  if (value < 48) return 0;
1003
1003
  if (value < 115) return 1;
@@ -3414,6 +3414,7 @@ var TurnController = class {
3414
3414
  this.#frames = [];
3415
3415
  this.#face("listening");
3416
3416
  this.#deps.agent.warm?.(this.#deps.sessionKey);
3417
+ if (this.#deps.speakAloud?.() ?? true) this.#deps.voice.prime?.();
3417
3418
  }
3418
3419
  audio(frame) {
3419
3420
  if (this.#state === "listening" && this.#frames.length < MAX_FRAMES) this.#frames.push(frame);
@@ -4378,21 +4379,25 @@ function audioOf(frame) {
4378
4379
  function voiceFor(language, voices) {
4379
4380
  return voices[language] ?? voices.en ?? DEFAULT_VOICES.en;
4380
4381
  }
4381
- function connect(text, language, voices, signal, options) {
4382
- const timeoutMs = options.timeoutMs ?? 15e3;
4382
+ function openSocket(options) {
4383
4383
  const connection = randomUUID().replace(/-/g, "");
4384
4384
  const url = `${options.url ?? SERVICE_URL}?TrustedClientToken=${TRUSTED_CLIENT_TOKEN}&Sec-MS-GEC=${secMsGec()}&Sec-MS-GEC-Version=${GEC_VERSION}&ConnectionId=${connection}`;
4385
- const packets = createChannel();
4386
- const reader = new WebmOpusReader();
4387
- let sent = 0;
4388
- let settled = false;
4389
- const socket = new WebSocket(url, {
4385
+ return new WebSocket(url, {
4390
4386
  headers: {
4391
4387
  "User-Agent": USER_AGENT,
4392
4388
  Origin: ORIGIN
4393
4389
  },
4394
- handshakeTimeout: timeoutMs
4390
+ handshakeTimeout: options.timeoutMs ?? 15e3
4395
4391
  });
4392
+ }
4393
+ function connect(text, language, voices, signal, options, ready) {
4394
+ const timeoutMs = options.timeoutMs ?? 15e3;
4395
+ const connection = randomUUID().replace(/-/g, "");
4396
+ const packets = createChannel();
4397
+ const reader = new WebmOpusReader();
4398
+ let sent = 0;
4399
+ let settled = false;
4400
+ const socket = ready ?? openSocket(options);
4396
4401
  const finish = (error) => {
4397
4402
  if (settled) return;
4398
4403
  settled = true;
@@ -4407,7 +4412,7 @@ function connect(text, language, voices, signal, options) {
4407
4412
  const timer = setTimeout(() => finish(/* @__PURE__ */ new Error("Microsoft's voice took too long")), timeoutMs);
4408
4413
  if (signal.aborted) onAbort();
4409
4414
  else signal.addEventListener("abort", onAbort, { once: true });
4410
- socket.on("open", () => {
4415
+ const request = () => {
4411
4416
  socket.send(message("speech.config", "application/json; charset=utf-8", JSON.stringify({ context: { synthesis: { audio: {
4412
4417
  metadataoptions: {
4413
4418
  sentenceBoundaryEnabled: "false",
@@ -4416,7 +4421,9 @@ function connect(text, language, voices, signal, options) {
4416
4421
  outputFormat: OUTPUT_FORMAT
4417
4422
  } } } })));
4418
4423
  socket.send(message("ssml", "application/ssml+xml", ssml(text, voiceFor(language, voices)), connection));
4419
- });
4424
+ };
4425
+ if (socket.readyState === WebSocket.OPEN) request();
4426
+ else socket.once("open", request);
4420
4427
  socket.on("message", (data, isBinary) => {
4421
4428
  const frame = Buffer.isBuffer(data) ? data : Buffer.concat(data);
4422
4429
  if (!isBinary) {
@@ -4444,9 +4451,48 @@ function createMicrosoftSpeaker(options = {}) {
4444
4451
  ...DEFAULT_VOICES,
4445
4452
  ...options.voices
4446
4453
  };
4447
- const stream = (text, signal, language = "en") => connect(text, language, voices, signal, options);
4454
+ const spareMs = options.spareMs ?? 2e4;
4455
+ let spare;
4456
+ const takeSpare = () => {
4457
+ const taken = spare;
4458
+ spare = void 0;
4459
+ if (!taken) return void 0;
4460
+ if ((taken.socket.readyState === WebSocket.OPEN || taken.socket.readyState === WebSocket.CONNECTING) && Date.now() < taken.until) return taken.socket;
4461
+ taken.socket.terminate();
4462
+ };
4463
+ const prime = () => {
4464
+ if (spare) return;
4465
+ const socket = openSocket(options);
4466
+ socket.on("error", () => void 0);
4467
+ spare = {
4468
+ socket,
4469
+ until: Date.now() + spareMs
4470
+ };
4471
+ setTimeout(() => {
4472
+ if (spare?.socket !== socket) return;
4473
+ spare = void 0;
4474
+ socket.terminate();
4475
+ }, spareMs).unref();
4476
+ };
4477
+ async function* stream(text, signal, language = "en") {
4478
+ const ready = takeSpare();
4479
+ if (ready) {
4480
+ let spoke = false;
4481
+ try {
4482
+ for await (const packet of connect(text, language, voices, signal, options, ready)) {
4483
+ spoke = true;
4484
+ yield packet;
4485
+ }
4486
+ return;
4487
+ } catch (error) {
4488
+ if (spoke || signal.aborted) throw error;
4489
+ }
4490
+ }
4491
+ yield* connect(text, language, voices, signal, options);
4492
+ }
4448
4493
  return {
4449
4494
  name: "microsoft",
4495
+ prime,
4450
4496
  stream,
4451
4497
  async synthesize(text, signal, language = "en") {
4452
4498
  const packets = [];
@@ -4691,6 +4737,9 @@ function speakWithFallback(chain, options = {}) {
4691
4737
  },
4692
4738
  warm: async () => {
4693
4739
  await Promise.allSettled(chain.map((speaker) => speaker.warm?.() ?? Promise.resolve()));
4740
+ },
4741
+ prime: () => {
4742
+ if ((resting.get(first) ?? 0) <= now()) first.prime?.();
4694
4743
  }
4695
4744
  };
4696
4745
  }
@@ -4750,7 +4799,8 @@ function createVoiceFromConfig(config) {
4750
4799
  language: config.voice.language,
4751
4800
  warm: async () => {
4752
4801
  await Promise.allSettled([listener.warm?.(), speaker.warm?.()]);
4753
- }
4802
+ },
4803
+ ...speaker.prime ? { prime: speaker.prime } : {}
4754
4804
  };
4755
4805
  }
4756
4806
  function charmToolsServers(config, tools) {
@@ -4969,7 +5019,7 @@ function formatIdentity(unit, version, commit) {
4969
5019
  let cached;
4970
5020
  function identity() {
4971
5021
  if (cached) return cached;
4972
- const commit = "bf6c38c";
5022
+ const commit = "e06b6db";
4973
5023
  cached = {
4974
5024
  version: `cli@${version}`,
4975
5025
  commit,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencharm",
3
- "version": "0.8.0",
3
+ "version": "0.8.1",
4
4
  "description": "An open-source body for the AI agent you already run: a face, a voice, one key. Claude Code, Codex, Gemini CLI, Hermes, OpenClaw or any ACP or OpenAI-compatible agent; on a small board, your Mac's notch or the browser.",
5
5
  "type": "module",
6
6
  "bin": {
Binary file