meldivo 1.0.0

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.
Files changed (43) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +161 -0
  3. package/THIRD_PARTY_NOTICES.md +59 -0
  4. package/bin/meldivo.mjs +514 -0
  5. package/dist/server/harnesses/claude.js +407 -0
  6. package/dist/server/harnesses/claude.js.map +1 -0
  7. package/dist/server/harnesses/index.js +27 -0
  8. package/dist/server/harnesses/index.js.map +1 -0
  9. package/dist/server/harnesses/opencode.js +391 -0
  10. package/dist/server/harnesses/opencode.js.map +1 -0
  11. package/dist/server/harnesses/pi.js +364 -0
  12. package/dist/server/harnesses/pi.js.map +1 -0
  13. package/dist/server/harnesses/types.js +2 -0
  14. package/dist/server/harnesses/types.js.map +1 -0
  15. package/dist/server/hub.js +126 -0
  16. package/dist/server/hub.js.map +1 -0
  17. package/dist/server/index.js +515 -0
  18. package/dist/server/index.js.map +1 -0
  19. package/dist/server/logger.js +98 -0
  20. package/dist/server/logger.js.map +1 -0
  21. package/dist/server/qr.js +16 -0
  22. package/dist/server/qr.js.map +1 -0
  23. package/dist/server/remote.js +166 -0
  24. package/dist/server/remote.js.map +1 -0
  25. package/dist/server/speech.js +323 -0
  26. package/dist/server/speech.js.map +1 -0
  27. package/dist/server/state.js +91 -0
  28. package/dist/server/state.js.map +1 -0
  29. package/dist/web/apple-touch-icon.png +0 -0
  30. package/dist/web/assets/_commonjsHelpers-CqkleIqs.js +1 -0
  31. package/dist/web/assets/browser-CujOq0-h.js +8 -0
  32. package/dist/web/assets/index-B1UGHGdh.css +1 -0
  33. package/dist/web/assets/index-CNq_igvY.js +43 -0
  34. package/dist/web/assets/index-CeWj4ejR.js +2948 -0
  35. package/dist/web/favicon-32.png +0 -0
  36. package/dist/web/index.html +15 -0
  37. package/dist/web/logo-192.png +0 -0
  38. package/dist/web/logo-512.png +0 -0
  39. package/dist/web/voice-assets/ort-wasm-simd-threaded.mjs +59 -0
  40. package/dist/web/voice-assets/ort-wasm-simd-threaded.wasm +0 -0
  41. package/dist/web/voice-assets/silero_vad_v5.onnx +0 -0
  42. package/dist/web/voice-assets/vad.worklet.bundle.min.js +1 -0
  43. package/package.json +78 -0
