SIGN IN SIGN UP
continuedev / continue UNCLAIMED

⏩ Source-controlled AI checks, enforceable in CI. Powered by the open-source Continue CLI

0 0 21 TypeScript
2024-08-13 16:46:37 -06:00
import { exec, ChildProcess } from "child_process";
2024-11-11 07:38:52 -08:00
import os from "node:os";
2024-12-04 17:37:30 -08:00
import { removeCodeBlocksAndTrim } from ".";
2024-12-04 17:21:04 -08:00
2024-12-05 21:53:09 -08:00
import type { IMessenger } from "../protocol/messenger";
import type { FromCoreProtocol, ToCoreProtocol } from "../protocol";
2024-08-13 16:46:37 -06:00
// The amount of time before a process is declared
// a zombie after executing .kill()
const ttsKillTimeout: number = 5000;
/**
* Cleans a message text to safely be used in 'exec' context on host.
*
* Return modified message text.
*/
2024-12-04 17:21:04 -08:00
export function sanitizeMessageForTTS(message: string): string {
message = removeCodeBlocksAndTrim(message);
2024-08-13 16:46:37 -06:00
// Remove or replace problematic characters
message = message
.replace(/"/g, "")
.replace(/`/g, "")
.replace(/\$/g, "")
.replace(/\\/g, "")
.replace(/[&|;()<>]/g, "");
message = message.trim().replace(/\s+/g, " ");
return message;
}
export class TTS {
static os: string | undefined = undefined;
static handle: ChildProcess | undefined = undefined;
static messenger: IMessenger<ToCoreProtocol, FromCoreProtocol>;
2024-08-13 16:46:37 -06:00
static async read(message: string) {
message = sanitizeMessageForTTS(message);
try {
// Kill any active TTS processes
await TTS.kill();
} catch (e) {
console.warn("Error killing TTS process: ", e);
return;
}
switch (TTS.os) {
case "darwin":
TTS.handle = exec(`say "${message}"`);
break;
case "win32":
// Replace single quotes on windows
TTS.handle = exec(
`powershell -Command "Add-Type -AssemblyName System.Speech; (New-Object System.Speech.Synthesis.SpeechSynthesizer).Speak('${message.replace(
/'/g,
"''",
)}')"`,
);
break;
case "linux":
TTS.handle = exec(`espeak "${message}"`);
break;
default:
console.log(
"Text-to-speech is not supported on this operating system.",
);
return;
2024-08-13 16:46:37 -06:00
}
2024-11-15 12:28:04 -08:00
void TTS.messenger.request("setTTSActive", true);
TTS.handle?.once("exit", () => {
2024-11-15 12:28:04 -08:00
void TTS.messenger.request("setTTSActive", false);
});
2024-08-13 16:46:37 -06:00
}
static async kill(): Promise<void> {
return new Promise<void>((resolve, reject) => {
// Only kill a TTS process if it's still running
if (TTS.handle && TTS.handle.exitCode === null) {
// Use a timeout in case of zombie processes
let killTimeout: NodeJS.Timeout = setTimeout(() => {
reject(`Unable to kill TTS process: ${TTS.handle?.pid}`);
}, ttsKillTimeout);
// Resolve our promise once the program has exited
TTS.handle.once("exit", () => {
clearTimeout(killTimeout);
TTS.handle = undefined;
resolve();
});
TTS.handle.kill();
} else {
resolve();
}
});
}
static async setup() {
TTS.os = os.platform();
}
}