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 +64 -14
- package/package.json +1 -1
- package/sim/charm_sim.wasm +0 -0
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.
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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 = "
|
|
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.
|
|
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": {
|
package/sim/charm_sim.wasm
CHANGED
|
Binary file
|