@@ -0,0 +1,16 @@
1
+ import QRCode from "qrcode";
2
+ /**
3
+ * Renders `text` as a compact terminal QR code (half-block UTF-8 glyphs,
4
+ * packing two pixel rows per character row), ready to print alongside a hub
5
+ * URL from the CLI.
6
+ */
7
+ export async function qrText(url) {
8
+ const block = await QRCode.toString(url, { type: "utf8", errorCorrectionLevel: "L" });
9
+ const lines = block.split("\n");
10
+ while (lines.length && lines[0].trim() === "")
11
+ lines.shift();
12
+ while (lines.length && lines[lines.length - 1].trim() === "")
13
+ lines.pop();
14
+ return lines.join("\n");
15
+ }
16
+ //# sourceMappingURL=qr.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"qr.js","sourceRoot":"","sources":["../../server/src/qr.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,QAAQ,CAAC;AAE5B;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,MAAM,CAAC,GAAW;IACtC,MAAM,KAAK,GAAG,MAAM,MAAM,CAAC,QAAQ,CAAC,GAAG,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,oBAAoB,EAAE,GAAG,EAAE,CAAC,CAAC;IACtF,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAChC,OAAO,KAAK,CAAC,MAAM,IAAI,KAAK,CAAC,CAAC,CAAE,CAAC,IAAI,EAAE,KAAK,EAAE;QAAE,KAAK,CAAC,KAAK,EAAE,CAAC;IAC9D,OAAO,KAAK,CAAC,MAAM,IAAI,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAE,CAAC,IAAI,EAAE,KAAK,EAAE;QAAE,KAAK,CAAC,GAAG,EAAE,CAAC;IAC3E,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC"}
@@ -0,0 +1,166 @@
1
+ import { spawn, spawnSync } from "node:child_process";
2
+ import { existsSync } from "node:fs";
3
+ import { homedir, networkInterfaces } from "node:os";
4
+ import path from "node:path";
5
+ export const remoteGuideUrl = "https://github.com/amirrouh/meldivo/blob/main/docs/remote-access.md";
6
+ const cloudflaredReadyTimeoutMs = 30_000;
7
+ const spawnSyncTimeoutMs = 5_000;
8
+ function tlsDir() {
9
+ const base = process.env.XDG_CONFIG_HOME || path.join(homedir(), ".config");
10
+ return path.join(base, "meldivo", "tls");
11
+ }
12
+ /** Reads `tailscale status --json`, without a shell, to see if it's ready to serve HTTPS. */
13
+ function detectTailscale() {
14
+ try {
15
+ const result = spawnSync("tailscale", ["status", "--json"], { timeout: spawnSyncTimeoutMs, encoding: "utf8" });
16
+ if (result.status !== 0 || !result.stdout)
17
+ return undefined;
18
+ const data = JSON.parse(result.stdout);
19
+ if (data.BackendState !== "Running")
20
+ return undefined;
21
+ const dnsName = data.Self?.DNSName?.replace(/\.$/, "");
22
+ return dnsName ? { dnsName } : undefined;
23
+ }
24
+ catch {
25
+ return undefined;
26
+ }
27
+ }
28
+ function detectCloudflared() {
29
+ try {
30
+ const result = spawnSync("cloudflared", ["--version"], { timeout: spawnSyncTimeoutMs });
31
+ return result.status === 0;
32
+ }
33
+ catch {
34
+ return false;
35
+ }
36
+ }
37
+ function detectCertificate() {
38
+ const dir = tlsDir();
39
+ return existsSync(path.join(dir, "cert.pem")) && existsSync(path.join(dir, "key.pem"));
40
+ }
41
+ function detect() {
42
+ return { tailscale: detectTailscale(), cloudflare: detectCloudflared(), certificate: detectCertificate() };
43
+ }
44
+ function nonInternalIPv4() {
45
+ const addresses = [];
46
+ for (const entries of Object.values(networkInterfaces())) {
47
+ for (const entry of entries ?? []) {
48
+ if (entry.family === "IPv4" && !entry.internal)
49
+ addresses.push(entry.address);
50
+ }
51
+ }
52
+ return addresses;
53
+ }
54
+ /** The three remote-access options, and whether each is currently ready to use. */
55
+ export function detectRemoteOptions() {
56
+ const detected = detect();
57
+ return [
58
+ { id: "tailscale", label: "Tailscale (private to your tailnet)", ready: Boolean(detected.tailscale) },
59
+ { id: "cloudflare", label: "Cloudflare quick tunnel (public link)", ready: detected.cloudflare },
60
+ { id: "certificate", label: "My certificate (LAN/VPN)", ready: detected.certificate },
61
+ ];
62
+ }
63
+ /** Owns the lifecycle of whichever remote-access transport is currently turned on. */
64
+ export class RemoteManager {
65
+ opts;
66
+ cloudflaredChild;
67
+ tailscaleOn = false;
68
+ httpsOn = false;
69
+ active = null;
70
+ constructor(opts) {
71
+ this.opts = opts;
72
+ }
73
+ status() {
74
+ return { active: this.active };
75
+ }
76
+ async start(id) {
77
+ await this.stop();
78
+ if (id === "tailscale")
79
+ return this.startTailscale();
80
+ if (id === "cloudflare")
81
+ return this.startCloudflare();
82
+ return this.startCertificate();
83
+ }
84
+ startTailscale() {
85
+ const detected = detectTailscale();
86
+ if (!detected)
87
+ throw new Error(`Tailscale is not ready. Setup guide: ${remoteGuideUrl}`);
88
+ const result = spawnSync("tailscale", ["serve", "--bg", String(this.opts.httpPort)], { timeout: 10_000, encoding: "utf8" });
89
+ if (result.status !== 0) {
90
+ const detail = (result.stderr || result.stdout || "unknown error").trim();
91
+ throw new Error(`Tailscale serve failed: ${detail}. Setup guide: ${remoteGuideUrl}`);
92
+ }
93
+ this.tailscaleOn = true;
94
+ const url = this.opts.buildUrl(`https://${detected.dnsName}`);
95
+ this.active = { id: "tailscale", url };
96
+ return { url };
97
+ }
98
+ startCloudflare() {
99
+ return new Promise((resolve, reject) => {
100
+ const child = spawn("cloudflared", ["tunnel", "--no-autoupdate", "--url", `http://127.0.0.1:${this.opts.httpPort}`], {
101
+ stdio: ["ignore", "ignore", "pipe"],
102
+ });
103
+ this.cloudflaredChild = child;
104
+ let buffer = "";
105
+ let settled = false;
106
+ const settle = (origin, error) => {
107
+ if (settled)
108
+ return;
109
+ settled = true;
110
+ clearTimeout(timer);
111
+ if (!origin) {
112
+ reject(new Error(`${error ?? "Cloudflare tunnel failed to start"}. Setup guide: ${remoteGuideUrl}`));
113
+ return;
114
+ }
115
+ const url = this.opts.buildUrl(origin);
116
+ this.active = { id: "cloudflare", url };
117
+ resolve({ url });
118
+ };
119
+ const timer = setTimeout(() => settle(undefined, "Cloudflare tunnel did not start in time"), cloudflaredReadyTimeoutMs);
120
+ child.stderr?.on("data", (chunk) => {
121
+ buffer += chunk.toString("utf8");
122
+ const match = buffer.match(/https:\/\/[a-z0-9-]+\.trycloudflare\.com/);
123
+ if (match)
124
+ settle(match[0]);
125
+ });
126
+ child.once("error", (error) => settle(undefined, `Cloudflare tunnel failed to start: ${error.message}`));
127
+ child.once("exit", () => {
128
+ if (this.cloudflaredChild === child)
129
+ this.cloudflaredChild = undefined;
130
+ settle(undefined, "Cloudflare tunnel exited before it was ready");
131
+ });
132
+ });
133
+ }
134
+ async startCertificate() {
135
+ if (!detectCertificate())
136
+ throw new Error(`No certificate found. Setup guide: ${remoteGuideUrl}`);
137
+ const port = await this.opts.enableHttps();
138
+ this.httpsOn = true;
139
+ const ips = nonInternalIPv4();
140
+ if (ips.length === 0) {
141
+ await this.opts.disableHttps().catch(() => undefined);
142
+ this.httpsOn = false;
143
+ throw new Error(`No non-internal network address found for the certificate link. Setup guide: ${remoteGuideUrl}`);
144
+ }
145
+ const url = this.opts.buildUrl(`https://${ips[0]}:${port}`);
146
+ const alternatives = ips.slice(1).map((ip) => this.opts.buildUrl(`https://${ip}:${port}`));
147
+ this.active = { id: "certificate", url };
148
+ return alternatives.length > 0 ? { url, alternatives } : { url };
149
+ }
150
+ async stop() {
151
+ if (this.cloudflaredChild) {
152
+ this.cloudflaredChild.kill();
153
+ this.cloudflaredChild = undefined;
154
+ }
155
+ if (this.tailscaleOn) {
156
+ spawnSync("tailscale", ["serve", "--https=443", "off"], { timeout: 10_000 });
157
+ this.tailscaleOn = false;
158
+ }
159
+ if (this.httpsOn) {
160
+ await this.opts.disableHttps().catch(() => undefined);
161
+ this.httpsOn = false;
162
+ }
163
+ this.active = null;
164
+ }
165
+ }
166
+ //# sourceMappingURL=remote.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"remote.js","sourceRoot":"","sources":["../../server/src/remote.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,323 @@
1
+ import { createHash } from "node:crypto";
2
+ import { createWriteStream } from "node:fs";
3
+ import { mkdir, mkdtemp, rename, rm, stat } from "node:fs/promises";
4
+ import os from "node:os";
5
+ import path from "node:path";
6
+ import { pipeline } from "node:stream/promises";
7
+ import { Readable } from "node:stream";
8
+ import { promisify } from "node:util";
9
+ import { execFile } from "node:child_process";
10
+ const execFileAsync = promisify(execFile);
11
+ // ---------------------------------------------------------------------------
12
+ // Model catalog
13
+ // ---------------------------------------------------------------------------
14
+ const PARAKEET_DIR = "sherpa-onnx-nemo-parakeet-tdt-0.6b-v3-int8";
15
+ const KOKORO_DIR = "kokoro-int8-multi-lang-v1_0";
16
+ const PARAKEET_SPEC = {
17
+ dirName: PARAKEET_DIR,
18
+ url: "https://github.com/k2-fsa/sherpa-onnx/releases/download/asr-models/sherpa-onnx-nemo-parakeet-tdt-0.6b-v3-int8.tar.bz2",
19
+ sha256: "5793d0fd397c5778d2cf2126994d58e9d56b1be7c04d13c7a15bb1b4eafb16bf",
20
+ sizeBytes: 487_170_055,
21
+ requiredFiles: ["encoder.int8.onnx", "decoder.int8.onnx", "joiner.int8.onnx", "tokens.txt"],
22
+ };
23
+ const KOKORO_SPEC = {
24
+ dirName: KOKORO_DIR,
25
+ url: "https://github.com/k2-fsa/sherpa-onnx/releases/download/tts-models/kokoro-int8-multi-lang-v1_0.tar.bz2",
26
+ sha256: "4c3052abaa60943a341f193888cf6abd68787dae6ab8ae5c925a706caa247e4e",
27
+ sizeBytes: 132_303_094,
28
+ requiredFiles: ["model.int8.onnx", "voices.bin", "tokens.txt"],
29
+ };
30
+ // Kokoro v1.0 multi-lang speaker table (index == speaker id), extracted from
31
+ // the model's embedded `speaker2id` metadata. af_heart is speaker id 3.
32
+ export const KOKORO_VOICES = [
33
+ "af_alloy", "af_aoede", "af_bella", "af_heart", "af_jessica", "af_kore",
34
+ "af_nicole", "af_nova", "af_river", "af_sarah", "af_sky", "am_adam",
35
+ "am_echo", "am_eric", "am_fenrir", "am_liam", "am_michael", "am_onyx",
36
+ "am_puck", "am_santa", "bf_alice", "bf_emma", "bf_isabella", "bf_lily",
37
+ "bm_daniel", "bm_fable", "bm_george", "bm_lewis", "ef_dora", "em_alex",
38
+ "ff_siwis", "hf_alpha", "hf_beta", "hm_omega", "hm_psi", "if_sara",
39
+ "im_nicola", "jf_alpha", "jf_gongitsune", "jf_nezumi", "jf_tebukuro",
40
+ "jm_kumo", "pf_dora", "pm_alex", "pm_santa", "zf_xiaobei", "zf_xiaoni",
41
+ "zf_xiaoxiao", "zf_xiaoyi", "zm_yunjian", "zm_yunxi", "zm_yunxia",
42
+ "zm_yunyang", "em_santa",
43
+ ];
44
+ const DEFAULT_VOICE = "af_heart";
45
+ /** Parses a RIFF/WAVE buffer, locating the fmt and data chunks by walking
46
+ * the chunk list rather than assuming a fixed 44-byte header. */
47
+ export function parseWav(buffer) {
48
+ if (buffer.length < 12 || buffer.toString("ascii", 0, 4) !== "RIFF" || buffer.toString("ascii", 8, 12) !== "WAVE") {
49
+ throw new Error("speech: not a RIFF/WAVE buffer");
50
+ }
51
+ let offset = 12;
52
+ let fmt;
53
+ let data;
54
+ while (offset + 8 <= buffer.length) {
55
+ const chunkId = buffer.toString("ascii", offset, offset + 4);
56
+ const chunkSize = buffer.readUInt32LE(offset + 4);
57
+ const bodyStart = offset + 8;
58
+ if (chunkId === "fmt ") {
59
+ fmt = {
60
+ audioFormat: buffer.readUInt16LE(bodyStart),
61
+ channels: buffer.readUInt16LE(bodyStart + 2),
62
+ sampleRate: buffer.readUInt32LE(bodyStart + 4),
63
+ bitsPerSample: buffer.readUInt16LE(bodyStart + 14),
64
+ };
65
+ }
66
+ else if (chunkId === "data") {
67
+ data = { start: bodyStart, length: Math.min(chunkSize, buffer.length - bodyStart) };
68
+ }
69
+ // Chunks are word-aligned: a chunk with odd size has one pad byte.
70
+ offset = bodyStart + chunkSize + (chunkSize % 2);
71
+ }
72
+ if (!fmt)
73
+ throw new Error("speech: WAV missing fmt chunk");
74
+ if (!data)
75
+ throw new Error("speech: WAV missing data chunk");
76
+ if (fmt.audioFormat !== 1)
77
+ throw new Error(`speech: unsupported WAV audio format ${fmt.audioFormat} (expected PCM)`);
78
+ if (fmt.bitsPerSample !== 16)
79
+ throw new Error(`speech: unsupported WAV bit depth ${fmt.bitsPerSample} (expected 16)`);
80
+ const bytesPerSample = fmt.bitsPerSample / 8;
81
+ const sampleCount = Math.floor(data.length / bytesPerSample);
82
+ const samples = new Float32Array(sampleCount);
83
+ for (let i = 0; i < sampleCount; i++) {
84
+ const raw = buffer.readInt16LE(data.start + i * bytesPerSample);
85
+ samples[i] = raw < 0 ? raw / 32_768 : raw / 32_767;
86
+ }
87
+ return { sampleRate: fmt.sampleRate, channels: fmt.channels, bitsPerSample: fmt.bitsPerSample, samples };
88
+ }
89
+ /** Encodes mono Float32 samples in [-1, 1] into a complete 16-bit PCM WAV buffer. */
90
+ export function encodeWav(samples, sampleRate) {
91
+ const bytes = Buffer.alloc(44 + samples.length * 2);
92
+ bytes.write("RIFF", 0, "ascii");
93
+ bytes.writeUInt32LE(36 + samples.length * 2, 4);
94
+ bytes.write("WAVE", 8, "ascii");
95
+ bytes.write("fmt ", 12, "ascii");
96
+ bytes.writeUInt32LE(16, 16); // fmt chunk size
97
+ bytes.writeUInt16LE(1, 20); // PCM
98
+ bytes.writeUInt16LE(1, 22); // mono
99
+ bytes.writeUInt32LE(sampleRate, 24);
100
+ bytes.writeUInt32LE(sampleRate * 2, 28); // byte rate
101
+ bytes.writeUInt16LE(2, 32); // block align
102
+ bytes.writeUInt16LE(16, 34); // bits per sample
103
+ bytes.write("data", 36, "ascii");
104
+ bytes.writeUInt32LE(samples.length * 2, 40);
105
+ for (let i = 0; i < samples.length; i++) {
106
+ const clamped = Math.max(-1, Math.min(1, samples[i]));
107
+ bytes.writeInt16LE(Math.round(clamped * (clamped < 0 ? 32_768 : 32_767)), 44 + i * 2);
108
+ }
109
+ return bytes;
110
+ }
111
+ export function createSherpaEngine(options) {
112
+ const modelsDir = options?.modelsDir ??
113
+ path.join(process.env.XDG_CACHE_HOME || path.join(os.homedir(), ".cache"), "meldivo", "models");
114
+ const state = { ready: false, downloading: false };
115
+ let loadPromise = null;
116
+ async function importSherpa() {
117
+ try {
118
+ // sherpa-onnx-node ships no type declarations; the shape we rely on
119
+ // is captured locally in `SherpaModule` and asserted here.
120
+ // @ts-expect-error -- untyped module, resolved dynamically so a
121
+ // missing native binary throws a clear runtime error instead of
122
+ // failing at import time for callers that never need speech.
123
+ const imported = await import("sherpa-onnx-node");
124
+ // sherpa-onnx-node is CJS; Node's cjs-module-lexer can't statically
125
+ // synthesize all of its named exports, so fall back to `.default`
126
+ // (the raw module.exports object) when a named export is missing.
127
+ const mod = (imported.OfflineRecognizer ? imported : imported.default);
128
+ return mod;
129
+ }
130
+ catch (cause) {
131
+ throw new Error("speech: sherpa-onnx-node is not installed or its native binary is missing for this platform. " +
132
+ "Install the `sherpa-onnx-node` dependency to enable speech.", { cause });
133
+ }
134
+ }
135
+ async function ensureModel(spec, weightStart, weightSpan) {
136
+ const dest = path.join(modelsDir, spec.dirName);
137
+ const alreadyInstalled = await allFilesExist(dest, spec.requiredFiles);
138
+ if (alreadyInstalled)
139
+ return dest;
140
+ await mkdir(modelsDir, { recursive: true });
141
+ const tmpRoot = await mkdtemp(path.join(modelsDir, `.dl-${spec.dirName}-`));
142
+ try {
143
+ const archivePath = path.join(tmpRoot, "archive.tar.bz2");
144
+ await downloadWithProgress(spec.url, archivePath, (fraction) => {
145
+ state.progress = weightStart + fraction * weightSpan;
146
+ });
147
+ await verifySha256(archivePath, spec.sha256);
148
+ const extractDir = path.join(tmpRoot, "extract");
149
+ await mkdir(extractDir, { recursive: true });
150
+ await execFileAsync("tar", ["-xjf", archivePath, "-C", extractDir]);
151
+ const extractedRoot = path.join(extractDir, spec.dirName);
152
+ const finalExists = await allFilesExist(extractedRoot, spec.requiredFiles);
153
+ if (!finalExists)
154
+ throw new Error(`speech: extracted archive for ${spec.dirName} is missing expected files`);
155
+ // Atomic move into place. rename() across the same filesystem (both
156
+ // paths are under modelsDir) is atomic.
157
+ await rename(extractedRoot, dest).catch(async (err) => {
158
+ // Another concurrent caller may have already installed it.
159
+ if (err.code === "ENOTEMPTY" || err.code === "EEXIST")
160
+ return;
161
+ throw err;
162
+ });
163
+ return dest;
164
+ }
165
+ finally {
166
+ await rm(tmpRoot, { recursive: true, force: true }).catch(() => { });
167
+ }
168
+ }
169
+ async function load() {
170
+ if (loadPromise)
171
+ return loadPromise;
172
+ state.downloading = true;
173
+ state.progress = 0;
174
+ state.error = undefined;
175
+ loadPromise = (async () => {
176
+ try {
177
+ const sherpa = await importSherpa();
178
+ const numThreads = Math.min(4, os.cpus().length);
179
+ // Weight progress: parakeet is ~79% of total download bytes, kokoro ~21%.
180
+ const total = PARAKEET_SPEC.sizeBytes + KOKORO_SPEC.sizeBytes;
181
+ const parakeetSpan = PARAKEET_SPEC.sizeBytes / total;
182
+ const kokoroSpan = KOKORO_SPEC.sizeBytes / total;
183
+ const parakeetDir = await ensureModel(PARAKEET_SPEC, 0, parakeetSpan);
184
+ const kokoroDir = await ensureModel(KOKORO_SPEC, parakeetSpan, kokoroSpan);
185
+ state.progress = 1;
186
+ state.downloading = false;
187
+ const recognizer = await sherpa.OfflineRecognizer.createAsync({
188
+ featConfig: { sampleRate: 16_000, featureDim: 80 },
189
+ modelConfig: {
190
+ transducer: {
191
+ encoder: path.join(parakeetDir, "encoder.int8.onnx"),
192
+ decoder: path.join(parakeetDir, "decoder.int8.onnx"),
193
+ joiner: path.join(parakeetDir, "joiner.int8.onnx"),
194
+ },
195
+ tokens: path.join(parakeetDir, "tokens.txt"),
196
+ modelType: "nemo_transducer",
197
+ numThreads,
198
+ provider: "cpu",
199
+ debug: false,
200
+ },
201
+ });
202
+ const tts = await sherpa.OfflineTts.createAsync({
203
+ model: {
204
+ kokoro: {
205
+ model: path.join(kokoroDir, "model.int8.onnx"),
206
+ voices: path.join(kokoroDir, "voices.bin"),
207
+ tokens: path.join(kokoroDir, "tokens.txt"),
208
+ dataDir: path.join(kokoroDir, "espeak-ng-data"),
209
+ lexicon: path.join(kokoroDir, "lexicon-us-en.txt"),
210
+ lang: "en-us",
211
+ },
212
+ },
213
+ maxNumSentences: 1,
214
+ numThreads,
215
+ provider: "cpu",
216
+ });
217
+ state.ready = true;
218
+ return { recognizer, tts };
219
+ }
220
+ catch (err) {
221
+ state.downloading = false;
222
+ state.error = err instanceof Error ? err.message : String(err);
223
+ loadPromise = null; // allow retry on next call
224
+ throw err;
225
+ }
226
+ })();
227
+ return loadPromise;
228
+ }
229
+ function rejectIfAborted(signal) {
230
+ if (signal?.aborted)
231
+ throw new DOMException("speech: aborted", "AbortError");
232
+ }
233
+ return {
234
+ defaultVoice: DEFAULT_VOICE,
235
+ async transcribe(wav, signal) {
236
+ rejectIfAborted(signal);
237
+ const { recognizer } = await load();
238
+ rejectIfAborted(signal);
239
+ const decoded = parseWav(wav);
240
+ const stream = recognizer.createStream();
241
+ stream.acceptWaveform({ samples: decoded.samples, sampleRate: decoded.sampleRate });
242
+ const result = await abortable(recognizer.decodeAsync(stream), signal);
243
+ return result.text;
244
+ },
245
+ async synthesize(text, voice, signal) {
246
+ rejectIfAborted(signal);
247
+ const { tts } = await load();
248
+ rejectIfAborted(signal);
249
+ const sid = KOKORO_VOICES.indexOf(voice);
250
+ if (sid < 0)
251
+ throw new Error(`speech: unknown voice "${voice}"`);
252
+ const audio = await abortable(tts.generateAsync({ text, sid, speed: 1.0 }), signal);
253
+ return encodeWav(audio.samples, audio.sampleRate);
254
+ },
255
+ voices() {
256
+ return [...KOKORO_VOICES];
257
+ },
258
+ status() {
259
+ return {
260
+ ready: state.ready,
261
+ downloading: state.downloading,
262
+ progress: state.progress,
263
+ error: state.error,
264
+ };
265
+ },
266
+ async warmup() {
267
+ await load();
268
+ },
269
+ };
270
+ }
271
+ function abortable(promise, signal) {
272
+ if (!signal)
273
+ return promise;
274
+ if (signal.aborted)
275
+ return Promise.reject(new DOMException("speech: aborted", "AbortError"));
276
+ return new Promise((resolve, reject) => {
277
+ const onAbort = () => reject(new DOMException("speech: aborted", "AbortError"));
278
+ signal.addEventListener("abort", onAbort, { once: true });
279
+ promise.then((value) => {
280
+ signal.removeEventListener("abort", onAbort);
281
+ resolve(value);
282
+ }, (err) => {
283
+ signal.removeEventListener("abort", onAbort);
284
+ reject(err);
285
+ });
286
+ });
287
+ }
288
+ async function allFilesExist(dir, files) {
289
+ for (const file of files) {
290
+ try {
291
+ await stat(path.join(dir, file));
292
+ }
293
+ catch {
294
+ return false;
295
+ }
296
+ }
297
+ return true;
298
+ }
299
+ async function downloadWithProgress(url, destPath, onProgress) {
300
+ const response = await fetch(url, { redirect: "follow" });
301
+ if (!response.ok || !response.body) {
302
+ throw new Error(`speech: failed to download ${url}: HTTP ${response.status}`);
303
+ }
304
+ const total = Number(response.headers.get("content-length") ?? 0);
305
+ let received = 0;
306
+ const nodeStream = Readable.fromWeb(response.body);
307
+ nodeStream.on("data", (chunk) => {
308
+ received += chunk.length;
309
+ if (total > 0)
310
+ onProgress(received / total);
311
+ });
312
+ await pipeline(nodeStream, createWriteStream(destPath));
313
+ }
314
+ async function verifySha256(filePath, expected) {
315
+ const hash = createHash("sha256");
316
+ const { createReadStream } = await import("node:fs");
317
+ await pipeline(createReadStream(filePath), hash);
318
+ const actual = hash.digest("hex");
319
+ if (actual !== expected) {
320
+ throw new Error(`speech: checksum mismatch for ${path.basename(filePath)}: expected ${expected}, got ${actual}`);
321
+ }
322
+ }
323
+ //# sourceMappingURL=speech.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"speech.js","sourceRoot":"","sources":["../../server/src/speech.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,91 @@
1
+ import { chmod, mkdir, readFile, rename, writeFile } from "node:fs/promises";
2
+ import { homedir } from "node:os";
3
+ import path from "node:path";
4
+ function emptyState() {
5
+ return { conversations: {}, voiceFork: {} };
6
+ }
7
+ /**
8
+ * Small on-disk store for the continuation state that lets voice turns land
9
+ * back in the same session across requests: which session a `new`/`quick`
10
+ * conversation created, and which fork a currently-open session's voice
11
+ * turns are writing into. Mutations are serialized through a queue so
12
+ * concurrent turns cannot interleave writes to the file.
13
+ */
14
+ export class SessionStateStore {
15
+ dir;
16
+ data = emptyState();
17
+ loaded = false;
18
+ queue = Promise.resolve();
19
+ constructor(dir) {
20
+ this.dir = dir;
21
+ }
22
+ get filePath() {
23
+ return path.join(this.dir, "sessions.json");
24
+ }
25
+ async ensureLoaded() {
26
+ if (this.loaded)
27
+ return;
28
+ try {
29
+ const raw = await readFile(this.filePath, "utf8");
30
+ const parsed = JSON.parse(raw);
31
+ this.data = {
32
+ conversations: parsed.conversations && typeof parsed.conversations === "object" ? parsed.conversations : {},
33
+ voiceFork: parsed.voiceFork && typeof parsed.voiceFork === "object" ? parsed.voiceFork : {},
34
+ };
35
+ }
36
+ catch {
37
+ this.data = emptyState();
38
+ }
39
+ this.loaded = true;
40
+ }
41
+ async persist() {
42
+ await mkdir(this.dir, { recursive: true, mode: 0o700 });
43
+ const tmpPath = path.join(this.dir, `.sessions.json.tmp-${process.pid}-${Date.now()}`);
44
+ await writeFile(tmpPath, JSON.stringify(this.data, null, 2), { mode: 0o600 });
45
+ await rename(tmpPath, this.filePath);
46
+ await chmod(this.filePath, 0o600).catch(() => undefined);
47
+ }
48
+ enqueue(task) {
49
+ const result = this.queue.then(task, task);
50
+ this.queue = result.then(() => undefined, () => undefined);
51
+ return result;
52
+ }
53
+ async getConversation(conversationId) {
54
+ return this.enqueue(async () => {
55
+ await this.ensureLoaded();
56
+ return this.data.conversations[conversationId];
57
+ });
58
+ }
59
+ async setConversation(conversationId, record) {
60
+ await this.enqueue(async () => {
61
+ await this.ensureLoaded();
62
+ this.data.conversations[conversationId] = record;
63
+ await this.persist();
64
+ });
65
+ }
66
+ async getFork(key) {
67
+ return this.enqueue(async () => {
68
+ await this.ensureLoaded();
69
+ return this.data.voiceFork[key];
70
+ });
71
+ }
72
+ async setFork(key, forkId) {
73
+ await this.enqueue(async () => {
74
+ await this.ensureLoaded();
75
+ this.data.voiceFork[key] = forkId;
76
+ await this.persist();
77
+ });
78
+ }
79
+ /** All currently-tracked fork session ids, used to hide forks from the session list. */
80
+ async allForkIds() {
81
+ return this.enqueue(async () => {
82
+ await this.ensureLoaded();
83
+ return new Set(Object.values(this.data.voiceFork));
84
+ });
85
+ }
86
+ }
87
+ export function defaultStateDir() {
88
+ const base = process.env.XDG_STATE_HOME || path.join(homedir(), ".local", "state");
89
+ return path.join(base, "meldivo");
90
+ }
91
+ //# sourceMappingURL=state.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state.js","sourceRoot":"","sources":["../../server/src/state.ts"],"names":[],"mappings":"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"}
Binary file
@@ -0,0 +1 @@
1
+ function e(t){return t&&t.__esModule&&Object.prototype.hasOwnProperty.call(t,"default")?t.default:t}export{e as g};